Well, it's been quite a while since I wrote one of these. As work is a little slow at the moment, I thought I'd do one on an important topic, that of color. An important caveat - I am color blind. So when I talk about how the colors work, I'm largely taking other people's word for it.
I'm sure most of us are aware that when you need to specify a color to your PC, you do it with an RGB triple, or ARGB if you want to specify transparency. What this in essence means is that your CRT has three color guns, and your LCD has sets of three colored lights to make up a pixel. The merging of differing levels of these three colors equate to the range of colors that can be displayed by your computer, like this:
This is, however, not the only possible way to describe color, nor is it a method that makes much sense to humans. If I were to ask you how to describe orange, or yellow, or purple, using RGB, chances are you'd have to undergo some trial and error to work it out. HSL is the most common color system that exists to be human friendly, rather than machine friendly.
As RGB stands for red, green, blue, so to, HSL is an acronym, in this case for hue, saturation, luminance. The three components of color are best specified in that order, as they represent a constant refining of the value ( that is, saturation and luminance values are close to meaningless without a hue ).
The hue is the actual base color being used, free of any modification to brightness or strength. It is commonly represented as a circle, in which the hue value ( which ranges from 0 to 360 ) indicates the angle in degrees of the color as present on the wheel. The following image shows the hue circle, with constant luminance and saturation at 50%.
Saturation describes how 'colorful' a color is, for example, a fluorescent color would have a high saturation. In order to demonstrate this, I have provided three screenshots, all of the hue wheel with 0 luminance, and with .25, .5 and .75 saturation. A saturation of 0 makes an image greyscale ( as it has no color in it ), and so I don't provide an image at 100, because I wanted the range shown to be even.
Luminance describes how bright a color is, so that full luminance is always white, and no luminance is always black. In order to demonstrate this, I have provided three screenshots, all of the hue wheel with 0 luminance, and with .25, .5 and .75 saturation.
The color chart
The sample application continues to build on previous installments, and thus builds the code base for use in other projects. There is now a new menu called 'colorspaces', with a view to expanding it to cover other color spaces in the future. The 'HSL Chart' menu item brings up a dialog with a hue circle, and a slider on the side, like this:
The hue wheel either modifies saturation, or luminance from the centre to the outside, and the slider then modifies luminance or saturation accordingly. This should give you a really good idea of exactly how these parameters work, and how they modify colors.
Using the code
So, we have this color space, but what do we do with it ? Well, two things pop immediately to mind. First, we can provide means so that a user can select colors using this color space, instead of having to provide an RGB triple. Secondly, we can provide image filters that allow modification of an image based on these three values. But in order to do any of this ( or even to do what you've seen already ) we need to be able to move within this color space, we need to be able to convert between HLS and RGB. In order to do this, the first component we will examine is the HLS class.
All the new code is in the ColorSpace.cs file. The first class in there is called YUV, another color system that I wrote a class for, but do not examine here. Next is the HLS class, which encapsulates a HLS color. It keeps these values in private members and exposes them through properties, so that we can correct out of bounds values. I choose not to throw an exception, because when we use this class with filters, we will amost certainly pass in an out of bounds value.
private float h;
private float s;
private float l;
public float Hue
h = (float)(Math.Abs(value)%360);
public float Saturation
s = (float)Math.Max(Math.Min(1.0, value), 0.0);
public float Luminance
l = (float)Math.Max(Math.Min(1.0, value), 0.0);
The constructor with no arguments is private so that we can't construct an HLS object without specifying it's values. We also provide a property called RGB, which returns a Color that maps to the current HLS values.
In addition, two static methods are provided, which return an HSL object from either a Color, or specified red, green and blue values. Our filters will use these static methods to build an HLS object, then modify one of these values before requesting the modifed colors.
Most HSL color pickers present a hue wheel with varying saturation from the centre to the edge, and then a slider to set luminance for the chosen hue/saturation combination. I don't like this format, because naturally values towards the centre of the circle are underrepresented, and harder to pick. Instead, I propose a system of three sliders, one each for hue, saturation and luminance. Saturation and luminance on the hue slider are set to .5, as is luminance on the saturation slider. Therefore, it's intuitive to move from left to right and select a color.
As you can see, text boxes are provided as well as sliders, the selected color is shown on the right, and it's RGB values are also displayed. The test application will remember the selected color and initialise the dialog with that color when OK is pressed. The HSLColorPicker has a SelectedColor property which can be set before displaying the dialog, and which returns the selected color after the dialog is closed. It returns a Color rather than a HSL object, but can easily be changed if desired.
Three image filters are provided, one each for hue, saturation and luminance. The filters take a float and multiply the value being filtered by that number, so that 1 is an identity transform. This causes all values to trend evenly, but has the side effect of stopping values of 0 from changing at all. There are numerous ways around this, including adding a small number to values before multiplication, or accepting a value to add as well as one to multiply by. The hue filter is kind of odd, give the nature of the hue wheel, it simply changes the colors to unrelated values. The saturation and luminance filters are, however, quite useful and worth incorperating into any image processing library.
As always, I present my son as a model for my filters. From top to bottom is the normal image, the hue filter, the saturation filter, and the luminance filter. I've tried to use extreme values to exaggerate the effect to make it obvious. The saturation effect in particular is not that obvious, because his car is fluorecently colored anyhow.
There are numerous ways to represent color, in this article I have focused on one way that is commonly used in paint programs and so on, and which translates easily to human understanding. This means both that it's a good way of asking people to select a color, and that filtering by enhancing or suppressing these values will result in an effect that has uniform meaning to the human eye. Any person who needs to ask a user to select a color should consider using HSL as the means of doing so.
1.0 First release. Also fixed a bug, I thought at the .Save method for an image would save in the correct format for the file extension, it seems it always saves as PNG. The code now works out the encoder on it's own.