Changing colors in an image

493 views (last 30 days)
Nancy
Nancy on 10 Jul 2011
Edited: DGM on 12 Feb 2023
Hi Guys,
I have a 1024*1024*3 size .tif picture. The main colors in it are white, black and green. I want to reverse the colors black and white and change green to red. Can anyone tell me how i can do that?
Thanks Nancy
  3 Comments
Image Analyst
Image Analyst on 20 May 2019
Yes it's possible. Just convert the RGB image to HSV, multiply the HSV image by some scale factor in side rem, then call hsv2rgb
hsvImage = rgb2hsv(rgbImage);
hsvImage(:,:,1) = rem(hsvImage(:,:,1) * scale factor, 1);
rgbImage = hsv2rgb(hsvImage);
See attached demos also.
DGM
DGM on 5 Nov 2022
Edited: DGM on 6 Nov 2022
This should have been a new question, but I can't move it and it's dead anyway. I hate to answer it in comments, but I'm going to.
You can colorize white objects, but it's not simple. Adjusting H obviously won't work, and multiplying H isn't the right way to do that anyway. Colorizing white objects presents challenges similar to trying to colorize black. Striking at realism will be more difficult than just getting a "red" shoe. This answer addresses colorization of black objects, and most of the lessons from that apply to white (and to gross masked color adjustment of photographs in general).
For these examples, and for the sake of brevity, I'm going to use the same MIMT tools that I used in the linked answer. At this point, the lessons are largely conceptual anyway. I've posted many answers on the subject of image composition, so I'm not going to cover that here.
I have a photo of some white sneakers and a mask which selects them.
So what happens if we just adjust H? The immediate question that should arise is "how much should we adjust H to get from white to red?". Well, what is the hue of white? Obviously the shoes aren't perfectly white. They're sort of an orange-yellow hue. For sake of visibility, let's just swing hue 180 degrees. That should make it clear what's going on.
%% select the shoe, adjust H
inpict = imread('whitesneakers.jpg');
mkfull = imread('fullmask.png');
modpict = imtweak(inpict,'hsv',[0.5 1 1]);
outpict = replacepixels(modpict,inpict,mkfull);
imshow(outpict)
What's going on is a lot of nothing. They were slightly orange. Now they're slightly blue. If I had adjusted H toward red, you wouldn't have been able to notice, since they're still nominally white. Unless you're splitting hairs, you can't get from white to red by adjusting hue alone.
Well okay, saturation is just too low, and it's too bright. All we need to do is adjust S and V accordingly, right?
% maybe adjust S and V too?
inpict = imread('whitesneakers.jpg');
mkfull = imread('fullmask.png');
modpict = imtweak(inpict,'hsv',[-0.08 15 0.8]);
outpict = replacepixels(modpict,inpict,mkfull);
imshow(outpict)
The source image is a JPG, so the S data is complete garbage due to chroma subsampling. Since the chroma in the ROI is low, that means H is also going to be all over the place. The damage will remain regardless of whether S is scaled or offset.
Okay, well H and S are largely uniform in the ROI, and the particular variations that exist don't really matter to us that much. Maybe we can just replace them entirely.
% maybe just replace H and S entirely?
inpict = imread('whitesneakers.jpg');
mkfull = imread('fullmask.png');
hsvpict = rgb2hsv(inpict);
hsvpict(:,:,1) = 0;
hsvpict(:,:,2) = 0.8;
modpict = hsv2rgb(hsvpict);
outpict = replacepixels(modpict,inpict,mkfull);
imshow(outpict)
That almost works, but much of the contour detail has been lost. This is essentially a very naive uncorrected colorization method, and HSV isn't generally a good model for color adjustment anyway.
There are various better ways to colorize an image. MIMT gcolorize() is a luma-corrected HSL colorization tool. It's pretty crude, but it's better than doing uncorrected work in HSV. At the very least, lighter neutral content can be preserved. Use a linearburn blend to help reinforce the shadows and object contours.
% just colorize the image
inpict = imread('whitesneakers.jpg');
mkfull = imread('fullmask.png');
modpict = gcolorize(inpict,[0 100 -25]);
modpict = imblend(inpict,modpict,1,'linearburn');
outpict = replacepixels(modpict,inpict,mkfull);
imshow(outpict)
That's a lot better, but it still looks pretty dumb. Would the whole shoe be red?
Maybe not.
% colorize parts of the image
inpict = imread('whitesneakers.jpg');
mkupper = imread('uppermask.png');
mklaces = imread('lacemask.png');
mkeyelets = imread('eyeletmask.png');
mkliner = imread('linermask.png');
modpict = gcolorize(inpict,[0 100 -25]);
modpict = imblend(inpict,modpict,1,'linearburn');
outpict = replacepixels(modpict,inpict,mkupper-mkeyelets);
lacepict = imtweak(modpict,'lchok',[1.2 0.8 -0.01]);
linerpict = imtweak(modpict,'lchok',[0.9 0.5 0]);
outpict = replacepixels(lacepict,outpict,mklaces);
outpict = replacepixels(linerpict,outpict,mkliner);
imshow(outpict)
That looks pretty good, but what about ...
inpict = imread('whitesneakers.jpg');
mkupper = imread('uppermask.png');
mklaces = imread('lacemask.png');
mkstripes = imread('lacemask2.png');
mkeyelets = imread('eyeletmask.png');
mkliner = imread('linermask.png');
modpict = gcolorize(inpict,[0 100 -25]);
modpict = imblend(inpict,modpict,1,'linearburn');
outpict = replacepixels(modpict,inpict,mkupper-mkeyelets);
lacepict = imtweak(modpict,'lchok',[1.2 0.8 -0.01]);
stripepict = imtweak(lacepict,'lchok',[1.15 1 0]);
linerpict = imtweak(modpict,'lchok',[0.9 0.5 0]);
outpict = replacepixels(lacepict,outpict,mklaces);
outpict = replacepixels(stripepict,outpict,mkstripes);
outpict = replacepixels(linerpict,outpict,mkliner);
% add some shine to the leather
hloverlay = replacepixels(0,inpict,~mkupper);
hloverlay = imgaussfilt(hloverlay,3); % ugh that JPG
hloverlay = imlnc(hloverlay,'lchab','in',[0.80 0.95],'g',15,'k',2);
shinepict = imblend(hloverlay,outpict,1,'lineardodge',0.05);
outpict = replacepixels(shinepict,outpict,mkupper-mkeyelets-mklaces-mkliner);
imshow(outpict)
Now those are some proper clown shoes. Of course, they don't have to be red.
Of course there's one big question that I have avoided. Where did I get those masks? The answer is simple. You do it by hand using some tool or application that can generate something other than a binary mask. Not everything has to be done in MATLAB. The striped shoelaces is a good example where there was no object content from which a mask could be created programmatically. It would be trivial to create that mask with a brush, but ridiculous to use a binarized polygon selection.
If you want to try to isolate the various white components of a white shoe on a white background using color-based masking, feel free to try. Recall the effective resolution of H and S. If you're fine dealing with jagged results and cumbersome interfaces, you can frustrate yourself with the ROI tools. I don't see the point of using binarized masks for photographic image editing, but that's just me.

