原始问题
我正在寻找一个试图量化两种颜色的"距离"(或不同)程度的函数。 这个问题实际上分为两个部分:
哪种色彩空间最能代表人类的视觉?
该空间中的哪个距离度量最能代表人类的视觉(欧几里得?)
转换为La * b *(也就是普通的" Lab",您还将看到对" CIELAB"的引用)。一个很好的快速测量色差的方法是
(L1-L2)^2 + (a1-a2)^2 + (b1-b2)^2
色彩科学家还有其他更精致的措施,根据您所做工作的准确性,这可能不值得打扰。
a和b值以类似于圆锥体工作方式的方式表示相反的颜色,并且可以为负或正。中性色-白色,灰色为a=0,b=0。 L是用特殊方式定义的亮度,从零(纯黑)到任何亮度。
粗略的解释:>>给定一种颜色,我们的眼睛会在两个较宽的波长范围之间进行区分-蓝色与更长的波长。然后,由于最近的遗传突变,较长的视锥分叉成两部分,为我们区分了红色与绿色。
顺便说一句,超越您的彩色穴居人同事,这对您的职业来说将是很棒的,他们只知道" RGB"或" CMYK",这对设备非常有用,但会严重影响您的感知工作。我曾为成像科学家工作,他们对这些东西一无所知!
要获得有关色差理论的更多乐趣,请尝试:
-
http://white.stanford.edu/~brian/scielab/introduction.html和信息
-
以及有关颜色理论的链接,通常是从http://www.efg2.com/Lab/Library/Color/开始的网络冲浪,以及
-
http://www.poynton.com/Poynton-color.html
http://en.kioskea.net/video/cie-lab.php3上有关Lab的更多详细信息,目前我无法找到一个实际包含转换公式的丑陋页面,但我敢肯定有人会对其进行编辑答案包括一个。
由于上面的cmetric.htm链接以及我发现的许多其他颜色距离实现方法(经过很长的争吵之后)都对我失败了(经过很长的漫长路程。)。使用OpenCV的RGB(!)值:
这需要3种颜色空间转换+某些代码从javascript(http://svn.int64.org/viewvc/int64/colors/colors.js)到C ++的转换
最后是代码(似乎可以立即使用,希望没人能在那里找到一个严重的错误……但是经过大量测试,它似乎还不错)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
| #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更适合人类的色彩感知。根据维基百科:
It is sometimes preferable in working with art materials, digitized images, or other media, to use the HSV or HSL color model over alternative models such as RGB or CMYK, because of differences in the ways the models emulate how humans perceive color. RGB and CMYK are additive and subtractive models, respectively, modelling the way that primary color lights or pigments (respectively) combine to form new colors when mixed.
维基百科上有关色差的文章列出了许多色空间和距离度量,旨在与人类对色距离的感知保持一致。
可能看起来像垃圾邮件,但不是,此链接对于色彩空间确实很有趣:)
http://www.compuphase.com/cmetric.htm
作为一个色盲的人,我相信尝试增加正常视力之外的其他间隔是很好的。色盲的最常见形式是红色/绿色不足。这并不意味着您看不到红色或绿色,而是意味着更难于看到并且更加难以看到差异。因此,色盲人才能分辨出差异需要更大的距离。
首先,我要说的是通用指标HSV(色相,饱和度和值)或HSL比RGB或CYMK更好地代表了人们对颜色的感知方式。请参阅Wikipedia上的HSL,HSV。
我想天真地将两种颜色绘制在HSL空间中的点,并计算差矢量的大小。但是,这意味着鲜黄色和鲜绿色将被视为与绿色和深绿色一样。但是随后许多人考虑了红色和粉红色两种不同的颜色。
而且,在该参数空间中相同方向上的差向量不相等。例如,人眼比其他颜色要好得多。色相从绿色的偏移量与从红色的偏移量相同似乎更大。同样,从少量到零的饱和度偏移是灰色和粉红色之间的差异,在其他地方,偏移将是两种红色阴影之间的差异。
从程序员的角度来看,您将需要绘制差异矢量,但需要通过比例矩阵进行修改,该比例矩阵会在HSL空间的各个区域中相应地调整长度-这是相当随意的,并且基于各种颜色理论的思想,但是可以根据您要应用的内容随意调整。
更好的是,您可以查看是否有人已经在网上进行过此类操作...
最简单的距离当然就是将颜色视为源自同一原点的3d矢量,并取其端点之间的距离。
如果需要考虑在判断强度上绿色更突出的因素,则可以权衡这些值。
ImageMagic提供以下缩放比例:
当然,像这样的值仅相对于其他颜色的其他值才有意义,而不是对人类有意义的东西,因此您可以使用的所有值都是相似性排序。