Ask Your Question
0

How to efficiently count the number of unique colors in Java

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

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 -0500 )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 -0500 )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 -0500 )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 -0500 )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 -0500 )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 -0500 )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 -0500 )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 -0500 )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 -0500 )edit

1 answer

Sort by ยป oldest newest most voted
1

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

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

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 -0500 )edit

Mat dblImg = new Mat();

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

@berak exact ;-)

petititi gravatar imagepetititi ( 2014-10-24 14:18:10 -0500 )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 -0500 )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 -0500 )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 -0500 )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 -0500 )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 -0500 )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 -0500 )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 -0500 )edit
Login/Signup to Answer

Question Tools

Stats

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

Seen: 1,560 times

Last updated: Oct 24 '14