Sign in to comment.

Accepted Answer

Image Analyst
Image Analyst on 11 Jul 2011
Try this:
% Construct the rgb image with swapped red and green channels.
thresholdValue = 5; % How much greener does it need to be?
greenishPixels = (single(greenChannel) - 0.5*(single(redChannel) + single(blueChannel))) > thresholdValue;
newRed = 255-greenChannel;
newRed(greenishPixels) = 255;
newGreen = 255-greenChannel;
newGreen(greenishPixels) = 0;
newBlue = 255-blueChannel;
newBlue(greenishPixels) = 0;
rgbImage2 = cat(3, newRed, newGreen, newBlue);
  5 Comments
Nipanshu Agarwal
Nipanshu Agarwal on 28 Sep 2018
HI Image Analyst. Can you please help!!
I have an image of black and grey. The image is containing a series of black spots (Darker to lighter) on grey background. I need to change the black intensity into RED to blue color format with colorbar. RED will be for dark dots and blue will for lighter dots. So that it will depict the intensity change in original image.
Image Analyst
Image Analyst on 28 Sep 2018
By keeping it as it is, gray scale, and applying a color mpa to the display? By segmenting it and converting it to RGB? By segmenting and overlaying a graphics channel? Please answer in a new question (not here), AFTER you read this link

Sign in to comment.

More Answers (5)

