コード例 #1
0
        public static ConfusionLine Get(ColorDistanceType type)
        {
            switch (type)
            {
            case ColorDistanceType.Protanope:
                return(new ConfusionLine()
                {
                    X = 0.7465,
                    Y = 0.2535,
                    M = 1.273463,
                    Yint = -0.073894
                });

            case ColorDistanceType.Deuteranope:
                return(new ConfusionLine()
                {
                    X = 1.4,
                    Y = -0.4,
                    M = 0.968437,
                    Yint = 0.003331
                });

            case ColorDistanceType.Tritanope:
                return(new ConfusionLine()
                {
                    X = 0.1748,
                    Y = 0.0,
                    M = 0.062921,
                    Yint = 0.292119
                });

            default:
                return(new ConfusionLine());
            }
        }
コード例 #2
0
 private double DistanceColorBlind(LabColor lab1,
     LabColor lab2,
     ColorDistanceType type)
 {
     var lab1Cb = Simulate(lab1, type);
     var lab2Cb = Simulate(lab2, type);
     return CmcDistance(lab1Cb, lab2Cb);
 }
コード例 #3
0
        private double GetColorDistance(
            LabColor lab1,
            LabColor lab2,
            ColorDistanceType type = ColorDistanceType.Default)
        {

            switch (type)
            {
                case ColorDistanceType.Default:
                case ColorDistanceType.Euclidian:
                    return EuclidianDistance(lab1, lab2);
                case ColorDistanceType.CMC:
                    return CmcDistance(lab1, lab2);
                case ColorDistanceType.Compromise:
                    return CompromiseDistance(lab1, lab2);
                default:
                    return DistanceColorBlind(lab1, lab2, type);
            }
        }
コード例 #4
0
        // WARNING: may return [NaN, NaN, NaN]
        private LabColor Simulate(LabColor lab, ColorDistanceType type, int amount = 1)
        {

            // Cache
            var key = string.Format("{0}-{1}{2}", lab, type, amount);
            if (this.simulateCache.ContainsKey(key))
            {
                return this.simulateCache[key];
            }

            // Get data from type
            var confuse = ConfusionLine.Get(type);

            // Code adapted from http://galacticmilk.com/labs/Color-Vision/Javascript/Color.Vision.Simulate.js
            var color = lab.ToRgb();
            var sr = color.R;
            var sg = color.G;
            var sb = color.B;
            double dr = sr; // destination color
            double dg = sg;
            double db = sb;
            // Convert source color into XYZ color space
            var pow_r = Math.Pow(sr, 2.2);
            var pow_g = Math.Pow(sg, 2.2);
            var pow_b = Math.Pow(sb, 2.2);
            var X = pow_r * 0.412424 + pow_g * 0.357579 + pow_b * 0.180464; // RGB->XYZ (sRGB:D65)
            var Y = pow_r * 0.212656 + pow_g * 0.715158 + pow_b * 0.0721856;
            var Z = pow_r * 0.0193324 + pow_g * 0.119193 + pow_b * 0.950444;
            // Convert XYZ into xyY Chromacity Coordinates (xy) and Luminance (Y)
            var chroma_x = X / (X + Y + Z);
            var chroma_y = Y / (X + Y + Z);
            // Generate the "Confusion Line" between the source color and the Confusion Point
            var m = (chroma_y - confuse.Y) / (chroma_x - confuse.X); // slope of Confusion Line
            var yint = chroma_y - chroma_x * m; // y-intercept of confusion line (x-intercept = 0.0)
                                                // How far the xy coords deviate from the simulation
            var deviate_x = (confuse.Yint - yint) / (m - confuse.M);
            var deviate_y = (m * deviate_x) + yint;
            // Compute the simulated color's XYZ coords
            X = deviate_x * Y / deviate_y;
            Z = (1.0 - (deviate_x + deviate_y)) * Y / deviate_y;
            // Neutral grey calculated from luminance (in D65)
            var neutral_X = 0.312713 * Y / 0.329016;
            var neutral_Z = 0.358271 * Y / 0.329016;
            // Difference between simulated color and neutral grey
            var diff_X = neutral_X - X;
            var diff_Z = neutral_Z - Z;
            var diff_r = diff_X * 3.24071 + diff_Z * -0.498571; // XYZ->RGB (sRGB:D65)
            var diff_g = diff_X * -0.969258 + diff_Z * 0.0415557;
            var diff_b = diff_X * 0.0556352 + diff_Z * 1.05707;
            // Convert to RGB color space
            dr = X * 3.24071 + Y * -1.53726 + Z * -0.498571; // XYZ->RGB (sRGB:D65)
            dg = X * -0.969258 + Y * 1.87599 + Z * 0.0415557;
            db = X * 0.0556352 + Y * -0.203996 + Z * 1.05707;
            // Compensate simulated color towards a neutral fit in RGB space
            var fit_r = ((dr < 0.0 ? 0.0 : 1.0) - dr) / diff_r;
            var fit_g = ((dg < 0.0 ? 0.0 : 1.0) - dg) / diff_g;
            var fit_b = ((db < 0.0 ? 0.0 : 1.0) - db) / diff_b;
            var adjust = Math.Max( // highest value
                Math.Max(
                    (fit_r > 1.0 || fit_r < 0.0) ? 0.0 : fit_r,
                    (fit_g > 1.0 || fit_g < 0.0) ? 0.0 : fit_g),
                (fit_b > 1.0 || fit_b < 0.0) ? 0.0 : fit_b
            );
            // Shift proportional to the greatest shift
            dr = dr + (adjust * diff_r);
            dg = dg + (adjust * diff_g);
            db = db + (adjust * diff_b);
            // Apply gamma correction
            dr = Math.Pow(dr, 1.0 / 2.2);
            dg = Math.Pow(dg, 1.0 / 2.2);
            db = Math.Pow(db, 1.0 / 2.2);
            // Anomylize colors
            dr = sr * (1.0 - amount) + dr * amount;
            dg = sg * (1.0 - amount) + dg * amount;
            db = sb * (1.0 - amount) + db * amount;
            var dcolor = Color.FromArgb(
                (int)Math.Round(dr),
                (int)Math.Round(dg),
                (int)Math.Round(db));
            var result = dcolor.ToLab();
            this.simulateCache[key] = result;
            return result;
        }
