简体   繁体   中英

How do I get the background color of Bitmap Image in c#?

I have the following code-

Bitmap img = new Bitmap(ControlsMap.BtnYes.CaptureImage());
img.Save(@"E:\images\btnyes.png");

The image is also captured and saved correctly -
在此处输入图片说明

I want to get the background color of the image.

I tried several variations of the following line -

var a = img.Palette.Entries;

but I'm unable to get background color of the image. Is there any other way?

Just iterate over the entire image and keep a count for each found colour, and then take the colour with the largest count.

The simplest way to do this is probably with GetPixel, though for large images that may take a long time. So instead, you could paste the image on a 32bppARGB image to ensure it's in a specific known format, then use LockBits and Marshal.Copy to get the raw bytes out, then iterate over those bytes, combine them into a colour per four, and check those colours.

For actually getting the maximum I'd use a Dictionary with the colours as keys, and the amounts as values.

All together, you get this:

public static Color FindMostCommonColor(Image image)
{
    // Avoid unnecessary getter calls
    Int32 height = image.Height;
    Int32 width = image.Width;
    Int32 stride;
    Byte[] imageData;
    // Expose bytes as 32bpp ARGB
    BitmapData sourceData = image.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
    stride = sourceData.Stride;
    imageData = new Byte[stride * bm.Height];
    Marshal.Copy(sourceData.Scan0, imageData, 0, imageData.Length);
    image.UnlockBits(sourceData);
    // Store colour frequencies in a dictionary.
    Dictionary<Color,Int32> colorFreq = new Dictionary<Color, Int32>();
    for (Int32 y = 0; y < height; y++)
    {
        // Reset offset on every line, since stride is not guaranteed to always be width * pixel size.
        Int32 inputOffs = y * stride;
        //Final offset = y * line length in bytes + x * pixel length in bytes.
        //To avoid recalculating that offset each time we just increase it with the pixel size at the end of each x iteration.
        for (Int32 x = 0; x < width; x++)
        {
            //Get colour components out. "ARGB" is actually the order in the final integer which is read as little-endian, so the real order is BGRA.
            Color col = Color.FromArgb(imageData[inputOffs + 3], imageData[inputOffs + 2], imageData[inputOffs + 1], imageData[inputOffs]);
            Color bareCol = Color.FromArgb(255, col);
            // Only look at nontransparent pixels; cut off at 127.
            if (col.A > 127)
            {
                if (!colorFreq.ContainsKey(bareCol))
                    colorFreq.Add(bareCol, 1);
                else
                    colorFreq[bareCol]++;
            }
            // Increase the offset by the pixel width. For 32bpp ARGB, each pixel is 4 bytes.
            inputOffs += 4;
        }
    }
    // Get the maximum value in the dictionary values
    Int32 max = colorFreq.Values.Max();
    // Get the first colour that matches that maximum.
    return colorFreq.FirstOrDefault(x => x.Value == max).Key;
    // In case you want to know if there are multiple with the exact same frequency,
    // this could be expanded to give an array with all maxima like this:
    // Color[] maxCols = colorFreq.Where(x => x.Value == max).Select(kvp => kvp.Key).ToArray();
}

Do note that if the image is jpeg or something, this might not work too well, since it has slight fades on all colours. Then you'll have to somehow do a reduction of similar colours, or a threshold system. That's a whole different can of worms, though.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM