Ask Your Question
0

How to efficiently count the number of unique colors in Java

asked 2014-10-20 15:07:53 -0600

costa_974 gravatar image

How to efficiently count the number of unique colors in Java?

edit retag flag offensive close merge delete

Comments

1

Can you be a little more explicit? What is unique color?

thdrksdfthmn gravatar imagethdrksdfthmn ( 2014-10-21 03:26:58 -0600 )edit
1

Well what you should do is the following

  1. First decide what you define as a unique color. This means you need to define a R G and B range for each unique color.
  2. Threshold any given image in steps over the threshold regions. If a white blob pops up in your binary result than you can decide that the colour is retrieved in the image.
  3. Finally store a vector with all the colors that are retrieved, even using masking to color in a black image size frame to show the different regions.
StevenPuttemans gravatar imageStevenPuttemans ( 2014-10-21 05:37:39 -0600 )edit

It's much simpler... let's rephrase the question in "How to efficiently count colors in a Mat object in Java", without "unique"... I know there's cvCountNonZero, but I have to split the channels... etc, or I could iterate every pixels. I would like a simple way to count colors like in Photoshop fo example. Thanks.

costa_974 gravatar imagecosta_974 ( 2014-10-21 12:18:42 -0600 )edit

I would not suggest you to iterate, because it is slow, here is the documentation of split and using cvCountNonZero, I do not think is really what you want, can you give a link of Photoshop color counter?

thdrksdfthmn gravatar imagethdrksdfthmn ( 2014-10-22 02:39:56 -0600 )edit

@costa_974, even if you just count colors. Then the combination is still endless if you do not predefine what you see as colors. With an 8 bit image and 3 channels RGB you have multiple thousands of comibnations. Keep in mind that color is NOT the same for a PC as for a human eye!

StevenPuttemans gravatar imageStevenPuttemans ( 2014-10-22 04:17:01 -0600 )edit

c# code: public static int CountImageColors(string fileName) { int count = 0; HashSet<Color> colors = new HashSet<Color>(); Bitmap bmp = null;

if (File.Exists(fileName))
{
    try
    {
        bmp = new Bitmap(fileName);
        if (bmp != null)
        {
            for (int y = 0; y &lt; bmp.Size.Height; y++)
            {
                for (int x = 0; x &lt; bmp.Size.Width; x++)
                {
                    colors.Add(bmp.GetPixel(x, y));
                }
            }
            count = colors.Count;
        }
    }
    catch 
    {
        throw;
    }
    finally
    {
        colors.Clear();
        bmp.Dispose();
    }
costa_974 gravatar imagecosta_974 ( 2014-10-22 05:13:26 -0600 )edit

OpenCV doesn't support C# probably you are using a wrapper? You should then address forum related to that wrapper and not this one.

StevenPuttemans gravatar imageStevenPuttemans ( 2014-10-22 06:00:07 -0600 )edit

ok... you guys are too smart! i'm not able to be clear! the c# code is just an example! how can I do the same in opencv? I just want to count the colors in a image!

costa_974 gravatar imagecosta_974 ( 2014-10-23 15:24:23 -0600 )edit
public int countColors(byte[] img_buffer, int channels) {

    Set&lt;Integer&gt; set =  new HashSet&lt;Integer&gt;();
    int img_buffer_length = img_buffer.length;

    for (int i = 0; i &lt; (img_buffer_length - channels); i += channels)
    {
         int pixelVal = 0;
         for (int j = 0; j &lt; channels; j++) {
            pixelVal += (img_buffer[i + j] &lt;&lt; (8 * j));
         }
         set.add(pixelVal);
    }

    int r = set.size();

    return r;

}
costa_974 gravatar imagecosta_974 ( 2014-10-25 06:05:12 -0600 )edit

1 answer

Sort by ยป oldest newest most voted
1

answered 2014-10-24 13:29:23 -0600

updated 2014-10-24 15:19:54 -0600

Hi costa_974!

The problem you have is not that simple : color is something very hard to define ;-) But if you just want to count the different values your Mat contains, the best you can do is to follow the C# example. I don't have opencv java-binding enabled here so I can't test if the solution I suggest is correct, but here is the idea:

import java.util.HashSet;
public static void main(String[] args) {
  Mat m = ... your stuff here. I suppose a CV_U8 matrix
  int size = (int)m.total();
  int nbChannels = m.channels();
  byte[] temp = new byte[size * nbChannels];
  m.get(0, 0, temp);
  Set<Integer> set =  new HashSet<Integer>();//will contain all different values
  for (int i = 0; i < size; i++)
  {
     int pixelVal = 0;
     for(int j=0; j<nbChannels; j++)
        pixelVal+= (temp[i*nbChannels+j] << (8*j));
     set.add(pixelVal);
 }
 System.out.println("Color number : " + set.size());
}

