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

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:

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

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.

@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.

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

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?

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.

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.

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.

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?