Prabhakar
Prabhakar on 10 Jul 2011
The tiff image seems to be in a RGB true color format. If this is so you can change the image from from true color to indexed format using the RGB2IND function.. Then once the data has been converted to an indexed colormap, you can use the inbuilt colormaps to change the indexing.
To switch particular colors, use the FIND function to find the pixels which have a particular color say white and then set the true color to black or something that you want.
A good resource on playing with colormaps is: http://www.mathworks.com/support/tech-notes/1200/1215.html
  1 Comment
Nancy
Nancy on 10 Jul 2011
Thanks prabhakar. When I tried using rgb2ind, it is giving me a warning which I have copypasted below.
Warning: RGB2IND(RGB) is an obsolete syntax.
Specify number of colors, tolerance, or colormap.

Sign in to comment.


Image Analyst
Image Analyst on 10 Jul 2011
All right, it was so easy that I decided to just do it and post it. Here is the code:
clc;
close all;
workspace;
clear;
fontSize = 16;
% Read in a standard MATLAB color demo image.
folder = fullfile(matlabroot, '\toolbox\images\imdemos');
baseFileName = 'peppers.png';
fullFileName = fullfile(folder, baseFileName);
% Get the full filename, with path prepended.
fullFileName = fullfile(folder, baseFileName);
if ~exist(fullFileName, 'file')
% Didn't find it there. Check the search path for it.
fullFileName = baseFileName; % No path this time.
if ~exist(fullFileName, 'file')
% Still didn't find it. Alert user.
errorMessage = sprintf('Error: %s does not exist.', fullFileName);
uiwait(warndlg(errorMessage));
return;
end
end
rgbImage = imread(fullFileName);
% Get the dimensions of the image. numberOfColorBands should be = 3.
[rows columns numberOfColorBands] = size(rgbImage);
% Display the original color image.
subplot(1, 2, 1);
imshow(rgbImage, []);
title('Original Color Image', 'FontSize', fontSize);
% Enlarge figure to full screen.
set(gcf, 'Position', get(0,'Screensize'));
cform = makecform('srgb2lab');
lab = applycform(rgbImage, cform);
L_channel = lab(:,:,1);
A_channel = lab(:,:,2);
B_channel = lab(:,:,3);
L_channelNew = 100 - L_channel;
A_channelNew = 255-A_channel;
labNew = cat(3, L_channelNew, A_channelNew, B_channel);
cform2 = makecform('lab2srgb');
rgbNew = applycform(labNew,cform2);
% Display the new color image.
subplot(1, 2, 2);
imshow(rgbNew, []);
title('Transformed Color Image', 'FontSize', fontSize);
  10 Comments
Image Analyst
Image Analyst on 28 Nov 2015
There is a new function you can use: rgb2lab() that does the color space conversion, using book formulas, all in one line of code.
DGM
DGM on 4 Jan 2023
If I'm not mistaken, the L channel returned by applycform() in this example will span [0 255] due to the uint8 output class. Accordingly,
L_channelNew = 255 - L_channel; % this should be 255-L, not 100-L for uint8 representations
Otherwise, the output will be abnormally dark and clipping will occur in regions which were originally light.

Sign in to comment.


Image Analyst
Image Analyst on 10 Jul 2011
Another way to do it is to convert RGB to LAB color space with makecform(), if you have the image processing toolbox. Then make a new L channel as 100 - the old L channel (to change light to dark), and negate the A channel (to convert green to red), and then convert back to RGB colorspace with makecform again. If you can't figure it out and need code, write back.
  1 Comment
Image Analyst
Image Analyst on 10 Jul 2011
By the way, my logo is a representation of the LAB color space.

Sign in to comment.


