Lyber
Lyber

Reputation: 157

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)

Upvotes: 4

Views: 7101

Answers (3)

samgak
samgak

Reputation: 24427

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.

Upvotes: 4

masad
masad

Reputation: 1567

Another way to look at color quantization is to think of bits/pixel. Normally a grayscale image is stored using 8-bits for each pixel, hence the resulting range of values are from 0 to 255.

We can reduce the number of colors in an image by quantizing or in other words dropping the Least Significant Bits (LSBs). An 8-bit representation normally looks like this:

V V V V V V V V

When reduced we can drop the LSBs for example:

V V V V V V x x

or

V V V V x x x x

where V represents a binary value and x shows bits that are dropped. In your case you want to reduce to 4 colors which means that you only need two bits to represent your image. In that case each byte will look like this:

V V x x x x x x

This can be done simply by using left shift operator >> on the uchar values in a grayscale image (as shown in the attached answer).

Upvotes: 1

Miki
Miki

Reputation: 41775

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.

Upvotes: 4

Related Questions