tags:

views:

144

answers:

5

I want to retrieve the majority color in a background image in .NET. Is it possible?

+6  A: 

You can loop through all the pixels in the image and use the getPixel method to determine the RGB value. You can then have a dictionary that store the ARGB value along with a count. You can then see which ARGB value is present most in the image.

var list = new Dictionary<int, int>();
Bitmap myImage = (Bitmap)Bitmap.FromFile("C:/test.jpeg");
for (int x = 0; x < myImage.Width; x++)
{
    for (int y = 0; y < myImage.Height; y++)
    {
        int rgb = myImage.GetPixel(x, y).ToArgb();
        if (!list.ContainsKey(rgb))
            list.Add(rgb, 1);
        else
            list[rgb]++;
    }
}

As pointed out this has no sympathy for similar colors. If you wanted a more 'general' majority color you could have a threshold on similarity. e.g. rather than:

if (!list.ContainsKey(rgb))
        list.Add(rgb, 1);
    else
        list[rgb]++;

you could do:

var added = false;
for (int i = 0; i > 10; i++)
{
    if (list.ContainsKey(rgb+i))
    {
        list[rgb+i]++;
        added = true;
        break;
    }
    if (list.ContainsKey(rgb-i))
    {
        list[rgb-i]++;
        added = true;
        break;
    }
}
if(!added)
    list.Add(rgb, 1);

You could up the threshold of 10 to whatever you needed.

CeejeeB
If this is a "true color" jpeg... that could be tons of values in the dictionary with very few counts per key.
Matthew Whited
Agreed. You'll need some kind of binning or grouping to work decently. With this one you could have 2 perfectly red pixels and a ton of subtle variants of black and come out with red as the majority color.
RandomEngy
Well technically speaking red would then be the majority color.
CeejeeB
@Ceejeeb... it depends on what counts as the majority color. If it was general color then black should be the majority in his example.
Matthew Whited
+4  A: 

You may also find this Algorithm Challenge on Stack Overflow useful:

http://stackoverflow.com/questions/1106190/algorithm-challenge-generate-color-scheme-from-an-image

Also consider creating a histogram of the image - and taking the highest valued color as the 'majority color': http://www.phpclasses.org/browse/file/15953.html

Seidr
+3  A: 

Assuming you characterize each pixel's color using RGB (versus, say, CMYK), you could build a 3d array (one dimension each for R, G and B). Then decide on the # of bins that you want to have in each dimensions - the more bins, the greater the differentiation you are making between similar hues.

Once that is done, just iterate through a bitmap representation of your image summing the # pixels that falls into each of the cells in you 3d array. The cell with the highest sum will be the predominant color.

You probably want to make your algorithm easily configurable for the # bins in each dimension so that you can adjust how much it discriminates between similar colors.

image = new Bitmap("C:\\test.bmp", true);
int x, y;
// Loop through the images pixels to product 3d histogram
for(x=0; x<image.Width; x++)
{
   for(y=0; y<image.Height; y++)
   {
      Color pixelColor = image.GetPixel(x, y);

      // Increment color count in appropriate cell of your 3d histogram here
      ...
   }
}
Canoehead
+4  A: 

This will return the average color for the image.

static Color AverageColor(string fileName)
{
    using (var bmp = new Bitmap(fileName))
    {
        int width = bmp.Width;
        int height = bmp.Height;
        int red = 0;
        int green = 0;
        int blue = 0;
        int alpha = 0;
        for (int x = 0; x < width; x++)
            for (int y = 0; y < height; y++)
            {
                var pixel = bmp.GetPixel(x, y);
                red += pixel.R;
                green += pixel.G;
                blue += pixel.B;
                alpha += pixel.A;
            }

        Func<int, int> avg = c => c / (width * height);

        red = avg(red);
        green = avg(green);
        blue = avg(blue);
        alpha = avg(alpha);

        var color = Color.FromArgb(alpha, red, green, blue);

        return color;
    }
}
Matthew Whited
I don't think the result qualifies as "majority color" as there might not be even one pixel that is average color.
Kaniu
Very true... and that is why I asked for more detail in my comment under the question. No response so this is my entry.
Matthew Whited
+2  A: 

This will return the average color for the image using unsafe pointer access. Note: code is only adapted for 24bppRgb, could be adapted for other pixel formats.

    unsafe static Color GetColor(string filename)
    {
        using (var image = (Bitmap)Bitmap.FromFile(filename))
        {
            if (image.PixelFormat != PixelFormat.Format24bppRgb) throw new NotSupportedException(String.Format("Unsupported pixel format: {0}", image.PixelFormat));

            var pixelSize = 3;
            var bounds = new Rectangle(0, 0, image.Width, image.Height);
            var data = image.LockBits(bounds, ImageLockMode.ReadOnly, image.PixelFormat);

            long r = 0;
            long g = 0;
            long b = 0;

            for (int y = 0; y < data.Height; ++y)
            {
                byte* row = (byte*)data.Scan0 + (y * data.Stride);
                for (int x = 0; x < data.Width; ++x)
                {
                    var pos = x * pixelSize;
                    b += row[pos];
                    g += row[pos + 1];
                    r += row[pos + 2];
                }
            }

            r = r / (data.Width * data.Height);
            g = g / (data.Width * data.Height);
            b = b / (data.Width * data.Height);
            image.UnlockBits(data);

            return Color.FromArgb((int)r, (int)g, (int)b);
        }
    }

gist: http://gist.github.com/349210

Joseph Kingry
+1 for performance. Calling GetPixel on every single pixel is extremely slow.
RandomEngy
Extremely slow is a bit of an exaggeration. I was able to use the GetPixel method to analyse video frames at 15fps 800x600. So if the poster just needs to analyse a single image GetPixel is more than adequate.
CeejeeB