How to colorize via HSL colors? - colors

I have an complex problem, but it could be solved via this little problem. I would like to make a colorizer, which works per pixel.
I have a defined base color for the picture (the picture has some pixels with this color and a lot of other pixels, which are near to this color):
Hex: #188DD9
HSL: 204° 80% 47%
RGB: 24 141 217
I know my target base color:
Hex: #23752E
HSL: 128° 54% 30%
RGB: 35 117 46
So, I would like to colorize an image.
My assumption is that if I find the correlation in this two HSL values, I can
colorize my picture pixel by pixel.
Currently I found that if I move the base color hue with (target hue - base hue) = -76, the hue will be fine.
Could you direct me where is the connexion between saturation and lightness to solve this problem?

I assume that, whatever mapping you end up using, you want to map black to black, white to white and grays to grays.
The additive mapping
H' = H + (Htarget - Hbase)
(with wrap-around) you use for hue is indeed probably fine. For saturation, the need to preserve gray values suggest a multiplicative mapping
S' = S * (Starget / Sbase),
with values exceeding 100% saturation clipped. However, for lightness, a linear map just isn't going to do it, since you want to fix both 0% and 100% lightness while adjusting intermediate values. A natural choice might instead be a gamma-type map, i.e.
L' = pow( L, log(Ltarget) / log(Lbase) ) = exp( log(L) * log(Ltarget) / log(Lbase) ).
where the lightness values are scaled to between 0 and 1. (Note: To calculate this map efficiently for lots of pixels, you probably want to precompute a lookup table of, say, 256 entries.)
Of course, there are plenty of other maps you could use, but I'd start with these and see if they give good enough results. Note that, ultimately, the quality of your results may also be limited by the perceptual non-uniformity of the HSL color space; for details, see this Wikipedia page.

Related

Relation of luminance in RGB/XYZ color and physical luminance

Short version: When a color described in XYZ or xyY coordinates has a luminance Y=1, what are the physical units of that? Does that mean 1 candela, or 1 lumen? Is there any way to translate between this conceptual space and physical brightness?
Long version: I want to simulate how the sky looks in different directions, at different times of day, and (eventually) under different cloudiness and air pollution conditions. I've learned enough to figure out how to translate a given spectrum into a chrominance, for example xyz coordinates. But almost everything I've read on color theory in graphical display is focused on relative color, so the luminance is always 1. Non-programming color theory describes the units of luminance, so that I can translate from a spectrum in watts/square meter/steradian to candela or lumens, but nothing that describes the units of luminance in programming. What are the units of luminance in XYZ coordinates? I understand that the actual brightness of a patch would depend on monitor settings, but I'm really not finding any hints as to how to proceed.
Below is an example of what I'm coming across. The base color, at relative luminance of 1, was calculated from first principles. All the other colors are generated by increasing or decreasing the luminance. Most of them are plausible colors for mid-day sky. For the parameters I've chosen, I believe the total intensity in the visible range is 6.5 W/m2/sr = 4434 cd/m2, which seems to be in the right ballpark according to Wiki: Orders of Magnitude. Which color would I choose to represent that patch of sky?
Without more, luminance is usually expressed in candelas per square meter (cd/m2), and CIE XYZ's Y component is a luminance in cd/m2 — if the convention used is "absolute XYZ", which is rare. (The link is to an article I wrote which contains more detailed information.) More commonly, XYZ colors are normalized such that the white point (such as the D65 or D50 white point) has Y = 1 (or Y = 100).

Why is color segmentation easier on HSV?

I've heard that if you need to do a color segmentation on your software (create a binary image from a colored image by setting pixels to 1 if they meet certain threshold rules like R<100, G>100, 10< B < 123) it is better to first convert your image to HSV. Is this really true? And why?
The big reason is that it separates color information (chroma) from intensity or lighting (luma). Because value is separated, you can construct a histogram or thresholding rules using only saturation and hue. This in theory will work regardless of lighting changes in the value channel. In practice it is just a nice improvement. Even by singling out only the hue you still have a very meaningful representation of the base color that will likely work much better than RGB. The end result is a more robust color thresholding over simpler parameters.
Hue is a continuous representation of color so that 0 and 360 are the same hue which gives you more flexibility with the buckets you use in a histogram. Geometrically you can picture the HSV color space as a cone or cylinder with H being the degree, saturation being the radius, and value being the height. See the HSV wikipedia page.

