代码之家  ›  专栏  ›  技术社区  ›  Louis Brandy

跟进:找出颜色之间的准确“距离”

  •  45
  • Louis Brandy  · 技术社区  · 17 年前

    Original Question

    我正在寻找一个函数,它试图量化两种颜色之间的距离(或不同)。这个问题实际上分为两部分:

    1. 什么颜色的空间最能代表人类的视觉?
    2. 在那个空间中,什么距离度量最能代表人类视觉(欧几里得原理?)
    8 回复  |  直到 9 年前
        1
  •  43
  •   gideon    13 年前

    转换为la*b*(也就是简单的“la b”,您还会看到“cielab”的引用)。一个好的快速测量色差的方法是

    (l1-l2)^2+(a1-a2)^2+(b1-b2)^2

    色彩科学家还有其他更精确的测量方法,这可能不值得麻烦,这取决于你所做的工作所需的准确度。

    这个 a b 值以类似于圆锥体工作方式的方式表示相反的颜色,可以是负数或正数。中性色-白色、灰色 a=0 , b=0 . 这个 L 亮度是以特定的方式定义的,从零(纯黑暗)到任何东西。

    粗略的解释:给定一种颜色,我们的眼睛可以区分两个波长范围——蓝色和较长波长。然后,由于最近的基因突变,长波锥分裂成两个,区别于我们的红色和绿色。

    顺便说一句,你的职业生涯会很好地超越那些只知道“rgb”或“cmyk”的色彩穴居人同事,他们对设备很好,但对严肃的感知工作很差劲。我为成像科学家工作过,他们对这种东西一无所知!

    阅读色差理论更有趣,请尝试:

    有关实验室的更多详细信息,请访问 http://en.kioskea.net/video/cie-lab.php3 我现在找不到一个真正有转换公式的非丑陋页面,但我相信有人会编辑这个答案以包含一个。

        2
  •  8
  •   user1241378    12 年前

    由于上面的cmmetric.htm链接对我来说失败了,以及其他许多关于颜色距离的实现,我发现(经过很长的jurney..)如何计算最佳颜色距离,以及..最科学准确的一个: 三角洲 从2个RGB(!)使用opencv的值:

    这需要3个颜色空间转换+一些来自javascript的代码转换( http://svn.int64.org/viewvc/int64/colors/colors.js )到C++

    最后,代码(似乎是开箱即用的,希望没有人发现一个严重的错误在那里…但经过几次测试后似乎没问题)

    #include <opencv2/core/core.hpp>
    #include <opencv2/imgproc/imgproc.hpp>
    #include <opencv2/highgui/highgui.hpp>
    #include <opencv2/photo/photo.hpp>
    #include <math.h>
    
    using namespace cv;
    using namespace std;
    
    #define REF_X 95.047; // Observer= 2°, Illuminant= D65
    #define REF_Y 100.000;
    #define REF_Z 108.883;
    
    void bgr2xyz( const Vec3b& BGR, Vec3d& XYZ );
    void xyz2lab( const Vec3d& XYZ, Vec3d& Lab );
    void lab2lch( const Vec3d& Lab, Vec3d& LCH );
    double deltaE2000( const Vec3b& bgr1, const Vec3b& bgr2 );
    double deltaE2000( const Vec3d& lch1, const Vec3d& lch2 );
    
    
    void bgr2xyz( const Vec3b& BGR, Vec3d& XYZ )
    {
        double r = (double)BGR[2] / 255.0;
        double g = (double)BGR[1] / 255.0;
        double b = (double)BGR[0] / 255.0;
        if( r > 0.04045 )
            r = pow( ( r + 0.055 ) / 1.055, 2.4 );
        else
            r = r / 12.92;
        if( g > 0.04045 )
            g = pow( ( g + 0.055 ) / 1.055, 2.4 );
        else
            g = g / 12.92;
        if( b > 0.04045 )
            b = pow( ( b + 0.055 ) / 1.055, 2.4 );
        else
            b = b / 12.92;
        r *= 100.0;
        g *= 100.0;
        b *= 100.0;
        XYZ[0] = r * 0.4124 + g * 0.3576 + b * 0.1805;
        XYZ[1] = r * 0.2126 + g * 0.7152 + b * 0.0722;
        XYZ[2] = r * 0.0193 + g * 0.1192 + b * 0.9505;
    }
    
    void xyz2lab( const Vec3d& XYZ, Vec3d& Lab )
    {
        double x = XYZ[0] / REF_X;
        double y = XYZ[1] / REF_X;
        double z = XYZ[2] / REF_X;
        if( x > 0.008856 )
            x = pow( x , .3333333333 );
        else
            x = ( 7.787 * x ) + ( 16.0 / 116.0 );
        if( y > 0.008856 )
            y = pow( y , .3333333333 );
        else
            y = ( 7.787 * y ) + ( 16.0 / 116.0 );
        if( z > 0.008856 )
            z = pow( z , .3333333333 );
        else
            z = ( 7.787 * z ) + ( 16.0 / 116.0 );
        Lab[0] = ( 116.0 * y ) - 16.0;
        Lab[1] = 500.0 * ( x - y );
        Lab[2] = 200.0 * ( y - z );
    }
    
    void lab2lch( const Vec3d& Lab, Vec3d& LCH )
    {
        LCH[0] = Lab[0];
        LCH[1] = sqrt( ( Lab[1] * Lab[1] ) + ( Lab[2] * Lab[2] ) );
        LCH[2] = atan2( Lab[2], Lab[1] );
    }
    
    double deltaE2000( const Vec3b& bgr1, const Vec3b& bgr2 )
    {
        Vec3d xyz1, xyz2, lab1, lab2, lch1, lch2;
        bgr2xyz( bgr1, xyz1 );
        bgr2xyz( bgr2, xyz2 );
        xyz2lab( xyz1, lab1 );
        xyz2lab( xyz2, lab2 );
        lab2lch( lab1, lch1 );
        lab2lch( lab2, lch2 );
        return deltaE2000( lch1, lch2 );
    }
    
    double deltaE2000( const Vec3d& lch1, const Vec3d& lch2 )
    {
        double avg_L = ( lch1[0] + lch2[0] ) * 0.5;
        double delta_L = lch2[0] - lch1[0];
        double avg_C = ( lch1[1] + lch2[1] ) * 0.5;
        double delta_C = lch1[1] - lch2[1];
        double avg_H = ( lch1[2] + lch2[2] ) * 0.5;
        if( fabs( lch1[2] - lch2[2] ) > CV_PI )
            avg_H += CV_PI;
        double delta_H = lch2[2] - lch1[2];
        if( fabs( delta_H ) > CV_PI )
        {
            if( lch2[2] <= lch1[2] )
                delta_H += CV_PI * 2.0;
            else
                delta_H -= CV_PI * 2.0;
        }
    
        delta_H = sqrt( lch1[1] * lch2[1] ) * sin( delta_H ) * 2.0;
        double T = 1.0 -
                0.17 * cos( avg_H - CV_PI / 6.0 ) +
                0.24 * cos( avg_H * 2.0 ) +
                0.32 * cos( avg_H * 3.0 + CV_PI / 30.0 ) -
                0.20 * cos( avg_H * 4.0 - CV_PI * 7.0 / 20.0 );
        double SL = avg_L - 50.0;
        SL *= SL;
        SL = SL * 0.015 / sqrt( SL + 20.0 ) + 1.0;
        double SC = avg_C * 0.045 + 1.0;
        double SH = avg_C * T * 0.015 + 1.0;
        double delta_Theta = avg_H / 25.0 - CV_PI * 11.0 / 180.0;
        delta_Theta = exp( delta_Theta * -delta_Theta ) * ( CV_PI / 6.0 );
        double RT = pow( avg_C, 7.0 );
        RT = sqrt( RT / ( RT + 6103515625.0 ) ) * sin( delta_Theta ) * -2.0; // 6103515625 = 25^7
        delta_L /= SL;
        delta_C /= SC;
        delta_H /= SH;
        return sqrt( delta_L * delta_L + delta_C * delta_C + delta_H * delta_H + RT * delta_C * delta_H );
    }
    

    希望它能帮助别人:)

        3
  •  4
  •   Community CDub    8 年前

    HSL和HSV对人的颜色感知效果更好。根据 Wikipedia :

    有时,在处理艺术材料、数字化图像或其他媒体时,最好使用hsv或hsl颜色模型,而不是其他模型,如rgb或cmyk,因为模型模拟人类感知颜色的方式不同。RGB和CMYK分别是加色和减色模型,分别模拟了主色灯或颜料(分别)混合时形成新颜色的方式。

    Graphical depiction of HSV

        4
  •  3
  •   palm3D    17 年前

    这个 Wikipedia article on color differences 列出了许多颜色空间和距离度量,旨在符合人类对颜色距离的感知。

        5
  •  2
  •   Vincent Robert    17 年前

    可能看起来像垃圾邮件,但不是,这个链接对于颜色空间很有趣:)

    http://www.compuphase.com/cmetric.htm

        6
  •  2
  •   Lasse V. Karlsen    17 年前

    最容易 距离 当然,只需将颜色视为源自同一原点的三维矢量,并取其端点之间的距离即可。

    如果你需要考虑绿色在判断强度上更突出的因素,你可以权衡这些值。

    ImageMagic 提供以下比例:

    • 红色:0.3
    • 格林:0.6
    • 蓝色:0.1

    当然,像这样的值只对其他颜色的其他值有意义,而不是对人类有意义的值,所以所有你可以使用的值都是相似性排序。

        7
  •  2
  •   Brendan    17 年前

    好吧,作为第一点,我想说的是普通指标hsv(色调、饱和度和值)或hsl比rgb或cymk更能代表人类对颜色的感知。见 HSL, HSV on Wikipedia .

    我很天真地想画出两种颜色在HSL空间中的点,并计算出差分向量的大小。然而,这意味着亮黄色和亮绿色将被视为与绿色和暗绿色一样不同。但很多人认为红色和粉色是两种不同的颜色。

    此外,在这个参数空间中,同一方向上的差分向量是不相等的。例如,人眼比其他颜色更容易吸收绿色。色调从绿色到红色的变化量可能会更大。另外,饱和度从一个小的值到零的变化是灰色和粉色之间的差异,而在其他地方,变化是两种颜色的红色之间的差异。

    从程序员的角度来看,你需要绘制出不同的向量,但是用比例矩阵修改,在HSL空间的不同区域相应地调整长度——这是相当随意的,基于不同的色彩理论思想,但是根据你想要的把这个给。

    更好的是,你可以看到是否有人已经在网上做了这样的事情…

        8
  •  2
  •   bruceatk    17 年前

    作为一个色盲的人,我相信尝试增加更多的分离比正常视力更好。色盲最常见的形式是红/绿缺陷。这并不意味着你看不到红色或绿色,而是意味着你更难看到,也更难看到不同之处。因此,在色盲者分辨出差异之前,需要进行更大的分离。