/// <summary> /// transformatin CIE L*u*v* -> CIE XYZ /// </summary> /// <author>Birthe Anne Wiegand</author> /// <returns>XYZ value</returns> public XYZ asXYZ(XYZ WP) { XYZ temp = new XYZ(); float temp_u = new float(); float temp_v = new float(); temp_u = this.U / (13f * this.L) + ColorHelper.function_XYZ_to_LUV(WP)[0]; temp_v = this.V / (13f * this.L) + ColorHelper.function_XYZ_to_LUV(WP)[1]; if (this.L <= 8) { temp.Y = WP.Y * this.L * 0.001107056f; } else { temp.Y = WP.Y * (float)Math.Pow(((this.L + 16f) / 116f), 3f); } temp.X = temp.Y * 9f * temp_u / (4f * temp_v); temp.Z = temp.Y * (12f - 3f * temp_u - 20f * temp_v) / (4f * temp_v); // to catch the otherwise occuring exception for L = 0 (black -> no color information) // X and Z are given the value 0 just for interface clarity as there is no information given about them. // (so 0 is as good as any other value, but does not imply anything wrong) if (float.IsNaN(temp.X)) temp.X = 0f; if (float.IsNaN(temp.Z)) temp.Z = 0f; return temp; }
/// <summary> /// transformatin CIE L*a*b* -> CIE XYZ /// </summary> /// <author>Birthe Anne Wiegand</author> /// <returns>XYZ value</returns> public XYZ asXYZ(XYZ WP) { XYZ temp = new XYZ(); temp.Y = WP.Y * function_LAB_to_XYZ((this.L + 16f) / 116f); temp.X = WP.X * function_LAB_to_XYZ((this.L + 16f) / 116f + this.A / 500); temp.Z = WP.Z * function_LAB_to_XYZ((this.L + 16f) / 116f - this.B / 200); return temp; }
public XYZ asXYZ() { XYZ temp = new XYZ(); // transformation Yxy -> CIE XYZ float normalizeFactor = this.Y / this.y; float Z = (1 - this.x - this.y) * normalizeFactor; temp.X = this.x * normalizeFactor; temp.Y = this.Y; temp.Z = Z; return temp; }
public static float Chromatic_Difference(XYZ input1, XYZ input2) { // returns the chromatic difference of two colors given in the XYZ format // using the euclidean distance on the u' and v' values // (e.g. needed for the just noticeable difference (JND) calculation) // source: http://files.cie.co.at/738_CIE_TN_001-2014.pdf float temp = new float(); Vector2 input1_vector = new Vector2(); Vector2 input2_vector = new Vector2(); input1_vector = function_XYZ_to_LUV(input1); input2_vector = function_XYZ_to_LUV(input2); temp = (float)Math.Sqrt((float)Math.Pow((input2_vector[0] - input1_vector[0]), 2f) + (float)Math.Pow((input2_vector[1] - input1_vector[1]), 2f)); return temp; }
/// <summary> /// this calclates the u' and v' values for a given XYZ value /// outsourced because it is used by more than one color class (mostly for L*u*v*, hence the name) /// source: http://en.wikipedia.org/wiki/CIELUV (checked with various google results, this was the most consistent one) /// </summary> /// <author>Birthe Anne Wiegand</author> /// <returns>a vector with u' in its first argument ([0]) and v' in the second ([1]), both floats</returns> public static Vector2 function_XYZ_to_LUV(XYZ input) { // calculates the u' and v' values for a given XYZ value // (needed for the transformation CIE XYZ -> CIE L*u*v* and back) // [0] gives the u' value and [1] the v' value Vector2 temp = new Vector2(); temp[0] = (4f * input.X) / (input.X + 15f * input.Y + 3f * input.Z); temp[1] = (9f * input.Y) / (input.X + 15f * input.Y + 3f * input.Z); return temp; }
/// <summary> /// (re-)sets (to) init values /// </summary> /// <author>Markus Strobel</author> private void setInitValues() { WP_default = WP_used = new XYZ(0.95045596f, 1f, 1.0890577f); RR_default = RR_used = new XYZ(0.64f, 0.33f, 0.03f); GG_default = GG_used = new XYZ(0.30f, 0.60f, 0.10f); BB_default = BB_used = new XYZ(0.15f, 0.06f, 0.79f); TranformationMatrices_Update(); }
/// <summary> /// </summary> /// <author> Birthe Anne Wiegand</author> private void ResetColorValues() { // set color values to 0 RGBvalue = new RGB(); customRGBvalue = new customRGB(); sRGBvalue = new sRGB(); HSLvalue = new HSL(); HSVvalue = new HSV(); XYZvalue = new XYZ(); LUVvalue = new LUV(); LABvalue = new LAB(); UpdateBoxes(); }
/// <summary> /// this method performs all tasks done with a left mouse click in XYZ space /// </summary> /// <param name="x">the current x coordinate</param> /// <param name="y">the current y coordinate</param> /// <param name="showCircle">if true the selected Color circle will be shown</param> /// <author>Markus Strobel</author> private void LeftClickXYZSpace(float x, float y, bool showCircle) { // set trackbar values if (!(hoveredRGBColor.R < 0 || hoveredRGBColor.G < 0 || hoveredRGBColor.B < 0)) { HSL hsl = hoveredRGBColor.asHSL(); // normalize float maxV = Math.Max(hoveredRGBColor.R, Math.Max(hoveredRGBColor.G, hoveredRGBColor.B)); hoveredRGBColor.R /= maxV; hoveredRGBColor.G /= maxV; hoveredRGBColor.B /= maxV; // set RGB tab values for trackbars labelTabRGB_R.Text = "R: " + hoveredRGBColor.R; labelTabRGB_G.Text = "G: " + hoveredRGBColor.G; labelTabRGB_B.Text = "B: " + hoveredRGBColor.B; trackBarR.Value = (int)(hoveredRGBColor.R * 255); trackBarG.Value = (int)(hoveredRGBColor.G * 255); trackBarB.Value = (int)(hoveredRGBColor.B * 255); //set HSL values for trackbars labelTabHSL_H.Text = "H: " + (hsl.H) + "°"; labelTabHSL_S.Text = "S: " + (hsl.S * 100) + "%"; labelTabHSL_L.Text = "L: " + (hsl.L * 100) + "%"; trackBarH.Value = (int)hsl.H; trackBarS.Value = (int)(hsl.S * 100); trackBarL.Value = (int)(hsl.L * 100); selectedRGBColor = hoveredRGBColor; selectedXYZColor = hoveredXYZColor; selectedYxyColor = new Yxy(x, y); if (checkBoxRGB.Checked) { showColorCircle = false; createRGBPreviewLines(selectedRGBColor); } else if (checkBoxHSL.Checked) { showColorCircle = false; createHSLPreviewLines(selectedRGBColor.asHSL()); createHSLPreviewCircle(selectedRGBColor.asHSL()); } else { showColorCircle = showCircle; createSelectedColorCircleVBO(x, y, 0.01f, 36); // computes color circle position } } else // (!(hoveredRGBColor.R < 0 || hoveredRGBColor.G < 0 || hoveredRGBColor.B < 0)) { trackBarR.Value = 0; trackBarG.Value = 0; trackBarB.Value = 0; labelTabRGB_R.Text = "R: 0"; labelTabRGB_G.Text = "G: 0"; labelTabRGB_B.Text = "B: 0"; trackBarH.Value = 1; trackBarS.Value = 100; trackBarL.Value = 50; labelTabHSL_H.Text = "H: 1°"; labelTabHSL_S.Text = "S: 100%"; labelTabHSL_L.Text = "L: 50%"; selectedRGBColor = null; selectedXYZColor = null; showColorCircle = false; } // take current preview Color as selected Color // we dont need to compute it twice // set selectedColor Panel, label panelColorSelected.BackColor = panelColorPreview.BackColor; labelColorSelectedHex.Text = labelColorPreviewHex.Text; labelColSelectedXH.Text = labelColPreviewXH.Text; labelColSelectedYS.Text = labelColPreviewYS.Text; }
/// <summary> /// </summary> /// <author> Birthe Anne Wiegand</author> private void KonvertierenButton_Click(object sender, EventArgs e) { RadioButton checkedButton = this.tabFarbRechner.Controls.OfType<RadioButton>().FirstOrDefault(radioButton => radioButton.Checked); switch (checkedButton.Name) { case "customRGB": // parse input to internal customRGBvalue customRGBvalue.R = float.Parse(customRGB_R.Text, CultureInfo.InvariantCulture); customRGBvalue.G = float.Parse(customRGB_G.Text, CultureInfo.InvariantCulture); customRGBvalue.B = float.Parse(customRGB_B.Text, CultureInfo.InvariantCulture); // update other internal values XYZvalue = customRGBvalue.asXYZ(); RGBvalue = XYZvalue.asRGB(); sRGBvalue = RGBvalue.as_sRGB(); HSLvalue = RGBvalue.asHSL(); HSVvalue = RGBvalue.asHSV(); LUVvalue = XYZvalue.asLUV(); LABvalue = XYZvalue.asLAB(); break; case "sRGB": // parse input to internal customRGBvalue sRGBvalue.R = float.Parse(sRGB_R.Text, CultureInfo.InvariantCulture); sRGBvalue.G = float.Parse(sRGB_G.Text, CultureInfo.InvariantCulture); sRGBvalue.B = float.Parse(sRGB_B.Text, CultureInfo.InvariantCulture); // update other internal values RGBvalue = sRGBvalue.asRGB(); XYZvalue = RGBvalue.asXYZ(); customRGBvalue = XYZvalue.as_customRGB(); HSLvalue = RGBvalue.asHSL(); HSVvalue = RGBvalue.asHSV(); LUVvalue = XYZvalue.asLUV(); LABvalue = XYZvalue.asLAB(); break; case "HSL": // parse input to internal HSLvalue HSLvalue.H = float.Parse(HSL_H.Text, CultureInfo.InvariantCulture); HSLvalue.S = float.Parse(HSL_S.Text, CultureInfo.InvariantCulture); HSLvalue.L = float.Parse(HSL_L.Text, CultureInfo.InvariantCulture); // update other internal values RGBvalue = HSLvalue.asRGB(); XYZvalue = RGBvalue.asXYZ(); customRGBvalue = XYZvalue.as_customRGB(); sRGBvalue = RGBvalue.as_sRGB(); HSVvalue = RGBvalue.asHSV(); LUVvalue = XYZvalue.asLUV(); LABvalue = XYZvalue.asLAB(); break; case "HSV": // parse input to internal HSVvalue HSVvalue.H = float.Parse(HSV_H.Text, CultureInfo.InvariantCulture); HSVvalue.S = float.Parse(HSV_S.Text, CultureInfo.InvariantCulture); HSVvalue.V = float.Parse(HSV_V.Text, CultureInfo.InvariantCulture); // update other internal values RGBvalue = HSVvalue.asRGB(); XYZvalue = RGBvalue.asXYZ(); customRGBvalue = XYZvalue.as_customRGB(); sRGBvalue = RGBvalue.as_sRGB(); HSLvalue = RGBvalue.asHSL(); LUVvalue = XYZvalue.asLUV(); LABvalue = XYZvalue.asLAB(); break; case "XYZ": // parse input to internal HSVvalue XYZvalue.X = float.Parse(XYZ_X.Text, CultureInfo.InvariantCulture); XYZvalue.Y = float.Parse(XYZ_Y.Text, CultureInfo.InvariantCulture); XYZvalue.Z = float.Parse(XYZ_Z.Text, CultureInfo.InvariantCulture); // update other internal values RGBvalue = XYZvalue.asRGB(); customRGBvalue = XYZvalue.as_customRGB(); sRGBvalue = RGBvalue.as_sRGB(); HSLvalue = RGBvalue.asHSL(); HSVvalue = RGBvalue.asHSV(); LUVvalue = XYZvalue.asLUV(ColorHelper.WP_used); LABvalue = XYZvalue.asLAB(ColorHelper.WP_used); break; case "LUV": // parse input to internal LUVvalue LUVvalue.L = float.Parse(LUV_L.Text, CultureInfo.InvariantCulture); LUVvalue.U = float.Parse(LUV_U.Text, CultureInfo.InvariantCulture); LUVvalue.V = float.Parse(LUV_V.Text, CultureInfo.InvariantCulture); // update other internal values XYZvalue = LUVvalue.asXYZ(ColorHelper.WP_used); RGBvalue = XYZvalue.asRGB(); customRGBvalue = XYZvalue.as_customRGB(); sRGBvalue = RGBvalue.as_sRGB(); HSLvalue = RGBvalue.asHSL(); HSVvalue = RGBvalue.asHSV(); LABvalue = XYZvalue.asLAB(ColorHelper.WP_used); break; case "LAB": // parse input to internal LABvalue LABvalue.L = float.Parse(LAB_L.Text, CultureInfo.InvariantCulture); LABvalue.A = float.Parse(LAB_A.Text, CultureInfo.InvariantCulture); LABvalue.B = float.Parse(LAB_B.Text, CultureInfo.InvariantCulture); // update other internal values XYZvalue = LABvalue.asXYZ(ColorHelper.WP_used); RGBvalue = XYZvalue.asRGB(); customRGBvalue = XYZvalue.as_customRGB(); sRGBvalue = RGBvalue.as_sRGB(); HSLvalue = RGBvalue.asHSL(); HSVvalue = RGBvalue.asHSV(); LUVvalue = XYZvalue.asLUV(ColorHelper.WP_used); break; } UpdateBoxes(); }
/// <summary> /// mouse hover ofer the OpenGL Window will be handled here /// </summary> /// <author>Markus Strobel</author> private void glControl_MouseHover(object sender, System.EventArgs e) { // Get current mouse position within the glControl Point mousePos = glControl1.PointToClient(Cursor.Position); // get x,y relative to an origin in the bottom left float x = mousePos.X; float y = glControl1.Height - mousePos.Y; // normalize to 0..1 x /= glControl1.Width; y /= glControl1.Height; if (radioButtonXYZView.Checked) { Yxy Yxy = new Yxy(x, y); hoveredXYZColor = Yxy.asXYZ(); hoveredRGBColor = hoveredXYZColor.asRGB(); //FarbRechner.CIEXYZ_to_RGB(FarbRechner.CIEYxy_to_CIEXYZ(Yxy)); x = (float)Math.Round((double)x, 2); y = (float)Math.Round((double)y, 2); // filter negative rgb values if (!(hoveredRGBColor.R < 0 || hoveredRGBColor.G < 0 || hoveredRGBColor.B < 0)) { try { // normalize float maxV = Math.Max(hoveredRGBColor.R, Math.Max(hoveredRGBColor.G, hoveredRGBColor.B)); hoveredRGBColor.R /= maxV; hoveredRGBColor.G /= maxV; hoveredRGBColor.B /= maxV; // gamma correction hoveredRGBColor.R = ColorHelper.Reverse_Gamma_Correction(hoveredRGBColor.R); hoveredRGBColor.G = ColorHelper.Reverse_Gamma_Correction(hoveredRGBColor.G); hoveredRGBColor.B = ColorHelper.Reverse_Gamma_Correction(hoveredRGBColor.B); panelColorPreview.BackColor = Color.FromArgb((int)(hoveredRGBColor.R * 255), (int)(hoveredRGBColor.G * 255), (int)(hoveredRGBColor.B * 255)); labelColorPreviewHex.Text = "#" + ((int)(hoveredRGBColor.R * 255)).ToString("X") + ((int)(hoveredRGBColor.G * 255)).ToString("X") + ((int)(hoveredRGBColor.B * 255)).ToString("X"); } catch { labelColorPreviewHex.Text = "#000"; panelColorPreview.BackColor = Color.Black; } } else { labelColorPreviewHex.Text = "#000"; panelColorPreview.BackColor = Color.Black; } labelColPreviewXH.Text = "x: " + x.ToString(); labelColPreviewYS.Text = "y: " + y.ToString(); } }
/// <summary> /// transformatin CIE XYZ -> CIE L*a*b* /// source: http://en.wikipedia.org/wiki/CIELUV /// </summary> /// <author>Birthe Anne Wiegand</author> /// <returns>L*u*v* value</returns> public LUV asLUV(XYZ WP) { LUV temp = new LUV(); float XXn = this.X / WP.X; // X / Xn float YYn = this.Y / WP.Y; // Y / Yn float ZZn = this.Z / WP.Z; // Z / Zn if (YYn <= 0.008856452) { temp.L = 903.2962963f * YYn; } else { temp.L = 116f * (float)Math.Pow(YYn, (1f / 3f)) - 16; } temp.U = 13f * temp.L * (ColorHelper.function_XYZ_to_LUV(this)[0] - ColorHelper.function_XYZ_to_LUV(WP)[0]); temp.V = 13f * temp.L * (ColorHelper.function_XYZ_to_LUV(this)[1] - ColorHelper.function_XYZ_to_LUV(WP)[1]); // to catch the otherwise occuring exception for (input.X + 15f * input.Y + 3f * input.Z) = 0 // (ColorHelper.function_XYZ_to_LUV divides by zero if (input.X + 15f * input.Y + 3f * input.Z) = 0) // u* and v* are given the value 0 just for interface clarity as there is no information given about them. // (so 0 is as good as any other value, but does not imply anything wrong) if (float.IsNaN(temp.U)) temp.U = 0f; if (float.IsNaN(temp.V)) temp.V = 0f; return temp; }
/// <summary> /// transformatin CIE XYZ -> CIE L*u*v* /// source: http://www.farbmetrik-gall.de/cielab/index.html, http://en.wikipedia.org/wiki/Lab_color_space /// </summary> /// <author>Birthe Anne Wiegand</author> /// <returns>L*a*b* value</returns> public LAB asLAB(XYZ WP) { LAB temp = new LAB(); float XXn = this.X / WP.X; // X / Xn float YYn = this.Y / WP.Y; // Y / Yn float ZZn = this.Z / WP.Z; // Z / Zn temp.L = 116f * function_XYZ_to_LAB(YYn) - 16f; temp.A = 500f * (function_XYZ_to_LAB(XXn) - function_XYZ_to_LAB(YYn)); temp.B = 200f * (function_XYZ_to_LAB(YYn) - function_XYZ_to_LAB(ZZn)); return temp; }