An other version (but doesn't seems to work) using double conversion in order to deal with unsigned char matrix...

import java.util.HashSet;

public static void main(String[] args) {
   Mat m = //... your image here.
   Mat dblImg = new Mat();//use double as unsigned byte doesn't exist in java...
   if(m.channels()==3)
      m.convertTo(dblImg, CvType.CV_64FC3);
   else
      m.convertTo(dblImg, CvType.CV_64FC1);
   int size = (int)m.total();
   int nbChannels = m.channels();
   double[] temp = new double[size * nbChannels];
   dblImg.get(0, 0, temp);
   Set<Double> set =  new HashSet<Double>();//will contain all different values
   for (int i = 0; i < size; i++)
   {
      double pixelVal = temp[i*nbChannels];
      for(int j=1; j<nbChannels; j++)
         pixelVal+= (temp[i*nbChannels+j] * 256. * j);
      set.add(pixelVal);
  }
  System.out.println("Color number : " + set.size()) ;
}

I hope this will solve your problem!

edit flag offensive delete link more

Comments

I updated some part of the code as it seems to exist some problem with Byte values!

petititi gravatar imagepetititi ( 2014-10-24 13:46:37 -0600 )edit

Mat dblImg = new Mat();

berak gravatar imageberak ( 2014-10-24 14:04:53 -0600 )edit

@berak exact ;-)

petititi gravatar imagepetititi ( 2014-10-24 14:18:10 -0600 )edit

also, the c++ version returns the same count as your java byte[] version before. (the double[] one returns less)

berak gravatar imageberak ( 2014-10-24 14:26:28 -0600 )edit

You're right, I write (255.*j) instead of (256.*j). That should be good now ;-)

petititi gravatar imagepetititi ( 2014-10-24 14:42:04 -0600 )edit

aww, did not see that. <strike>ofc. ;)</strike> (some more, but still less)

(still, i'm thinking, your byte[] version before was alreay perfect)

berak gravatar imageberak ( 2014-10-24 14:51:41 -0600 )edit

Ok thank you very much! But I think that java will be a little slow iterating every pixel... I hoped there was a fast native method.

But... by the way... do you know that opencv.org is not accessible since 2 days... Site not configured, Coming soon: Another fine website hosted by WebFaction.

costa_974 gravatar imagecosta_974 ( 2014-10-24 15:03:05 -0600 )edit

Another doubt... I don't understand why opencv doesn't have a simple array of 32bit integers representing RGBA.

costa_974 gravatar imagecosta_974 ( 2014-10-24 15:12:44 -0600 )edit

@berak If the byte[] version works, it's indeed the best way to do this. I will then post the two version...

@costa_974 I noticed this before, they are probably working on that. Maybe you can fill a bug report in code.opencv.org, but I'm not sure it's the right place to do that...

petititi gravatar imagepetititi ( 2014-10-24 15:13:29 -0600 )edit
  • not all images are rgba, most algos work on plain grayscale(1channel) even.

    also, alpha is most useless to computer-vision..

  • don't worry, that looks more like someone's working on it.

the docs are still there, if you need them.

( i'm more hoping, they're transferring to a better hoster.. )

berak gravatar imageberak ( 2014-10-24 15:13:54 -0600 )edit

I'm glad berak and petititi you have understood my question! Maybe others are too focused on computer vision... but I'm using opencv for a photo-website... and I would warn or discard photo with too little colors. Maybe now I'm clearer! :)

costa_974 gravatar imagecosta_974 ( 2014-10-24 15:19:51 -0600 )edit

I understand, maybe opencv is not the right/best tool, but I'm working on a photo-website and I would like to warn or discard photo with bad quality... I'm naive... but I was thinking on counting colors! :)

costa_974 gravatar imagecosta_974 ( 2014-10-24 15:25:26 -0600 )edit
1

Wahoo, that's an ambitious project ;-) Detect bad quality images is not as easy as it sounds! And counting the colors is probably not the good way to do that. Maybe you can try to convert image to grayscale and compute the abs difference between the grayscale version and the color version. This should have more sense...

petititi gravatar imagepetititi ( 2014-10-24 15:51:25 -0600 )edit

@costa_974 i see 2 objections :

  • what, a webserver ? i guess, you do not own that machine ? in that case, you can probably forget using opencv, since you will have to build/install native binaries.

  • would you say, warhol's marylin is a bad picture ? it has only 5 unique colors. remember, there's instagram and such. imho, you're on the wrong track here.

berak gravatar imageberak ( 2014-10-25 02:04:03 -0600 )edit
1

berak... I own the server. warhol's marylin has 5 unique colors only for human beings. http://blog.virginmegastore.me/index.php/are-you-the-next-big-artist/andy-warhols-marilyn-monroe-pink-fluo/ has for example has 81912 colors. Thanks anyway for your comment.

costa_974 gravatar imagecosta_974 ( 2014-10-25 05:20:28 -0600 )edit

Question Tools

Stats

Asked: 2014-10-20 15:07:53 -0600

Seen: 2,941 times

Last updated: Oct 24 '14