// Catalano Imaging Library
// The Catalano Framework
//
// Copyright © Diego Catalano, 2012-2016
// diego.catalano at live.com
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
//
package Catalano.Imaging.Tools;
import Catalano.Imaging.Color;
import Catalano.Math.Matrix;
/**
* Convert between different color spaces supported.
*
* RGB -> CIE-L*A*B* -> RGB
* RGB -> CIE-L*c*h -> RGB
* RGB -> CMYK -> RGB
* RGB -> IHS -> RGB
* RGB -> HLS -> RGB
* RGB -> HunterLAB -> RGB
* RGB -> HSV -> RGB
* RGB -> RGChromaticity
* RGB -> XYZ -> RGB
* RGB -> YCbCr -> RGB
* RGB -> YCC -> RGB
* RGB -> YCoCg -> RGB
* RGB -> YES -> RGB
* RGB -> YIQ -> RGB
* RGB -> YUV -> RGB
* XYZ -> CIE-L*A*B* -> XYZ
* XYZ -> HunterLAB -> XYZ
* XYZ -> LMS -> XYZ
* XYZ -> xyY -> XYZ
*
* @author Diego Catalano
*/
public class ColorConverter {
//HPE forward
private static final double[][] hpe_f = new double[][]{
{0.38971, 0.68898,-0.07868},
{-0.22981, 1.18340, 0.04641},
{0.00000, 0.00000, 1.00000}
};
//HPE backward
private static final double[][] hpe_b = new double[][]{
{1.91020, -1.11212, 0.20191},
{0.37095, 0.62905, -0.00001},
{0.00000, 0.00000, 1.00000}
};
//Bradford forward
private static final double[][] bradford_f = new double[][]{
{0.8951000,0.2664000,-0.1614000},
{-0.7502000,1.7135000,0.0367000},
{0.0389000,-0.0685000,1.0296000}
};
//Bradford backward
private static final double[][] bradford_b = new double[][]{
{0.9869929,-0.1470543,0.1599627},
{0.4323053,0.5183603,0.0492912},
{-0.0085287,0.0400428,0.9684867}
};
//VonKries forward
private static final double[][] vonkries_f = new double[][]{
{0.4002, 0.7076, -0.0808},
{-0.2263, 1.1653, 0.0457},
{0, 0, 0.9182}
};
//VonKries backward
private static final double[][] vonkries_b = new double[][]{
{1.86007, -1.12948, 0.21990},
{0.36122, 0.63880, -0.00001},
{0.00000, 0.00000, 1.08909}
};
//CAT97 forward
private static final double[][] cat97_f = new double[][]{
{0.8562, 0.3372, -0.1934},
{-0.8360, 1.8327, 0.0033},
{0.0357, -0.00469, 1.0112}
};
//CAT97 backward
private static final double[][] cat97_b = new double[][]{
{0.9838112, -0.1805292, 0.1887508},
{0.4488317, 0.4632779, 0.0843307},
{-0.0326513, 0.0085222, 0.9826514}
};
//CAT02 forward
private static final double[][] cat02_f = new double[][]{
{0.7328, 0.4296, -0.1624},
{-0.7036, 1.6975, 0.0061},
{0.0030, 0.0136, 0.9834}
};
//CAT02 backward
private static final double[][] cat02_b = new double[][]{
{1.0961238, -0.2788690, 0.1827452},
{0.4543690, 0.4735332, 0.0720978},
{-0.0096276, -0.0056980, 1.0153256}
};
/**
* LMS Transformation matrix.
*/
public static enum LMS{
/**
* Hunt-Pointer-Estevez.
*/
HPE,
/**
* Bradford.
*/
Bradford,
/**
* Von Kries.
*/
VonKries,
/**
* CIECAM97s.
*/
CAT97,
/**
* CIECAM02.
*/
CAT02
}
public static enum YCbCrColorSpace {ITU_BT_601,ITU_BT_709_HDTV};
//Used in CIE-LAB conversions
private static double k = 903.2962962962963; //24389/27
private static double e = 0.0088564516790356; //216/24389
/**
* Don't let anyone instantiate this class.
*/
private ColorConverter() {}
/**
* RGB -> CMYK
* @param color Color.
* @return CMYK color space. Normalized.
*/
public static double[] RGBtoCMYK(Color color){
return RGBtoCMYK(color.r, color.g, color.b);
}
/**
* RGB -> CMYK
* @param rgb RGB values.
* @return CMYK color space. Normalized.
*/
public static double[] RGBtoCMYK(int[] rgb){
return RGBtoCMYK(rgb[0], rgb[1], rgb[2]);
}
/**
* RGB -> 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 double[] RGBtoCMYK(int red, int green, int blue){
double[] cmyk = new double[4];
double r = red / 255f;
double g = green / 255f;
double b = blue / 255f;
double k = 1.0f - Math.max(r, Math.max(g, b));
double c = (1f-r-k) / (1f-k);
double m = (1f-g-k) / (1f-k);
double y = (1f-b-k) / (1f-k);
cmyk[0] = c;
cmyk[1] = m;
cmyk[2] = y;
cmyk[3] = k;
return cmyk;
}
/**
* CMYK -> RGB
* @param cmyk CMYK values.
* @return RGB color space.
*/
public static int[] CMYKtoRGB(double[] cmyk){
return CMYKtoRGB(cmyk[0], cmyk[1], cmyk[2], cmyk[3]);
}
/**
* CMYK -> RGB
* @param c Cyan.
* @param m Magenta.
* @param y Yellow.
* @param k Black.
* @return RGB color space.
*/
public static int[] CMYKtoRGB(double c, double m, double y, double 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 -> IHS
* @param color Color.
* @return IHS color space. Normalized.
*/
public static double[] RGBtoIHS(Color color){
return RGBtoCMYK(color.r, color.g, color.b);
}
/**
* RGB -> IHS
* @param rgb RGB values.
* @return RGB color space.
*/
public static double[] RGBtoIHS(int[] rgb){
return RGBtoIHS(rgb[0], rgb[1], rgb[2]);
}
/**
* RGB -> IHS
* @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 IHS color space. Normalized.
*/
public static double[] RGBtoIHS(int red, int green, int blue){
double r = red / 255f;
double g = green / 255f;
double b = blue / 255f;
double i = r+g+b;
double h;
if(b == Math.min(Math.min(r, g), b)){
h = (g-b) / (i-3*b);
}
else if (r == Math.min(Math.min(r, g), b)){
h = (b-r) / (i-3*r) + 1;
}
else{
h = (r-g) / (i-3*g) + 2;
}
double s;
if(h >= 0 && h <= 1){
s = (i-3*b) / i;
}
else if(h >= 1 && h <= 2){
s = (i-3*r) / i;
}
else{
s = (i-3*g) / i;
}
return new double[] {i,h,s};
}
/**
* IHS -> RGB
* @param ihs IHS vector.
* @return RGB color space.
*/
public static double[] IHStoRGB(double[] ihs){
if(ihs[1] >= 0 && ihs[1] <= 1){
double r = ihs[0] * (1 + 2*ihs[2]-3*ihs[2]*ihs[1]) / 3;
double g = ihs[0] * (1 - ihs[2]+3*ihs[2]*ihs[1]) / 3;
double b = ihs[0] * (1 - ihs[2]) / 3;
return new double[] {r*255,g*255,b*255};
}
else if(ihs[1] >= 1 && ihs[1] <= 2){
double r = ihs[0] * (1 - ihs[2]) / 3;
double g = ihs[0] * (1 + 2*ihs[2] - 3*ihs[2]*(ihs[1] - 1)) / 3;
double b = ihs[0] * (1 - ihs[2] + 3*ihs[2]*(ihs[1] - 1)) / 3;
return new double[] {r*255,g*255,b*255};
}
else{
double r = ihs[0] * (1 - ihs[2] + 3*ihs[2]*(ihs[1] - 2)) / 3;
double g = ihs[0] * (1 - ihs[2]) / 3;
double b = ihs[0] * (1 + 2*ihs[2] - 3*ihs[2]*(ihs[1] - 2)) / 3;
return new double[] {r*255,g*255,b*255};
}
}
/**
* RGB -> YUV.
* @param color Color.
* @return YUV color space.
*/
public static double[] RGBtoYUV(Color color){
return RGBtoYUV(color.r, color.g, color.b);
}
/**
* 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 double[] RGBtoYUV(int red, int green, int blue){
double r = (double)red / 255;
double g = (double)green / 255;
double b = (double)blue / 255;
double[] yuv = new double[3];
double y,u,v;
y = (double)(0.299 * r + 0.587 * g + 0.114 * b);
u = (double)(-0.14713 * r - 0.28886 * g + 0.436 * b);
v = (double)(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(double y, double u, double v){
int[] rgb = new int[3];
double r,g,b;
r = (double)((y + 0.000 * u + 1.140 * v) * 255);
g = (double)((y - 0.396 * u - 0.581 * v) * 255);
b = (double)((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 color Color.
* @return YIQ color space.
*/
public static double[] RGBtoYIQ(Color color){
return RGBtoYIQ(color.r, color.g, color.b);
}
/**
* 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 double[] RGBtoYIQ(int red, int green, int blue){
double[] yiq = new double[3];
double y,i,q;
double r = (double)red / 255;
double g = (double)green / 255;
double b = (double)blue / 255;
y = (double)(0.299 * r + 0.587 * g + 0.114 * b);
i = (double)(0.596 * r - 0.275 * g - 0.322 * b);
q = (double)(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 double[] RGBtoYCbCr(Color color, YCbCrColorSpace colorSpace){
return RGBtoYCbCr(color.r, color.g, color.b, colorSpace);
}
public static double[] RGBtoYCbCr(int red, int green, int blue, YCbCrColorSpace colorSpace){
double r = (double)red / 255;
double g = (double)green / 255;
double b = (double)blue / 255;
double[] YCbCr = new double[3];
double y,cb,cr;
if (colorSpace == YCbCrColorSpace.ITU_BT_601) {
y = (double)(0.299 * r + 0.587 * g + 0.114 * b);
cb = (double)(-0.169 * r - 0.331 * g + 0.500 * b);
cr = (double)(0.500 * r - 0.419 * g - 0.081 * b);
}
else{
y = (double)(0.2215 * r + 0.7154 * g + 0.0721 * b);
cb = (double)(-0.1145 * r - 0.3855 * g + 0.5000 * b);
cr = (double)(0.5016 * r - 0.4556 * g - 0.0459 * b);
}
YCbCr[0] = (double)y;
YCbCr[1] = (double)cb;
YCbCr[2] = (double)cr;
return YCbCr;
}
public static int[] YCbCrtoRGB(double y, double cb, double cr, YCbCrColorSpace colorSpace){
int[] rgb = new int[3];
double r,g,b;
if (colorSpace == YCbCrColorSpace.ITU_BT_601) {
r = (double)(y + 0.000 * cb + 1.403 * cr) * 255;
g = (double)(y - 0.344 * cb - 0.714 * cr) * 255;
b = (double)(y + 1.773 * cb + 0.000 * cr) * 255;
}
else{
r = (double)(y + 0.000 * cb + 1.5701 * cr) * 255;
g = (double)(y - 0.1870 * cb - 0.4664 * cr) * 255;
b = (double)(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.
* @param color Color.
* @return Normalized RGChromaticity. Range[0..1].
*/
public static double[] RGChromaticity(Color color){
return RGChromaticity(color.r, color.g, color.b);
}
/**
* 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 color Color.
* @return HSV color space.
*/
public static double[] RGBtoHSV(Color color){
return RGBtoHSV(color.r, color.g, color.b);
}
/**
* 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 double[] RGBtoHSV(int red, int green, int blue){
double[] hsv = new double[3];
double r = red / 255f;
double g = green / 255f;
double b = blue / 255f;
double max = Math.max(r, Math.max(g, b));
double min = Math.min(r, Math.min(g, b));
double 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(double hue, double saturation, double value){
int[] rgb = new int[3];
double hi = (double)Math.floor(hue / 60.0) % 6;
double f = (double)((hue / 60.0) - Math.floor(hue / 60.0));
double p = (double)(value * (1.0 - saturation));
double q = (double)(value * (1.0 - (f * saturation)));
double t = (double)(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 color Color.
* @return YCC color space. In the range [0..1].
*/
public static double[] RGBtoYCC(Color color){
return RGBtoYCC(color.r, color.g, color.b);
}
/**
* 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 double[] RGBtoYCC(int red, int green, int blue){
double[] ycc = new double[3];
double r = red / 255f;
double g = green / 255f;
double b = blue / 255f;
double y = 0.213f * r + 0.419f * g + 0.081f * b;
double c1 = -0.131f * r - 0.256f * g + 0.387f * b + 0.612f;
double 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(double y, double c1, double c2){
int[] rgb = new int[3];
double r = 0.981f * y + 1.315f * (c2 - 0.537f);
double g = 0.981f * y - 0.311f * (c1 - 0.612f)- 0.669f * (c2 - 0.537f);
double 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 color Color.
* @return YCoCg color space.
*/
public static double[] RGBtoYCoCg(Color color){
return RGBtoYCoCg(color.r, color.g, color.b);
}
/**
* 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 double[] RGBtoYCoCg(int red, int green, int blue){
double[] yCoCg = new double[3];
double r = red / 255f;
double g = green / 255f;
double b = blue / 255f;
double y = r / 4f + g / 2f + b / 4f;
double co = r / 2f - b / 2f;
double cg = -r / 4f + g / 2f - b / 4f;
yCoCg[0] = y;
yCoCg[1] = co;
yCoCg[2] = cg;
return yCoCg;
}
/**
* RGB -> YES.
* @param color Color.
* @return YES color space.
*/
public static double[] RGBtoYES(Color color){
return RGBtoYES(color.r, color.g, color.b);
}
/**
* RGB -> YES.
* @param rgb RGB values.
* @return YES color space.
*/
public static double[] RGBtoYES(int[] rgb){
return RGBtoYES(rgb[0], rgb[1], rgb[2]);
}
/**
* RGB -> YES.
* @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 YES color space.
*/
public static double[] RGBtoYES(int red, int green, int blue){
double[] yes = new double[3];
double r = red / 255f;
double g = green / 255f;
double b = blue / 255f;
yes[0] = r * 0.253 + g * 0.684 + b * 0.063;
yes[1] = r * 0.500 + g * -0.500;
yes[2] = r * 0.250 + g * 0.250 + b * -0.5;
return yes;
}
/**
* YES -> RGB
* @param yes YES color space.
* @return RGB color space.
*/
public static int[] YEStoRGB(double[] yes){
return YEStoRGB(yes[0], yes[1], yes[2]);
}
/**
* YES -> RGB
* @param y Luminance component. [0..1]
* @param e Chrominance factor. Difference of red and green channels. [-0.5..0.5]
* @param s Chrominance factor. Difference of yellow and blue. [-0.5..0.5]
* @return RGB color space.
*/
public static int[] YEStoRGB(double y, double e, double s){
int[] rgb = new int[3];
rgb[0] = (int)((y + e * 1.431 + s * 0.126) * 255);
rgb[1] = (int)((y + e * -0.569 + s * 0.126) * 255);
rgb[2] = (int)((y + e * 0.431 + s * -1.874) * 255);
return rgb;
}
/**
* YCoCg -> RGB.
* @param y Pseudo luminance, or intensity.
* @param co Orange chrominance.
* @param cg Green chrominance.
* @return RGB color space.
*/
public static int[] YCoCgtoRGB(double y, double co, double cg){
int[] rgb = new int[3];
double r = y + co - cg;
double g = y + cg;
double b = y - co - cg;
rgb[0] = (int)(r * 255f);
rgb[1] = (int)(g * 255f);
rgb[2] = (int)(b * 255f);
return rgb;
}
/**
* RGB -> XYZ.
* @param color Color.
* @return XYZ color space.
*/
public static double[] RGBtoXYZ(Color color){
return RGBtoXYZ(color.r, color.g, color.b);
}
/**
* 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 double[] RGBtoXYZ(int red, int green, int blue){
return RGBtoXYZ(new int[]{red, green, blue});
}
/**
* RGB -> XYZ.
* @param rgb sRGB color space.
* @return XYZ color space.
*/
public static double[] RGBtoXYZ(int[] rgb){
double[] xyz = new double[3];
double r = rgb[0] / 255D;
double g = rgb[1] / 255D;
double b = rgb[2] / 255D;
//R
if ( r > 0.04045)
r = (double)Math.pow(( ( r + 0.055D ) / 1.055D ), 2.4D);
else
r /= 12.92D;
//G
if ( g > 0.04045)
g = (double)Math.pow(( ( g + 0.055D ) / 1.055D ), 2.4D);
else
g /= 12.92D;
//B
if ( b > 0.04045)
b = (double)Math.pow(( ( b + 0.055D ) / 1.055D ), 2.4D);
else
b /= 12.92D;
//Used for scale
r *= 100;
g *= 100;
b *= 100;
double x = 0.412453D * r + 0.35758D * g + 0.180423D * b;
double y = 0.212671D * r + 0.71516D * g + 0.072169D * b;
double z = 0.019334D * r + 0.119193D * g + 0.950227D * 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(double x, double y, double z){
return XYZtoRGB(new double[]{x,y,z});
}
/**
* XYZ -> RGB
* @param xyz XYZ color space.
* @return sRGB color space.
*/
public static int[] XYZtoRGB(double[] xyz){
int[] rgb = new int[3];
//Used for scale
double x = xyz[0] / 100;
double y = xyz[1] / 100;
double z = xyz[2] / 100;
double r = 3.240479D * x - 1.53715D * y - 0.498535D * z;
double g = -0.969256D * x + 1.875991D * y + 0.041556D * z;
double b = 0.055648D * x - 0.204043D * y + 1.057311D * z;
if ( r > 0.0031308 )
r = 1.055 * Math.pow(r, 1 / 2.4) - 0.055;
else
r = 12.92D * r;
if ( g > 0.0031308 )
g = 1.055 * Math.pow(g, 1 / 2.4) - 0.055;
else
g = 12.92D * g;
if ( b > 0.0031308 )
b = 1.055 * Math.pow(b, 1 / 2.4) - 0.055;
else
b = 12.92D * b;
r = r < 0 ? 0 : r;
g = g < 0 ? 0 : g;
b = b < 0 ? 0 : b;
r = r > 255 ? 255 : r;
g = g > 255 ? 255 : g;
b = b > 255 ? 255 : b;
rgb[0] = (int)Math.round(r * 255);
rgb[1] = (int)Math.round(g * 255);
rgb[2] = (int)Math.round(b * 255);
return rgb;
}
/**
* XYZ to xyY color space.
* @param x X coordinate.
* @param y Y coordinate.
* @param z Z coordinate.
* @return xyY color space.
*/
public static double[] XYZtoXyY(double x, double y, double z){
return XYZtoXyY(new double[]{x,y,z});
}
/**
* XYZ to xyY color space.
* @param xyz XYZ color space.
* @return xyY color space.
*/
public static double[] XYZtoXyY(double[] xyz){
double[] xyy = new double[3];
double sum = xyz[0] + xyz[1] + xyz[2];
xyy[0] = xyz[0] / sum;
xyy[1] = xyz[1] / sum;
xyy[2] = xyz[1];
return xyy;
}
/**
* XYZ -> HunterLAB
* @param x X coefficient.
* @param y Y coefficient.
* @param z Z coefficient.
* @return HunterLab coefficient.
*/
public static double[] XYZtoHunterLAB(double x, double y, double z){
double[] hunter = new double[3];
double sqrt = (double)Math.sqrt(y);
double l = 10 * sqrt;
double a = 17.5f * (((1.02f * x) - y) / sqrt);
double b = 7f * ((y - (0.847f * z)) / sqrt);
hunter[0] = l;
hunter[1] = a;
hunter[2] = b;
return hunter;
}
/**
* XYZ -> LMS
* CIECAM02 transformation matrix default.
* @param x X coordinate.
* @param y Y coordinate.
* @param z Z coordinate.
* @return LMS color space.
*/
public static double[] XYZtoLMS(double x, double y, double z){
return XYZtoLMS(new double[]{x,y,z}, LMS.CAT02);
}
/**
* XYZ -> LMS
* @param x X coordinate.
* @param y Y coordinate.
* @param z Z coordinate.
* @param matrix LMS transformation matrix.
* @return LMS color space.
*/
public static double[] XYZtoLMS(double x, double y, double z, LMS matrix){
return XYZtoLMS(new double[] {x,y,z}, matrix);
}
/**
* XYZ -> LMS
* CIECAM02 transformation matrix default.
* @param xyz XYZ Color space.
* @return LMS color space.
*/
public static double[] XYZtoLMS(double[] xyz){
return XYZtoLMS(xyz, LMS.CAT02);
}
/**
* XYZ -> LMS
* @param xyz XYZ color space.
* @param matrix LMS transformation matrix.
* @return LMS color space.
*/
public static double[] XYZtoLMS(double[] xyz, LMS matrix){
switch(matrix){
case HPE:
return Matrix.Multiply(xyz, hpe_f);
case Bradford:
return Matrix.Multiply(xyz, bradford_f);
case VonKries:
return Matrix.Multiply(xyz, vonkries_f);
case CAT97:
return Matrix.Multiply(xyz, cat97_f);
default:
return Matrix.Multiply(xyz, cat02_f);
}
}
/**
* xyY to XYZ.
* @param x X coordinate.
* @param y Y coordinate.
* @param Y Z coordinate.
* @return XYZ color space.
*/
public static double[] XYYtoXYZ(double x, double y, double Y){
return XYYtoXYZ(new double[]{x,y,Y});
}
/**
* xyY to XYZ.
* @param xyY color space..
* @return XYZ color space.
*/
public static double[] XYYtoXYZ(double[] xyY){
double x = (xyY[0] * xyY[2]) / xyY[1];
double z = ((1D - xyY[0] - xyY[1]) * xyY[2]) / xyY[1];
return new double[] {x, xyY[2], z};
}
/**
* HunterLAB -> XYZ
* @param l L coefficient.
* @param a A coefficient.
* @param b B coefficient.
* @return XYZ color space.
*/
public static double[] HunterLABtoXYZ(double l, double a, double b){
double[] xyz = new double[3];
double tempY = l / 10f;
double tempX = a / 17.5f * l / 10f;
double tempZ = b / 7f * l / 10f;
double y = tempY * tempY;
double x = (tempX + y) / 1.02f;
double z = -(tempZ - y) / 0.847f;
xyz[0] = x;
xyz[1] = y;
xyz[2] = z;
return xyz;
}
/**
* RGB -> HunterLAB.
* @param color Color.
* @return HunterLAB color space.
*/
public static double[] RGBtoHunterLAB(Color color){
return RGBtoHunterLAB(color.r, color.g, color.b);
}
/**
* 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 double[] RGBtoHunterLAB(int red, int green, int blue){
double[] 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(double l, double a, double b){
double[] xyz = HunterLABtoXYZ(l, a, b);
return XYZtoRGB(xyz[0], xyz[1], xyz[2]);
}
/**
* RGB -> HSL.
* @param color Color.
* @return HLS color space.
*/
public static double[] RGBtoHSL(Color color){
return RGBtoHSL(color.r, color.g, color.b);
}
/**
* RGB -> HSL.
* @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 double[] RGBtoHSL(int red, int green, int blue){
double[] hsl = new double[3];
double r = red / 255f;
double g = green / 255f;
double b = blue / 255f;
double max = Math.max(r,Math.max(g,b));
double min = Math.min(r,Math.min(g,b));
double delta = max - min;
//HSK
double h = 0;
double s = 0;
double 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 / ( 2f - max - min ) );
// get hue value
double hue;
if ( r == max )
{
hue = ( ( g - b ) / 6f ) / delta;
}
else if ( g == max )
{
hue = ( 1.0f / 3f ) + ( ( b - r ) / 6f ) / delta;
}
else
{
hue = ( 2.0f / 3f ) + ( ( r - g ) / 6f ) / delta;
}
// correct hue if needed
if ( hue < 0 )
hue += 1;
if ( hue > 1 )
hue -= 1;
h = (int) ( hue * 360f );
}
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(double hue, double saturation, double luminance){
int[] rgb = new int[3];
double r = 0, g = 0, b = 0;
if ( saturation == 0 )
{
// gray values
r = g = b = (int) ( luminance * 255 );
}
else
{
double v1, v2;
double h = (double) 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 double Hue_2_RGB( double v1, double v2, double 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;
}
public static double[] RGBtoLAB(int[] rgb){
return RGBtoLAB(rgb, Illuminant.CIE2.D65);
}
public static double[] RGBtoLAB(int[] rgb, double[] tristimulus){
double[] xyz = RGBtoXYZ(rgb);
return XYZtoLAB(xyz, tristimulus);
}
/**
* RGB -> CIE-LAB.
* @param color Color.
* @param tristimulus XYZ Tristimulus.
* @return CIE-LAB color space.
*/
public static double[] RGBtoLAB(Color color, double[] tristimulus){
return RGBtoLAB(color.r, color.g, color.b, tristimulus);
}
/**
* RGB -> CIE-LAB.
* Default: CIE 2o D65 Tristimulus.
*
* @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 CIE-L*A*B* color space.
*/
public static double[] RGBtoLAB(int red, int green, int blue){
return RGBtoLAB(red, green, blue, Illuminant.CIE2.D65);
}
/**
* 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 double[] RGBtoLAB(int red, int green, int blue, double[] tristimulus){
double[] xyz = RGBtoXYZ(red, green, blue);
double[] lab = XYZtoLAB(xyz[0], xyz[1], xyz[2], tristimulus);
return lab;
}
/**
* RGB -> CIE L*C*h.
* @param color Color.
* @return CIE-L*c*h color space.
*/
public static double[] RGBtoLCH(Color color){
return RGBtoLCH(color.r, color.g, color.b);
}
/**
* RGB -> CIE L*C*h.
* @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 CIE L*C*h color space.
*/
public static double[] RGBtoLCH(int red, int green, int blue){
return RGBtoLCH(red, green, blue, Illuminant.CIE2.D65);
}
/**
* RGB -> CIE L*C*h.
* @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 L*c*h color space.
*/
public static double[] RGBtoLCH(int red, int green, int blue, double[] tristimulus){
double[] lab = RGBtoLAB(red, green, blue, tristimulus);
return LABtoLCH(lab[0], lab[1], lab[2]);
}
/**
* RGB -> LMS color space.
* CIECAM02 transformation matrix default.
*
* @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 LMS color space.
*/
public static double[] RGBtoLMS(int red, int green, int blue){
return RGBtoLMS(new int[]{red, green, blue}, LMS.CAT02);
}
/**
* RGB -> LMS color space.
*
* @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 matrix LMS matrix.
* @return LMS color space.
*/
public static double[] RGBtoLMS(int red, int green, int blue, LMS matrix){
return RGBtoLMS(new int[]{red, green, blue}, matrix);
}
/**
* RGB -> LMS color space.
* CIECAM02 transformation matrix default.
*
* @param rgb sRGB color space.
* @return LMS color space.
*/
public static double[] RGBtoLMS(int[] rgb){
return RGBtoLMS(rgb, LMS.CAT02);
}
/**
* RGB -> LMS color space.
*
* @param rgb sRGB color space.
* @param matrix LMS matrix.
* @return LMS color space.
*/
public static double[] RGBtoLMS(int[] rgb, LMS matrix){
double[] xyz = RGBtoXYZ(rgb);
return XYZtoLMS(xyz, matrix);
}
/**
* CIE-L*A*B* to RGB.
* @param lab L*A*B* color space.
* @return RGB color space.
*/
public static int[] LABtoRGB(double[] lab){
return LABtoRGB(lab[0], lab[1], lab[2], Illuminant.CIE2.D65);
}
/**
* CIE-L*A*B* to RGB.
* @param lab L*A*B* color space.
* @param tristimulus XYZ Tristimulus.
* @return RGB color space.
*/
public static int[] LABtoRGB(double[] lab, double[] tristimulus){
return LABtoRGB(lab[0], lab[1], lab[2], tristimulus);
}
/**
* CIE-L*A*B* to RGB.
* @param l L coefficient.
* @param a a* coefficient.
* @param b b* coefficient.
* @return RGB color space.
*/
public static int[] LABtoRGB(double l, double a, double b){
return LABtoRGB(l, a, b, Illuminant.CIE2.D65);
}
/**
* 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(double l, double a, double b, double[] tristimulus){
double[] xyz = LABtoXYZ(l, a, b, tristimulus);
return XYZtoRGB(xyz[0], xyz[1], xyz[2]);
}
/**
* CIE-L*A*B* -> L*C*h
* @param lab CIE-L*A*B* color space.
* @return L*C*h color space.
*/
public static double[] LABtoLCH(double[] lab){
return LABtoLCH(lab[0], lab[1], lab[2]);
}
/**
* CIE-L*A*B* -> L*c*h
* @param l L coefficient.
* @param a A coefficient.
* @param b B coefficient.
* @return L*h*c color space.
*/
public static double[] LABtoLCH(double l, double a, double b){
double[] lch = new double[3];
double h = Math.toDegrees(Math.atan2(b, a));
if(h < 0) h += 360;
lch[0] = l;
lch[1] = Math.sqrt(a*a + b*b);
lch[2] = h;
return lch;
}
/**
* LMS -> RGB.
* @param l Long wavelength.
* @param m Medium wavelength.
* @param s Short wavelength.
* @return sRGB color space.
*/
public static int[] LMStoRGB(double l, double m, double s){
return LMStoRGB(new double[] {l,m,s}, LMS.CAT02);
}
/**
* LMS -> RGB.
* @param l Long wavelength.
* @param m Medium wavelength.
* @param s Short wavelength.
* @param matrix LMS transformation matrix.
* @return sRGB color space.
*/
public static int[] LMStoRGB(double l, double m, double s, LMS matrix){
return LMStoRGB(new double[] {l,m,s}, matrix);
}
/**
* LMS -> RGB.
* @param lms LMS color space.
* @return sRGB color space.
*/
public static int[] LMStoRGB(double[] lms){
return LMStoRGB(lms, LMS.CAT02);
}
/**
* LMS -> RGB.
* @param lms LMS color space.
* @param matrix LMS transformation matrix.
* @return sRGB color space.
*/
public static int[] LMStoRGB(double[] lms, LMS matrix){
double[] xyz = LMStoXYZ(lms, matrix);
return XYZtoRGB(xyz);
}
public static double[] XYZtoLAB(double[] xyz){
return XYZtoLAB(xyz, Illuminant.CIE2.D65);
}
public static double[] XYZtoLAB(double[] xyz, double[] tristimulus){
return XYZtoLAB(xyz[0], xyz[1], xyz[2], tristimulus);
}
/**
* 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 double[] XYZtoLAB(double x, double y, double z, double[] tristimulus){
double[] lab = new double[3];
//Need divide tristimulus/100 if needs scale
x /= tristimulus[0];
y /= tristimulus[1];
z /= tristimulus[2];
if (x > 0.008856)
x = (double)Math.pow(x,1/3D);
else
x = 7.787036 * x + 0.1379310344827586;
if (y > 0.008856)
y = (double)Math.pow(y,1/3D);
else
y = 7.787036 * y + 0.1379310344827586;
if (z > 0.008856)
z = (double)Math.pow(z,1/3D);
else
z = 7.787036 * z + 0.1379310344827586;
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 double[] LABtoXYZ(double l, double a, double b, double[] tristimulus){
double[] xyz = new double[3];
double y = ( l + 16D ) / 116D;
double x = (a / 500D) + y;
double z = y - (b / 200D);
//X
if ( Math.pow(x,3) > e )
x = (double)Math.pow(x,3);
else
x = (double)(116 * x - 16) / k;
//Y
if ( l > 8 )
y = Math.pow(((l + 16) / 116D),3);
else
y = l / k;
// Z
if ( Math.pow(z,3) > e )
z = (double)Math.pow(z,3);
else
z = (double)(116 * z - 16) / k;
//Need divide tristimulus/100 if needs scale
xyz[0] = x * tristimulus[0];
xyz[1] = y * tristimulus[1];
xyz[2] = z * tristimulus[2];
return xyz;
}
/**
* L*c*h -> CIE-L*A*B*
* @param l L coefficient.
* @param c *c coefficient.
* @param h *h coefficient.
* @return CIE-L*A*B* color space.
*/
public static double[] LCHtoLAB(double l, double c, double h){
double[] lab = new double[3];
lab[0] = l;
lab[1] = c * Math.cos(Math.toRadians(h));
lab[2] = c * Math.sin(Math.toRadians(h));
return lab;
}
/**
* L*c*h -> RGB.
* @param l L coefficient.
* @param c *c coefficient.
* @param h *h coefficient.
* @return RGB color space.
*/
public static int[] LCHtoRGB(double l, double c, double h){
double[] lab = LCHtoLAB(l, c, h);
return LABtoRGB(lab[0], lab[1], lab[2], Illuminant.CIE2.D65);
}
/**
* LMS -> XYZ
* @param l Long wavelengths.
* @param m Medium wavelengths.
* @param s Short wavelengths.
* @param matrix Transformation matrix.
* @return XYZ color space.
*/
public static double[] LMStoXYZ(double l, double m, double s, LMS matrix){
return LMStoXYZ(new double[] {l,m,s}, matrix);
}
/**
* LMS -> XYZ
* @param lms LMS color space.
* @param matrix Transformation matrix.
* @return XYZ color space.
*/
public static double[] LMStoXYZ(double[] lms, LMS matrix){
switch(matrix){
case HPE:
return Matrix.Multiply(lms, hpe_b);
case Bradford:
return Matrix.Multiply(lms, bradford_b);
case VonKries:
return Matrix.Multiply(lms, vonkries_b);
case CAT97:
return Matrix.Multiply(lms, cat97_b);
default:
return Matrix.Multiply(lms, cat02_b);
}
}
/**
* RGB -> C1C2C3.
* @param color Color.
* @return C1C2C3 color space.
*/
public static double[] RGBtoC1C2C3(Color color){
return RGBtoC1C2C3(color.r, color.g, color.b);
}
/**
* 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 double[] RGBtoC1C2C3(int r, int g, int b){
double[] c = new double[3];
c[0] = (double)Math.atan(r / Math.max(g, b));
c[1] = (double)Math.atan(g / Math.max(r, b));
c[2] = (double)Math.atan(b / Math.max(r, g));
return c;
}
/**
* RGB -> O1O2.
* @param color Color.
* @return O1O2 color space.
*/
public static double[] RGBtoO1O2(Color color){
return RGBtoO1O2(color.r, color.g, color.b);
}
/**
* 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 double[] RGBtoO1O2(int r, int g, int b){
double[] o = new double[2];
o[0] = (r - g) / 2f;
o[1] = (r + g) / 4f - (b / 2f);
return o;
}
/**
* RGB -> Grayscale.
* @param color Color.
* @return Grayscale color space.
*/
public static double RGBtoGrayscale(Color color){
return RGBtoGrayscale(color.r, color.g, color.b);
}
/**
* 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 double RGBtoGrayscale(int r, int g, int b){
return r*0.2125f + g*0.7154f + b*0.0721f;
}
}
Ads help maintain this website.