mirror of
https://github.com/bwssytems/ha-bridge.git
synced 2025-12-16 10:14:36 +00:00
Continue color validation
This commit is contained in:
942
src/main/java/com/bwssystems/HABridge/hue/ColorConverter.java
Normal file
942
src/main/java/com/bwssystems/HABridge/hue/ColorConverter.java
Normal file
@@ -0,0 +1,942 @@
|
||||
package com.bwssystems.HABridge.hue;
|
||||
|
||||
/**
|
||||
* Convert between different color spaces supported.
|
||||
* RGB -> CMYK -> RGB
|
||||
* RGB -> YIQ -> RGB
|
||||
* RGB -> YCbCr -> RGB
|
||||
* RGB -> YUV -> RGB
|
||||
* RGB -> RGChromaticity
|
||||
* RGB -> HSV -> RGB
|
||||
* RGB -> YCC -> RGB
|
||||
* RGB -> YCoCg -> RGB
|
||||
* RGB -> XYZ -> RGB
|
||||
* RGB -> HunterLAB -> RGB
|
||||
* RGB -> HLS -> RGB
|
||||
* RGB -> CIE-LAB -> RGB
|
||||
* XYZ -> HunterLAB -> XYZ
|
||||
* XYZ -> CIE-LAB -> XYZ
|
||||
* @author Diego Catalano
|
||||
*/
|
||||
public class ColorConverter {
|
||||
|
||||
/**
|
||||
* Don't let anyone instantiate this class.
|
||||
*/
|
||||
private ColorConverter() {}
|
||||
|
||||
public static enum YCbCrColorSpace {ITU_BT_601,ITU_BT_709_HDTV};
|
||||
|
||||
// XYZ (Tristimulus) Reference values of a perfect reflecting diffuser
|
||||
|
||||
//2o Observer (CIE 1931)
|
||||
// X2, Y2, Z2
|
||||
public static float[] CIE2_A = {109.850f, 100f, 35.585f}; //Incandescent
|
||||
public static float[] CIE2_C = {98.074f, 100f, 118.232f};
|
||||
public static float[] CIE2_D50 = {96.422f, 100f, 82.521f};
|
||||
public static float[] CIE2_D55 = {95.682f, 100f, 92.149f};
|
||||
public static float[] CIE2_D65 = {95.047f, 100f, 108.883f}; //Daylight
|
||||
public static float[] CIE2_D75 = {94.972f, 100f, 122.638f};
|
||||
public static float[] CIE2_F2 = {99.187f, 100f, 67.395f}; //Fluorescent
|
||||
public static float[] CIE2_F7 = {95.044f, 100f, 108.755f};
|
||||
public static float[] CIE2_F11 = {100.966f, 100f, 64.370f};
|
||||
|
||||
//10o Observer (CIE 1964)
|
||||
// X2, Y2, Z2
|
||||
public static float[] CIE10_A = {111.144f, 100f, 35.200f}; //Incandescent
|
||||
public static float[] CIE10_C = {97.285f, 100f, 116.145f};
|
||||
public static float[] CIE10_D50 = {96.720f, 100f, 81.427f};
|
||||
public static float[] CIE10_D55 = {95.799f, 100f, 90.926f};
|
||||
public static float[] CIE10_D65 = {94.811f, 100f, 107.304f}; //Daylight
|
||||
public static float[] CIE10_D75 = {94.416f, 100f, 120.641f};
|
||||
public static float[] CIE10_F2 = {103.280f, 100f, 69.026f}; //Fluorescent
|
||||
public static float[] CIE10_F7 = {95.792f, 100f, 107.687f};
|
||||
public static float[] CIE10_F11 = {103.866f, 100f, 65.627f};
|
||||
|
||||
/**
|
||||
* RFB -> CMYK
|
||||
* @param red Values in the range [0..255].
|
||||
* @param green Values in the range [0..255].
|
||||
* @param blue Values in the range [0..255].
|
||||
* @return CMYK color space. Normalized.
|
||||
*/
|
||||
public static float[] RGBtoCMYK(int red, int green, int blue){
|
||||
float[] cmyk = new float[4];
|
||||
|
||||
float r = red / 255f;
|
||||
float g = green / 255f;
|
||||
float b = blue / 255f;
|
||||
|
||||
float k = 1.0f - Math.max(r, Math.max(g, b));
|
||||
float c = (1f-r-k) / (1f-k);
|
||||
float m = (1f-g-k) / (1f-k);
|
||||
float y = (1f-b-k) / (1f-k);
|
||||
|
||||
cmyk[0] = c;
|
||||
cmyk[1] = m;
|
||||
cmyk[2] = y;
|
||||
cmyk[3] = k;
|
||||
|
||||
return cmyk;
|
||||
}
|
||||
|
||||
/**
|
||||
* CMYK -> RGB
|
||||
* @param c Cyan.
|
||||
* @param m Magenta.
|
||||
* @param y Yellow.
|
||||
* @param k Black.
|
||||
* @return RGB color space.
|
||||
*/
|
||||
public static int[] CMYKtoRGB(float c, float m, float y, float k){
|
||||
int[] rgb = new int[3];
|
||||
|
||||
rgb[0] = (int)(255 * (1-c) * (1-k));
|
||||
rgb[1] = (int)(255 * (1-m) * (1-k));
|
||||
rgb[2] = (int)(255 * (1-y) * (1-k));
|
||||
|
||||
return rgb;
|
||||
}
|
||||
|
||||
/**
|
||||
* RGB -> YUV.
|
||||
* Y in the range [0..1].
|
||||
* U in the range [-0.5..0.5].
|
||||
* V in the range [-0.5..0.5].
|
||||
* @param red Values in the range [0..255].
|
||||
* @param green Values in the range [0..255].
|
||||
* @param blue Values in the range [0..255].
|
||||
* @return YUV color space.
|
||||
*/
|
||||
public static float[] RGBtoYUV(int red, int green, int blue){
|
||||
|
||||
float r = (float)red / 255;
|
||||
float g = (float)green / 255;
|
||||
float b = (float)blue / 255;
|
||||
|
||||
float[] yuv = new float[3];
|
||||
float y,u,v;
|
||||
|
||||
y = (float)(0.299 * r + 0.587 * g + 0.114 * b);
|
||||
u = (float)(-0.14713 * r - 0.28886 * g + 0.436 * b);
|
||||
v = (float)(0.615 * r - 0.51499 * g - 0.10001 * b);
|
||||
|
||||
yuv[0] = y;
|
||||
yuv[1] = u;
|
||||
yuv[2] = v;
|
||||
|
||||
return yuv;
|
||||
}
|
||||
|
||||
/**
|
||||
* YUV -> RGB.
|
||||
* @param y Luma. In the range [0..1].
|
||||
* @param u Chrominance. In the range [-0.5..0.5].
|
||||
* @param v Chrominance. In the range [-0.5..0.5].
|
||||
* @return RGB color space.
|
||||
*/
|
||||
public static int[] YUVtoRGB(float y, float u, float v){
|
||||
int[] rgb = new int[3];
|
||||
float r,g,b;
|
||||
|
||||
r = (float)((y + 0.000 * u + 1.140 * v) * 255);
|
||||
g = (float)((y - 0.396 * u - 0.581 * v) * 255);
|
||||
b = (float)((y + 2.029 * u + 0.000 * v) * 255);
|
||||
|
||||
rgb[0] = (int)r;
|
||||
rgb[1] = (int)g;
|
||||
rgb[2] = (int)b;
|
||||
|
||||
return rgb;
|
||||
}
|
||||
|
||||
/**
|
||||
* RGB -> YIQ.
|
||||
* @param red Values in the range [0..255].
|
||||
* @param green Values in the range [0..255].
|
||||
* @param blue Values in the range [0..255].
|
||||
* @return YIQ color space.
|
||||
*/
|
||||
public static float[] RGBtoYIQ(int red, int green, int blue){
|
||||
float[] yiq = new float[3];
|
||||
float y,i,q;
|
||||
|
||||
float r = (float)red / 255;
|
||||
float g = (float)green / 255;
|
||||
float b = (float)blue / 255;
|
||||
|
||||
y = (float)(0.299 * r + 0.587 * g + 0.114 * b);
|
||||
i = (float)(0.596 * r - 0.275 * g - 0.322 * b);
|
||||
q = (float)(0.212 * r - 0.523 * g + 0.311 * b);
|
||||
|
||||
yiq[0] = y;
|
||||
yiq[1] = i;
|
||||
yiq[2] = q;
|
||||
|
||||
return yiq;
|
||||
}
|
||||
|
||||
/**
|
||||
* YIQ -> RGB.
|
||||
* @param y Luma. Values in the range [0..1].
|
||||
* @param i In-phase. Values in the range [-0.5..0.5].
|
||||
* @param q Quadrature. Values in the range [-0.5..0.5].
|
||||
* @return RGB color space.
|
||||
*/
|
||||
public static int[] YIQtoRGB(double y, double i, double q){
|
||||
int[] rgb = new int[3];
|
||||
int r,g,b;
|
||||
|
||||
r = (int)((y + 0.956 * i + 0.621 * q) * 255);
|
||||
g = (int)((y - 0.272 * i - 0.647 * q) * 255);
|
||||
b = (int)((y - 1.105 * i + 1.702 * q) * 255);
|
||||
|
||||
r = Math.max(0,Math.min(255,r));
|
||||
g = Math.max(0,Math.min(255,g));
|
||||
b = Math.max(0,Math.min(255,b));
|
||||
|
||||
rgb[0] = r;
|
||||
rgb[1] = g;
|
||||
rgb[2] = b;
|
||||
|
||||
return rgb;
|
||||
}
|
||||
|
||||
public static float[] RGBtoYCbCr(int red, int green, int blue, YCbCrColorSpace colorSpace){
|
||||
|
||||
float r = (float)red / 255;
|
||||
float g = (float)green / 255;
|
||||
float b = (float)blue / 255;
|
||||
|
||||
float[] YCbCr = new float[3];
|
||||
float y,cb,cr;
|
||||
|
||||
if (colorSpace == YCbCrColorSpace.ITU_BT_601) {
|
||||
y = (float)(0.299 * r + 0.587 * g + 0.114 * b);
|
||||
cb = (float)(-0.169 * r - 0.331 * g + 0.500 * b);
|
||||
cr = (float)(0.500 * r - 0.419 * g - 0.081 * b);
|
||||
}
|
||||
else{
|
||||
y = (float)(0.2215 * r + 0.7154 * g + 0.0721 * b);
|
||||
cb = (float)(-0.1145 * r - 0.3855 * g + 0.5000 * b);
|
||||
cr = (float)(0.5016 * r - 0.4556 * g - 0.0459 * b);
|
||||
}
|
||||
|
||||
YCbCr[0] = (float)y;
|
||||
YCbCr[1] = (float)cb;
|
||||
YCbCr[2] = (float)cr;
|
||||
|
||||
return YCbCr;
|
||||
}
|
||||
|
||||
public static int[] YCbCrtoRGB(float y, float cb, float cr, YCbCrColorSpace colorSpace){
|
||||
int[] rgb = new int[3];
|
||||
float r,g,b;
|
||||
|
||||
if (colorSpace == YCbCrColorSpace.ITU_BT_601) {
|
||||
r = (float)(y + 0.000 * cb + 1.403 * cr) * 255;
|
||||
g = (float)(y - 0.344 * cb - 0.714 * cr) * 255;
|
||||
b = (float)(y + 1.773 * cb + 0.000 * cr) * 255;
|
||||
}
|
||||
else{
|
||||
r = (float)(y + 0.000 * cb + 1.5701 * cr) * 255;
|
||||
g = (float)(y - 0.1870 * cb - 0.4664 * cr) * 255;
|
||||
b = (float)(y + 1.8556 * cb + 0.000 * cr) * 255;
|
||||
}
|
||||
|
||||
rgb[0] = (int)r;
|
||||
rgb[1] = (int)g;
|
||||
rgb[2] = (int)b;
|
||||
|
||||
return rgb;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rg-Chromaticity space is already known to remove ambiguities due to illumination or surface pose.
|
||||
* @see Neural Information Processing - Chi Sing Leung. p. 668
|
||||
* @param red Red coefficient.
|
||||
* @param green Green coefficient.
|
||||
* @param blue Blue coefficient.
|
||||
* @return Normalized RGChromaticity. Range[0..1].
|
||||
*/
|
||||
public static double[] RGChromaticity(int red, int green, int blue){
|
||||
double[] color = new double[5];
|
||||
|
||||
double sum = red + green + blue;
|
||||
|
||||
//red
|
||||
color[0] = red / sum;
|
||||
|
||||
//green
|
||||
color[1] = green / sum;
|
||||
|
||||
//blue
|
||||
color[2] = 1 - color[0] - color[1];
|
||||
|
||||
double rS = color[0] - 0.333;
|
||||
double gS = color[1] - 0.333;
|
||||
|
||||
//saturation
|
||||
color[3] = Math.sqrt(rS * rS + gS * gS);
|
||||
|
||||
//hue
|
||||
color[4] = Math.atan(rS / gS);
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
/**
|
||||
* RGB -> HSV.
|
||||
* Adds (hue + 360) % 360 for represent hue in the range [0..359].
|
||||
* @param red Red coefficient. Values in the range [0..255].
|
||||
* @param green Green coefficient. Values in the range [0..255].
|
||||
* @param blue Blue coefficient. Values in the range [0..255].
|
||||
* @return HSV color space.
|
||||
*/
|
||||
public static float[] RGBtoHSV(int red, int green, int blue){
|
||||
float[] hsv = new float[3];
|
||||
float r = red / 255f;
|
||||
float g = green / 255f;
|
||||
float b = blue / 255f;
|
||||
|
||||
float max = Math.max(r, Math.max(g, b));
|
||||
float min = Math.min(r, Math.min(g, b));
|
||||
float delta = max - min;
|
||||
|
||||
// Hue
|
||||
if (max == min){
|
||||
hsv[0] = 0;
|
||||
}
|
||||
else if (max == r){
|
||||
hsv[0] = ((g - b) / delta) * 60f;
|
||||
}
|
||||
else if (max == g){
|
||||
hsv[0] = ((b - r) / delta + 2f) * 60f;
|
||||
}
|
||||
else if (max == b){
|
||||
hsv[0] = ((r - g) / delta + 4f) * 60f;
|
||||
}
|
||||
|
||||
// Saturation
|
||||
if (delta == 0)
|
||||
hsv[1] = 0;
|
||||
else
|
||||
hsv[1] = delta / max;
|
||||
|
||||
//Value
|
||||
hsv[2] = max;
|
||||
|
||||
return hsv;
|
||||
}
|
||||
|
||||
/**
|
||||
* HSV -> RGB.
|
||||
* @param hue Hue.
|
||||
* @param saturation Saturation. In the range[0..1].
|
||||
* @param value Value. In the range[0..1].
|
||||
* @return RGB color space. In the range[0..255].
|
||||
*/
|
||||
public static int[] HSVtoRGB(float hue, float saturation, float value){
|
||||
int[] rgb = new int[3];
|
||||
|
||||
float hi = (float)Math.floor(hue / 60.0) % 6;
|
||||
float f = (float)((hue / 60.0) - Math.floor(hue / 60.0));
|
||||
float p = (float)(value * (1.0 - saturation));
|
||||
float q = (float)(value * (1.0 - (f * saturation)));
|
||||
float t = (float)(value * (1.0 - ((1.0 - f) * saturation)));
|
||||
|
||||
if (hi == 0){
|
||||
rgb[0] = (int)(value * 255);
|
||||
rgb[1] = (int)(t * 255);
|
||||
rgb[2] = (int)(p * 255);
|
||||
}
|
||||
else if (hi == 1){
|
||||
rgb[0] = (int)(q * 255);
|
||||
rgb[1] = (int)(value * 255);
|
||||
rgb[2] = (int)(p * 255);
|
||||
}
|
||||
else if (hi == 2){
|
||||
rgb[0] = (int)(p * 255);
|
||||
rgb[1] = (int)(value * 255);
|
||||
rgb[2] = (int)(t * 255);
|
||||
}
|
||||
else if (hi == 3){
|
||||
rgb[0] = (int)(p * 255);
|
||||
rgb[1] = (int)(value * 255);
|
||||
rgb[2] = (int)(q * 255);
|
||||
}
|
||||
else if (hi == 4){
|
||||
rgb[0] = (int)(t * 255);
|
||||
rgb[1] = (int)(value * 255);
|
||||
rgb[2] = (int)(p * 255);
|
||||
}
|
||||
else if (hi == 5){
|
||||
rgb[0] = (int)(value * 255);
|
||||
rgb[1] = (int)(p * 255);
|
||||
rgb[2] = (int)(q * 255);
|
||||
}
|
||||
|
||||
return rgb;
|
||||
}
|
||||
|
||||
/**
|
||||
* RGB -> YCC.
|
||||
* @param red Red coefficient. Values in the range [0..255].
|
||||
* @param green Green coefficient. Values in the range [0..255].
|
||||
* @param blue Blue coefficient. Values in the range [0..255].
|
||||
* @return YCC color space. In the range [0..1].
|
||||
*/
|
||||
public static float[] RGBtoYCC(int red, int green, int blue){
|
||||
float[] ycc = new float[3];
|
||||
|
||||
float r = red / 255f;
|
||||
float g = green / 255f;
|
||||
float b = blue / 255f;
|
||||
|
||||
float y = 0.213f * r + 0.419f * g + 0.081f * b;
|
||||
float c1 = -0.131f * r - 0.256f * g + 0.387f * b + 0.612f;
|
||||
float c2 = 0.373f * r - 0.312f * r - 0.061f * b + 0.537f;
|
||||
|
||||
ycc[0] = y;
|
||||
ycc[1] = c1;
|
||||
ycc[2] = c2;
|
||||
|
||||
return ycc;
|
||||
}
|
||||
|
||||
/**
|
||||
* YCC -> RGB.
|
||||
* @param y Y coefficient.
|
||||
* @param c1 C coefficient.
|
||||
* @param c2 C coefficient.
|
||||
* @return RGB color space.
|
||||
*/
|
||||
public static int[] YCCtoRGB(float y, float c1, float c2){
|
||||
int[] rgb = new int[3];
|
||||
|
||||
float r = 0.981f * y + 1.315f * (c2 - 0.537f);
|
||||
float g = 0.981f * y - 0.311f * (c1 - 0.612f)- 0.669f * (c2 - 0.537f);
|
||||
float b = 0.981f * y + 1.601f * (c1 - 0.612f);
|
||||
|
||||
rgb[0] = (int)(r * 255f);
|
||||
rgb[1] = (int)(g * 255f);
|
||||
rgb[2] = (int)(b * 255f);
|
||||
|
||||
return rgb;
|
||||
}
|
||||
|
||||
/**
|
||||
* RGB -> YCoCg.
|
||||
* @param red Red coefficient. Values in the range [0..255].
|
||||
* @param green Green coefficient. Values in the range [0..255].
|
||||
* @param blue Blue coefficient. Values in the range [0..255].
|
||||
* @return YCoCg color space.
|
||||
*/
|
||||
public static float[] RGBtoYCoCg(int red, int green, int blue){
|
||||
float[] yCoCg = new float[3];
|
||||
|
||||
float r = red / 255f;
|
||||
float g = green / 255f;
|
||||
float b = blue / 255f;
|
||||
|
||||
float y = r / 4f + g / 2f + b / 4f;
|
||||
float co = r / 2f - b / 2f;
|
||||
float cg = -r / 4f + g / 2f - b / 4f;
|
||||
|
||||
yCoCg[0] = y;
|
||||
yCoCg[1] = co;
|
||||
yCoCg[2] = cg;
|
||||
|
||||
return yCoCg;
|
||||
}
|
||||
|
||||
/**
|
||||
* YCoCg -> RGB.
|
||||
* @param y Pseudo luminance, or intensity.
|
||||
* @param co Orange chrominance.
|
||||
* @param cg Green chrominance.
|
||||
* @return RGB color space.
|
||||
*/
|
||||
public static int[] YCoCgtoRGB(float y, float co, float cg){
|
||||
int[] rgb = new int[3];
|
||||
|
||||
float r = y + co - cg;
|
||||
float g = y + cg;
|
||||
float b = y - co - cg;
|
||||
|
||||
rgb[0] = (int)(r * 255f);
|
||||
rgb[1] = (int)(g * 255f);
|
||||
rgb[2] = (int)(b * 255f);
|
||||
|
||||
return rgb;
|
||||
}
|
||||
|
||||
/**
|
||||
* RGB -> XYZ
|
||||
* @param red Red coefficient. Values in the range [0..255].
|
||||
* @param green Green coefficient. Values in the range [0..255].
|
||||
* @param blue Blue coefficient. Values in the range [0..255].
|
||||
* @return XYZ color space.
|
||||
*/
|
||||
public static float[] RGBtoXYZ(int red, int green, int blue){
|
||||
float[] xyz = new float[3];
|
||||
|
||||
float r = red / 255f;
|
||||
float g = green / 255f;
|
||||
float b = blue / 255f;
|
||||
|
||||
//R
|
||||
if ( r > 0.04045)
|
||||
r = (float)Math.pow(( ( r + 0.055f ) / 1.055f ), 2.4f);
|
||||
else
|
||||
r /= 12.92f;
|
||||
|
||||
//G
|
||||
if ( g > 0.04045)
|
||||
g = (float)Math.pow(( ( g + 0.055f ) / 1.055f ), 2.4f);
|
||||
else
|
||||
g /= 12.92f;
|
||||
|
||||
//B
|
||||
if ( b > 0.04045)
|
||||
b = (float)Math.pow(( ( b + 0.055f ) / 1.055f ), 2.4f);
|
||||
else
|
||||
b /= 12.92f;
|
||||
|
||||
r *= 100;
|
||||
g *= 100;
|
||||
b *= 100;
|
||||
|
||||
float x = 0.412453f * r + 0.35758f * g + 0.180423f * b;
|
||||
float y = 0.212671f * r + 0.71516f * g + 0.072169f * b;
|
||||
float z = 0.019334f * r + 0.119193f * g + 0.950227f * b;
|
||||
|
||||
xyz[0] = x;
|
||||
xyz[1] = y;
|
||||
xyz[2] = z;
|
||||
|
||||
return xyz;
|
||||
}
|
||||
|
||||
/**
|
||||
* XYZ -> RGB
|
||||
* @param x X coefficient.
|
||||
* @param y Y coefficient.
|
||||
* @param z Z coefficient.
|
||||
* @return RGB color space.
|
||||
*/
|
||||
public static int[] XYZtoRGB(float x, float y, float z){
|
||||
int[] rgb = new int[3];
|
||||
|
||||
x /= 100;
|
||||
y /= 100;
|
||||
z /= 100;
|
||||
|
||||
float r = 3.240479f * x - 1.53715f * y - 0.498535f * z;
|
||||
float g = -0.969256f * x + 1.875991f * y + 0.041556f * z;
|
||||
float b = 0.055648f * x - 0.204043f * y + 1.057311f * z;
|
||||
|
||||
if ( r > 0.0031308 )
|
||||
r = 1.055f * ( (float)Math.pow(r, 0.4166f) ) - 0.055f;
|
||||
else
|
||||
r = 12.92f * r;
|
||||
|
||||
if ( g > 0.0031308 )
|
||||
g = 1.055f * ( (float)Math.pow(g, 0.4166f) ) - 0.055f;
|
||||
else
|
||||
g = 12.92f * g;
|
||||
|
||||
if ( b > 0.0031308 )
|
||||
b = 1.055f * ( (float)Math.pow(b, 0.4166f) ) - 0.055f;
|
||||
else
|
||||
b = 12.92f * b;
|
||||
|
||||
rgb[0] = (int)(r * 255);
|
||||
rgb[1] = (int)(g * 255);
|
||||
rgb[2] = (int)(b * 255);
|
||||
|
||||
return rgb;
|
||||
}
|
||||
|
||||
/**
|
||||
* XYZ -> HunterLAB
|
||||
* @param x X coefficient.
|
||||
* @param y Y coefficient.
|
||||
* @param z Z coefficient.
|
||||
* @return HunterLab coefficient.
|
||||
*/
|
||||
public static float[] XYZtoHunterLAB(float x, float y, float z){
|
||||
float[] hunter = new float[3];
|
||||
|
||||
|
||||
float sqrt = (float)Math.sqrt(y);
|
||||
|
||||
float l = 10 * sqrt;
|
||||
float a = 17.5f * (((1.02f * x) - y) / sqrt);
|
||||
float b = 7f * ((y - (0.847f * z)) / sqrt);
|
||||
|
||||
hunter[0] = l;
|
||||
hunter[1] = a;
|
||||
hunter[2] = b;
|
||||
|
||||
return hunter;
|
||||
}
|
||||
|
||||
/**
|
||||
* HunterLAB -> XYZ
|
||||
* @param l L coefficient.
|
||||
* @param a A coefficient.
|
||||
* @param b B coefficient.
|
||||
* @return XYZ color space.
|
||||
*/
|
||||
public static float[] HunterLABtoXYZ(float l, float a, float b){
|
||||
float[] xyz = new float[3];
|
||||
|
||||
|
||||
float tempY = l / 10f;
|
||||
float tempX = a / 17.5f * l / 10f;
|
||||
float tempZ = b / 7f * l / 10f;
|
||||
|
||||
float y = tempY * tempY;
|
||||
float x = (tempX + y) / 1.02f;
|
||||
float z = -(tempZ - y) / 0.847f;
|
||||
|
||||
xyz[0] = x;
|
||||
xyz[1] = y;
|
||||
xyz[2] = z;
|
||||
|
||||
return xyz;
|
||||
}
|
||||
|
||||
/**
|
||||
* RGB -> HunterLAB.
|
||||
* @param red Red coefficient. Values in the range [0..255].
|
||||
* @param green Green coefficient. Values in the range [0..255].
|
||||
* @param blue Blue coefficient. Values in the range [0..255].
|
||||
* @return HunterLAB color space.
|
||||
*/
|
||||
public static float[] RGBtoHunterLAB(int red, int green, int blue){
|
||||
float[] xyz = RGBtoXYZ(red, green, blue);
|
||||
return XYZtoHunterLAB(xyz[0], xyz[1], xyz[2]);
|
||||
}
|
||||
|
||||
/**
|
||||
* HunterLAB -> RGB.
|
||||
* @param l L coefficient.
|
||||
* @param a A coefficient.
|
||||
* @param b B coefficient.
|
||||
* @return RGB color space.
|
||||
*/
|
||||
public static int[] HunterLABtoRGB(float l, float a, float b){
|
||||
float[] xyz = HunterLABtoXYZ(l, a, b);
|
||||
return XYZtoRGB(xyz[0], xyz[1], xyz[2]);
|
||||
}
|
||||
|
||||
/**
|
||||
* RGB -> HLS.
|
||||
* @param red Red coefficient. Values in the range [0..255].
|
||||
* @param green Green coefficient. Values in the range [0..255].
|
||||
* @param blue Blue coefficient. Values in the range [0..255].
|
||||
* @return HLS color space.
|
||||
*/
|
||||
public static float[] RGBtoHLS(int red, int green, int blue){
|
||||
float[] hsl = new float[3];
|
||||
|
||||
float r = red / 255f;
|
||||
float g = green / 255f;
|
||||
float b = blue / 255f;
|
||||
|
||||
float max = Math.max(r,Math.max(r,b));
|
||||
float min = Math.min(r,Math.min(r,b));
|
||||
float delta = max - min;
|
||||
|
||||
//HSK
|
||||
float h = 0;
|
||||
float s = 0;
|
||||
float l = (max + min) / 2;
|
||||
|
||||
if ( delta == 0 ){
|
||||
// gray color
|
||||
h = 0;
|
||||
s = 0.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
// get saturation value
|
||||
s = ( l <= 0.5 ) ? ( delta / ( max + min ) ) : ( delta / ( 2 - max - min ) );
|
||||
|
||||
// get hue value
|
||||
float hue;
|
||||
|
||||
if ( r == max )
|
||||
{
|
||||
hue = ( ( g - b ) / 6 ) / delta;
|
||||
}
|
||||
else if ( g == max )
|
||||
{
|
||||
hue = ( 1.0f / 3 ) + ( ( b - r ) / 6 ) / delta;
|
||||
}
|
||||
else
|
||||
{
|
||||
hue = ( 2.0f / 3 ) + ( ( r - g ) / 6 ) / delta;
|
||||
}
|
||||
|
||||
// correct hue if needed
|
||||
if ( hue < 0 )
|
||||
hue += 1;
|
||||
if ( hue > 1 )
|
||||
hue -= 1;
|
||||
|
||||
h = (int) ( hue * 360 );
|
||||
}
|
||||
|
||||
hsl[0] = h;
|
||||
hsl[1] = s;
|
||||
hsl[2] = l;
|
||||
|
||||
return hsl;
|
||||
}
|
||||
|
||||
/**
|
||||
* HLS -> RGB.
|
||||
* @param hue Hue.
|
||||
* @param saturation Saturation.
|
||||
* @param luminance Luminance.
|
||||
* @return RGB color space.
|
||||
*/
|
||||
public static int[] HSLtoRGB(float hue, float saturation, float luminance){
|
||||
int[] rgb = new int[3];
|
||||
float r = 0, g = 0, b = 0;
|
||||
|
||||
if ( saturation == 0 )
|
||||
{
|
||||
// gray values
|
||||
r = g = b = (int) ( luminance * 255 );
|
||||
}
|
||||
else
|
||||
{
|
||||
float v1, v2;
|
||||
float h = (float) hue / 360;
|
||||
|
||||
v2 = ( luminance < 0.5 ) ?
|
||||
( luminance * ( 1 + saturation ) ) :
|
||||
( ( luminance + saturation ) - ( luminance * saturation ) );
|
||||
v1 = 2 * luminance - v2;
|
||||
|
||||
r = (int) ( 255 * Hue_2_RGB( v1, v2, h + ( 1.0f / 3 ) ) );
|
||||
g = (int) ( 255 * Hue_2_RGB( v1, v2, h ) );
|
||||
b = (int) ( 255 * Hue_2_RGB( v1, v2, h - ( 1.0f / 3 ) ) );
|
||||
}
|
||||
|
||||
rgb[0] = (int)r;
|
||||
rgb[1] = (int)g;
|
||||
rgb[2] = (int)b;
|
||||
|
||||
return rgb;
|
||||
}
|
||||
|
||||
private static float Hue_2_RGB( float v1, float v2, float vH ){
|
||||
if ( vH < 0 )
|
||||
vH += 1;
|
||||
if ( vH > 1 )
|
||||
vH -= 1;
|
||||
if ( ( 6 * vH ) < 1 )
|
||||
return ( v1 + ( v2 - v1 ) * 6 * vH );
|
||||
if ( ( 2 * vH ) < 1 )
|
||||
return v2;
|
||||
if ( ( 3 * vH ) < 2 )
|
||||
return ( v1 + ( v2 - v1 ) * ( ( 2.0f / 3 ) - vH ) * 6 );
|
||||
return v1;
|
||||
}
|
||||
|
||||
/**
|
||||
* RGB -> CIE-LAB.
|
||||
* @param red Red coefficient. Values in the range [0..255].
|
||||
* @param green Green coefficient. Values in the range [0..255].
|
||||
* @param blue Blue coefficient. Values in the range [0..255].
|
||||
* @param tristimulus XYZ Tristimulus.
|
||||
* @return CIE-LAB color space.
|
||||
*/
|
||||
public static float[] RGBtoLAB(int red, int green, int blue, float[] tristimulus){
|
||||
float[] xyz = RGBtoXYZ(red, green, blue);
|
||||
float[] lab = XYZtoLAB(xyz[0], xyz[1], xyz[2], tristimulus);
|
||||
|
||||
return lab;
|
||||
}
|
||||
|
||||
/**
|
||||
* CIE-LAB -> RGB.
|
||||
* @param l L coefficient.
|
||||
* @param a A coefficient.
|
||||
* @param b B coefficient.
|
||||
* @param tristimulus XYZ Tristimulus.
|
||||
* @return RGB color space.
|
||||
*/
|
||||
public static int[] LABtoRGB(float l, float a, float b, float[] tristimulus){
|
||||
float[] xyz = LABtoXYZ(l, a, b, tristimulus);
|
||||
return XYZtoRGB(xyz[0], xyz[1], xyz[2]);
|
||||
}
|
||||
|
||||
/**
|
||||
* XYZ -> CIE-LAB.
|
||||
* @param x X coefficient.
|
||||
* @param y Y coefficient.
|
||||
* @param z Z coefficient.
|
||||
* @param tristimulus XYZ Tristimulus.
|
||||
* @return CIE-LAB color space.
|
||||
*/
|
||||
public static float[] XYZtoLAB(float x, float y, float z, float[] tristimulus){
|
||||
float[] lab = new float[3];
|
||||
|
||||
x /= tristimulus[0];
|
||||
y /= tristimulus[1];
|
||||
z /= tristimulus[2];
|
||||
|
||||
if (x > 0.008856)
|
||||
x = (float)Math.pow(x,0.33f);
|
||||
else
|
||||
x = (7.787f * x) + ( 0.1379310344827586f );
|
||||
|
||||
if (y > 0.008856)
|
||||
y = (float)Math.pow(y,0.33f);
|
||||
else
|
||||
y = (7.787f * y) + ( 0.1379310344827586f );
|
||||
|
||||
if (z > 0.008856)
|
||||
z = (float)Math.pow(z,0.33f);
|
||||
else
|
||||
z = (7.787f * z) + ( 0.1379310344827586f );
|
||||
|
||||
lab[0] = ( 116 * y ) - 16;
|
||||
lab[1] = 500 * ( x - y );
|
||||
lab[2] = 200 * ( y - z );
|
||||
|
||||
return lab;
|
||||
}
|
||||
|
||||
/**
|
||||
* CIE-LAB -> XYZ.
|
||||
* @param l L coefficient.
|
||||
* @param a A coefficient.
|
||||
* @param b B coefficient.
|
||||
* @param tristimulus XYZ Tristimulus.
|
||||
* @return XYZ color space.
|
||||
*/
|
||||
public static float[] LABtoXYZ(float l, float a, float b, float[] tristimulus){
|
||||
float[] xyz = new float[3];
|
||||
|
||||
float y = ( l + 16f ) / 116f;
|
||||
float x = a / 500f + y;
|
||||
float z = y - b / 200f;
|
||||
|
||||
//Y
|
||||
if ( Math.pow(y,3) > 0.008856 )
|
||||
y = (float)Math.pow(y,3);
|
||||
else
|
||||
y = (float)(( y - 16 / 116 ) / 7.787);
|
||||
|
||||
//X
|
||||
if ( Math.pow(x,3) > 0.008856 )
|
||||
x = (float)Math.pow(x,3);
|
||||
else
|
||||
x = (float)(( x - 16 / 116 ) / 7.787);
|
||||
|
||||
// Z
|
||||
if ( Math.pow(z,3) > 0.008856 )
|
||||
z = (float)Math.pow(z,3);
|
||||
else
|
||||
z = (float)(( z - 16 / 116 ) / 7.787);
|
||||
|
||||
xyz[0] = x * tristimulus[0];
|
||||
xyz[1] = y * tristimulus[1];
|
||||
xyz[2] = z * tristimulus[2];
|
||||
|
||||
return xyz;
|
||||
}
|
||||
|
||||
/**
|
||||
* RGB -> C1C2C3.
|
||||
* @param r Red coefficient. Values in the range [0..255].
|
||||
* @param g Green coefficient. Values in the range [0..255].
|
||||
* @param b Blue coefficient. Values in the range [0..255].
|
||||
* @return C1C2C3 color space.
|
||||
*/
|
||||
public static float[] RGBtoC1C2C3(int r, int g, int b){
|
||||
|
||||
float[] c = new float[3];
|
||||
|
||||
c[0] = (float)Math.atan(r / Math.max(g, b));
|
||||
c[1] = (float)Math.atan(g / Math.max(r, b));
|
||||
c[2] = (float)Math.atan(b / Math.max(r, g));
|
||||
|
||||
return c;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* RGB -> O1O2.
|
||||
* @param r Red coefficient. Values in the range [0..255].
|
||||
* @param g Green coefficient. Values in the range [0..255].
|
||||
* @param b Blue coefficient. Values in the range [0..255].
|
||||
* @return O1O2 color space.
|
||||
*/
|
||||
public static float[] RGBtoO1O2(int r, int g, int b){
|
||||
|
||||
float[] o = new float[2];
|
||||
|
||||
o[0] = (r - g) / 2f;
|
||||
o[1] = (r + g) / 4f - (b / 2f);
|
||||
|
||||
return o;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* RGB -> Grayscale.
|
||||
* @param r Red coefficient. Values in the range [0..255].
|
||||
* @param g Green coefficient. Values in the range [0..255].
|
||||
* @param b Blue coefficient. Values in the range [0..255].
|
||||
* @return Grayscale color space.
|
||||
*/
|
||||
public static float RGBtoGrayscale(int r, int g, int b){
|
||||
|
||||
return r*0.2125f + g*0.7154f + b*0.0721f;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* XYZ -> Philips Hue XY
|
||||
* @param x X coefficient.
|
||||
* @param y Y coefficient.
|
||||
* @param z Z coefficient.
|
||||
* @return Hue xy array
|
||||
*/
|
||||
public static XYColorSpace XYZtoXY(float x, float y, float z){
|
||||
float[] xy = new float[2];
|
||||
|
||||
xy[0] = x / (x + y + z);
|
||||
xy[1] = y / (x + y + z);
|
||||
|
||||
XYColorSpace xyColor = new XYColorSpace();
|
||||
xyColor.setBrightness((int)Math.round(y * 254.0f));
|
||||
xyColor.setXy(xy);
|
||||
return xyColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Philips Hue XY -> XYZ
|
||||
* @param x X coefficient.
|
||||
* @param y Y coefficient.
|
||||
* @return XYZ array
|
||||
*/
|
||||
public static float[] XYtoXYZ(XYColorSpace xy){
|
||||
float[] xyz = new float[3];
|
||||
|
||||
xyz[0] = (xy.getBrightnessAdjusted() / xy.getXy()[1]) * xy.getXy()[0];
|
||||
xyz[1] = xy.getBrightnessAdjusted();
|
||||
xyz[2] = (xy.getBrightnessAdjusted() / xy.getXy()[1]) * (1.0f - xy.getXy()[0] - xy.getXy()[1]);
|
||||
|
||||
return xyz;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -122,64 +122,64 @@ public class ColorDecode {
|
||||
List<Integer> rgb;
|
||||
double x = xy.get(0); // the given x value
|
||||
double y = xy.get(1); // the given y value
|
||||
double z = 1.0 - x - y;
|
||||
double Y = (double) brightness / (double) 254.00; // The given brightness value
|
||||
double z = 1.0f - x - y;
|
||||
double Y = (double) brightness / (double) 254.00f; // The given brightness value
|
||||
double X = (Y / y) * x;
|
||||
double Z = (Y / y) * z;
|
||||
|
||||
double r = X * 1.656492 - Y * 0.354851 - Z * 0.255038;
|
||||
double g = -X * 0.707196 + Y * 1.655397 + Z * 0.036152;
|
||||
double b = X * 0.051713 - Y * 0.121364 + Z * 1.011530;
|
||||
double r = X * 1.656492f - Y * 0.354851f - Z * 0.255038f;
|
||||
double g = -X * 0.707196f + Y * 1.655397f + Z * 0.036152f;
|
||||
double b = X * 0.051713f - Y * 0.121364f + Z * 1.011530f;
|
||||
|
||||
if (r > b && r > g && r > 1.0) {
|
||||
if (r > b && r > g && r > 1.0f) {
|
||||
|
||||
g = g / r;
|
||||
b = b / r;
|
||||
r = 1.0;
|
||||
} else if (g > b && g > r && g > 1.0) {
|
||||
r = 1.0f;
|
||||
} else if (g > b && g > r && g > 1.0f) {
|
||||
|
||||
r = r / g;
|
||||
b = b / g;
|
||||
g = 1.0;
|
||||
} else if (b > r && b > g && b > 1.0) {
|
||||
g = 1.0f;
|
||||
} else if (b > r && b > g && b > 1.0f) {
|
||||
|
||||
r = r / b;
|
||||
g = g / b;
|
||||
b = 1.0;
|
||||
b = 1.0f;
|
||||
}
|
||||
|
||||
r = r <= 0.0031308 ? 12.92 * r : (1.0 + 0.055) * Math.pow(r, (1.0 / 2.4)) - 0.055;
|
||||
g = g <= 0.0031308 ? 12.92 * g : (1.0 + 0.055) * Math.pow(g, (1.0 / 2.4)) - 0.055;
|
||||
b = b <= 0.0031308 ? 12.92 * b : (1.0 + 0.055) * Math.pow(b, (1.0 / 2.4)) - 0.055;
|
||||
r = r <= 0.0031308f ? 12.92f * r : (1.0f + 0.055f) * Math.pow(r, (1.0f / 2.4f)) - 0.055f;
|
||||
g = g <= 0.0031308f ? 12.92f * g : (1.0f + 0.055f) * Math.pow(g, (1.0f / 2.4f)) - 0.055f;
|
||||
b = b <= 0.0031308f ? 12.92f * b : (1.0f + 0.055f) * Math.pow(b, (1.0f / 2.4f)) - 0.055f;
|
||||
|
||||
if (r > b && r > g) {
|
||||
// red is biggest
|
||||
if (r > 1.0) {
|
||||
if (r > 1.0f) {
|
||||
g = g / r;
|
||||
b = b / r;
|
||||
r = 1.0;
|
||||
r = 1.0f;
|
||||
}
|
||||
} else if (g > b && g > r) {
|
||||
// green is biggest
|
||||
if (g > 1.0) {
|
||||
if (g > 1.0f) {
|
||||
r = r / g;
|
||||
b = b / g;
|
||||
g = 1.0;
|
||||
g = 1.0f;
|
||||
}
|
||||
} else if (b > r && b > g) {
|
||||
// blue is biggest
|
||||
if (b > 1.0) {
|
||||
if (b > 1.0f) {
|
||||
r = r / b;
|
||||
g = g / b;
|
||||
b = 1.0;
|
||||
b = 1.0f;
|
||||
}
|
||||
}
|
||||
if (r < 0.0)
|
||||
r = 0;
|
||||
if (g < 0.0)
|
||||
g = 0;
|
||||
if (b < 0.0)
|
||||
b = 0;
|
||||
if (r < 0.0f)
|
||||
r = 0.0f;
|
||||
if (g < 0.0f)
|
||||
g = 0.0f;
|
||||
if (b < 0.0f)
|
||||
b = 0.0f;
|
||||
|
||||
rgb = new ArrayList<Integer>();
|
||||
rgb.add((int) Math.round(r * 255));
|
||||
|
||||
26
src/main/java/com/bwssystems/HABridge/hue/XYColorSpace.java
Normal file
26
src/main/java/com/bwssystems/HABridge/hue/XYColorSpace.java
Normal file
@@ -0,0 +1,26 @@
|
||||
package com.bwssystems.HABridge.hue;
|
||||
|
||||
public class XYColorSpace {
|
||||
float[] xy;
|
||||
int brightness;
|
||||
|
||||
public float[] getXy() {
|
||||
return xy;
|
||||
}
|
||||
|
||||
public void setXy(float[] xy) {
|
||||
this.xy = xy;
|
||||
}
|
||||
|
||||
public int getBrightness() {
|
||||
return brightness;
|
||||
}
|
||||
|
||||
public float getBrightnessAdjusted() {
|
||||
return ((float) brightness / 254.0f) * 100f;
|
||||
}
|
||||
|
||||
public void setBrightness(int brightness) {
|
||||
this.brightness = brightness;
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,8 @@ import org.junit.Test;
|
||||
|
||||
import com.bwssystems.HABridge.hue.ColorData;
|
||||
import com.bwssystems.HABridge.hue.ColorDecode;
|
||||
import com.bwssystems.HABridge.hue.XYColorSpace;
|
||||
import com.bwssystems.HABridge.hue.ColorConverter;
|
||||
|
||||
public class ConvertCIEColorTestCase {
|
||||
|
||||
@@ -16,6 +18,29 @@ public class ConvertCIEColorTestCase {
|
||||
public void testColorConversion() {
|
||||
ArrayList<Double> xy = new ArrayList<Double>(Arrays.asList(Double.parseDouble("0.671254"), Double.parseDouble("0.303273")));
|
||||
|
||||
XYColorSpace xyColor = new XYColorSpace();
|
||||
xyColor.setBrightness(254);
|
||||
float[] xyFloat = new float[2];
|
||||
xyFloat[0] = xy.get(0).floatValue();
|
||||
xyFloat[1] = xy.get(1).floatValue();
|
||||
xyColor.setXy(xyFloat);
|
||||
float[] xyz = ColorConverter.XYtoXYZ(xyColor);
|
||||
int[] rgb = ColorConverter.XYZtoRGB(xyz[0], xyz[1], xyz[2]);
|
||||
List<Integer> rgbDecode = new ArrayList<Integer>();
|
||||
rgbDecode.add(0, rgb[0]);
|
||||
rgbDecode.add(1, rgb[1]);
|
||||
rgbDecode.add(2, rgb[2]);
|
||||
List<Integer> assertDecode = new ArrayList<Integer>();
|
||||
assertDecode.add(0, 255);
|
||||
assertDecode.add(1, 47);
|
||||
assertDecode.add(2, 43);
|
||||
Assert.assertEquals(rgbDecode, assertDecode);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testColorConversionRGB() {
|
||||
ArrayList<Double> xy = new ArrayList<Double>(Arrays.asList(Double.parseDouble("0.671254"), Double.parseDouble("0.303273")));
|
||||
|
||||
List<Integer> colorDecode = ColorDecode.convertCIEtoRGB(xy, 254);
|
||||
List<Integer> assertDecode = new ArrayList<Integer>();
|
||||
assertDecode.add(0, 255);
|
||||
|
||||
Reference in New Issue
Block a user