Calculate Dominant Color of Bitmap

The code section below is an example of how to calculate the dominant color in a bitmap. The code uses euclidean distance calculations for each color channel in each pixel. Then it sorts the pixels to find the color which is the closest to all other colors.

To improve the output I take to top %2 of colors and average them. Also the Alpha channel is treated differently though it could be changed to use the same distance logic.

The results are much better than the average color calculation. Though it is more computationally expensive so it will take longer to complete.

C#
<code>
 
public static Color CalculateDominantColor(BitmapSource source)
        {
            if (source.Format.BitsPerPixel != 32 || source.Format != PixelFormats.Bgra32)
                throw new ApplicationException("expected 32bit image");
 
 
            Dictionary<Color, double> colorDist = new Dictionary<Color, double>();
 
            System.Windows.Size sz = new System.Windows.Size(source.PixelWidth, source.PixelHeight);
 
            //read bitmap 
            int pixelsSz = (int)sz.Width * (int)sz.Height * (source.Format.BitsPerPixel / 8);
            int stride = ((int)sz.Width * source.Format.BitsPerPixel + 7) / 8;
            int pixelBytes = (source.Format.BitsPerPixel / 8);
 
            byte[] pixels = new byte[pixelsSz];
            source.CopyPixels(pixels, stride, 0);
 
            const int alphaThershold = 10;
            UInt64 pixelCount = 0;
            UInt64 avgAlpha = 0;
 
            for (int y = 0; y < sz.Height; y++)
            {
                for (int x = 0; x < sz.Width; x++)
                {
                    int index = (int)((y * sz.Width) + x) * (pixelBytes);
                    byte r1, g1, b1, a1; r1 = g1 = b1 = a1 = 0;
                    a1 = pixels[index + 3];
                    r1 = pixels[index + 2];
                    g1 = pixels[index + 1];
                    b1 = pixels[index];
 
                    if (a1 <= alphaThershold)
                        continue; //ignore
 
                    pixelCount++;
                    avgAlpha += (UInt64)a1;
 
                    Color cl = Color.FromArgb(0, r1, g1, b1);
                    double dist = 0;
                    if (!colorDist.ContainsKey(cl))
                    {
                        colorDist.Add(cl, 0);
 
                        for (int y2 = 0; y2 < sz.Height; y2++)
                        {
                            for (int x2 = 0; x2 < sz.Width; x2++)
                            {
                                int index2 = (int)(y2 * sz.Width) + x2;
                                byte r2, g2, b2, a2; r2 = g2 = b2 = a2 = 0;
                                a2 = pixels[index2 + 3];
                                r2 = pixels[index2 + 2];
                                g2 = pixels[index2 + 1];
                                b2 = pixels[index2];
 
                                if (a2 <= alphaThershold)
                                    continue; //ignore
 
                                dist += Math.Sqrt(Math.Pow(r2 - r1, 2) + 
                                                  Math.Pow(g2 - g1, 2) + 
                                                  Math.Pow(b2 - b1, 2));
                            }
                        }
 
                        colorDist[cl] = dist;
                    }
                }
            }
 
            //clamp alpha
            avgAlpha = avgAlpha / pixelCount;
            if (avgAlpha >= (255 - alphaThershold))
                avgAlpha = 255;
 
            //take weighted average of top 2% of colors         
            var clrs = (from entry in colorDist
                        orderby entry.Value ascending
                        select new { Color = entry.Key, Dist = 1.0/Math.Max(1,entry.Value) }).ToList().Take( Math.Max(1, (int)(colorDist.Count * 0.02)) );
 
            double sumDist = clrs.Sum(x => x.Dist);
            Color result = Color.FromArgb((byte)avgAlpha,
                                          (byte)(clrs.Sum(x => x.Color.R * x.Dist) / sumDist),
                                          (byte)(clrs.Sum(x => x.Color.G * x.Dist) / sumDist),
                                          (byte)(clrs.Sum(x => x.Color.B * x.Dist) / sumDist));
 
            return result;
        }
 
</code>

Comments (0)

› No comments yet.

Pingbacks (0)

› No pingbacks yet.