Image Analyst
Image Analyst on 11 Jul 2011
It would have been helpful to post the image in the first place. It looks like you're dealing with graphics. So if you can't just draw it in another color, then just swap the red and green channels with cat():
rgbImage2 = cat(3, greenChannel, redChannel, blueChannel);
The Full Demo:
% Read in a color demo image.
folder = 'C:\Documents and Settings\user\My Documents\Temporary stuff';
baseFileName = 'outline.jpg';
fullFileName = fullfile(folder, baseFileName);
% Get the full filename, with path prepended.
fullFileName = fullfile(folder, baseFileName);
if ~exist(fullFileName, 'file')
% Didn't find it there. Check the search path for it.
fullFileName = baseFileName; % No path this time.
if ~exist(fullFileName, 'file')
% Still didn't find it. Alert user.
errorMessage = sprintf('Error: %s does not exist.', fullFileName);
uiwait(warndlg(errorMessage));
return;
end
end
rgbImage = imread(fullFileName);
% Get the dimensions of the image. numberOfColorBands should be = 3.
[rows columns numberOfColorBands] = size(rgbImage);
% Display the original color image.
subplot(1, 2, 1);
imshow(rgbImage, []);
title('Original Color Image', 'FontSize', fontSize);
% Enlarge figure to full screen.
set(gcf, 'Position', get(0,'Screensize'));
% Here's where the real meat of the code begins:
% Extract the individual red, green, and blue color channels.
redChannel = rgbImage(:, :, 1);
greenChannel = rgbImage(:, :, 2);
blueChannel = rgbImage(:, :, 3);
% Construct the rgb image with swapped red and green channels.
rgbImage2 = cat(3, greenChannel, redChannel, blueChannel);
% Display the new color image.
subplot(1, 2, 2);
imshow(rgbImage2, []);
title('Green is now red', 'FontSize', fontSize);
  2 Comments
Nancy
Nancy on 11 Jul 2011
Thanks imageanalyst. How does one know if you are dealing with graphics or RGB image. And what about reversing black and white in the image?
Image Analyst
Image Analyst on 11 Jul 2011
Edited: DGM on 12 Feb 2023
Use this code:
% Construct the rgb image with swapped red and green channels.
thresholdValue = 5; % How much greener does it need to be?
greenishPixels = (single(greenChannel) - 0.5*(single(redChannel) + single(blueChannel))) > thresholdValue;
newRed = 255-greenChannel;
newRed(greenishPixels) = 255;
newGreen = 255-greenChannel;
newGreen(greenishPixels) = 0;
newBlue = 255-blueChannel;
newBlue(greenishPixels) = 0;
rgbImage2 = cat(3, newRed, newGreen, newBlue);

Sign in to comment.


