Calculating the Hue of a Color as a 0 - 255 range - wpf

I am trying to write a method which will work on WPF's Color struct to return a byte representing the Hue of the Color. I am struggling with the formula, I have written unit tests for the hues of Black (255, 255, 255), Red (255, 0, 0), Green (0, 255, 0), Blue (0, 0, 255), Yellow (255, 255, 0), Cyan (0, 255, 255), Magenta (255, 0, 255) and White (255, 255, 255) - they all pass except Yellow (which returns 1 instead of 42) and Magenta (which tries to return -1 instead of 212 but fails as -1 won't cast into a byte).
My extension methods for GetHue appear as follows:
public static byte GetHue(this Color color)
=> GetHue(color.ToRgbDictionary(), color.GetMinComponent(), color.GetMaxComponent());
private static byte GetHue(Dictionary<ColorComponent, byte> rgbDictionary, ColorComponent minimumRgbComponent, ColorComponent maximumRgbComponent)
{
decimal chroma = rgbDictionary[maximumRgbComponent] - rgbDictionary[minimumRgbComponent];
return (byte)
(
chroma == 0
? 0
: thirdOfByte
* (byte)maximumRgbComponent
+
(
rgbDictionary[MathHelper.Wrap(maximumRgbComponent + 1)]
- rgbDictionary[MathHelper.Wrap(maximumRgbComponent - 1)]
)
/ chroma
);
}
ToRgbDictionary simply turns the Color into a dictionary of its red, green and blue components; ColorComponent is an enum used as the key for those components. MathHelper.Wrap is a method which wraps an enum back into its declared range if it overflows or underflows. GetMinComponent and GetMaxComponent are two other extension methods on Color which return the first lowest and first highest ColorComponents of the Color (where the ColorComponent order is: Red, Green, Blue). thirdOfByte is a constant equal to the byte.MaxValue / 3.
I have based this formula on another I found here (http://www.blackwasp.co.uk/rgbhsl_2.aspx) which looks like this:
private decimal GetH(RGB rgb, decimal max, decimal chroma)
{
decimal h;
if (rgb.R == max)
h = ((rgb.G - rgb.B) / chroma);
else if (rgb.G == max)
h = ((rgb.B - rgb.R) / chroma) + 2M;
else
h = ((rgb.R - rgb.G) / chroma) + 4M;
return 60M * ((h + 6M) % 6M);
}
The problem with that formula is it's mapped to a 0 - 360 hue range, not a 0 - 255 range which is what I need. I'm not sure what the constant numbers in this formula do but I'm guessing the 60 has something to do with 360 / 3 = 120 hence my third of byte constant to try to do something similar. As for the 6Ms I have no idea where they come from nor why they are added to and modulused with the hue value at that stage. Could anyone with a mind for maths help me out here?
UPDATE
The reason for the 0 - 255 range was that I thought that was all that was required having seen it used in another piece of software (as shown) for Hue, Saturation and Lightness. It is also where I obtained the 42 Hue value for Yellow. I am now trying to find the correct range for Hues to cover all possible different hues in a 16777216 (256 * 256 * 256) colour range.

Your "...except Yellow (which returns 1 instead of 42)" what's the logic behind expecting a 42?
It's "mapped to a 0 - 360 hue range" because there are 360 hues in the colour wheel. Even if you had an unsigned byte (holding 256 values), you'd be cropping off the other 104 possible hues. It won't be an accurate representation.
Why not just use a 2-byte short to hold the possible 360 hues instead of using 1 byte?
If you still want to fit 360 inside a 255 range, just do simple math :
byte_value = hue_value / (360/255); //where hue_value is the 0-360 amount.
Try involving Math methods like Math.round to get none fractions as final byte value.
Example 180 hue :
byte_value = Math.round( 180 / (360/255) ); //gives 127

Related

How to interpolate a HSV color?

I am creating an application in win32 api, which will use a progress bar. This progress bar, should change its color. From red (left end) to green(right), and in the middle some yellow.
I searched a little, and found out, that I should use HSV to achieve this. I just don't know how? I found in this link, two functions, to convert the color, from RGB to HSV and back.
But what should I do if the color has been converted to HSV?
Like RGB coordinates, HSV coordinates define a point in a three dimensional space.
You may find a trajectory form one point (x0, one color) to the second (x1) with a formula like:
x = x0 + alpha * (x1-x0)
with alpha varying form 0.0 to 1.0
You can do this for all three components simultaneaously.
With a trajectory from green to red in HSV space you will mainly modify the H (Hue) value. If you want to see some yellow in the middle of your path (and not violett) you need to define a second or even third color and walk
green -> yellow -> red
Edit: Example
int hue0 = 0; // red
int hue2 = 120; // green
// find 100 colors between red and green
for(double alpha = 0; alpha <= 1.0; alpha += 0.01)
{
hueX = hue0 + alpha * (hue1 - hue0);
// same for value, saturation:
// valX = val0 + alpha * (val1 - val0)
// ...
// plot this color
}

Get new HSL Color by lumMod/lumOff

Am I doing this correctly?
For example, I have an RGB(165,165,165) and convert it to the HSL(Get (0, 0, 64.7)).
Actually I parse xml, and there I have two parameters lumMod = '60 000' and lumOff = '40 000' (As I know it means that I must multiply the L component by 0.6 and then add 0.4 to the L component)
Okey new_l = 0.647*0.6 + 0.4 = 0.7882
And now I convert back from HSL(0, 0, 78.8) to RGB(198, 198, 198)
Actually I need get RGB(183,183,183). Because it's right answer. Where I was wrong?
After all night debugging I have been founded answer for my question.
Thanks to King Salemno from this topic
The algorithm the charts are performing, the following steps are
performed:
Obtain RGB of the base color they’re interested in (e.g. Accent 3)
Convert to HSL
Multiply the L component by lumMod
Add lumOff to the L component
Convert back to RGB
The same logic also applies to satMod, satOff, hueMod, & hueOff.
But for rgb-color, which have only one value for all levels (like as (255, 255, 255) or (123, 123, 123)) I change lumMod to lumMod = lumMod - 0.04(+-0.005) and do the same for lumoff parameter.
So if I have RGB(165,165,165) and convert it to the HSL(0, 0, 0.647).
After I change HSL.L = HSL.L*(lumMod - 0.04) + (lumOff - 0.04) = 0.718
And now I convert back from HSL(0, 0, 0.718) to RGB(183,183,183)
I don't know why it work like this, but it work. I tried to find out it.

Ansi C re-evaluating of Y coordinates

Im trying to do a graph from evalued math function and this is last think I need to do. I have graph with limit coordinates -250:-250 left down and 250:250 right up. I have Y-limit function, which is defined as -10:10, but it could be user redefined and if it is redefined, I need to calculate new coordinates.
I have now field of y-coordinates with 20000 values and each of is multiply by:
ratioY = 25 / (fabs( up-limit - down-limit ) / 20) which will make coordinates adapt for new Y-limit (if limit is -5:5, graph looks 2x bigger), this works good, but now isnt graph exactly where it should be (see pictures). Simply 25 is multiplied for postscript coordinates and (up-limit - down-limit) / 20 is ratio for "zooming" Y coordinates. This works fine.
Now Im trying to "move coordinates" which will subtracted from revaluated value:
ycoor = (ycoor * ratioY) - move-coorY ;.
Now I have something like this:
move-coorY = 25* ( ( up-limit - down-limit) /2 );
and it doesnt work correctly. I need to do sin(0) start from 0.
This is a correct graph which is -10:10
(source: matematika.cz)
This is a bad graph which is -5:10
(source: matematika.cz)
Maybe its easier not to do this with fixed numbers (like your ratioY) but with two different coordinate systems. Physical coordinates are in your problem domain, i.e. they are the real values of your sine curves. Logical coordinates refer to the device, in your case they are the point values in Postscript, but they might be pixels on a HTML canvas or whatever.
I'll denote the physical coordinates of the first axis with a small x and the corresponding logical coordinate with a capital X. In each coordinate system we have:
Lower bound: x_min, X_max
Upper bound: x_max, X_max
Range: dx = x_max - x_min
dX = X_max - X_min
Then you can calculate your logical coordinates from the physical ones:
X(x) = X_min + (x - x_min) * dX / dx
This also works vice versa, which is not an issue for Postscript files, but max be useful for an intractive canvas where a mouse click should yield the physical coordinates.
x(X) = x_min + (X - X_min) * dx / dX
In your case, the ratio or scale factor is dX / dx, which you can calculate once for each axis. Let's plot the first point with y == 0 in your first graph:
y_min = -10
y_max = 10
dy = 20
Y_min = -250
Y_max = 250
dX = 500
Y(0) = -250 + (0 - (-10)) * 500 / 20
= -250 + 10 * 500 / 20
= 0
In the second graph, the logical coordinates are the same, but:
y_min = -5
y_max = 10
dy = 15
Y(0) = -250 + (0 - (-5)) * 500 / 15
= -250 + 5 * 500 / 15
= -83.3333
If you change the range of your graph, e.g. from (-10, 10) to (-5, 10), just adjust the physical coordinates. If you resize your graph, change the logical coordinates. (Also, calculating the point in the graph is the same as calculating the position of the tick mark for the axis. Strangely, you got the tick marks right, but not your graph. I think your problem was to account for the non-zero lower bound in both graph and curve data.)
Also, it's probably better to re-evaluate the logical coordinates when printing instead of transfroming them from a previous plot. You can do that on the fly, so that you only need to keep the physical data in an array.
(And lastly, I'll admit that I'm not entirely sure these two kinds of cooirdinates are called physical and logical. I know these terms are used, but it may be the other way round or they might even mean sonething different altogether.)
My friend did a well yob for me and programmed this...
double zeroPosition(double startY, double endY){
double range = endY - startY;
double topSize = endY / range;
return 250.0 - 500 * topSize;
}
This will calculate position of zero, which I just add to my Y position with ratio and It works exactly how I need!
But thanks M Oehm ;)

