private string GetColor(byte[] image) { // Lock Bits would be quicker TypeConverter tc = TypeDescriptor.GetConverter(typeof(Bitmap)); Bitmap bitmap = (Bitmap)tc.ConvertFrom(image); using (bitmap) { List <Color> colors = new List <Color>(bitmap.Width * bitmap.Height); for (int x = 0; x < bitmap.Width; x++) { for (int y = 0; y < bitmap.Height; y++) { colors.Add(bitmap.GetPixel(x, y)); } } List <Color> filtered = colors .Where(c => (KCluster.EuclideanDistance(c, Color.Black) >= 200) && (KCluster.EuclideanDistance(c, Color.White) >= 200)) .ToList(); KMeansClusteringCalculator clustering = new KMeansClusteringCalculator(); IList <Color> dominantColours = clustering.Calculate(10, filtered, 20.0d); int index = closestColor(dominantColours.Skip(1).ToList(), dominantColours.First()); if (index == 0) { return(ToRGBAString(dominantColours[2])); } return(ToRGBAString(dominantColours[1])); } }
/// <summary> /// Calculates the <paramref name="k"/> clusters for <paramref name="colours"/>. Iterations continues until clusters move by less than <paramref name="threshold"/> /// </summary> /// <param name="k">The number of clusters to calculate (eg. The number of results to return)</param> /// <param name="colours">The list of colours to calculate <paramref name="k"/> for</param> /// <param name="threshold">Threshold for iteration. A lower value should produce more correct results but requires more iterations and for some <paramref name="colours"/> may never produce a result</param> /// <returns>The <paramref name="k"/> colours for the image in descending order from most common to least common</returns> public IList <Color> Calculate(int k, IList <Color> colours, double threshold = 0.0d) { List <KCluster> clusters = new List <KCluster>(); // 1. Initialisation. // Create K clusters with a random data point from our input. // We make sure not to use the same index twice for two inputs Random random = new Random(); List <int> usedIndexes = new List <int>(); while (clusters.Count < k) { int index = random.Next(0, colours.Count); if (usedIndexes.Contains(index) == true) { continue; } usedIndexes.Add(index); KCluster cluster = new KCluster(colours[index]); clusters.Add(cluster); } bool updated = false; do { updated = false; // 2. For each colour in our input determine which cluster's centre point is the closest and add the colour to the cluster foreach (Color colour in colours) { double shortestDistance = float.MaxValue; KCluster closestCluster = null; foreach (KCluster cluster in clusters) { double distance = cluster.DistanceFromCentre(colour); if (distance < shortestDistance) { shortestDistance = distance; closestCluster = cluster; } } closestCluster.Add(colour); } // 3. Recalculate the clusters centre. foreach (KCluster cluster in clusters) { if (cluster.RecalculateCentre(threshold) == true) { updated = true; } } // 4. If we updated any centre point this iteration then iterate again } while (updated == true); return(clusters.OrderByDescending(c => c.PriorCount).Select(c => c.Centre).ToList()); }