Why does greyscale work the way it does?

My original question
I read that to convert a RGB pixel into greyscale RGB, one should use
r_new = g_new = b_new = r_old * 0.3 + g_old * 0.59 + b_old * 0.11
I also read, and understand, that g has a higher weighting because the human eye is more sensitive to green. Implementing that, I saw the results were the same as I would get from setting an image to 'greyscale' in an image editor like the Gimp.
Before I read this, I imagined that to convert a pixel to greyscale, one would convert it to HSL or HSV, then set the saturation to zero (hence, removing all colour). However, when I did this, I got a quite different image output, even though it also lacked colour.
How does s = 0 exactly differ from the 'correct' way I read, and why is it 'incorrect'?
Ongoing findings based on answers and other research
It appears that which luminance coefficients to use is the subject of some debate. Various combinations and to-greyscale algorithms have different results. The following are some presets used in areas like TV standards:
the coefficients defined by ITU-R BT.601 (NTSC?) are 0.299r + 0.587g + 0.114b
the coefficients defined by ITU-R BT.709 (newer) are 0.2126r + 0.7152g + 0.0722b
the coefficients of equal thirds, (1/3)(rgb), is equivalent to s = 0
This scientific article details various greyscale techniques and their results for various images, plus subjective survey of 119 people.
However, when converting an image to greyscale, to achieve the 'best' artistic effect, one will almost certainly not be using these predefined coefficients, but tweaking the contribution from each channel to produce the best output for the particular image.
Although these transformation coefficients exist, nothing binds you to using them. As long as the total intensity of each pixel is unchanged, the contributions from each channel can be anything, ranging from 0 to 100%.
Photographers converting images to grayscale use channel mixers to adjust levels of each channel (RGB or CMYK). In your image, there are many reds and greens, so it might be desirable (depending on your intent) to have those channels more highly represented in the gray level intensity than the blue.
This is what distinguishes "scientific" transformation of the image from an "artistic" combination of the bands.
An additional consideration is the dynamic range of values in each band, and attempting to preserve them in the grayscale image. Boosting shadows and/or highlights might require increasing the contribution of the blue band, for example.
An interesting article on the topic here.... "because human eyes don't detect brightness linearly with color".
http://www.scantips.com/lumin.html
Looks like these coefficients come from old CRT technology and are not well adapted to today's monitors, from the Color FAQ:
The coefficients 0.299, 0.587 and
0.114 properly computed luminance for monitors having phosphors that were
contemporary at the introduction of
NTSC television in 1953. They are
still appropriate for computing video
luma to be discussed below in section
11. However, these coefficients do not accurately compute luminance for
contemporary monitors.
Couldn't find the right conversion coefficient, however.
See also RGB to monochrome conversion
Using s = 0 in HSL/HSV and converting to RGB results in R = G = B, so is the same as doing r_old * 1/3 + g_old * 1/3 + b_old * 1/3.
To understand why, have a look at the Wikipedia page that describes conversion HSV->RGB. Saturation s will be 0, so C and X will be, too. You'll end up with R_1,G_1,B_1 being (0,0,0) and then add m to the final RGB values which results in (m,m,m) = (V,V,V). Same for HSL, result will be (m,m,m) = (L,L,L).
EDIT: OK, just figured out the above is not the complete answer, although it's a good starting point. RGB values will be all the same, either L or V, but it still depends on how L and V were originally calculated, again, see Wikipedia. Seems the program/formulas you've used for converting used the 1/3 * R + 1/3 * G + 1/3 * B solution or one of the other two (hexcone/bi-hexcone).
So after all, using HSL/HSV just means you'll have to decide which formula to use earlier and conversion to RGB grayscale values later is just isolating the last component.

Convert grayscale value to RGB representation?

