Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

OpenCV: Grayscale color reduction

I'm trying to reduce my grayscale image color from 256 to 4 using this formula from http://docs.opencv.org/2.4/doc/tutorials/core/how_to_scan_images/how_to_scan_images.html enter image description here

I assume that n is the reduction factor, for this case, it will be 10 color from the formula. My code is as below.

void Reduction(Mat image1)
{
for (int r = 0;r < image1.rows;r++) {

    for (int c = 0;c < image1.cols;c++) {
        // get pixel

        int tmp = (image1.at<Vec3b>(r, c)[0] + image1.at<Vec3b>(r, c)[1] + image1.at<Vec3b>(r, c)[2])/3 ;
        tmp =  (tmp/4)* 4;
        image1.at<Vec3b>(r, c)[0] = tmp;
        image1.at<Vec3b>(r, c)[1] = tmp;
        image1.at<Vec3b>(r, c)[2] = tmp;
    }

    }
}

my expected result is enter image description here

but from tmp = (tmp/4)*4; or tmp = ( tmp/8)*8;

my image looks the same as the original image;enter image description here

then i tried changing it to tmp = (tmp/40)*40;

enter image description hereand I got this as the result which is similar to what I wanted for my result.

How does the formula works and what should I edit from my code to accurately get the result I wanted? ( like the expected result above)

like image 317
Lyber Avatar asked Jan 06 '23 04:01

Lyber


2 Answers

This is a color quantization. This is a simple technique to reduce the number of colors in an image that relies on the integer division

Since the starting range has 256 values, if you want to end up with N colors, you need to integer divide and then multiply by K = 256 / N.

So in your case, for N=8 you need a K = 256 / 8 = 32, and for N = 4 you need K = 256 / 4 = 64.

like image 135
Miki Avatar answered Jan 07 '23 19:01

Miki


You won't be able to get the 4 color image in your "expected result" image using a variation on the formula you have given, because integer division always rounds down. It produces this result:

input       output
0-63        0
64-127      64
128-191     128
192-255     192

So no pixel in the image will never be more than 3/4 of full brightness (light grey), and the target image contains pure white.

Simple rounding to the mid-point of a range won't work either, because the target image contains pure black, as well as pure white, so for black you need to always round down and for white you need to always round up.

You might also want to evenly divide the 256-color range into 4 equal portions, which won't work if you do simple rounding either because black and white will end up covered by a smaller range.

It helps to block out exactly how you want the range divided up. For example, suppose you want to divide it into 4 and then create an evenly spaced range including both white and black:

input       output
0-63        0
64-127      85
128-191     170
192-255     255

You can open your expected result image in a paint app and use the eye-dropper tool to verify that these are the correct output values.

The formula for this is:

int new_value = (value / 64) * 85;

Or more generically:

int num_colors = 4;
int divisor = 256 / num_colors;
int max_quantized_value = 255 / divisor;
int new_value = ((value / divisor) * 255) / max_quantized_value;

This way you are guaranteed to get a min new_value of 0 and a max new_value of 255.

like image 40
samgak Avatar answered Jan 07 '23 19:01

samgak