1

So somebody helped me to make this code which will tell me the most used color in a photo:

class PictureAnalysis
{
    public static List<Color> TenMostUsedColors { get; private set; }
    public static List<int> TenMostUsedColorIncidences { get; private set; }

    public static Color MostUsedColor { get; private set; }
    public static int MostUsedColorIncidence { get; private set; }

    private static int pixelColor;

    private static Dictionary<int, int> dctColorIncidence;

    public static void GetMostUsedColor(Bitmap theBitMap)
    {
        TenMostUsedColors = new List<Color>();
        TenMostUsedColorIncidences = new List<int>();

        MostUsedColor = Color.Empty;
        MostUsedColorIncidence = 0;

        // does using Dictionary<int,int> here
        // really pay-off compared to using
        // Dictionary<Color, int> ?

        // would using a SortedDictionary be much slower, or ?

        dctColorIncidence = new Dictionary<int, int>();

        // this is what you want to speed up with unmanaged code
        for (int row = 0; row < theBitMap.Size.Width; row++)
        {
            for (int col = 0; col < theBitMap.Size.Height; col++)
            {
                pixelColor = theBitMap.GetPixel(row, col).ToArgb();

                if (dctColorIncidence.Keys.Contains(pixelColor))
                {
                    dctColorIncidence[pixelColor]++;
                }
                else
                {
                    dctColorIncidence.Add(pixelColor, 1);
                }
            }
        }

        // note that there are those who argue that a
        // .NET Generic Dictionary is never guaranteed
        // to be sorted by methods like this
        var dctSortedByValueHighToLow = dctColorIncidence.OrderByDescending(x => x.Value).ToDictionary(x => x.Key, x => x.Value);

        // this should be replaced with some elegant Linq ?
        foreach (KeyValuePair<int, int> kvp in dctSortedByValueHighToLow.Take(10))
        {
            TenMostUsedColors.Add(Color.FromArgb(kvp.Key));
            TenMostUsedColorIncidences.Add(kvp.Value);
        }

        MostUsedColor = Color.FromArgb(dctSortedByValueHighToLow.First().Key);
        MostUsedColorIncidence = dctSortedByValueHighToLow.First().Value;
    }

}

and I am trying to implement like this but I don't really know what I should do to show me the most used color?

string filep = @"C:\Users\User\Desktop\Gallery\image" + NumberOfClick.ToString() + "cropped.png";

                Bitmap bMap = Bitmap.FromFile(filep) as Bitmap;

                PictureAnalysis.GetMostUsedColor(bMap);

I want to determine the most used color from a "real" photo like this one: I am cropping her "jacket" from the photo and I want a program that determines it as it is black

C. Cristi
  • 569
  • 1
  • 7
  • 21
  • 1
    How do you want to show it? – Alfie Goodacre Nov 30 '16 at 10:19
  • 3
    as a string, like in a messagebox – C. Cristi Nov 30 '16 at 10:22
  • 3
    `MessageBox.Show("Most used color is " + PictureAnalysis.MostUsedColor.ToString());` ... but seriously. How do you want to display this color? Showing a new dialog, put this somewhere in UI (like rectangle filled with this color) ? – mrogal.ski Nov 30 '16 at 10:23
  • In that case, wither give it a Color return type, or user a `Color` as a `ref` variable. Then you can change the `textBox` text to something like `theColor.r.ToString() + " " + theColor.g.ToString() + " " + theColor.b.ToString();` – Alfie Goodacre Nov 30 '16 at 10:24
  • 2
    @m.rogalski I tried that way but It shows on any photo Color[A=0; R=0; G = 0; B =0] and I want something like red, blue etc. – C. Cristi Nov 30 '16 at 10:31
  • @C.Cristi Are you sure that you actually want the most used color of a **photograph**? I'm asking because the most used color would be reasonable for something like video game sprites, which feature only a small amount of colors, like a hundred. Photographs have thousands of different colors and the most frequent color might be only used thrice, or maybe even only once. For an analysis you might be better of to analyse neighborhoods of colors. Or maybe simplify it into something like the hue and then analyze the resulting graph, depending on the application. – Aziuth Nov 30 '16 at 10:45
  • Well I want into a "real" photo to determine the most used color I will update my question so you can understand better and also give me advices. See the update! – C. Cristi Nov 30 '16 at 10:49

3 Answers3

0

There are two ways:

First change the return type of method GetMostUsedColor from void to int

public static int GetMostUsedColor(Bitmap theBitMap)
{
    TenMostUsedColors = new List<Color>();
    /*unchangeable code here*/
    return dctSortedByValueHighToLow.First().Value;
}

After that, this method will return the most used color.

If you run your program in console,

You can replace last line PictureAnalysis.GetMostUsedColor(bMap) with Console.WriteLine(PictureAnalysis.GetMostUsedColor(bMap)) in order to receive the most used color

