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

原来的问题

我正在寻找一个函数,试图量化“遥远”(或不同)两种颜色是如何。 这个问题真的有两个部分:

  1. 什么颜色空间最能代表人类视觉?
  2. 那个空间中的哪个距离度量最能代表人类的视觉(欧几里德?)

转换成La * b *(也就是简单的“Lab”,你也会看到对“CIELAB”的引用)。 一个很好的颜色差异的测量是

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

色彩科学家还有其他更精细的措施,这可能不值得麻烦,这取决于你所做的准确性。

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

粗略的解释:>>给定一种颜色,我们的眼睛区分两种波长范围 – 蓝色和较长的波长。 然后,由于更近期的基因突变,长波长的锥体分叉成两个,为我们区分红色和绿色。

顺便说一下,你的职业生涯将会超越你的颜色穴居人同事,他们只知道“RGB”或“CMYK”,这对于设备来说是非常好的,但是吸引人的是认真的工作。 我曾经为那些不了解这个东西的成像科学家工作过!

为了更好地阅读色差理论,请尝试:

实验室的更多细节http://en.kioskea.net/video/cie-lab.php3我不能在这个时候find一个非丑陋的页面,实际上有转换公式,但我敢肯定有人会编辑这个答案包括一个。;

因为上面的cmetric.htm链接失败了,以及许多其他颜色距离的实现(在一个非常长的jurney ..之后)如何计算最佳的色彩距离,以及..最科学准确的一个: deltaE和从2使用OpenCV的RGB(!)值:

这需要3个颜色空间转换+从JavaScript( viewvc/int64/colors/colors.html )到C ++的一些代码转换

最后的代码(似乎开箱即用,希望没有人会发现一个严重的错误在那里…但似乎罚款经过多次testing)

 #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 ); } 

希望它可以帮助别人:)

HSL和HSV对于人类色彩感觉更好。 根据维基百科 :

由于模型模拟人类如何感知颜色的方式不同,所以在与艺术材料,数字化图像或其他媒体合作时,最好将HSV或HSL颜色模型用于RGB或CMYK等替代模型。 RGB和CMYK分别是加法和减法模型,分别模拟原色光或颜料(分别)混合时形成新颜色的方式。

HSV的图形描述

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

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

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

最简单的距离当然就是将颜色看作是源自同一个原点的三维vector,并取其终点之间的距离。

如果需要考虑绿色在判断强度方面更为突出的因素,可以对这些值进行权衡。

ImageMagic提供了以下比例尺:

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

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

那么,作为第一个呼叫点,我想说的是HSV(色相,饱和度和数值)或HSL这些常见指标比人们认为比RGB或CYMK更能代表人类感知颜色。 请参阅维基百科上的HSL,HSV 。

我想天真地我会绘制两种颜色的HSL空间中的点,并计算差异向量的大小。 但是,这意味着明亮的黄色和鲜绿色将被视为与绿色到深绿色不同。 但是很多人认为红色和粉红色两种不同的颜色。

而且,这个参数空间中相同方向上的差向量不相等。 比如,人眼比其他颜色好得多。 绿色的色调从与红色的转变相同的量可能看起来更大。 另外,饱和度从小到零的变化是灰色和粉红色之间的差异,其他地方的变化将是两个红色阴影之间的差异。

从程序员的angular度来看,您需要绘制差异向量,但是需要修改比例matrix,以便在HSL空间的不同区域相应地调整长度 – 这将是相当随意的,并且将基于各种颜色理论思路,但是根据你想要应用的内容来进行相当随意的调整。

更好的是,你可以看到有没有人在网上做过这样的事情…

作为色盲的人,我认为尝试添加更多的分离,然后正常的视力是很好的。 色盲的最常见forms是红/绿缺陷。 这并不意味着你看不到红色或绿色,这意味着更难以看到,更难以看出差异。 所以在色盲人士可以分辨出来之前需要更大的分离。