relation between light intensity and R,G,B

I have a one ambient light with intesity ( 10000,10000, 5000 ). I am trying to color the primitive.
As you know, color values for R,G, and B are between 0 and 255. How can I find color of the pixel according to light intesity ?
platform : linux and programming language c
EDIT :
In ray tracer, we are calculating
for each ambient light in the environment
color . R += Intensity of the light * ambient coefficient for color R
color . G += Intensity of the light * ambient coefficient for color G
color . B += Intensity of the light * ambient coefficient for color B
However, whenever I have tried to emit this pixel color value on the screen with openGL.
set pixel color ( color )
I have taken wrong color because of intensity is high and maximum color value is low.
The question is unclear, as written, so here's some general advice.
Colours in renderers are typically held as values with a nominal range of [0..1] for each of the RGB components.
When those colours are rendered to pixels, they're usually just multiplied by 255 to give a 24-bit colour value (8 bits per component).
If the original values are outside of the [0..1] range they must be "clamped" so that the resulting pixel values fall in the [0..255] range.
That clamping can either be done "per component", which in your case would result in (255, 255, 255), or each component could be divided by the maximum component, giving (255, 255, 127) - i.e. preserving their relative intensities in pseudo-code:
float scale = max(r, g, b);
if (scale < 1) {
scale = 1; // don't normalise colours that are "in range"
}
byte R = 255 * (r < 0 ? 0 : r / scale);
byte G = 255 * (g < 0 ? 0 : g / scale);
byte B = 255 * (b < 0 ? 0 : b / scale);
It's usual for all intermediate calculations to preserve the full dynamic range of intensities. For example, it wouldn't make sense for the Sun to have a brightness of "1", since every other object in the scene would by comparison have an almost infinitesimally small value.
The net result of the clamping will therefore be that light sources which contribute too much light to the scene will produce "saturation" of the image - i.e. the same effect that you get if you leave the shutter open too long on a picture of a bright scene.
It's this one you want:
brightness = sqrt( .241*R*R + .691*G*G + .068*B*B )
Find more here: http://www.nbdtech.com/Blog/archive/2008/04/27/Calculating-the-Perceived-Brightness-of-a-Color.aspx
or here: http://en.wikipedia.org/wiki/Luminance_(relative)