コード例 #5
0
        public Color[] GenerateKMean(int colorsCount,
            PaletteOptions options = null,
            ColorDistanceType distanceType = ColorDistanceType.Default)
        {
            options = options ?? PaletteOptions.GetDefault();
            var kMeans = new List<LabColor>();
            for (var i = 0; i < colorsCount; i++)
            {
                var lab = LabColor.GetRandom();
                while (!lab.IsValidRgb())
                {
                    lab = LabColor.GetRandom();
                }
                kMeans.Add(lab);
            }

            var colorSamples = GenerateColorSamples(options);

            // Steps
            var steps = options.Quality;
            var samplesCount = colorSamples.Count;
            var samplesClosest = new int?[samplesCount];
            while (steps-- > 0)
            {
                // kMeans -> Samples Closest
                for (var i = 0; i < samplesCount; i++)
                {
                    var lab = colorSamples[i];
                    var minDistance = double.MaxValue;
                    for (var j = 0; j < kMeans.Count; j++)
                    {
                        var kMean = kMeans[j];
                        var distance = GetColorDistance(lab, kMean, distanceType);
                        if (distance < minDistance)
                        {
                            minDistance = distance;
                            samplesClosest[i] = j;
                        }
                    }
                }
                // Samples -> kMeans
                var freeColorSamples = colorSamples
                    .Select((lab) => (LabColor)lab.Clone()) // todo do I really need clone each item?
                    .ToList();
                for (var j = 0; j < kMeans.Count; j++)
                {
                    var count = 0;
                    var candidateKMean = new LabColor();
                    for (var i = 0; i < colorSamples.Count; i++)
                    {
                        if (samplesClosest[i] == j)
                        {
                            count++;
                            var color = colorSamples[i];
                            candidateKMean.L += color.L;
                            candidateKMean.A += color.A;
                            candidateKMean.B += color.B;
                        }
                    }
                    if (count != 0)
                    {
                        candidateKMean.L /= count;
                        candidateKMean.A /= count;
                        candidateKMean.B /= count;
                    }

                    if (count != 0 && 
                        candidateKMean.IsValidRgb() &&
                        options.ValidateColor(candidateKMean))
                    {
                        kMeans[j] = candidateKMean;
                    }
                    else
                    {
                        // The candidate kMean is out of the boundaries of the color space, or unfound.
                        if (freeColorSamples.Count > 0)
                        {
                            // We just search for the closest FREE color of the candidate kMean
                            var minDistance = double.MaxValue;
                            var closest = -1;
                            for (var i = 0; i < freeColorSamples.Count; i++)
                            {
                                var distance = GetColorDistance(freeColorSamples[i], candidateKMean, distanceType);
                                if (distance < minDistance)
                                {
                                    minDistance = distance;
                                    closest = i;
                                }
                            }
                            kMeans[j] = colorSamples[closest];
                        }
                        else
                        {
                            // Then we just search for the closest color of the candidate kMean
                            var minDistance = double.MaxValue;
                            var closest = -1;
                            for (var i = 0; i < colorSamples.Count; i++)
                            {
                                var distance = GetColorDistance(colorSamples[i], candidateKMean, distanceType);
                                if (distance < minDistance)
                                {
                                    minDistance = distance;
                                    closest = i;
                                }
                            }
                            kMeans[j] = colorSamples[closest];
                        }
                    }
                    var baseColor = kMeans[j];
                    freeColorSamples = freeColorSamples
                        .Where((lab) => !lab.Equals(baseColor))
                        .ToList();
                }
            }
            return kMeans.Select((lab) => lab.ToRgb()).ToArray();
        }
