Ask Your Question
1

find all the colors of a given image and their pixel position

asked 2016-03-30 10:32:12 -0600

theodore gravatar image

updated 2016-10-18 05:25:51 -0600

Hi guys/girls as the title says I would like find all the existing colors of a given image and label them despite their position within the image. To note here that there might be the same color in different spots of the image. I can think some ways to do it but I was wondering if there is a faster/efficient way to do it from what I am thinking, so any other suggestions are welcome.

First thought is to loop through the whole image and manually keep track of each color and the positions of the pixels with that color, but then I thought we can do better than that ,right? There should be a way.

Second thought includes histogram analysis. Transform my image to grayscale and then each active bin will give me a color. Then create a mask image for each active bin value, and get the needed info(intensity and positions) from the rgb image. However, I am not sure how accurate this will be since I will be limited to 255 different colors, if I am not wrong right? While if I go for the color histogram, I cannot see how I can combine the bins from the different r,g. and b channels or even from other color spaces to create the colors of my images since the combinations in some cases might not be obvious. However, I am not sure about that I might be missing something.

Third thought was to use k-means to cluster the colors of my image, but again I am not sure how accurate this will be (since pixels with small distance will be clustered together, and I want every different color of my image) plus you need to know the number of your clusters beforehand which is not the case here.

Fourth, I came up with some other ideas(use contours, connected components, edges ,etc...) but I think that they are too complicated for such a deal. There should be something simpler.

So any ideas? Thanks


Update

for example imagine that I have the following image:

image description

then as you can see I would like to get 17 clusters (the order does not matter)

image description

in the above case the different colors are solid stable so it can be considered an easy case but I want to cover the case were two close colors (for example cluster 13 should be two different clusters in case one component was RGB(255,0,0) and the other RGB(253,0,0) despite the fact that in the eye there are both red-"ish") should be different clusters.

edit retag flag offensive close merge delete

Comments

To be clear, does color mean a general color, like red, or a specific pixel level? IE, is (255,100,50) a different color from (255,100,51)?

If you mean general colors, take a look at the segmentation functions. grabCut, watershed, and the ones in ximgprog.

If you mean exact, specific pixel levels, then why? What are you trying to do? It can be done, but simple is not the word. You could theoretically end up with a different label for every pixel.

Tetragramm gravatar imageTetragramm ( 2016-03-30 10:49:33 -0600 )edit

@Tetragramm yes (255,100,50) and (255,100,51) should be different colors, that the issue unfortunately. I want to do that because I need to create some labeling benchmark based on the extracted labels. I am aware of the case that theoretically I could end up with a different label for each pixel, and that's the one of the cases I want to take into account, though I do think it will happen in my dataset.

theodore gravatar imagetheodore ( 2016-03-30 10:59:39 -0600 )edit

3 answers

Sort by ยป oldest newest most voted
4

answered 2016-03-30 12:37:54 -0600

berak gravatar image

updated 2016-03-31 05:22:31 -0600

theodore gravatar image

if you meant: "all pixels with exact same values should go into the same cluster", - you could try with partition :

#include "opencv2/opencv.hpp"
using namespace std;
using namespace cv;

static bool pixel_equal(const Vec3b &a, const Vec3b &b)
{   return a == b;   } // this is the "exact match"
// {   return norm(a,b) < 32;   } // if you want something more tolerant


int main( int argc, char** argv )
{
    // opencv logo from webpage
    Mat im = imread("logo_2.png");
    // keep small for demo output below
    resize(im,im,Size(8,8)); 

    // partition wants a vector, so we need a copy ;(
    Vec3b *p = im.ptr<Vec3b>();
    vector<Vec3b> pix(p, p+im.total());

    // now cluster:
    vector<int> labels;
    int unique = cv::partition(pix, labels, pixel_equal);

    // debug output
    Mat lm = Mat(labels).reshape(1,im.rows);
    cerr << im << endl;
    cerr << lm << endl;
    cerr << unique << " unique colors" << endl;
    return 0;
}

image description ===> image description

// squint hard, and you can still see the opencv logo. (0..3:red, 4--6:green, 7..10:blue)
[0, 0, 1, 2, 2, 3, 0, 0;
 0, 0, 2, 0, 0, 2, 0, 0;
 0, 0, 4, 0, 0, 2, 0, 0;
 5, 6, 6, 0, 0, 7, 0, 8;
 6, 0, 0, 9, 8, 0, 0, 8;
 6, 6, 6, 0, 0, 8, 8, 10;
 0, 0, 0, 0, 0, 0, 0, 0;
 0, 0, 0, 0, 0, 0, 0, 0]
11 unique colors

[edit]

if you don't want an exact match, but more some epsilon region, you could change the predicate to:

static bool pixel_equal(const Vec3b &a, const Vec3b &b)
{   return norm(a,b) < 90;   } // some heuristic value

then, ofc. you get less, but more "general" clusters:

[0, 0, 0, 1, 1, 1, 0, 0;
 0, 0, 1, 0, 0, 1, 0, 0;
 0, 0, 1, 0, 0, 1, 0, 0;
 2, 2, 2, 0, 0, 0, 0, 3;
 2, 0, 0, 0, 3, 0, 0, 3;
 2, 2, 2, 0, 0, 3, 3, 3;
 0, 0, 0, 0, 0, 0, 0, 0;
 0, 0, 0, 0, 0, 0, 0, 0]
4 unique colors
edit flag offensive delete link more

Comments

