Change specific colour in an image
Show older comments
Hi everybody
I have to change the colors of an object in a picture but when i change red to black, it also change the orange colour. Can you help me solve this please ?
a = imread('coloredChips.png');
figure(1), imshow(a);
% Creat menu of color
disp('Color selection')
disp('r for red')
disp('b for blue')
disp('y for yellow')
% Now we want input from user to tell us which color to change
x = input(' Please select the color you want to change: ','s');
copy = a;
dimension = size(a);
row_n = dimension (1);
col_n = dimension (2);
switch x
case 'r'
for row = 1:row_n
for col = 1:col_n
pixel_red = a(row,col,1);
pixel_green = a(row,col,2);
pixel_blue = a(row,col,3);
if (pixel_red >= 200) && (pixel_green <= 50) && (pixel_green >= 5) && (pixel_blue <= 80) && (pixel_blue >=15)
copy(row,col,1) = 0;
copy(row,col,2) = 0;
copy(row,col,3) = 0;
end
end
end
otherwise
disp('Incorrect selection, please choose from the menu');
end
% display the modified image
figure(2),imshow(copy);
6 Comments
Dyuman Joshi
on 28 Mar 2024
Edited: Dyuman Joshi
on 28 Mar 2024
I don't get the logic of comparison here - The red color is the one where the blue and green values are zero.
The comparison you have made is quite different.
There is some information missing in the question - Please clarify.
Phat
on 28 Mar 2024
Dyuman Joshi
on 28 Mar 2024
Edited: Dyuman Joshi
on 30 Mar 2024
The image is actually an indexed image with a colormap. Use the map to convert it to an rgb image via ind2rgb -
%Read the file accordingly
[a, map] = imread('coloredChips.png');
size(a)
%showing image w/o map
%imshow(a)
%showing image w map
%imshow(a, map)
%Change the indexed image to a rgb utilising the colormap
img = ind2rgb(a, map);
%show the image
imshow(img)
coloredChips.png is a standard image that comes with MATLAB.
which coloredChips.png
The copy that OP attached is a screenshot of the original, which has been resized, padded, and converted to a degraded indexed-color copy.
The proposal to use JPG will only make the damage worse. To expand upon the comment I gave below:
% read the original image
goodpict = im2double(imread('coloredChips.png'));
% read the screenshot and try to fix it
[indpict map] = imread('screenshot.png');
badpict = ind2rgb(indpict,map); % convert to RGB
imwrite(badpict,'worsescreenshot.jpg'); % convert to a trash-quality JPG
badpict = im2double(imread('worsescreenshot.jpg'));
badpict = imcrop(badpict,[90.51 29.51 517.98 390.98]); % crop off the padding
badpict = imresize(badpict,size(goodpict,1:2)); % return to original size
% the error is strongest near edges
err = normalize(abs(goodpict-badpict),'range');
imshow(err,'border','tight')
I'm not showing it here, but MSE only tripled by using JPG. That might almost seem like a small change, but look at the apparent resolution of the error map. The image saved by MATLAB is a 75% 4:2:0 JPG. You're throwing away 75% of your chroma information, when your task is critically dependent on using that chroma information.
Using JPG as part of an image processing routine is never the answer.
Dyuman Joshi
on 30 Mar 2024
"coloredChips.png is a standard image that comes with MATLAB."
I was not aware about this. Thanks for the info!
Answers (3)
See also:
While this same assignment has been answered before, this is probably the first time that someone tried doing it with an indexed-color screenshot of the test image. The version of the file that comes with MATLAB is already RGB.
inpict = imread('coloredChips.png');
size(inpict) % you should already have a clean RGB copy
This is worth pointing out, since I see no point in using a degraded screenshot when you already have the original image.
% read the original image
goodpict = im2double(imread('coloredChips.png'));
% read the screenshot and try to fix it
[indpict map] = imread('screenshot.png');
badpict = ind2rgb(indpict,map); % convert to RGB
badpict = imcrop(badpict,[90.51 29.51 517.98 390.98]); % crop off the padding
badpict = imresize(badpict,size(goodpict,1:2)); % return to original size
% the error is strongest near edges
err = normalize(abs(goodpict-badpict),'range');
imshow(err,'border','tight')
Hi Phat,
What you are doing is correct, but in MATLAB pixel values are normalized that means each pixel ranges from “0” to “1” and not from “0” to “255” as you can see in the following link :
When extracting individual pixel, multiply each value with “255” and your code works just fine.
Here is an updated code that works fine in MATLAB R2023b:
[img, map] = imread('coloredChips.png');
a = ind2rgb(img, map);
if size(a, 3) ~= 3
error('The image must be an RGB image.');
end
figure(1), imshow(a);
% Creat menu of color
disp('Color selection')
disp('r for red')
disp('b for blue')
disp('y for yellow')
% Now we want input from user to tell us which color to change
%x = input(' Please select the color you want to change: ','s');
x = 'r';
copy = a;
dimension = size(a);
row_n = dimension (1);
col_n = dimension (2);
switch x
case 'r'
for row = 1:row_n
for col = 1:col_n
pixel_red = a(row,col,1)*255;
pixel_green = a(row,col,2)*255;
pixel_blue = a(row,col,3)*255;
if (pixel_red >= 200) && (pixel_green <= 50) && (pixel_green >= 5) && (pixel_blue <= 80) && (pixel_blue >=15)
copy(row,col,1) = 0;
copy(row,col,2) = 0;
copy(row,col,3) = 0;
end
end
end
otherwise
disp('Incorrect selection, please choose from the menu');
end
% display the modified image
figure(2),imshow(copy);
Hope this helps.
3 Comments
Brett Shoelson
on 15 Aug 2024
Hi all,
I'd like to clarify that Prami's comment ("in MATLAB pixel values are normalized that means each pixel ranges from “0” to “1” and not from “0” to “255”) is true SOMETIMES, BUT NOT ALWAYS. That is typically (but not always) true of "single," "double," or [sometimes] "indexed" images, but it is not typically true for integer-typed images like UINT8, UINT16, INT16.
Consider:
>> img = imread('rice.png');
>> class(img)
ans =
'uint8'
>> [min(img(:)), max(img(:))]
ans = [40 204]
%%
>> img2 = im2double(img);
>> class(img2)
ans =
'double'
>> [min(img2(:)), max(img2(:))]
ans =
0.1569 0.8000
Pay attention to the type of image, and to how the image was created. ("double" can be [0, 1] or [0 255], for instance.) I suggest reading this doc:
Also, im2* conversions. (Generally speaking, you can cast your images to different formats/intensity ranges.)
Cheers,
Brett
% IPT getrangefromclass() operates on an array
getrangefromclass(double(1))
getrangefromclass(int16(1))
% MIMT imclassrange() operates on a class name
% and has other extended functionality
imclassrange('double')
imclassrange('int16')
@Brett Shoelson and @DGM Thank you for pointing that out! I appreciate the correction and your help in improving my understanding.
Simply use the Color Thresholder app on the Apps tab of the tool ribbon to create a mask for the red chips. Then use that mask to blacken the red chips. Full Demo:
% Demo by Image Analyst
clc; % Clear the command window.
close all; % Close all figures (except those of imtool.)
clear; % Erase all existing variables. Or clearvars if you want.
workspace; % Make sure the workspace panel is showing.
format long g;
format compact;
%--------------------------------------------------------------------------------------------------------
% READ IN TEST IMAGE
folder = [];
baseFileName = 'coloredChips.png';
fullFileName = fullfile(folder, baseFileName);
% Check if file exists.
if ~exist(fullFileName, 'file')
% The file doesn't exist -- didn't find it there in that folder.
% Check the entire search path (other folders) for the file by stripping off the folder.
fullFileNameOnSearchPath = baseFileName; % No path this time.
if ~exist(fullFileNameOnSearchPath, 'file')
% Still didn't find it. Alert user.
errorMessage = sprintf('Error: %s does not exist in the search path folders.', fullFileName);
uiwait(warndlg(errorMessage));
return;
end
end
rgbImage = imread(fullFileName);
%--------------------------------------------------------------------------------------------------------
% DISPLAY THE ORIGINAL IMAGE
subplot(2, 2, 1);
imshow(rgbImage, []);
impixelinfo;
axis('on', 'image');
title('Original RGB Image', 'FontSize', fontSize, 'Interpreter', 'None');
% Update the dimensions of the image.
% numberOfColorChannels should be = 1 for a gray scale image, and 3 for an RGB color image.
[rows, columns, numberOfColorChannels] = size(rgbImage);
% Maximize window.
g = gcf;
g.WindowState = 'maximized';
g.Name = 'Demo by Image Analyst';
g.NumberTitle = 'off';
drawnow;
%--------------------------------------------------------------------------------------------
% MASK THE IMAGE.
[mask,maskedRGBImage] = createMask(rgbImage);
% Take the largest 6 blobs.
mask = bwareafilt(mask, 6);
subplot(2, 2, 2);
imshow(mask)
impixelinfo;
axis('on', 'image');
title('Red Mask', 'FontSize', fontSize, 'Interpreter', 'None');
drawnow;
%--------------------------------------------------------------------------------------------
% BLACKEN THE RED CHIPS USING THE MASK.
% Mask the image with the new mask by multiplying each channel by the mask.
% Invert the mask with ~ so that the red will turn to black.
maskedRgbImage = rgbImage .* cast(~mask, 'like', rgbImage);
% Show the masked image with red now turned into black..
subplot(2, 2, 3);
imshow(maskedRgbImage)
impixelinfo;
axis('on', 'image');
title('Masked RGB Image', 'FontSize', fontSize, 'Interpreter', 'None');
%===============================================================================================
% FUNCTION EXPORTED BY THE COLOR THRESHOLDER APP
function [BW,maskedRGBImage] = createMask(RGB)
%createMask Threshold RGB image using auto-generated code from colorThresholder app.
% [BW,MASKEDRGBIMAGE] = createMask(RGB) thresholds image RGB using
% auto-generated code from the colorThresholder app. The colorspace and
% range for each channel of the colorspace were set within the app. The
% segmentation mask is returned in BW, and a composite of the mask and
% original RGB images is returned in maskedRGBImage.
% Auto-generated by colorThresholder app on 15-Aug-2024
%------------------------------------------------------
% Convert RGB image to chosen color space
I = rgb2hsv(RGB);
% Define thresholds for channel 1 based on histogram settings
channel1Min = 0.907;
channel1Max = 0.005;
% Define thresholds for channel 2 based on histogram settings
channel2Min = 0.401;
channel2Max = 1.000;
% Define thresholds for channel 3 based on histogram settings
channel3Min = 0.408;
channel3Max = 1.000;
% Create mask based on chosen histogram thresholds
sliderBW = ( (I(:,:,1) >= channel1Min) | (I(:,:,1) <= channel1Max) ) & ...
(I(:,:,2) >= channel2Min ) & (I(:,:,2) <= channel2Max) & ...
(I(:,:,3) >= channel3Min ) & (I(:,:,3) <= channel3Max);
BW = sliderBW;
% Initialize output masked image based on input image.
maskedRGBImage = RGB;
% Set background pixels where BW is false to zero.
maskedRGBImage(repmat(~BW,[1 1 3])) = 0;
end
Categories
Find more on Display 2-D Images in Help Center and File Exchange
Products
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!