コード例 #6
0
        public Color[] GenerateForce(int colorsCount,
            PaletteOptions options = null,
            ColorDistanceType distanceType = ColorDistanceType.Default)
        {
            var random = new Random();
            var colors = new LabColor[colorsCount];
            options = options ?? PaletteOptions.GetDefault();
            // Init
            for (var i = 0; i < colorsCount; i++)
            {
                // Find a valid Lab color
                var color = LabColor.GetRandom();
                while (color.IsValidRgb() || options.ValidateColor(color))
                {
                    color = LabColor.GetRandom();
                }
                colors[i] = color;
            }

            // Force vector: repulsion
            var repulsion = 100;
            var speed = 100;
            var steps = options.Quality * 20;
            var vectors = new LabVector[colorsCount];
            while (steps-- > 0)
            {
                // Init
                for (var i = 0; i < colors.Length; i++)
                {
                    vectors[i] = new LabVector();
                }
                // Compute Force
                for (var i = 0; i < colors.Length; i++)
                {
                    var colorA = colors[i];
                    for (var j = 0; j < i; j++)
                    {
                        var colorB = colors[j];

                        // repulsion force
                        var dl = colorA.L - colorB.L;
                        var da = colorA.A - colorB.A;
                        var db = colorA.B - colorB.B;
                        var d = GetColorDistance(colorA, colorB, distanceType);
                        if (d > 0)
                        {
                            var force = repulsion / Math.Pow(d, 2);

                            vectors[i].dL += dl * force / d;
                            vectors[i].dA += da * force / d;
                            vectors[i].dB += db * force / d;

                            vectors[j].dL -= dl * force / d;
                            vectors[j].dA -= da * force / d;
                            vectors[j].dB -= db * force / d;
                        }
                        else
                        {
                            // Jitter
                            vectors[j].dL += 2 - 4 * random.NextDouble();
                            vectors[j].dA += 2 - 4 * random.NextDouble();
                            vectors[j].dB += 2 - 4 * random.NextDouble();
                        }
                    }
                }
                // Apply Force
                for (var i = 0; i < colors.Length; i++)
                {
                    var color = colors[i];
                    var displacement = speed * vectors[i].Magnitude; ;
                    if (displacement > 0)
                    {
                        var ratio = speed * Math.Min(0.1, displacement) / displacement;
                        var l = color.L + vectors[i].dL * ratio;
                        var a = color.A + vectors[i].dA * ratio;
                        var b = color.B + vectors[i].dB * ratio;
                        var candidateLab = new LabColor(l, a, b);
                        if (candidateLab.IsValidRgb() && options.ValidateColor(candidateLab))
                        {
                            colors[i] = candidateLab;
                        }
                    }
                }
            }

            return colors.Select((lab) => lab.ToRgb()).ToArray();
        }