Re: RGB to L*a*b* conversion
Posted by Duane and Julie on
URL: http://imagej.273.s1.nabble.com/RGB-to-L-a-b-Conversion-tp3703729p3703730.html
Hi all,
My previous post was rejected, because I had an attached java source
file (!?). This is the repost, with code in line.
----
RGB is an uncalibrated space, L*a*b* is a calibrated space. That
said, if you assume the RGB to really be sRGB you can do a conversion
that is probably what you want, or close to it.
I wrote a color space converter, which I have attached. The code
converts one pixel. To convert an image, visit each pixel and set
each channel to the output. As an example, if you want to convert to
L*a*b* from RGB you notice that the R is in the first channel, the G
in the second and the B in the third. Simply set the first channel
to L*, the second to a* and the third to b*. Writing a plugin to
convert an entire image is straightforward.
Hope that helps!
duane
----
/*
* ColorSpaceConverter.java
* Created on Jan 15, 2004
*
*/
package mil.nawcwd.isp;
import java.text.DecimalFormat;
/**
* ColorSpaceConverter
* @author dvs
* Created Jan 15, 2004
*/
public class ColorSpaceConverter {
/**
* reference white in XYZ coordinates
*/
public static double[] D65 = {95.047, 100.0, 108.883};
/**
* sRGB to XYZ conversion matrix
*/
public static double[][] M = {{0.412424, 0.212656, 0.0193324},
{0.357579, 0.715158, 0.119193 },
{0.180464, 0.0721856, 0.950444 }};
/**
* XYZ to sRGB conversion matrix
*/
public static double[][] Mi = {{3.24071, -0.969258, 0.0556352},
{-1.53726, 1.87599, -0.203996 },
{-0.498571, 0.0415557, 1.05707 }};
/**
* Main method for testing.
* @param args
* ColorSpaceConverter [-rgb R G B | -xyz X Y Z | -lab L a b]
*/
public static void main(String[] args) {
// local variables
ColorSpaceConverter csc = new ColorSpaceConverter();
int R = 0, G = 0, B = 0;
double X = 0, Y = 0, Z = 0;
double L = 0, a = 0, b = 0;
double[] rf;
int[] ri;
DecimalFormat dfi = new DecimalFormat(" ##0;-##0");
DecimalFormat dfd = new DecimalFormat(" #0.000;-#0.000");
// for each argument
for (int i = 0; i < args.length; i++) {
try {
if (args[i].equalsIgnoreCase("-rgb")) {
i++;
R = new Integer(args[i]).intValue();
i++;
G = new Integer(args[i]).intValue();
i++;
B = new Integer(args[i]).intValue();
rf = csc.RGBtoXYZ(R, G, B);
X = rf[0];
Y = rf[1];
Z = rf[2];
rf = csc.XYZtoLAB(X, Y, Z);
L = rf[0];
a = rf[1];
b = rf[2];
}
else if (args[i].equalsIgnoreCase("-xyz")) {
i++;
X = new Double(args[i]).doubleValue();
i++;
Y = new Double(args[i]).doubleValue();
i++;
Z = new Double(args[i]).doubleValue();
rf = csc.XYZtoLAB(X, Y, Z);
L = rf[0];
a = rf[1];
b = rf[2];
ri = csc.XYZtoRGB(X, Y, Z);
R = ri[0];
G = ri[1];
B = ri[2];
}
else if (args[i].equalsIgnoreCase("-lab")) {
i++;
L = new Double(args[i]).doubleValue();
i++;
a = new Double(args[i]).doubleValue();
i++;
b = new Double(args[i]).doubleValue();
rf = csc.LABtoXYZ(L, a, b);
X = rf[0];
Y = rf[1];
Z = rf[2];
ri = csc.LABtoRGB(L, a, b);
R = ri[0];
G = ri[1];
B = ri[2];
}
}
catch (ArrayIndexOutOfBoundsException e) {
System.err.println("Invalid input");
e.printStackTrace();
System.exit(-1);
}
System.out.println("");
System.out.println("RGB: " + dfi.format(R) + ",\t" + dfi.format
(G)
+ ",\t" + dfi.format(B));
System.out.println("XYZ: " + dfd.format(X) + ",\t" + dfd.format
(Y)
+ ",\t" + dfd.format(Z));
System.out.println("Lab: " + dfd.format(L) + ",\t" + dfd.format
(a)
+ ",\t" + dfd.format(b));
}
} // main
/**
* Convert LAB to RGB.
* @param L
* @param a
* @param b
* @return RGB values
*/
public int[] LABtoRGB(double L, double a, double b) {
return XYZtoRGB(LABtoXYZ(L, a, b));
}
/**
* @param Lab
* @return RGB values
*/
public int[] LABtoRGB(double[] Lab) {
return XYZtoRGB(LABtoXYZ(Lab));
}
/**
* Convert LAB to XYZ.
* @param L
* @param a
* @param b
* @return XYZ values
*/
public double[] LABtoXYZ(double L, double a, double b) {
double[] result = new double[3];
double y = (L + 16.0) / 116.0;
double y3 = Math.pow(y, 3.0);
double x = (a / 500.0) + y;
double x3 = Math.pow(x, 3.0);
double z = y - (b / 200.0);
double z3 = Math.pow(z, 3.0);
if (y3 > 0.008856) {
y = y3;
}
else {
y = (y - (16.0 / 116.0)) / 7.787;
}
if (x3 > 0.008856) {
x = x3;
}
else {
x = (x - (16.0 / 116.0)) / 7.787;
}
if (z3 > 0.008856) {
z = z3;
}
else {
z = (z - (16.0 / 116.0)) / 7.787;
}
result[0] = x * D65[0];
result[1] = y * D65[1];
result[2] = z * D65[2];
return result;
}
/**
* Convert LAB to XYZ.
* @param Lab
* @return XYZ values
*/
public double[] LABtoXYZ(double[] Lab) {
return LABtoXYZ(Lab[0], Lab[1], Lab[2]);
}
/**
* @param R
* @param G
* @param B
* @return Lab values
*/
public double[] RGBtoLAB(int R, int G, int B) {
return XYZtoLAB(RGBtoXYZ(R, G, B));
}
/**
* @param RGB
* @return Lab values
*/
public double[] RGBtoLAB(int[] RGB) {
return XYZtoLAB(RGBtoXYZ(RGB));
}
/**
* Convert RGB to XYZ
* @param R
* @param G
* @param B
* @return XYZ in double array.
*/
public double[] RGBtoXYZ(int R, int G, int B) {
double[] result = new double[3];
// convert 0..255 into 0..1
double r = R / 255.0;
double g = G / 255.0;
double b = B / 255.0;
// assume sRGB
if (r <= 0.04045) {
r = r / 12.92;
}
else {
r = Math.pow(((r + 0.055) / 1.055), 2.4);
}
if (g <= 0.04045) {
g = g / 12.92;
}
else {
g = Math.pow(((g + 0.055) / 1.055), 2.4);
}
if (b <= 0.04045) {
b = b / 12.92;
}
else {
b = Math.pow(((b + 0.055) / 1.055), 2.4);
}
r *= 100.0;
g *= 100.0;
b *= 100.0;
// [X Y Z] = [r g b][M]
result[0] = (r * M[0][0]) + (g * M[1][0]) + (b * M[2][0]);
result[1] = (r * M[0][1]) + (g * M[1][1]) + (b * M[2][1]);
result[2] = (r * M[0][2]) + (g * M[1][2]) + (b * M[2][2]);
return result;
}
/**
* Convert RGB to XYZ
* @param RGB
* @return XYZ in double array.
*/
public double[] RGBtoXYZ(int[] RGB) {
return RGBtoXYZ(RGB[0], RGB[1], RGB[2]);
}
/**
* @param x
* @param y
* @param Y
* @return XYZ values
*/
public double[] xyYtoXYZ(double x, double y, double Y) {
double[] result = new double[3];
if (y == 0) {
result[0] = 0;
result[1] = 0;
result[2] = 0;
}
else {
result[0] = (x * Y) / y;
result[1] = Y;
result[2] = ((1 - x - y) * Y) / y;
}
return result;
}
/**
* @param xyY
* @return XYZ values
*/
public double[] xyYtoXYZ(double[] xyY) {
return xyYtoXYZ(xyY[0], xyY[1], xyY[2]);
}
/**
* Convert XYZ to LAB.
* @param X
* @param Y
* @param Z
* @return Lab values
*/
public double[] XYZtoLAB(double X, double Y, double Z) {
double x = X / D65[0];
double y = Y / D65[1];
double z = Z / D65[2];
if (x > 0.008856) {
x = Math.pow(x, 1.0 / 3.0);
}
else {
x = (7.787 * x) + (16.0 / 116.0);
}
if (y > 0.008856) {
y = Math.pow(y, 1.0 / 3.0);
}
else {
y = (7.787 * y) + (16.0 / 116.0);
}
if (z > 0.008856) {
z = Math.pow(z, 1.0 / 3.0);
}
else {
z = (7.787 * z) + (16.0 / 116.0);
}
double[] result = new double[3];
result[0] = (116.0 * y) - 16.0;
result[1] = 500.0 * (x - y);
result[2] = 200.0 * (y - z);
return result;
}
/**
* Convert XYZ to LAB.
* @param XYZ
* @return Lab values
*/
public double[] XYZtoLAB(double[] XYZ) {
return XYZtoLAB(XYZ[0], XYZ[1], XYZ[2]);
}
/**
* Convert XYZ to RGB.
* @param X
* @param Y
* @param Z
* @return RGB in int array.
*/
public int[] XYZtoRGB(double X, double Y, double Z) {
int[] result = new int[3];
double x = X / 100.0;
double y = Y / 100.0;
double z = Z / 100.0;
// [r g b] = [X Y Z][Mi]
double r = (x * Mi[0][0]) + (y * Mi[1][0]) + (z * Mi[2][0]);
double g = (x * Mi[0][1]) + (y * Mi[1][1]) + (z * Mi[2][1]);
double b = (x * Mi[0][2]) + (y * Mi[1][2]) + (z * Mi[2][2]);
// assume sRGB
if (r > 0.0031308) {
r = ((1.055 * Math.pow(r, 1.0 / 2.4)) - 0.055);
}
else {
r = (r * 12.92);
}
if (g > 0.0031308) {
g = ((1.055 * Math.pow(g, 1.0 / 2.4)) - 0.055);
}
else {
g = (g * 12.92);
}
if (b > 0.0031308) {
b = ((1.055 * Math.pow(b, 1.0 / 2.4)) - 0.055);
}
else {
b = (b * 12.92);
}
r = (r < 0) ? 0 : r;
g = (g < 0) ? 0 : g;
b = (b < 0) ? 0 : b;
// convert 0..1 into 0..255
result[0] = (int) Math.round(r * 255);
result[1] = (int) Math.round(g * 255);
result[2] = (int) Math.round(b * 255);
return result;
}
/**
* Convert XYZ to RGB
* @param XYZ in a double array.
* @return RGB in int array.
*/
public int[] XYZtoRGB(double[] XYZ) {
return XYZtoRGB(XYZ[0], XYZ[1], XYZ[2]);
}
/**
* @param X
* @param Y
* @param Z
* @return xyY values
*/
public double[] XYZtoxyY(double X, double Y, double Z) {
double[] result = new double[3];
if ((X + Y + Z) == 0) {
result[0] = D65[0];
result[1] = D65[1];
result[2] = D65[2];
}
else {
result[0] = X / (X + Y + Z);
result[1] = Y / (X + Y + Z);
result[2] = Y;
}
return result;
}
/**
* @param XYZ
* @return xyY values
*/
public double[] XYZtoxyY(double[] XYZ) {
return XYZtoxyY(XYZ[0], XYZ[1], XYZ[2]);
}
}