From your examples, you are looking for HSV to RGB with the Saturation fixed at a specific value of 1 ( Maximum ).
Here you go:
For more check out Color conversion math and formulas HSV —> RGBif ( S == 0 ) //HSV from 0 to 1
{
R = V * 255
G = V * 255
B = V * 255
}
else
{
var_h = H * 6
if ( var_h == 6 ) var_h = 0 //H must be < 1
var_i = int( var_h ) //Or ... var_i = floor( var_h )
var_1 = V * ( 1 - S )
var_2 = V * ( 1 - S * ( var_h - var_i ) )
var_3 = V * ( 1 - S * ( 1 - ( var_h - var_i ) ) )
if ( var_i == 0 ) { var_r = V ; var_g = var_3 ; var_b = var_1 }
else if ( var_i == 1 ) { var_r = var_2 ; var_g = V ; var_b = var_1 }
else if ( var_i == 2 ) { var_r = var_1 ; var_g = V ; var_b = var_3 }
else if ( var_i == 3 ) { var_r = var_1 ; var_g = var_2 ; var_b = V }
else if ( var_i == 4 ) { var_r = var_3 ; var_g = var_1 ; var_b = V }
else { var_r = V ; var_g = var_1 ; var_b = var_2 }
R = var_r * 255 //RGB results from 0 to 255
G = var_g * 255
B = var_b * 255
}
This is written from the perspective of building a computer program to convert mouse clicks on an image into RGB.
Steaphany has shown you the formulas for a HSV to RGB conversion. So from your mouse click you still need the HSV values. The HSV space is actually a cylinder with the angle around the central vertical axis corresponds to Hue, the distance from the axis corresponds to Saturation, and the distance along the axis corresponds to Value.
So from a 2D diagram you will only be able to get Hue and Saturation. You will need to have a separate input for Value. This is why you often see colour pickers like this where Hue/Saturation is adjusted in the 2D grid and a slider at the side adjusts the Value:
The following is assuming you actually want to convert the (a,b) of a mouse click on your image into the Hue/Saturation. These coordinates will have to be converted to a vector (x,y) specifying the distance from the centre of the image (the white in the middle of the diagram). The key to this is using half the image width, or the circle radius R.
x = a - R
y = b - R
The Hue is the angle. So it will be equal to arctan2(Y/X) / (2*pi). This should be in the range 0 to 1 with 0 starting at the top (12 o'clock) on the image and increasing clockwise. The arctan2 function should be available in most programming languages. It returns the correct angle for all four possible XY quandrants. In this case I have assumed an output of radians and so divided by 2*pi to get a value from 0 to 1.
The Saturation will be the distance from the centre. This will be square_root(X*X + Y*Y) / R. This should be clipped to maximum of 1 (e.g. if the use clicks outside the HSV image circle).
E.g. Image is 400 x 400 and click on point (235,34)
R = Image Width / 2 = 200
X = 235 - 200 = 35
Y = 34 - 200 = -166
Hue = arctan2(-166/35) / (2*pi) = 0.467 (or 168 degrees)
Saturation = sqrt(35*35 + 166*166) / R = sqrt(28781) / 200 = 0.848
Note again that you will need a Value. If this is 1 your RGB output will be (255,255,255). If it is 0 your RGB will be (0,0,0) irrespective of Hue and Saturation. A Value of 0.5 should at least allow you to get some RGB output but I suggest you add a method to input the Value manually.
You will also need to ensure that your image has the correct Hue at 0. If not then you will need to adjust the Hue value by adding a constant angle, in effect to rotate it.
Hope this helps.
Alex
Alex, yes, thanks. I can easily do a value slider once I have R,G,B....
I still need to get from (x,y) in one of those diagrams to R,G,B which Steaphany seems to have missed.
In short: you cannot do that...
You want to go from a 2D space to a 3D space, so you end up with one coordinate unspecified...
Your (x,y) diagrams are actually cuts through a 3D space, so that's where your 3rd coordinate is fixed. Then the problem is reduced to getting the other two from the (x,y) values from your mouse. Exactly how to do that, depends on your 2D image.
In the circle you showed, I'd do something like: get the mouse points transformed to a vector relative to the centre of the circle, then the angle of that vector with a fixed axis gives you one of the missing values (Hue in your case), the length (or distance to the centre) gives you the second (Saturation). Value seems to be fixed at 1.0 in this case, but is NOT derived from the (x,y) coordinates of the point in the image.
Again, you cannot go from (x,y) in an image to (RGB) without extra information, you need to get the third value from elsewhere (stated in image description, slider on the side, ...). The same goes for any 2D -> 3D transformation, btw, so (x,y)->(HSV) is equally impossible.
Remco
I am seriously confused, probably due to coordinate systems orientations and scales...
I am using this image:
with this code:
Code:// Colour Picker based on // http://www.easyrgb.com/index.php?X=MATH&H=19#text19 // Globals integer giHueSatFaceNum = 0; integer giLevelFaceNum = 4; integer giResultFaceNum = 2; // vectors dotted as v.x, v.y, v.z to extract values. vector gvHSL = <0.0, 0.0, 0.5>; // start at max saturation vector gvRGB; // Debug routine myDebug (string msg) { Say(msg); } // Function: Calculate a conversion parameter from HSL float hsl2param( float tf1, float tf2, float tfH ) { if ( tfH < 0.0 ) tfH += 1.0; if ( tfH > 1.0 ) tfH -= 1.0; if ( ( 6.0 * tfH ) < 1.0 ) return ( tf1 + ( tf2 - tf1 ) * 6.0 * tfH ); if ( ( 2.0 * tfH ) < 1.0 ) return ( tf2 ); if ( ( 3.0 * tfH ) < 2.0 ) return ( tf1 + ( tf2 - tf1 ) * ( ( 2 / 3 ) - tfH ) * 6.0 ); return ( tf1 ); } Main_Loop { touch (integer n) { // allow continuous update until mouse-Up integer tiFaceNum = fTouchedFace( ); vector tvXY = fTouchedXY( ); myDebug("XY: " + (string)tvXY); if(tiFaceNum == giHueSatFaceNum) { // Hue Saturation face gvHSL = <1.0 - tvXY.y, tvXY.x, gvHSL.z>; // use current Level myDebug( "HS pick, new HSL = " + (string) gvHSL); } else if(tiFaceNum == giLevelFaceNum) { // Level face gvHSL = <gvHSL.x, gvHSL.y, tvXY.x>; // use current HS myDebug( "Level pick, new HSL = " + (string) gvHSL); } Sleep(0.1); // slow it down some } touch_end (integer n) { myDebug( "Final HSL color: " + (string) gvHSL ); float tf1; float tf2; if ( gvHSL.y < 0.00001 ) gvRGB = <1.0, 1.0, 1.0>; else { if ( gvHSL.z < 0.5 ) tf2 = gvHSL.z * (1.0 + gvHSL.y); else tf2 = (gvHSL.z + gvHSL.y) - (gvHSL.y * gvHSL.z); tf1 = 2.0 * gvHSL.z - tf2; gvRGB = < hsl2param(tf1, tf2, gvHSL.x + 0.3333333), hsl2param(tf1, tf2, gvHSL.x), hsl2param(tf1, tf2, gvHSL.x - 0.3333333) >; } myDebug( "Final RGB color256: " + (string) (gvRGB * 256.0) ); } }
I am seriously confused, probably due to coordinate systems orientations and scales...
I am using this image:
with this code:
Code:// Colour Picker based on // http://www.easyrgb.com/index.php?X=MATH&H=19#text19 // Globals integer giHueSatFaceNum = 0; integer giLevelFaceNum = 4; integer giResultFaceNum = 2; // vectors dotted as v.x, v.y, v.z to extract values. vector gvHSL = <0.0, 0.0, 0.5>; // start at max saturation vector gvRGB; // Debug routine myDebug (string msg) { Say(msg); } // Function: Calculate a conversion parameter from HSL float hsl2param( float tf1, float tf2, float tfH ) { if ( tfH < 0.0 ) tfH += 1.0; if ( tfH > 1.0 ) tfH -= 1.0; if ( ( 6.0 * tfH ) < 1.0 ) return ( tf1 + ( tf2 - tf1 ) * 6.0 * tfH ); if ( ( 2.0 * tfH ) < 1.0 ) return ( tf2 ); if ( ( 3.0 * tfH ) < 2.0 ) return ( tf1 + ( tf2 - tf1 ) * ( ( 2 / 3 ) - tfH ) * 6.0 ); return ( tf1 ); } Main_Loop { touch (integer n) { // allow continuous update until mouse-Up integer tiFaceNum = fTouchedFace( ); vector tvXY = fTouchedXY( ); myDebug("XY: " + (string)tvXY); if(tiFaceNum == giHueSatFaceNum) { // Hue Saturation face gvHSL = <1.0 - tvXY.y, tvXY.x, gvHSL.z>; // use current Level myDebug( "HS pick, new HSL = " + (string) gvHSL); } else if(tiFaceNum == giLevelFaceNum) { // Level face gvHSL = <gvHSL.x, gvHSL.y, tvXY.x>; // use current HS myDebug( "Level pick, new HSL = " + (string) gvHSL); } Sleep(0.1); // slow it down some } touch_end (integer n) { myDebug( "Final HSL color: " + (string) gvHSL ); float tf1; float tf2; if ( gvHSL.y < 0.00001 ) gvRGB = <1.0, 1.0, 1.0>; else { if ( gvHSL.z < 0.5 ) tf2 = gvHSL.z * (1.0 + gvHSL.y); else tf2 = (gvHSL.z + gvHSL.y) - (gvHSL.y * gvHSL.z); tf1 = 2.0 * gvHSL.z - tf2; gvRGB = < hsl2param(tf1, tf2, gvHSL.x + 0.3333333), hsl2param(tf1, tf2, gvHSL.x), hsl2param(tf1, tf2, gvHSL.x - 0.3333333) >; } myDebug( "Final RGB color256: " + (string) (gvRGB * 256.0) ); } }
Sorry, I can't say why you get coordinates in the range (0,1) returned, even after scaling the image: with the parameter names in the code as is, it's too much work to figure out what happens. What I can say is that you want the coordinates in a constant range, independent of image scale... And (0,1) is a convenient range to use, and can easily be converted to 0..255 if needed.
Don't forget that the use of 0..255 is a convention, chosen for its compactness: 255 is the largest integer value that can be encoded in an (8-bit) byte, the standard information unit when PC's became available (and still sufficient fro display). Integers were used as working with them was much (as in 'orders of magnitude') faster than floating point numbers. With modern CPU's, floating point has become a lot faster, but it still needs more space (4 bytes/number minimum). And everyone is used to the 0..255 scale, so why change
Revi, sorry, you misunderstand. I am saying that all coords are parametric, and lie between 0 and 1. This is what I expect, but not sure how to plug those into the formula from the EasyRGB website.
Hi,
The formula from the EasyRGB website expects HSV to be in the range of 0-1 so you should not have a problem. Surely something like this:
I am not sure of the exact syntax needed but you get the idea. I think you just need to pass the HSL values to the conversion function.Code:// Colour Picker based on // http://www.easyrgb.com/index.php?X=MATH&H=19#text19 // Globals integer giHueSatFaceNum = 0; integer giLevelFaceNum = 4; integer giResultFaceNum = 2; // vectors dotted as v.x, v.y, v.z to extract values. vector gvHSL = <0.0, 0.0, 0.5>; // start at max saturation vector gvRGB; // Debug routine myDebug (string msg) { Say(msg); } // Function: Convert HSL to RGB vector hsl2rgb(float H, float S, float V) { if ( S == 0 ) { R = V; G = V; B = V; } else { var_h = H * 6; if ( var_h == 6 ) var_h = 0; //H must be < 1 var_i = int( var_h ); var_1 = V * ( 1 - S ); var_2 = V * ( 1 - S * ( var_h - var_i ) ); var_3 = V * ( 1 - S * ( 1 - ( var_h - var_i ) ) ); if ( var_i == 0 ) { var_r = V ; var_g = var_3 ; var_b = var_1; } else if ( var_i == 1 ) { var_r = var_2 ; var_g = V ; var_b = var_1; } else if ( var_i == 2 ) { var_r = var_1 ; var_g = V ; var_b = var_3; } else if ( var_i == 3 ) { var_r = var_1 ; var_g = var_2 ; var_b = V; } else if ( var_i == 4 ) { var_r = var_3 ; var_g = var_1 ; var_b = V; } else { var_r = V ; var_g = var_1 ; var_b = var_2; } R = var_r; G = var_g; B = var_b; } return <R,G,B> } Main_Loop { touch (integer n) { // allow continuous update until mouse-Up integer tiFaceNum = fTouchedFace( ); vector tvXY = fTouchedXY( ); myDebug("XY: " + (string)tvXY); if(tiFaceNum == giHueSatFaceNum) { // Hue Saturation face gvHSL = <1.0 - tvXY.y, tvXY.x, gvHSL.z>; // use current Level myDebug( "HS pick, new HSL = " + (string) gvHSL); } else if(tiFaceNum == giLevelFaceNum) { // Level face gvHSL = <gvHSL.x, gvHSL.y, tvXY.x>; // use current HS myDebug( "Level pick, new HSL = " + (string) gvHSL); } Sleep(0.1); // slow it down some } touch_end (integer n) { myDebug( "Final HSL color: " + (string) gvHSL ); gvRGB = hsl2rgb(gvHSL.x, gvHSL.y, gxHSL.z); myDebug( "Final RGB color256: " + (string) (gvRGB * 256.0) ); } }
Alex