Second way (if you use console) just replace one line

public static void GetMostUsedColor(Bitmap theBitMap)
{
    /*unchangeable code here*/
     Console.WriteLine(dctSortedByValueHighToLow.First().Value);
}
  • 2
    ok, i used the first one and it shows me the hex color and I want to turn it into like dark blue, red etc.. – C. Cristi Nov 30 '16 at 10:39
  • So search for `C# get color from hex` on your favorite search engine, read the first result, profit. – Equalsk Nov 30 '16 at 10:42
0

This class is static, the method call is static, and the results are too...

So to use this :

string filep = @"C:\Users\User\Desktop\Gallery\image" + NumberOfClick.ToString() + "cropped.png";
Bitmap bMap = Bitmap.FromFile(filep) as Bitmap;
PictureAnalysis.GetMostUsedColor(bMap);

//Here you get the most used color
var Color MostUsed = PictureAnalysis.MostUsedColor;
//Here you get the ten most used colors
var List<Color> TenMostUsed = PictureAnalysis.TenMostUsedColor;

Display the color as a string

In the System.Drawing namespace, Microsoft defined a lot of colors in the Color structure. So there is a possibility to create a color from a known color name.

However, you must understand that all colors do not have a name. Because there are lots of them. So it is not always possible to put a name on a color.

You can try to write a method that would take a color and return the color name... But remember there are over 16 million possibilities...

A kind of solution

What I would do, is put the color inside a ColorDialog. You then set its Color property to your most used color.

That way, the user will at least be able to view the color

Another option is to view it inside a PropertyGrid, because their ColorPicker is nice too.

Martin Verjans
  • 4,675
  • 1
  • 21
  • 48
0

There are several ways of doing this.

Here are two solutions:

1.

Take a list of all colors you want to compare against. I would take a list of all the colors in Color.*

Then loop through all pixels and compare each color against the list to take the closest one and then save that color with a value of 1 occurence, then take the next and so on and finally you have a list of the most dominant color of the ones in the list. Then just take that color and get the name since it is a real Color. Like:

string nameOfColorRed = Color.Red.Name;

To loop through all pixels in an image you could use GetPixel() but that is a bit slow so you should use faster methods like you can find here: Travel through pixels in BMP

2.

Another simple solution that should work is to cheat a bit and resize the image to the size 1x1 pixels and then you kind of get the most dominant color on that single pixel. This has worked on a few simple tests i made and it might be good enough? Otherwise use the 1st solution.

Then just compare that color against the list.

Here is a sample for the last solution:

        var listOfColors = GetAllColors();

        var filename = @"D:\Nature-View.jpg";
        var image = Image.FromFile(filename);

        var smallImage = ResizeImage(image, 1, 1);

        var index = ClosestColor2(listOfColors, smallImage.GetPixel(0, 0));

        var closestColorName = listOfColors[index].Name;

The methods for finding ClosestColor is taken from here: How to compare Color object and get closest Color in an Color[]?

Here are the rest of the methods:

    private List<Color> GetAllColors() {
        var list = new List<Color>();
        var colorType = typeof(Color);
        var propInfos = colorType.GetProperties(BindingFlags.Static | BindingFlags.DeclaredOnly | BindingFlags.Public);
        foreach (var propInfo in propInfos) {
            var color = Color.FromName(propInfo.Name);
            list.Add(color);
        }
        return list;
    }

    // closed match in RGB space
    private int ClosestColor2(List<Color> colors, Color target) {
        var colorDiffs = colors.Select(n => ColorDiff(n, target)).Min(n => n);
        return colors.FindIndex(n => ColorDiff(n, target) == colorDiffs);
    }

    // distance in RGB space
    private static int ColorDiff(Color c1, Color c2) {
        return (int) Math.Sqrt((c1.R - c2.R)*(c1.R - c2.R)
                               + (c1.G - c2.G)*(c1.G - c2.G)
                               + (c1.B - c2.B)*(c1.B - c2.B));
    }

    public static Bitmap ResizeImage(Image image, int width, int height) {
        var destRect = new Rectangle(0, 0, width, height);
        var destImage = new Bitmap(width, height);

        destImage.SetResolution(image.HorizontalResolution, image.VerticalResolution);

        using (var graphics = Graphics.FromImage(destImage)) {
            graphics.CompositingMode = CompositingMode.SourceCopy;
            graphics.CompositingQuality = CompositingQuality.HighQuality;
            graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
            graphics.SmoothingMode = SmoothingMode.HighQuality;
            graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;

            using (var wrapMode = new ImageAttributes()) {
                wrapMode.SetWrapMode(WrapMode.TileFlipXY);
                graphics.DrawImage(image, destRect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, wrapMode);
            }
        }

        return destImage;
    }
Community
  • 1
  • 1
MrApnea
  • 1,776
  • 1
  • 9
  • 17