1
$\begingroup$

I'm trying to quantize an image 8 bits to 4 or 2-bits uniformly. I searched internet, interestingly I could not find what I want exactly. Then I wrote an simple code for it myself. I'm curious about whether there is a build-in function in MATLAB which convert 8-bit image to 4-bits uniformly . My results using the methods from internet is not good. Am I doing something wrong when I use these methods? Thank you in advance.

  1. Method from internet giving strange results
reducedImage = uint8((single(monalisa)/256)*2^4);

enter image description here

  1. Method writen by me. I'm putting code to clarify what I want to do.
monalisa_2= monalisa;
figure
monalisa_2(monalisa<=63)               = 32  ; %% 00 
monalisa_2(monalisa<=127&monalisa>63)  = 85  ; %% 01
monalisa_2(monalisa<=191&monalisa>127) = 159 ; %% 10
monalisa_2(monalisa<=255&monalisa>191) = 223 ; %% 11
imshow(monalisa_2)

enter image description here

$\endgroup$
1
  • $\begingroup$ your method not so good .. it's not depending on actulally cretiria i already did this , Quantization on image ,, by using histogram distribution and get details to building your resulted matrix .. $\endgroup$ Commented Mar 26, 2015 at 12:09

1 Answer 1

2
$\begingroup$

I point you to this https://stackoverflow.com/questions/12723699/changing-image-bit-depth-using-matlab it can be done. This particular post is only for png, but if I'm not mistaken all image types have the depth parameter so they can likely all be changed in a similar manner

EDIT

For your particular problem, since your images are already grayscale you would do something like

%change this as needed
desired_bit_depth = 2;

my_pixel_depth = 2 ^ desired_bit_depth;
%converts grayscale to an indexed image at the appropriate depth
[ind_im, reduced_colormap ] = gray2ind(my_gray_image, my_pixel_depth)

%displays your new image
figure()
subplot(1,2,1);imshow(my_gray_image); title('original')
subplot(1,2,2);imshow(ind_im,reduced_colormap); title(sprintf('reduced to %d bits',desired_bit_depth))

% save indexed png
imwrite(ind_im, reduced_colormap, 'test.png', 'bitdepth', desired_bit_depth);

EDIT #2: While reducing the "bits" to display the image, the "colormap" is made by taking the average of each range I kept the original answer because it still works and may suit someone elses needs in the future.

For this solution, rather than using evenly spaced values for our "colormap' we actually use the gray value that is the average over the specified range

ex

to convert to 1 bit image we have two ranges 0,128 and 128,256

replace all values between 0,128 with the average value between 0,128

replace all values between 128,256 with the average value between 128,256

function result_im = ChangeBitDepthGrayImage(gray_im, desired_bit_depth)
    if (desired_bit_depth < 1)
        disp('converting to binary(1 bit) image');
        desired_bit_depth = 1;
    end

    if (desired_bit_depth > 8)
        disp('converting to 8 bit image');
        desired_bit_depth = 8;
    end

    %assuming we start with 8 bit image 256 levels
    num_levels = 2 ^ desired_bit_depth;

    %figures out how big each range should be, we use +1 because if we
    %divide the data into N levels, there should be N+1 boundaries
    limits = linspace(0,256,num_levels + 1);

    result_im = uint8(zeros(size(gray_im)));

    for i = 1:num_levels
        lower_lim = limits(i);
        upper_lim = limits(i+1);

        %creates a binary mask of values between the limits, the output is
        %0 or 1, but we need to make it uint8 for the next step
        temp_mask = uint8((gray_im >= lower_lim) & (gray_im < upper_lim)); 

        %multiplies image by mask, this isolates only pixels in the given
        %range
        image_only_in_range = temp_mask .* gray_im;

        %finds the mean of that small part of the image. this weird notation is
        %taking the average of nonzero elements
        avg_val_for_range = round(mean(image_only_in_range(image_only_in_range~=0)));

        %replaces all pixels in that range with the average val
        result_im = result_im +(avg_val_for_range * temp_mask);
    end


    %i just picked some random figure
    figure(32)
    subplot(1,2,1);imshow(gray_im);title('original image');
    subplot(1,2,2);imshow(result_im);title(sprintf('modified to %dbit image',desired_bit_depth));
end 
$\endgroup$
4
  • $\begingroup$ My image is already grayscale and png. What will parameter 'map' be? $\endgroup$
    – Toygan
    Commented Mar 2, 2015 at 22:11
  • 1
    $\begingroup$ 'map' is not a parameter, but an output. It tells matlab what color each value in your image should be displayed as (just like using matlabs built-in colormap mathworks.com/help/matlab/ref/colormap.html). Since you reduced the depth of your images matlab has to know how to display these images so it uses the map. Notice in my example, the imshow command is passed the image, AND the new colormap $\endgroup$
    – andrew
    Commented Mar 2, 2015 at 23:44
  • $\begingroup$ Firstly, thank you for your interest. When I tried your code with 1 bit depth, I obtain 0s and 1s. However what I want is uniform quantization. For example: range of 0 to 127 => mean(0,127) range of 128 to 255 => mean(128,255) $\endgroup$
    – Toygan
    Commented Mar 3, 2015 at 0:13
  • $\begingroup$ Ok now I understand the problem. As far as I know there is no built in function to do this, but I came up with a method. It uses lots of binary masking. I'm not sure if there is a better way to do it, but this works and is relatively quick. $\endgroup$
    – andrew
    Commented Mar 3, 2015 at 1:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.