DGM
DGM on 4 Jan 2023
Edited: DGM on 4 Jan 2023
OP's original image is long gone, but I can perhaps guess at its appearance. While I have my suspicions, let's start where this thread originally started. @Image Analyst suggested a L,A inversion in LAB, so let's start there. As usual, I'm going to use MIMT tools for these examples because it is much more concise in this case, but the main lessons here are conceptual and maybe a bit unexpected.
So let's assume we have some photograph. We want to trade black with white and green with red. As Image Analyst shows, this is simple to do in LAB. This is one of a number of opponent color spaces. You could say that the A channel is the difference of redness and greenness. If we invert A, reddish colors become greenish and vice-versa. Likewise, if we invert L, the brightness of all colors is inverted; black and white are swapped. YCbCr and other luma-chroma models are similar. In the case of YCbCr and YPbPr, the third channel is a red-green axis.
How would we accomplish this in practice? One way is shown in IA's answer, though in 2011 there weren't as many options as there are today. You could do the conversions with applycform() as shown, or you can use the newer rgb2lab() and lab2rgb(). As this is a basic color adjustment task and I don't like reinventing wheels every time, I just use MIMT imtweak(). Let's do this in both LAB and YPbPr.
inpict = imread('peppers.png');
OPbr = imtweak(inpict,'ypbpr',[-1 1 -1; 1 0 0]);
OPab = imtweak(inpict,'lab',[-1 -1 1; 1 0 0]);
outpict = cat(2,OPbr,OPab);
Consider the parameter [-1 1 -1; 1 0 0] used in the first call to imtweak(). This describes a simple linear transformation of the channels. The first row are scaling factors and the second row are offsets. So Yout = 1-Yin, Pbout = Pbin, and Prout = -Prin. By default, these parameters are normalized with respect to the extent of sRGB within the given color space. This generally helps make the parameter selection independent of the color model.
Note that the offset on Pr, A is zero in both YPbPr and LAB. Unless we're dealing with unsigned integer representations as in the case with the applycform() example, the neutral axis is where the chroma channels are zero. Note the axis limits in the plots above.
While flipping A or Pr certainly makes the red pepper green and makes the green peppers red-ish (more of an odd fleshy color), this might be done in a slightly different way. Look back at those plots above. Pay particular attention to YPbPr and the angles formed between the red and green corners with respect to the Pr = 0 line. Those two angles are not equal, so flipping Pr or A will roughly swap red and green, but you could get the angles closer by doing the transformation in polar space. Instead of flipping Pr, we could just transform hue in the same way.
The difference is small in the case of LAB, but perhaps more noticeable in YPbPr. The red pepper is less teal. The green peppers are slightly less yellow.
OPbr = imtweak(inpict,'lchbr',[-1 1 -1; 1 0 340/360]);
OPab = imtweak(inpict,'lchab',[-1 1 -1; 1 0 176/360]);
outpict = cat(2,OPbr,OPab);
Of course, it's a bit difficult to tell how this scene should look given the brightness inversion, but it's arguable that we've accomplished the task of swapping red and green, black and white.
Now as to OP's mystery image, I can only guess. Instead of a photograph, it seems that it was likely some kind of simple synthetic image, perhaps a graph or chart. Does that change anything? After all, we've already solved this for photographs using LAB, and that should be good enough for a simpler image, right? Anyone who has seen my other answers about color adjustments knows that I often discourage using simple HSV/HSL for color adjustment. Here's where things get counterintuitive. Depending on the particular colors that populate the image (I assume there are very few), using LAB might not do a good job (or at least it might be difficult to make it do a good job).
Consider the following simple image full of very simple colors. At the top is white; at the bottom is black. The middle row (ignoring gray) are colors which lie along the primary-secondary edges of the RGB cube -- the six corners and midpoints in between.
What happens if we try to process this as before? I'm going to use the LCH method here.
It seems vivid green turned into dark red and everything else looks off in unexpected ways. What's the deal? Part (but not all) of what's going on is a consequence of truncation. The rest is all a matter of geometry. When we did those flips/rotations before, all the color points that get moved need to eventually end up back in gamut (the colored area in the plots). It doesn't take much visualization to realize that gross rotations and brightness inversions will move a lot of things out of gamut. Bold colors on the corners of the RGB cube are the easiest to push out of gamut, and they may be fairly common in basic graphics. Likewise, these primary-secondary colors are not coplanar in YPbPr or LAB, so brightness inversion throws things all over. Consider [0 1 0] green; if we rotate it to the same angle as [1 0 0] red, then invert its lightness, it winds up out of gamut where the blue star is.
If we truncate after converting to RGB, the blue-yellow dashed line shows where it winds up -- an ugly dark red. If we truncate chroma prior to converting to RGB, it follows the black dotted line -- to another dark red. The default behavior of imtweak() is to truncate chroma, but this can be changed. In either case, it's dark red.
It's the image content that really determines what our expectations are. Whether these distortions are small or large, if they complement our expectations, this may be an appropriate result. Often it's desirable for primary green to not become primary red after a hue rotation. Maybe the preservation of an object's relative brightness in a scene is more important than its exact hue/chroma. When the amount of detail and number of colors in an image are reduced dramatically, it's easy to come to different conclusions about what should happen. Maybe primary green should become primary red?
So what if we had a simple image composed of a handful of these simple colors (and maybe some antialiasing). What else could we do? How about those basic polar models like HSV/HSL/HSI?
OPhsl = imtweak(inpict,'hsl',[-1 1 -1; 1/3 0 1]);
OPhsi = imtweak(inpict,'hsi',[-1 1 -1; 1/3 0 1]);
OPhsv = imtweak(inpict,'hsv',[-1 1 -1; 1/3 0 1]);
For these colors on the face of the cube, HSL actually swaps R and G. The reason this happens is that all the colors on the primary-secondary edges of the RGB cube are coplanar and uniformly spaced in HSL. The model is symmetrical on L and there's no risk of moving out of gamut, so the rotation and inversion works neatly.
Unlike HSV/HSL, HSI is not constrained. Since primary-secondary colors are not coplanar in HSI, half the colors on this locus will be pushed out of gamut when I is inverted. This causes apparent distortions of local color and contrast in our simplified image, much like it did in LAB and YPbPr.
HSV won't work directly due to its asymmetry on V. HSV can be visualized as an inverted cone. Primary-secondary colors, white, and everything inbetween (the upper faces of the RGB cube) are all coplanar in HSV, so inverting V takes all these colors on the base of the cone and collapses them to a single point (black).
So there. Depending on the colors and content, sometimes the simple tools like HSL are the elegant solution.

Categories

Find more on Image Processing Toolbox in Help Center and File Exchange

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!