Converting two bit color to eight bit color

I've color values coming in six bits, with two bits each for red, green and blue. So black would be represented as binary 000000, red 110000, blue 000011, yellow 111100 and so on.
I have to convert this color into to 24 bit rgb value to pass it to the graphics layer (DirectFB). Since three (binary 11) is to become 255 (0xFF), I used the following formula with 85 (=255/3) as the conversion factor.
r = (color_6bit >> 4) * FACTOR;
g = ((color_6bit >> 2) & 0x3) * FACTOR;
b = (color_6bit & 0x3) * FACTOR;
color_32bit = (r << 16)| (g << 8) | b;
This correctly converts the colors (white [0x3F -> 0xFFFFFF], red [0x30 -> 0xFF0000] and so on).
Now, these colors are text and background colors of captions to be displayed on TV and we have test streams that have reference color palette embedded in the video. When I draw the eight bit colors obtained using this formula on to the screen, it is not exactly matching with the reference color present in the video - it is fairly close, but there is a difference.
Am I doing the conversion correctly or is there any standard algorithm for converting two bit rgb color values to eight bit rgb values? Could DirectFB be using some different representation (like RGB565) internally?
For what it is worth, when the factor 85 is replaced with 48 (value found by trial and error), the colors are matching almost perfectly.
The only standard I know of is EGA - there's a reference on wikipedia.
For what it's worth, 6 bits is a very small space - only 64 values. The quickest way of doing the conversion in terms of both cpu time and memory is almost certainly just looking up the value in an array of size 64. Especially if you have test data, this is really easy - just put the correct 64 values from the test data in the array. That way you don't need to worry if it is a standard - the standard is exactly whatever works for this system.

Resources