Search code examples
imageopencvimage-processingcolorscbir

How to reduce the number of colors in an image with OpenCV?


I have a set of image files, and I want to reduce the number of colors of them to 64. How can I do this with OpenCV?

I need this so I can work with a 64-sized image histogram. I'm implementing CBIR techniques

What I want is color quantization to a 4-bit palette.


Solution

  • There are many ways to do it. The methods suggested by jeff7 are OK, but some drawbacks are:

    • method 1 have parameters N and M, that you must choose, and you must also convert it to another colorspace.
    • method 2 answered can be very slow, since you should compute a 16.7 Milion bins histogram and sort it by frequency (to obtain the 64 higher frequency values)

    I like to use an algorithm based on the Most Significant Bits to use in a RGB color and convert it to a 64 color image. If you're using C/OpenCV, you can use something like the function below.

    If you're working with gray-level images I recommed to use the LUT() function of the OpenCV 2.3, since it is faster. There is a tutorial on how to use LUT to reduce the number of colors. See: Tutorial: How to scan images, lookup tables... However I find it more complicated if you're working with RGB images.

    void reduceTo64Colors(IplImage *img, IplImage *img_quant) {
        int i,j;
        int height   = img->height;   
        int width    = img->width;    
        int step     = img->widthStep;
    
        uchar *data = (uchar *)img->imageData;
        int step2 = img_quant->widthStep;
        uchar *data2 = (uchar *)img_quant->imageData;
    
        for (i = 0; i < height ; i++)  {
            for (j = 0; j < width; j++)  {
    
              // operator XXXXXXXX & 11000000 equivalent to  XXXXXXXX AND 11000000 (=192)
              // operator 01000000 >> 2 is a 2-bit shift to the right = 00010000 
              uchar C1 = (data[i*step+j*3+0] & 192)>>2;
              uchar C2 = (data[i*step+j*3+1] & 192)>>4;
              uchar C3 = (data[i*step+j*3+2] & 192)>>6;
    
              data2[i*step2+j] = C1 | C2 | C3; // merges the 2 MSB of each channel
            }     
        }
    }