我有大约 200 种颜色的 RGB 格式数组。我想编写一个程序,它采用任何 RGB 颜色并尝试匹配数组中最“相似”的颜色。
我需要一个“相似”的好定义,尽可能接近人类的感知。
我还想展示一些关于匹配精度的信息。例如黑白:100%,对于色调略有不同的相似颜色:-4%。
我需要使用神经网络吗?有更简单的选择吗?
不,这里不需要神经网络!简单地将 HSL 颜色值视为一个向量,并为该向量定义一个加权模函数,如下所示:
modulus = sqrt(a*H1*H1 + b*S1*S1 + c*L1*L1);
where a,b,c are weights you should decide based on your visual definition of what
creates a bigger difference in perceived color - a 1% change in Hue or a 1%
change in Saturation
我建议你使用 a = b = 0.5 和 c = 1
最后,找出模量的范围,并将相似的颜色定义为模量彼此非常接近的颜色(比如 5%)
我还要指出 least squares 方法,只是稍微简单一些。也就是说,你取一个数的差,平方,然后对所有这些平方差求和。
我一直在寻找这个东西,但没有找到很多答案,我决定创建这个小图书馆。
https://github.com/sebastienjouhans/c-sharp-colour-utilities
我实现这一点的最快方法是将颜色添加到八叉树,然后,就像量化一样,你使用每一位来引导你到最深的子节点。一旦你不能再深入,要么你处于最深的层次(最低位),在这种情况下你已经击中了确切的颜色,要么你需要的下一个子节点不存在——此时你只是需要孩子的钻头最接近您正在寻找的钻头,并且是您最接近的颜色。这比将所有内容转换为 HSL 并返回,或计算每个欧几里德距离要快得多。
这是我在 CodeProject 上的代码:https://www.codeproject.com/tips/1046574/octtree-based-nearest-color-search
最近遇到了同样的问题,比较了网上找的各种算法。起初我对使用 CIELAB 色彩空间犹豫不决,因为它很复杂,但它确实没有乍一看那么糟糕。这是比较两个 RGB 值所需的所有代码。
struct CIELAB {
float L, a, b;
};
float gammaCorrect( float v )
{
return v <= 0.04045f ? v / 12.92f : powf( (v + 0.055f) / 1.055f, 2.4f );
}
float nonLinearToLinear( float v )
{
return v > 0.008856f ? cbrtf( v ) : 7.787f * v + 16.0f / 116.0f;
}
CIELAB RGBToCIELAB( int R, int G, int B )
{
float red = 100.0f * gammaCorrect( R / 255.0f );
float green = 100.0f * gammaCorrect( G / 255.0f );
float blue = 100.0f * gammaCorrect( B / 255.0f );
float xr = nonLinearToLinear( (red * 0.4124564f + green * 0.3575761f + blue * 0.1804375f) / 95.047f );
float yr = nonLinearToLinear( (red * 0.2126729f + green * 0.7151522f + blue * 0.0721750f) / 100.000f );
float zr = nonLinearToLinear( (red * 0.0193339f + green * 0.1191920f + blue * 0.9503041f) / 108.883f );
return { 116.0f * yr - 16.0f, 500.0f * (xr - yr), 200.0f * (yr - zr) };
}
float similarity( int R0, int G0, int B0, int R1, int G1, int B1 )
{
CIELAB lab0 = RGBToCIELAB( R0, G0, B0 );
CIELAB lab1 = RGBToCIELAB( R1, G1, B1 );
float dL = lab0.L - lab1.L;
float da = lab0.a - lab1.a;
float db = lab0.b - lab1.b;
return dL*dL + da*da + db*db;
}
对于 similarity() 函数,结果越低匹配越好。为了提高效率,请将 RGB 颜色列表预先转换为 CIELAB 空间。
如果需要更简单的算法,维基百科的Color difference 页面有一个非常有效的算法。您可以使用整数运算来实现它,如果只比较相似性,则可以跳过平方根计算。
int similarity( int R0, int G0, int B0, int R1, int G1, int B1 )
{
int dr = R0 - R1;
int dg = G0 - G1;
int db = B0 - B1;
int redsum = R0 + R1;
return (1024 + redsum) * dr*dr + 2048 * dg*dg + (1534 - redsum) * db*db;
}
计算不会超过 32 位有符号整数。
我发现这种匹配明显不如 CIELAB 空间中的匹配,但计算是微不足道的。
我也尝试在 HSV 颜色空间中进行匹配,但对于某些颜色对没有得到好的结果。例如,纯白色和纯黑色(两种颜色可能完全不同)可以具有相同的色调和饱和度,因此可能比您想要的更匹配。