Increase RGB image saturation.

28 views (last 30 days)
Matt
Matt on 30 Jun 2011
Commented: DGM on 27 Feb 2025
How would I go about increasing the saturation of an RGB image?

Accepted Answer

Jan
Jan on 30 Jun 2011
This is easier in HSV colorspace:
RGB = rand(100, 100, 3);
HSV = rgb2hsv(RGB);
% "20% more" saturation:
HSV(:, :, 2) = HSV(:, :, 2) * 1.2;
% or add:
% HSV(:, :, 2) = HSV(:, :, 2) + 0.2;
HSV(HSV > 1) = 1; % Limit values
RGB = hsv2rgb(HSV);
  2 Comments
Andrei
Andrei on 26 Feb 2025
From my point of view this is not exactly correct. And as far as I see, basically all image processing suffers from this problem.
For a correct color space conversion (which is, in fact, just some matrix algebra), all images should be in linear space.
However, sRGB is not a linear space! So, before applying any kind of image processing on images that you know are standard RGB images, the first thing you need to do is to de-gamma the image (that is, to linearize the space). Normally, for sRGB, gamma = 2.2.
So a correct implementation would be:
RGB = double(imread('peppers.png'))/255; %make sure the image is treated as floating point
% additionally, for the de-gamma you need the image to range from 0 to 1
gamma = 2.2; %standard gamma value - adjust based on application - for
%RAW images form the camera, the gamma most probably would be 1, but for
%normal JPG images in sRGB color space, gamma is 2.2
%de-gamma the image
RGB_de_gamma = RGB.^(1/gamma);
%apply the color conversion
HSV = rgb2hsv(RGB_de_gamma);
% "20% more" saturation:
HSV(:, :, 2) = HSV(:, :, 2) * 1.2;
% or add:
% HSV(:, :, 2) = HSV(:, :, 2) + 0.2;
HSV(HSV > 1) = 1; % Limit values
%convert back to RGB values
RGB_de_gamma = hsv2rgb(HSV);
%re-apply gamma to convert to sRGB color space
RGB = RGB_de_gamma.^gamma;
%restore levels between 0 and 255 (for uint8 range)
RGB = uint8(RGB * 255);
imshow([imread('peppers.png') RGB])
DGM
DGM on 27 Feb 2025
I get the motivation, but I always got poor results from doing it. It's why I abandoned the idea of adding this as a direct option to imtweak(). It sometimes turns out agreeable for small adjustments on images with moderate contrast, but it often seems to exacerbate the limitations of HSx. Consider two reciprocal adjustments (1.5x and 0.67x) in HSV and HSL, compared to a (similar) adjustment done in LCHab:
HSV:
HSL:
Regardless of whether the input is linear or sRGB, the HSx models aren't terribly good at doing significant HS adjustments without gross distortion of brightness. To compound the problem, using them with linearized RGB tends to cause odd brightness inversions and other particular artifacts.
For significant saturation increases in HSV, it's easy to see blown out regions, and we get that in both cases, but note the contrast inversion in the yellow and orange peppers. For saturation decreases, the use of linear RGB only makes the change in colorfulness notably less uniform. The yellow pepper is still vibrant, while the red peppers are sickly.
I usually consider HSL to be a bit better for the task than HSV due to the symmetry of the saturation space, but these arguably extreme increases still don't turn out very well. The linear version suffers a bit more generally, but there are tons of damage in the green peppers caused by aggressive contrast artifacts. The saturation decrease examples are both a bit dark overall, but the linear RGB version is significantly darker, the brightest and most colorful regions being disproportionately skewed.
In all cases, there are regions where it does seem that the use of linear RGB does help preserve some brightness or contrast information that the sRGB workflow loses, but these scattered benefits don't seem to come consistently without detriment. That said, this image might not be the most universal example. These are relatively significant adjustments on an image with smooth graduations, significant larger-scale contrast, and relatively high average saturation -- basically the opposite of what I suggested might be a good use for linear RGB and HSx. YMMV.
I still think that models other than simple HSx will usually (but not always) do a better job at HS adjustments with less hassle. The downside is that they're generally slower, and they're inconvenient with base tools. I'd rather have all of the above options at my disposal, and use them as the results dictate.
FWIW, here's the script I used. Since this is just a comparison, I used convenience tools, but the result is the same either way.
% parameters
ks = 1.5;
model = 'hsv';
% the image
inpict = imread('peppers.png');
% HSx adjustment in linear rgb
gamma = 2.2;
lin = im2double(inpict).^(1/gamma);
op1 = imtweak(lin,model,[0 ks 1]);
op1 = op1.^gamma;
op1 = im2uint8(op1); % reassert original class
op1 = puttext(op1,'lin RGB HSx','scale',2); % add a label
% HSx adjustment in srgb
op2 = imtweak(inpict,model,[0 ks 1]);
op2 = puttext(op2,'sRGB HSx','scale',2);
% just use something other than HSx
op3 = imtweak(inpict,'lchab',[1 ks 0]);
op3 = puttext(op3,'LCHab','scale',2);
% build the composite image
ip0 = puttext(inpict,'original','scale',2);
outpict = [ip0, op3; op2, op1];
imshow2(outpict,'invert')

