RGB to L*a*b* Conversion

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
9 messages Options
Reply | Threaded
Open this post in threaded view
|

RGB to L*a*b* Conversion

Vladimir Hegyi
Hi,
I need to transform my RGB images to the L*a*b* space, but I need all 3
channels to be displayed together.
Could somebody help?
Thank you.

Assoc.Prof.Vladimir Hegyi,MD,PhD.
Dept. of Pediatric Dermatovenerology
School of Medicine
Comenius University
Limbova 1
833 40 Bratislava
Slovak Republic
e-mail: [hidden email]
www.dermatology.sk
Reply | Threaded
Open this post in threaded view
|

Re: RGB to L*a*b* Conversion

dscho
Hi,

On Fri, 10 Feb 2006, Vladimir Hegyi wrote:

> I need to transform my RGB images to the L*a*b* space, but I need all 3
> channels to be displayed together.

Note that there is no standard RGB. See e.g.

        http://www.brucelindbloom.com

for details.

Hth,
Dscho
Reply | Threaded
Open this post in threaded view
|

Re: RGB to L*a*b* conversion

Duane and Julie
In reply to this post by Vladimir Hegyi
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]);
   }

}
Reply | Threaded
Open this post in threaded view
|

Re: RGB to L*a*b* conversion

Vladimir Hegyi
Hi,
Thank you very much for your help.
Vlado

----- Original Message -----
From: "Duane & Julie" <[hidden email]>
To: <[hidden email]>
Sent: Saturday, February 11, 2006 11:44 PM
Subject: Re: RGB to L*a*b* conversion


> 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]);
>   }
>
> }
>
>
Reply | Threaded
Open this post in threaded view
|

Re: RGB to L*a*b* conversion

michael shaffer
In reply to this post by Duane and Julie
 Duane writes ...

> 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, [...]

  Given that sRGB is a much smaller gamut than L*a*b ... It wouldn't be much
more difficult to convert from one of the larger gamut color spaces ...
e.g., ProPhoto.  Granted, there are few devices that can take advantage of
large gamut color spaces, but sRGB (... A de facto monitor standard ...)
seems too small to be worthy of a conversion to L*a*b.

  ... Just a suggestion ...

Genuinely, Michael Shaffer  :o)

SEM/MLA Research Coordinator
     (709) 737-6799 (ofc)
     (709) 737-6790 (lab)
     (709) 737-6193 (FAX)
     http://www.mun.ca/creait/maf/
     http://www.esd.mun.ca/epma/ 

Inco Innovation Centre
c/o Memorial University
230 Elizabeth Avenue
P.O. Box 4200
St. John's, NL A1C 5S7
Reply | Threaded
Open this post in threaded view
|

Re: RGB to L*a*b* conversion

Vladimir Hegyi
In reply to this post by Duane and Julie
Hi Duane,
I tried to compile attached java code but without the success.
Would you be so kind to help me again?
Thanks.
Vlado

----- Original Message -----
From: "Duane & Julie" <[hidden email]>
To: <[hidden email]>
Sent: Saturday, February 11, 2006 11:44 PM
Subject: Re: RGB to L*a*b* conversion


> 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]);
>   }
>
> }
>
>
Reply | Threaded
Open this post in threaded view
|

Re: RGB to L*a*b* conversion

Duane and Julie
In reply to this post by michael shaffer
Michael,

I agree that sRGB has a smaller gamut, but you deal with what you've  
got.  :-)  I picked sRGB as my uncalibrated space because it's the  
only one where we know the phosphors.  It's also more prevalent  
nowadays.  You will note in the code I posted that I actually convert  
to XYZ from whatever and then on to whatever is requested.  The  
problem is, when's the last time you found a camera that put out XYZ?

    duane

On Feb 11, 2006, at 4:45 PM, michael shaffer wrote:

>  Duane writes ...
>
>> 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, [...]
>
>   Given that sRGB is a much smaller gamut than L*a*b ... It  
> wouldn't be much
> more difficult to convert from one of the larger gamut color  
> spaces ...
> e.g., ProPhoto.  Granted, there are few devices that can take  
> advantage of
> large gamut color spaces, but sRGB (... A de facto monitor  
> standard ...)
> seems too small to be worthy of a conversion to L*a*b.
>
>   ... Just a suggestion ...
>
> Genuinely, Michael Shaffer  :o)
>
> SEM/MLA Research Coordinator
>      (709) 737-6799 (ofc)
>      (709) 737-6790 (lab)
>      (709) 737-6193 (FAX)
>      http://www.mun.ca/creait/maf/
>      http://www.esd.mun.ca/epma/
>
> Inco Innovation Centre
> c/o Memorial University
> 230 Elizabeth Avenue
> P.O. Box 4200
> St. John's, NL A1C 5S7
Reply | Threaded
Open this post in threaded view
|

Re: RGB to L*a*b* conversion

Duane and Julie
OK, the post below was rejected as well.  If anyone besides Vlado would like the color space conversion plugin, let me know, or tell me how to post a link on the list.

Thanks,

   duane

 
Vlado,

I've finished the plugin.  It contains an inner class so that you didn't have to load my entire library as well.

Some notes:

This plugin converts between RGB, HSB, LAB and XYZ.
Multiple conversions lead to round off error.
All conversions are done in place.
The range of the color space is adjusted to fit an RGB image space.
  HSB is normally 0..360, 0..1, 0..1
  LAB is normally 0..100, -100..100, -100..100
  XYZ is normally 0..100, 0..100, 0..100
  All are scaled to fit 0..255
The planes are set according to the abbreviation.
  HSB is H->R, S->G, B->B, etc.
No warranties, and limited testing has been done.  Use at your own risk.

I've attached the source as a zip this time, to see if it will get through.

If anyone else finds this useful, use it.  If you find bugs, let me know, I might be able to fix them.  If anyone wants enhancements, let me know, I might be able to add them.

Cheers!

   duane
Reply | Threaded
Open this post in threaded view
|

Re: RGB to L*a*b* conversion

Duane and Julie
In reply to this post by Duane and Julie
There appears to be some interest in the color space conversion plugin.

Since I can't seem to post it here, I sent it to Wayne.

Feedback, bug reports and enhancement requests are welcome, but no guarantees are expressed or implied.

   duane