// Shrink the color palette. public static List <RGB5> ShrinkColorPalette(int numCols, List <RGB5> colors, out List <int> closestIndices) { //Run the algorithm. int bucketsFilled = 1; // We must convert to CIELAB first as RGB sucks at representing what the human eye sees. List <CIELAB> cols = new List <CIELAB>(); foreach (var c in colors) { cols.Add(new CIELAB(c)); } List <List <CIELAB> > buckets = new List <List <CIELAB> >(); buckets.Add(cols); // Run until we get the desired number of colors. What we do here is split the bucket with the most variance. while (bucketsFilled < numCols) { List <CIELAB> largestBucketVar = null; double largestVar = double.MinValue; foreach (var b in buckets) { double variance = CIELAB.CalculateVariance(b); if (variance >= largestVar) { largestBucketVar = b; largestVar = variance; } } buckets.Remove(largestBucketVar); List <CIELAB> v1, v2; CIELAB.SplitBucket(largestBucketVar, out v1, out v2); buckets.Add(v1); buckets.Add(v2); bucketsFilled++; } // Convert each bucket into RGB5. List <RGB5> outputRGB = new List <RGB5>(); List <CIELAB> outputCIE = new List <CIELAB>(); foreach (var b in buckets) { var avg = CIELAB.AverageColor(b); outputCIE.Add(avg); outputRGB.Add(avg.ToRGB5()); } // Get closest value for RGB indices. closestIndices = new List <int>(); for (int i = 0; i < cols.Count; i++) { closestIndices.Add(cols[i].ClosestColorIndex(outputCIE)); } // Return the final buckets. return(outputRGB); }
// The variance of a group of CIELab colors. public static double CalculateVariance(List <CIELAB> colors) { double ret = 0; CIELAB avg = AverageColor(colors); foreach (var c in colors) { ret += c.DeltaESquared(avg); // (x - xBar)^2 } return(ret / (colors.Count - 1)); // Use sample variance instead of population. Stats! }
// Distance squared public double DeltaESquared(CIELAB other) { var deltaL = L - other.L; var deltaA = A - other.A; var deltaB = B - other.B; var c1 = Math.Sqrt(A * A + B * B); var c2 = Math.Sqrt(other.A * other.A + other.B * other.B); var deltaC = c1 - c2; var deltaH = deltaA * deltaA + deltaB * deltaB - deltaC * deltaC; deltaH = deltaH < 0 ? 0 : Math.Sqrt(deltaH); var sc = 1.0 + 0.045 * c1; var sh = 1.0 + 0.015 * c1; var deltaLKlsl = deltaL / (1.0); var deltaCkcsc = deltaC / (sc); var deltaHkhsh = deltaH / (sh); var i = deltaLKlsl * deltaLKlsl + deltaCkcsc * deltaCkcsc + deltaHkhsh * deltaHkhsh; return(i); }