Sign in to comment.

More Answers (1)

DGM
DGM on 22 May 2021
Edited: DGM on 2 Jul 2023
Out of boredom and self-interest, I'll add the convenient but nonstandard way of doing this with MIMT. Base MATLAB/IPT doesn't have any convenient tools to do color adjustment of images, but MIMT has imtweak():
As in the above example, you could use HSV if you wanted:
inpict = imread('peppers.png');
amount = 2; % a rather significant saturation/chroma alteration
A = imtweak(inpict,'hsv',[0 amount 1]);
imshow([inpict A]) % show original and modified image side by side
or you could use HSL
B = imtweak(inpict,'hsl',[0 amount 1]);
imshow([inpict B])
or whatever color model you want
C = imtweak(inpict,'lchab',[1 amount 0]);
imshow([inpict C])
even esoteric things that nobody uses
D = imtweak(inpict,'huslok',[0 amount 1]);
imshow([inpict D])
I can never find good example images for demonstrating these things, but you get the drift. Generally, the LCH methods are going to have the least risk of causing brightness distortions due to large saturation/chroma increases or hue rotations. Note the loss of highlight contrast in the HSV image. HSL tends to do a better job due to its symmetry, but note how it still exaggerates the yellowish regions on the green peppers.
It's worth noting that increasing chroma (in LCH) pushes color points radially toward the gamut extents (and beyond). How that is handled can make a significant difference in the apparent quality of the result. Consider the third example. Increasing the chroma 50% pushes roughly 15% of color points out of gamut. By default, imtweak() internally clamps those color points at the gamut extent prior to converting back to RGB. When using simple methods (e.g. using rgb2lab() and editing the chroma directly) truncation typically will happen at some point after conversion to RGB. If you were to let the truncation occur in RGB, this is what you'd get instead.
Truncation in LCH merely limits the allowed chroma increase in saturated regions. Truncation in RGB pushes color points toward the primary-secondary corners of the RGB cube and can cause noticeable hue/brightness shifts in regions where clipping occurs. Keep that in mind if you're trying to manually edit channels in the often-suggested manner.
The HuSLok example is only included for sake of demonstrating flexibility, not to suggest that this is a particularly good application for HuSL.
imtweak() is part of MIMT:
  2 Comments
Dinesh
Dinesh on 28 Jun 2023
But is it possible increase the Saturation from RGB itself? without changing the color space?
DGM
DGM on 28 Jun 2023
Edited: DGM on 29 Jun 2023
Yes, you can. To increase the saturation of a given pixel in RGB, simply move it radially away from the neutral diagonal of the RGB cube in proportion to its position between the neutral diagonal and the surface of the cube. While that's easy to visualize, the math required to effect such a geometric transformation is equivalent to converting to HSL, HSV, HSI, or HSY (depending on which assumptions and simplifications are made).
Is there a quick and dirty trick that you can do that kind of works strictly in RGB? Yes. Kind of.
kc = 1.5; % chroma scale factor
inpict = imread('peppers.png');
inpict = im2double(inpict);
V = max(inpict,[],3);
outpict = V + kc*(inpict - V);
imshow(outpict,'border','tight')
... but it's essentially an ugly, worse version of boosting saturation in HSV -- which is already the worst option to begin with.
To be more accurate, this is manipulating the HSV concept of chroma, which is simply the spread of the values within an RGB tuple. Since the brightness metric in HSV is the maximum value in an RGB tuple, we can manipulate C independent of V by stretching the value range while keeping the maximum value fixed. While it works, it's still pretty terrible. V is a terrible brightness metric, and the asymmetry of HSV (even this pseudo HSV) makes it a poor choice for HS adjustment.
You could do similar using I instead of V, to preserve the mean. While I is a better brightness metric than V, stretching C this way is still going to be fairly poor but for very small adjustments.
I = mean(inpict,3);
outpict = I + kc*(inpict - I);
Since we're working with C, which is a denormalized quantity, there's nothing stopping us from pushing colors out of gamut with this trick. Depending on what you do next, you might want to clamp the output values.
In short, you could reinvent the wheel if you really wanted to, but it's questionable whether there is a clear benefit to doing so. Bear in mind that HSV was developed for computers in 1974. It's hard to find a scenario today where HSx models are too expensive to use, but it's inarguable that the above trick can certainly be faster than a round-trip HSV conversion. It's up to you to decide what compromises you're willing to make.

Sign in to comment.

Community Treasure Hunt

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

Start Hunting!