# Reduce the Number of Intensity Levels of a Grayscale Image in MATLAB

I have written a Matlab script to reduce the number of intensity levels of each pixel of a grayscale image from 256 to some power of 2.

img_color = imread('photo.jpg');
img_gray = rgb2gray(img_color);
imshow(img_gray);
[rows, cols] = size(img_gray);
noOfDesiredIntensityLevels = 2; // test data. will check for 4,8,16,32,etc.
bitsNeededToRepresentIntensityLevels = log2(noOfDesiredIntensityLevels);
new_img = img_gray;
for i = 1 : rows
for j = 1 : cols
new_img(i,j) = floor(img_gray(i,j)/(2^(8-bitsNeededToRepresentIntensityLevels)));
end
end
figure
imshow(new_img);


On execution, the script returns a black image. My expectation was that the image will be turned into black-and-white (intensity value for each pixel will be either a 0 or a 1).

What am I missing here?

P.S: I am a novice in Matlab and Image Processing. So, please ignore any mistakes in my understanding.

• Never ask to be ignored! Mistakes in understanding are normal and happen, and this is a place where people will explain your mistakes to you and allow you to get things right :) – Marcus Müller Aug 11 at 12:34

I think by number of levels you want the image full scale grey-scale to be divided piece-wise into given number of levels. For example: -

• If number of levels = 2, then you want only two grey-scales in your image i.e. (0 and 128) or (128 and 255) depending upon if you are using floor or ceil within the range
• If number of levels = 4, then you want 4 different levels in your image i.e. (0, 64, 124, 192)

Solution - This operation can be done in a single line

new_img = ceil(img_gray./step)*step;

Here I have divide the entire range into desired number of parts (levels) with size as step and used ceil function to restrict the result to the upper bounds (you can use floor for lower bounds)

Full Code -

img_color = imread('peppers.png');
img_gray = rgb2gray(img_color);
imshow(img_gray);

[rows, cols] = size(img_gray);
noOfDesiredIntensityLevels = 2;
step = ceil(255/(noOfDesiredIntensityLevels - 1));

new_img = ceil(img_gray./step)*step;

figure
imshow(new_img);

% optional code to show the levels
allSteps = 0;
currStep = 0;
while(currStep < 255)
currStep = currStep + step;
allSteps = [allSteps currStep];
end

allSteps


Edit 1 - I have included few extra lines which shows you the different levels (steps) in an array. Its just optional but nice to know the exact values of the levels.

• I understood your explanation. Thanks! – flamingo_stark Aug 23 at 16:55

This is one way to do it

img_color = imread('peppers.png');
img_gray = rgb2gray(img_color);
figure(1); imshow(img_gray);

nLevels = 4; % change this to the desired level 2 or more
maxLevel = 256;
threshGap = maxLevel / nLevels;
thresholds = zeros(1, nLevels);
thresholds(nLevels) = maxLevel-1;
thresholds(1) = floor(threshGap);
for i = 2:nLevels-1
thresholds(i) = floor(thresholds(i-1) + threshGap);
end

grayGap = maxLevel / (nLevels - 1);
grayLevels = zeros(1, nLevels);
grayLevels(nLevels) = maxLevel - 1;
for i = 2:nLevels-1
grayLevels(i) = floor(grayLevels(i-1) + grayGap);
end

new_img = img_gray;
[rows, cols] = size(img_gray);

for i = nLevels:-1:1
%new_img(img_gray < thresholds(i)) = grayLevels(i); % you can use this line of
% code and remove the next double nested loops

for x=1:rows
for y =1:cols
if img_gray(x, y) < thresholds(i)
new_img(x, y) = grayLevels(i);
end
end
end
end

figure(2); imshow(new_img);


Here is a result with 4 gray-levels:

Note: The code is pretty straightforward but If you don't understand something just comment.

• Thank you for your code. But, this being my initial learning phase of the subject, I am more interested in getting feedback on my code than to get a solution written down for me. Request you to kindly point out the errors in my code. – flamingo_stark Aug 16 at 18:49

With Matlab, you don't need to loop other all pixels. Being matrix-based (hence "MatLab"), you can divide a whole image directly. Then, imshow can be tricky on images that are not on 8 bits. Instead imagesc can scales images.

clear all;
noOfDesiredIntensityLevels = 2;
bitsIntialToRepresentIntensityLevels = 8;

img_gray = double(rgb2gray(img_color));

bitsNeededToRepresentIntensityLevels = log2(noOfDesiredIntensityLevels);
if ~isequal(bitsNeededToRepresentIntensityLevels,floor(bitsNeededToRepresentIntensityLevels))
error('Number of intensity levels is not a power of two')
end
new_img = floor(img_gray/(2^(bitsIntialToRepresentIntensityLevels-bitsNeededToRepresentIntensityLevels)));

figure(1)
subplot(1,2,1)
imagesc(img_gray);
subplot(1,2,2)
imagesc((2^(bitsIntialToRepresentIntensityLevels-bitsNeededToRepresentIntensityLevels))*new_img);

• Your thought process follows mine, and it makes it easy for me to follow it. Apart from scaling the image, I probably did the same thing. In my initial learning phase, I am more interested in getting feedback on my code than to get a solution. Can you please point out the error in my code? – flamingo_stark Aug 12 at 10:21
• It is possible that it is related to the treatment of color images and imshow. Could you try this replacements? % img_gray = rgb2gray(img_color); img_gray = double(rgb2gray(img_color)); %imshow(img_gray); imshow(img_gray/255); – Laurent Duval Aug 12 at 10:37
• Is there any change if you modify the two lines mentioned above? – Laurent Duval Aug 13 at 11:27
• Yes, there are changes observed. On setting noOfDesiredIntensityLevels = 2, a binary image is displayed as the output. However, when setting noOfDesiredIntensityLevels to any other value, like 16, 32, etc., the image displayed is all white. – flamingo_stark Aug 16 at 17:55
• I tried debugging the code by manually computing the values of the new_img matrix from the img_gray matrix for intensity levels 2 and 8. It matched exactly with the respective matrices computed in Matlab during execution. Now, I do not understand, if the computed new_img is correct, why is the rendering of the image not as per expectation? – flamingo_stark Aug 16 at 18:40