@berak thanks I think partition will do the trick. To be honest I was not aware of the function so thanks for letting me know ;-). Moreover, I would suggest to add also the resized opencv logo image above the output in order the users to be able to visualize the output as well compared to the labeled output matrix. Moreover, while this will work for my case since I am not that really interested for the colors but more for the labels I was thinking how we can make it to recover the actual colors as an output and not just how many different colors I have for a more complete answer. I mean someone having the labels he can get the position of the pixels plus the intensities but how he can clarify that label 2 for example in your case here is red. I know that this is quite difficult

theodore gravatar imagetheodore ( 2016-03-31 03:03:28 -0600 )edit

considering that there are 256x256x256 different combinations. So I guess a more "in range" solution would be more proper in order to say that the color of labels 2 and 3 are red"-ish". Therefore, creating some predifined ranges for the main colors (e.g. red, green, blue, yellow, magenta, pink, etc..) and compare against would be the answer right?

theodore gravatar imagetheodore ( 2016-03-31 03:09:05 -0600 )edit

regarding your confused part what I mean is: as your result is now the output is the number of labels and a labeled matrix what I was saying is to get the labeled matrix and in text form to say:

label 1 --> red or red-ish
label 2 --> red or red-ish
label 3 --> red or red-ish
etc...

based on the intensity of the labeled pixels but as you agreed this can be done with a lookup ;-). Thanks again.

theodore gravatar imagetheodore ( 2016-03-31 03:58:41 -0600 )edit

@berak I added a bigger image compared to the one you had in order to be better visible the matching of pixels to labels, I hope you do not mind :-)

theodore gravatar imagetheodore ( 2016-03-31 05:05:23 -0600 )edit

no, thank you, totally ok ;)

(and again, it was only that small, to keep the debug print neat)

berak gravatar imageberak ( 2016-03-31 05:14:17 -0600 )edit
1

yes I know ;-), I think now is better :-p...

theodore gravatar imagetheodore ( 2016-03-31 05:23:43 -0600 )edit

cute ! thanks a lot ;)

berak gravatar imageberak ( 2016-03-31 05:29:30 -0600 )edit
1

answered 2016-03-30 12:35:03 -0600

Guyygarty gravatar image

updated 2016-03-30 12:36:04 -0600

Unless you have a >16 MegaPixel image, this is probably one of the rare occasions where looping over all pixels and performing pixel-wise operations would be the fastest. Here's how I would do it (trying to minimize required storage, even so this requires quite a lot):

You would need

  • a 256x256x256 Mat Let's call it Colors (you may be able to use a SparseMat for this and should if you can)
  • a Mat the size of your original image, lets call it Next

The type of both Mats should be CV_32SC1. You could get away with a CV_16UC1 if the original image is <256x256. Both Mats initialized with all zeros.

What you then do is cycle through the pixels.


For each pixel:

Calculate its index=X+Y*width+1

It's color values are R, G, B.

If the value of the Colors Mat at location (R,G,B) is zero set it to index and go to next pixel

if it is not zero (say it is p), go to position p in the Next mat. If that is zero, set it to index and continue. if not zero, repeat the last step as many times as is required to get to zero, then set that zero to index


Finally, to get all the pixel values of a certain color, you would find the corresponding pixel in Colors, if it is zero there are no such pixels, if it is a value index!=0, that is the index of the first pixel of that color. You then cycle through the Next matrix starting with position index, the value of that position is the index of the second pixel and so on, until you get to index==0.

Good luck

guy

edit flag offensive delete link more

Comments

@Guyygarty thanks for the response though I think @berak 's answer would do the trick, plus I am not sure that I get you last part. You got my upper vote though ;-)

theodore gravatar imagetheodore ( 2016-03-31 03:15:46 -0600 )edit

I'm sure @berak 's answer will do the trick for you, I was not familiar with the partition function.

One small concern I have is that, per the documentation, the partition function is O(N^2) where I guess N would be the number of pixels in your image. My solution can be tweaked to run in linear time WRT the number of pixels. There will probably, be a (very) large overhead for memory allocation and initialization. so if you're not working with huge images I would try Berak's solution before mine.

The problem I see with @Tetragramm 's solution is that there is no easy way to decide if the color of the pixel you are looking at was seen before, without checking a long list of color values one by one. You would probably want to figure out some way to sort the list of colors.

guy

Guyygarty gravatar imageGuyygarty ( 2016-03-31 20:54:29 -0600 )edit

Quite so. Mine was for if it needed to be separate in space as well as color, where it wouldn't matter if it had been seen before.

Tetragramm gravatar imageTetragramm ( 2016-03-31 21:12:37 -0600 )edit
0

answered 2016-03-30 19:22:31 -0600

Tetragramm gravatar image

If you meant: "all pixels with exact same values should go into the same cluster", use berak's answer. If you didn't I think we can improve on Guyygarty's answer.

Make a structure that holds a Point3i called color, and a vector of Point2i called pixels.

Make a mask the size of your image set to zero.

For every pixel that has the mask equal to zero, create a structure with the color it contains and push the location to the pixels vector, and set the mask to 255. Then recursively check every mask == 0 pixel it touches and if it's the same color add it to the vector and set the mask to 255.

For a refinement, make the mask image an int, and set the mask value to the index of the structure in the container you're storing them in.

edit flag offensive delete link more

Comments

@Tetragramm but I do not get something both @Guyygarty 's with your refinement here and @berak 's approaches will give me "all pixels with exact same values should go into the same cluster" output, won't they? Do I miss something, because this is what I understand?

theodore gravatar imagetheodore ( 2016-03-31 03:31:19 -0600 )edit

Question Tools

2 followers

Stats

Asked: 2016-03-30 10:32:12 -0600

Seen: 8,775 times

Last updated: Mar 31 '16