How can I convert a grayscale value (0-255) to an RGB value/representation?
It is for using in an SVG image, which doesn't seem to come with a grayscale support, only RGB...
Note: this is not RGB -> grayscale, which is already answered in another question, e.g. Converting RGB to grayscale/intensity)
The quick and dirty approach is to repeat the grayscale intensity for each component of RGB. So, if you have grayscale 120, it translates to RGB (120, 120, 120).
This is quick and dirty because the effective luminance you get depends on the actual luminance of the R, G and B subpixels of the device that you're using.
If you have the greyscale value in the range 0..255 and want to produce a new value in the form 0x00RRGGBB, then a quick way to do this is:
int rgb = grey * 0x00010101;
or equivalent in your chosen language.
Conversion of a grayscale to RGB is simple. Simply use R = G = B = gray value. The basic idea is that color (as viewed on a monitor in terms of RGB) is an additive system.
http://en.wikipedia.org/wiki/Additive_color
Thus adding red to green yields yellow. Add in some blue to that mix in equal amounts, and you get a neutral color. Full on [red, green, blue] = [255 255 255] yields white. [0,0,0] yields monitor black. Intermediate values, when R=G=B are all equal will yield nominally neutral colors of the given level of gray.
A minor problem is depending on how you view the color, it may not be perfectly neutral. This will depend on how your monitor (or printer) is calibrated. There are interesting depths of color science we could go into from this point. I'll stop here.
Grey-scale means that all values have the same intensity. Set all channels (in RGB) equal to the the grey value and you will have the an RGB black and white image.
Woudln't setting R,G,and B to the same value (the greyscale value) for each pixel get you a correct shade of gray?
You may also take a look at my solution Faster assembly optimized way to convert RGB8 image to RGB32 image. Gray channel is simply repeated in all other channels.
The purpose was to find the fasted possible solution for conversion using x86/SSE.

What is the formula for alpha blending for a number of pixels?

I have a number of RGBA pixels, each of them has an alpha component.
So I have a list of pixels: (p0 p1 p2 p3 p4 ... pn) where p_0_ is the front pixel and p_n_ is the farthest (at the back).
The last (or any) pixel is not necessary opaque, so the resulting blended pixel can be somehow transparent also.
I'm blending from the beginning of the list to the end, not vice-versa (yes, it is raytracing). So if the result at any moment becomes opaque enough I can stop with correct enough result.
I'll apply the blending algorithm in this way: ((((p0 # p1) # p2) # p3) ... )
Can anyone suggest me a correct blending formula not only for R, G and B, but for A component also?
UPD: I wonder how is it possible that for determined process of blending colors we can have many formulas? Is it some kind of aproximation? This looks crazy, as for me: formulas are not so different that we really gain efficiency or optimization. Can anyone clarify this?
Alpha-blending is one of those topics that has more depth than you might think. It depends on what the alpha value means in your system, and if you guess wrong, then you'll end up with results that look kind of okay, but that display weird artifacts.
Check out Porter and Duff's classic paper "Compositing Digital Images" for a great, readable discussion and all the formulas. You probably want the "over" operator.
It sounds like you're doing something closer to volume rendering. For a formula and references, see the Graphics FAQ, question 5.16 "How do I perform volume rendering?".
There are various possible ways of doing this, depending on how the RGBA values actually represent the properties of the materials.
Here's a possible algorithm. Start with final pixel colours lightr=lightg=lightb=0, lightleft=1;
For each r,g,b,a pixel encountered evaluate:
lightr += lightleft*r*(1-a)
lightg += lightleft*g*(1-a)
lightb += lightleft*b*(1-a)
lightleft *= 1-a;
(The RGBA values are normalised between 0 and 1, and I'm assuming that a=1 means opaque, a=0 means wholly transparent)
If the first pixel encountered is blue with opacity 50%, then 50% of the available colour is set to blue, and the rest unknown. If a red pixel with opacity 50% is next, then 25% of the remaining light is set to red, so the pixel has 50% blue, 25% red. If a green pixel with opacity 60% is next, then the pixel is 50% blue, 25% red, 15% green, with 10% of the light remaining.
The physical materials that correspond to this function are light-emitting but partially opaque materials: thus, a pixel in the middle of the stack can never darken the final colour: it can only prevent light behind it from increasing the final colour (by being black and fully opaque).

Resources