2

im trying to get the most dominant color of an image (perfect case: getting the 5 dominant colors sorted by most used). Is there a way to make that in processing? I tried already a code i found but with that im only getting the average color:

color extractColorFromImage(PImage img) 
{ 
    img.loadPixels(); 
    int r = 0, g = 0, b = 0; 
    for (int i=0; i<img.pixels.length; i++) 
    { 
        color c = img.pixels[i]; 
        r += c>>16&0xFF; 
        g += c>>8&0xFF; 
        b += c&0xFF;
    } 
    r /= img.pixels.length; g /= img.pixels.length; b /= img.pixels.length;
    return color(r, g, b);
}

So its not really that what i need. I already read that i could do it with HSV, k-means and so on.... and any way to do it in processing?

Example: Here i want to get the color red as the dominant color, with the example above im getting a dark orange. Red-Blue Picture

Kevin Workman
  • 41,537
  • 9
  • 68
  • 107
SeGa
  • 27
  • 3
  • Try to edit your question, improve it's formatting. Other than that, try to do something on your own rather than ask for ready-made code. – Mike Jan 11 '16 at 14:36
  • Sorry, tried to formatting it but didnt kinda work. Was my first post. Secondly im working on it since some days (really new to processing still) and in fact i didnt asked for an ready solution, only if someone knows a way to make it in processing (because i found many things with other tools such as python... – SeGa Jan 11 '16 at 19:48

4 Answers4

1

What about this? Set the image in a bitmap and analyze every pixel. Just add up the amount of times a pixel is in the image.

static Dictionary<Color, int> CalcImageColors(Bitmap image)
    {
        var frequency = new Dictionary<Color, int>();
        for (var h = 0; h < image.Height; h++)
        {
            for (var w = 0; w < image.Width; w++)
            {
                var pixel = image.GetPixel(w, h);
                if (frequency.ContainsKey(pixel))
                {
                    frequency[pixel]++;
                }
                else
                {
                    frequency.Add(pixel, 1);
                }
            }
        }
       return frequency.OrderByDescending(x => x.Value).ToDictionary(x => x.Key, x => x.Value);
    }
Jeffrey Rosselle
  • 745
  • 1
  • 9
  • 25
  • That's an elegant answer(+1). Just wanted to mention that [Processing](http://processing.org) relies on an older version of the JRE which doesn't support fancy features like lambdas :/ – George Profenza Jan 11 '16 at 16:55
  • Thought of it too, but i wasnt sure if theres not a elegant way to make it. Like the other answer i will have to work on it tomorrow, so i will try that too. Thanks for it – SeGa Jan 11 '16 at 20:00
0

Break your problem down into smaller steps.

Step 1: Can you iterate over the pixels in the image? Check out the get() function to help with that. Or you can use the for loop in your code. But first, try just printing out the RGB value of each cell.

Step 2: When you have that working, try keeping track of the count of each color you see. The way you do this depends on exactly what you want to do: should (255, 0, 0) and (200, 0, 0) both count as red? But one way might be to use a HashMap<color, Integer> that keeps track of the count of each color.

Step 3: Given the counts of each color, now you can output the dominant color. How you do this depends on the data structure you used in step 2.

If you get stuck on a specific step, post an MCVE and we'll go from there. Good luck!

Community
  • 1
  • 1
Kevin Workman
  • 41,537
  • 9
  • 68
  • 107
0

The RGB colour space may not always work as you'd expect in terms of mixing/averaging colours. You should convert to a perceptual colour space like CIE LAB. To do that you need to first convert from RGB to CIE XYZ then from CIE XYZ to CIE RGB. For more info check out these pages on CIE XYZ and CIE LAB.

In terms of Processing, here's a prototype using RGB<>CIE XYZ<>CIE LAB colour conversion code from this answer (with small tweaks to compile in the Processing IDE (which is antsy about using the static keyword)):

void setup(){
  PImage src = loadImage("https://i.stack.imgur.com/0H1OM.png");

  size(src.width*4,src.height);
  noStroke();

  //display original image
  image(src,0,0);

  //display RGB average color
  fill(extractColorFromImage(src));
  rect(src.width,0,src.width,src.height);

  //display (perceptual)Lab average color
  fill(extractAverageColorFromImage(src));
  rect(src.width*2,0,src.width,src.height);

  //display the most dominant colour
  fill(extractDominantColorFromImage(src));
  rect(src.width*3,0,src.width,src.height);

}

color extractDominantColorFromImage(PImage img){
  //create a hashmap - the key is the colour, the value associated is the number of pixels per colour
  HashMap<Integer,Integer> colorCounter = new HashMap<Integer,Integer>();
  int numPixels = img.pixels.length;

  for (int i=0; i<numPixels; i++){
    int colorKey = img.pixels[i];
    //if the colour has already been added to the hashmap, increment the count
    if(colorCounter.containsKey(colorKey)){
      colorCounter.put(colorKey,colorCounter.get(colorKey)+1);
    }else{//otherwise count it as 1
      colorCounter.put(colorKey,1);
    }
  }
  //find the most dominant colour - note you can implement this to return more than one if you need to
  int max = 0;//what's the highest density of pixels per one colour
  int dominantColor = 0;//which colour is it

  //for each key (colour) in the keyset  
  for(int colorKey : colorCounter.keySet()){
    //get the pixel count per colour
    int count = colorCounter.get(colorKey);
    //if this one is the highest, updated the max value and keep track of the colour
    if(count > max){
      max = count;
      dominantColor = colorKey; 
    }
  }
  //return the winner (colour with most pixels associated)
  return dominantColor;
}

color extractColorFromImage(PImage img) 
{ 
    img.loadPixels(); 
    int r = 0, g = 0, b = 0; 
    for (int i=0; i<img.pixels.length; i++) 
    { 
        color c = img.pixels[i]; 
        r += c>>16&0xFF; 
        g += c>>8&0xFF; 
        b += c&0xFF;
    } 
    r /= img.pixels.length; g /= img.pixels.length; b /= img.pixels.length;
    return color(r, g, b);
}

color extractAverageColorFromImage(PImage img){
  float[] average = new float[3];
  CIELab lab = new CIELab();

  int numPixels = img.pixels.length;
  for (int i=0; i<numPixels; i++){
    color rgb = img.pixels[i];

    float[] labValues = lab.fromRGB(new float[]{red(rgb),green(rgb),blue(rgb)});

    average[0] += labValues[0];
    average[1] += labValues[1];
    average[2] += labValues[2];
  }

  average[0] /= numPixels;
  average[1] /= numPixels;
  average[2] /= numPixels;

  float[] rgb = lab.toRGB(average);

  return color(rgb[0] * 255,rgb[1] * 255,rgb[2] * 255);
}

//from https://stackoverflow.com/questions/4593469/java-how-to-convert-rgb-color-to-cie-lab
import java.awt.color.ColorSpace;

public class CIELab extends ColorSpace {

    @Override
    public float[] fromCIEXYZ(float[] colorvalue) {
        double l = f(colorvalue[1]);
        double L = 116.0 * l - 16.0;
        double a = 500.0 * (f(colorvalue[0]) - l);
        double b = 200.0 * (l - f(colorvalue[2]));
        return new float[] {(float) L, (float) a, (float) b};
    }

    @Override
    public float[] fromRGB(float[] rgbvalue) {
        float[] xyz = CIEXYZ.fromRGB(rgbvalue);
        return fromCIEXYZ(xyz);
    }

    @Override
    public float getMaxValue(int component) {
        return 128f;
    }

    @Override
    public float getMinValue(int component) {
        return (component == 0)? 0f: -128f;
    }    

    @Override
    public String getName(int idx) {
        return String.valueOf("Lab".charAt(idx));
    }

    @Override
    public float[] toCIEXYZ(float[] colorvalue) {
        double i = (colorvalue[0] + 16.0) * (1.0 / 116.0);
        double X = fInv(i + colorvalue[1] * (1.0 / 500.0));
        double Y = fInv(i);
        double Z = fInv(i - colorvalue[2] * (1.0 / 200.0));
        return new float[] {(float) X, (float) Y, (float) Z};
    }

    @Override
    public float[] toRGB(float[] colorvalue) {
        float[] xyz = toCIEXYZ(colorvalue);
        return CIEXYZ.toRGB(xyz);
    }

    CIELab() {
        super(ColorSpace.TYPE_Lab, 3);
    }

    private double f(double x) {
        if (x > 216.0 / 24389.0) {
            return Math.cbrt(x);
        } else {
            return (841.0 / 108.0) * x + N;
        }
    }

    private double fInv(double x) {
        if (x > 6.0 / 29.0) {
            return x*x*x;
        } else {
            return (108.0 / 841.0) * (x - N);
        }
    }

//    private Object readResolve() {
//        return getInstance();
//    }

//    private static class Holder {
//        static final CIELab INSTANCE = new CIELab();
//    }

//    private static final long serialVersionUID = 5027741380892134289L;

    private final ColorSpace CIEXYZ =
        ColorSpace.getInstance(ColorSpace.CS_CIEXYZ);

    private final double N = 4.0 / 29.0;

}

You can see a preview bellow with the original image, then (in this order):

  • the RGB average
  • the LAB average
  • the most dominant colour

Average and dominant colour in an image

Community
  • 1
  • 1
George Profenza
  • 50,687
  • 19
  • 144
  • 218
  • thanks for you answer, i will check it tomorrow, when i have to work on it. still new to processing and i think it will take a time to understand what you wrote there^^ – SeGa Jan 11 '16 at 19:44
0

You might want to look at this tutorial on finding dominant colors in an image. - it's a more mathematical take on the problem. The idea is to use statistics on the image to figure out the main colors. Source code is available for OpenCV - so it should be possible to adapt it to use for Processing!

Utkarsh Sinha
  • 3,295
  • 4
  • 30
  • 46