language agnostic -查找颜色之间的精确距离

Translate

原始问题

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

  1. 哪种色彩空间最能代表人类的视觉?
  2. 该空间中的哪个距离度量最能代表人类的视觉(欧几里得?)
This question and all comments follow the "Attribution Required."

所有的回答

Translate

转换为La * b *(也就是普通的“ Lab”,您还将看到对“ CIELAB”的引用)。一个很好的快速测量色差的方法是

(L1-L2)^ 2 +(a1-a2)^ 2 +(b1-b2)^ 2

色彩科学家还有其他更精致的措施,根据您所做工作的准确性,这可能不值得打扰。

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

粗略的解释:>>给定一种颜色,我们的眼睛会在两个较大的波长范围之间进行区分-蓝色与更长的波长。然后,由于最近的遗传突变,较长的视锥分叉成两部分,为我们区分了红色与绿色。

顺便说一句,超越您的彩色穴居人同事,这对您的职业来说将是非常重要的,他们只知道“ RGB”或“ CMYK”,这对设备非常有用,但会严重影响您的感知工作。我曾为成像科学家工作,他们对这些东西一无所知!

有关色差理论的更多乐趣阅读,请尝试:

有关Lab的更多详细信息,请访问http://en.kioskea.net/video/cie-lab.php3我目前无法找到一个实际包含转换公式的丑陋页面,但我敢肯定有人会编辑此答案以包含其中一个。

来源
Berton Lee
Translate

由于上面的cmetric.htm链接以及我发现的许多其他色距离实现方法(经过很长的争吵之后)对我来说都失败了(经过很长的路程。)如何计算最佳色距离,并且..科学上最准确的方法是:三角洲并使用OpenCV从2个RGB(!)值进行计算:

这需要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 );
}

希望它可以帮助某人:)

来源
Translate

HSL和HSV更适合人类的色彩感知。根据维基百科:

有时在处理美术材料,数字化图像或其他媒体时,最好使用HSV或HSL颜色模型,而不是诸如RGB或CMYK之类的替代模型,因为这些模型在模拟人类感知颜色的方式上存在差异。 RGB和CMYK分别是加法和减法模型,分别模拟原色光或颜料混合时形成新颜色的方式。

Graphical depiction of HSV

来源
Translate

维基百科有关颜色差异的文章列出了一些色彩空间和距离度量,旨在与人类对色彩距离的感知相一致。

来源
Translate

可能看起来像垃圾邮件,但不是,此链接对于色彩空间确实很有趣:)

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

来源
Translate

最简单的距离当然只是将颜色视为源自同一原点的3d向量,并取其端点之间的距离。

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

ImageMagic提供以下尺度:

  • 红色:0.3
  • 绿色:0.6
  • 蓝色:0.1

当然,像这样的值仅相对于其他颜色的其他值才有意义,而对于人类而言则没有意义,因此您可以使用的所有值都是相似性排序。

来源
Translate

首先,我要说的是通用指标HSV(色相,饱和度和值)或HSL比RGB或CYMK更好地代表了人们对颜色的感知方式。看到HSL,HSV在Wikipedia上.

我想天真地将两种颜色绘制在HSL空间中的点并计算差矢量的大小。但是,这意味着鲜黄色和鲜绿色将被视为与绿色和深绿色一样。但是随后许多人考虑了红色和粉红色两种不同的颜色。

而且,在该参数空间中相同方向上的差向量不相等。例如,人眼比其他颜色要好得多。色相从绿色的偏移量与从红色的偏移量相同,这似乎更大。同样,从少量到零的饱和度偏移是灰色和粉红色之间的差异,在其他地方,该偏移将是两种红色阴影之间的差异。

从程序员的角度来看,您将需要绘制差异矢量,但需要通过比例矩阵进行修改,该比例矩阵会在HSL空间的各个区域中相应地调整长度-这是相当随意的,并且基于各种颜色理论思想根据您要应用的内容随意调整。

更好的是,您可以查看是否有人已经在网上进行过此类操作...

来源
Translate

作为一个色盲的人,我相信尝试增加正常视野之外的其他分隔是很好的。色盲的最常见形式是红色/绿色不足。这并不意味着您看不到红色或绿色,而是意味着更难于看到并且更加难以看到差异。因此,色盲人才能分辨出差异需要更大的距离。

来源
上一个问题:
c -Rockbox音频格式