【问题标题】:Java: how to convert RGB color to CIE LabJava:如何将 RGB 颜色转换为 CIE Lab
【发布时间】:2011-06-03 08:51:40
【问题描述】:

如何在 CIE Lab 颜色模型中呈现对象颜色。

Color c = ...
float[] lab = {0,0,0};
...
c.getColorComponents(ColorSpace.getInstance(???), lab);

但我无法强制使用 CIE Lab 进行这项工作(尽管 TYPE_Lab 出现在 ColorSpace 类中)

感谢关注。

【问题讨论】:

    标签: java color-space


    【解决方案1】:

    很抱歉打了一个旧帖子,但任何新帖子都可能被标记为重复 - 我觉得评价最高的答案很复杂或设计过度,而其他答案不完整或只是缺乏信息。

    public static float[] fromRGB(int r, int g, int b) {
        return ColorSpace.getInstance(ColorSpace.CS_CIEXYZ).fromRGB(new float[]{r / 255f, g / 255f, b / 255f});
    }
    

    使用 awt.color.ColorSpace 的 Easy 1 班轮 - 在我的实践中效果很好。你可以这样计算距离

    // Euclidean Distance
    public static double distance(Color target, Color control) {
        float[] a = rgbToLab(target), b = rgbToLab(control);
        double L = a[0] - b[0], A = a[1] - b[1], B = a[2] - b[2];
    
        return Math.sqrt((L * L) + (A * A) + (B * B));
    }
    

    这会产生这样的结果;

    // Control color = #D9C967
    #213B1E | DISTANCE: 2.5532837723818224E-4
    #19301C | DISTANCE: 2.74658203125E-4
    #1E2D10 | DISTANCE: 2.74658203125E-4
    #DDC669 | DISTANCE: 0.0
    #DDC56B | DISTANCE: 0.0
    #DAC761 | DISTANCE: 0.0
    

    【讨论】:

      【解决方案2】:

      有一个TYPE_Lab,但没有对应的CS_Lab。您将需要扩展 ColorSpace 并覆盖抽象方法以在 XYZ、RGB 和 Lab 之间进行转换。所需的转换可以在Lab color space (Wikipedia)找到。

      【讨论】:

        【解决方案3】:

        我使用了这段代码,它成功了:

        public double[] rgbToLab(int R, int G, int B) {
        
            double r, g, b, X, Y, Z, xr, yr, zr;
        
            // D65/2°
            double Xr = 95.047;  
            double Yr = 100.0;
            double Zr = 108.883;
        
        
            // --------- RGB to XYZ ---------//
        
            r = R/255.0;
            g = G/255.0;
            b = B/255.0;
        
            if (r > 0.04045)
                r = Math.pow((r+0.055)/1.055,2.4);
            else
                r = r/12.92;
        
            if (g > 0.04045)
                g = Math.pow((g+0.055)/1.055,2.4);
            else
                g = g/12.92;
        
            if (b > 0.04045)
                b = Math.pow((b+0.055)/1.055,2.4);
            else
                b = b/12.92 ;
        
            r*=100;
            g*=100;
            b*=100;
        
            X =  0.4124*r + 0.3576*g + 0.1805*b;
            Y =  0.2126*r + 0.7152*g + 0.0722*b;
            Z =  0.0193*r + 0.1192*g + 0.9505*b;
        
        
            // --------- XYZ to Lab --------- //
        
            xr = X/Xr;
            yr = Y/Yr;
            zr = Z/Zr;
        
            if ( xr > 0.008856 )
                xr =  (float) Math.pow(xr, 1/3.);
            else
                xr = (float) ((7.787 * xr) + 16 / 116.0);
        
            if ( yr > 0.008856 )
                yr =  (float) Math.pow(yr, 1/3.);
            else
                yr = (float) ((7.787 * yr) + 16 / 116.0);
        
            if ( zr > 0.008856 )
                zr =  (float) Math.pow(zr, 1/3.);
            else
                zr = (float) ((7.787 * zr) + 16 / 116.0);
        
        
            double[] lab = new double[3];
        
            lab[0] = (116*yr)-16;
            lab[1] = 500*(xr-yr); 
            lab[2] = 200*(yr-zr); 
        
            return lab;
        
        } 
        

        对于上面的代码,我使用了here 提供的公式,以便从 rgb 转换为 XYZ,然后从 XYZ 转换为 CIELab。我得到的结果和this在线转换器一样。

        【讨论】:

          【解决方案4】:

          我在使用@finw 答案中的代码时遇到了一些问题。我相信它们主要是因为要进行 CIELab 转换,您应该指定一个光源:

          http://en.wikipedia.org/wiki/Standard_illuminant

          其中一个流行的标准是D50,它基本上只是一个标准的日光。因为@finw 的代码没有对照明进行校正,所以应该是中性灰色的颜色会略微着色。检查这一点的一种方法是尝试:

           float[] g = { 50.0f, 0f, 0f };
           CIELab.getInstance().toRGB(g); 
           for (float f : g) System.out.println(f);
          

          您应该在所有三个通道上获得大致相同的数字,但您最终会得到一个明显(尽管略微)蓝色的 RGB 配置文件。我确信可以在@finw 的代码中纠正这个问题,但经过一番尝试和搜索后,我在这里找到了一些出色的转换代码:

          http://www.f4.fhtw-berlin.de/~barthel/ImageJ/ColorInspector//HTMLHelp/farbraumJava.htm

          为了完整起见,这里是。

          public void rgb2lab(int R, int G, int B, int[] lab) {
              //http://www.brucelindbloom.com
          
              float r, g, b, X, Y, Z, fx, fy, fz, xr, yr, zr;
              float Ls, as, bs;
              float eps = 216.f/24389.f;
              float k = 24389.f/27.f;
          
              float Xr = 0.964221f;  // reference white D50
              float Yr = 1.0f;
              float Zr = 0.825211f;
          
              // RGB to XYZ
              r = R/255.f; //R 0..1
              g = G/255.f; //G 0..1
              b = B/255.f; //B 0..1
          
              // assuming sRGB (D65)
              if (r <= 0.04045)
                  r = r/12;
              else
                  r = (float) Math.pow((r+0.055)/1.055,2.4);
          
              if (g <= 0.04045)
                  g = g/12;
              else
                  g = (float) Math.pow((g+0.055)/1.055,2.4);
          
              if (b <= 0.04045)
                  b = b/12;
              else
                  b = (float) Math.pow((b+0.055)/1.055,2.4);
          
          
              X =  0.436052025f*r     + 0.385081593f*g + 0.143087414f *b;
              Y =  0.222491598f*r     + 0.71688606f *g + 0.060621486f *b;
              Z =  0.013929122f*r     + 0.097097002f*g + 0.71418547f  *b;
          
              // XYZ to Lab
              xr = X/Xr;
              yr = Y/Yr;
              zr = Z/Zr;
          
              if ( xr > eps )
                  fx =  (float) Math.pow(xr, 1/3.);
              else
                  fx = (float) ((k * xr + 16.) / 116.);
          
              if ( yr > eps )
                  fy =  (float) Math.pow(yr, 1/3.);
              else
              fy = (float) ((k * yr + 16.) / 116.);
          
              if ( zr > eps )
                  fz =  (float) Math.pow(zr, 1/3.);
              else
                  fz = (float) ((k * zr + 16.) / 116);
          
              Ls = ( 116 * fy ) - 16;
              as = 500*(fx-fy);
              bs = 200*(fy-fz);
          
              lab[0] = (int) (2.55*Ls + .5);
              lab[1] = (int) (as + .5); 
              lab[2] = (int) (bs + .5);       
          } 
          

          在我的测试中,它会产生适当无色度的灰度值,并且启动速度要快得多。

          【讨论】:

          • 你确定这是正确的实现吗?我试图隐藏白色 (255, 255, 255),结果是 (L=255, a=0, b=0) 而不是 (L=100, a=0, b=0)。我还检查了colormine.org/convert/rgb-to-lab 以与其他颜色(例如红色、黄色和绿色)进行比较。
          • 看来Ls 是按比例放大以填充 [0, 255] 范围。只返回Ls 就足够了。我也省略了+ .5s。它们似乎是多余的,因为它们会导致值溢出。我可以将值四舍五入,这样更有意义。
          【解决方案5】:

          这是我的实现:

          import java.awt.color.ColorSpace;
          
          public class CIELab extends ColorSpace {
          
              public static CIELab getInstance() {
                  return Holder.INSTANCE;
              }
          
              @Override
              public float[] fromCIEXYZ(float[] colorvalue) {
                  double l = f(colorvalue[1]);
                  double L = 116.0 * l - 16.0;
                  double a = 500.0 * (f(colorvalue[0]) - l);
                  double b = 200.0 * (l - f(colorvalue[2]));
                  return new float[] {(float) L, (float) a, (float) b};
              }
          
              @Override
              public float[] fromRGB(float[] rgbvalue) {
                  float[] xyz = CIEXYZ.fromRGB(rgbvalue);
                  return fromCIEXYZ(xyz);
              }
          
              @Override
              public float getMaxValue(int component) {
                  return 128f;
              }
          
              @Override
              public float getMinValue(int component) {
                  return (component == 0)? 0f: -128f;
              }    
          
              @Override
              public String getName(int idx) {
                  return String.valueOf("Lab".charAt(idx));
              }
          
              @Override
              public float[] toCIEXYZ(float[] colorvalue) {
                  double i = (colorvalue[0] + 16.0) * (1.0 / 116.0);
                  double X = fInv(i + colorvalue[1] * (1.0 / 500.0));
                  double Y = fInv(i);
                  double Z = fInv(i - colorvalue[2] * (1.0 / 200.0));
                  return new float[] {(float) X, (float) Y, (float) Z};
              }
          
              @Override
              public float[] toRGB(float[] colorvalue) {
                  float[] xyz = toCIEXYZ(colorvalue);
                  return CIEXYZ.toRGB(xyz);
              }
          
              CIELab() {
                  super(ColorSpace.TYPE_Lab, 3);
              }
          
              private static double f(double x) {
                  if (x > 216.0 / 24389.0) {
                      return Math.cbrt(x);
                  } else {
                      return (841.0 / 108.0) * x + N;
                  }
              }
          
              private static double fInv(double x) {
                  if (x > 6.0 / 29.0) {
                      return x*x*x;
                  } else {
                      return (108.0 / 841.0) * (x - N);
                  }
              }
          
              private Object readResolve() {
                  return getInstance();
              }
          
              private static class Holder {
                  static final CIELab INSTANCE = new CIELab();
              }
          
              private static final long serialVersionUID = 5027741380892134289L;
          
              private static final ColorSpace CIEXYZ =
                  ColorSpace.getInstance(ColorSpace.CS_CIEXYZ);
          
              private static final double N = 4.0 / 29.0;
          
          }
          

          【讨论】:

          • @Peter Perháč,Creative Commons Attribution Share Alike 许可证适用。
          • 非常好 - 我使用它没有任何问题,但是我做了一个小改动。在我的应用程序中,我将 RGB 图像转换为 CIELab 颜色空间,在 L 通道上执行了一些处理,然后转换回 RGB,并发现了丑陋的伪影。我使用的解决方案是将上述toCIEXYZ 方法中计算的X、Y、Z 值限制在[0,1] 范围内。不确定这是否是“正确”的做事方式,但它摆脱了伪影。
          • @James 代表yasir-gakhar:嗨,James。我已经计算了图像的 R、G 和 B 值,并进行了一些计算以获得 LAB 色彩空间的 L、a 和 b 值。现在如何在 Android Studio 中使用这些 L、a 和 b 值将我的 RGB 图像转换为 LAB 图像(OpenCV 的内置函数除外,因为我想先将 RGB 转换为 XYZ,然后最后将 XYZ 转换为 LAB 颜色空间)?
          • @jps @yasir-gakhar 这个答案中的实现正是这样做的——看到fromRGB方法调用fromCIEXYZ
          【解决方案6】:

          当前 Java 库中似乎仅通过名称支持 CIELAB - 如果您查看 java.awt.color.Colorspace 的源代码,您会发现仅支持少数命名的颜色空间。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2014-05-02
            • 2014-03-01
            • 1970-01-01
            • 2013-08-04
            • 2010-09-26
            • 2011-08-25
            • 1970-01-01
            • 2019-02-08
            相关资源
            最近更新 更多