HSV Image Intensity Restrictions
15 views (last 30 days)
DGM on 3 Jan 2022
Edited: DGM on 5 Jan 2022
It's easy enough to find out.
rgbpict = imread('peppers.png');
hsvpict = rgb2hsv(rgbpict);
% threshold at 50% V
hsvpict(:,:,3) = hsvpict(:,:,3)>0.5;
modifiedpict = hsv2rgb(hsvpict);
Note that in HSV, 0% V is black, but 100% V is not white. It's any color on the upper three faces of the RGB cube. The tuples [1 0 0] and [1 1 1] are both 100% V.
With HSL, HSI, and others, colors with a 100% brightness metric are white -- but they might not turn out that way unless you're careful. For example. We can do the same thing with HSL (using these tools):
That comes out fine, but if the same is done with HSI, YCbCr, or LCHab, we get these:
In all three of these cases, pixels with either 0% or 100% of their brightness metric are black or white respectively. The reason that these images don't fit that description is a matter of constraint. In HSV and HSL, points are constrained. You cannot specify an HSV or HSL tuple in the proper range which does not lie within the parent RGB space. This is not true for the other cases.
Consider the case of YCbCr. Consider a pixel with the color tuple [0 1 0] (green). When in YCbCr, this color resides on a corner of the projected RGB space. It cannot travel along the luma axis (vertically) without going outside.
As the luma of this color is >0.5, it will be given a new luma of 1 (235 in YCbCr), forcing it out of gamut. In the diagram above, the blue star will be the new location of the green pixel. Upon conversion back to RGB, these out-of-gamut colors will have supramaximal or subminimal component values. In other words, they will have unit-scale values <0 and >1. In this case, the new tuple is roughly [0.41 1.41 0.41].
What happens next depends. Some of the conversion tools (e.g. ycbcr2rgb, colorspace) will clamp these values internally. Some tools (e.g.lab2rgb) won't. Even if you get unclamped values out of the conversion, imwrite(), imshow(), or any integer-class recasting will clamp them. Whenever these OOG pixels get clamped in RGB, they move parallel to the RGB axes. In the above case with the green pixel, it is projected back onto the gamut boundary at roughly [0.41 1 0.41] (the dashed line). The resultant tuple no longer has 100% luma as intended. This is why the thresholded images above are not black & white as should be expected. Note also that even though we only edited the luma channel, the chroma has changed as well. If instead of changing the luma, we had altered hue or increased chroma, similar distortions would occur.
How this color distortion is avoided or handled in these cases is a matter of context and usually requires larger-scale (overall workflow) considerations.