我有一个包含约200个RGB格式颜色的数组。 我想编写一个程序,它可以接受任何RGB颜色并尝试匹配最“相似”的颜色。
我需要一个很好的“相似”定义,尽可能接近于人类感知。
我还想显示有关匹配准确性的一些信息。例如黑白:100%,对于具有稍微不同色调的相似颜色:-4%。
我需要使用神经网络吗? 是否有更简单的替代方法?
我有一个包含约200个RGB格式颜色的数组。 我想编写一个程序,它可以接受任何RGB颜色并尝试匹配最“相似”的颜色。
我需要一个很好的“相似”定义,尽可能接近于人类感知。
我还想显示有关匹配准确性的一些信息。例如黑白:100%,对于具有稍微不同色调的相似颜色:-4%。
我需要使用神经网络吗? 是否有更简单的替代方法?
将所有颜色转换为CIE Lab颜色空间,并在该空间中计算距离。
deltaE = sqrt(deltaL^2 + deltaA^2 + deltaB^2)
颜色的最低 DeltaE 值越小,它们在视觉上就越相似。
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%)。
a * (H1 - H2)**2 + ...
,对吧? - hobbs最近我也遇到了同样的问题,于是在网上比较了各种算法。一开始我对使用CIELAB颜色空间感到犹豫,因为它看起来很复杂,但实际上并没有想象中那么难。下面是你需要比较两个RGB值的全部代码。
struct CIELAB {
float L, a, b;
};
float gammaCorrect( float v )
{
return 100.0f * (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 = gammaCorrect( R / 255.0f );
float green = gammaCorrect( G / 255.0f );
float blue = 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颜色空间中进行匹配,但对于某些颜色对没有得到良好的结果。例如,纯白和纯黑(它们之间的差异很大)可能具有相同的色调和饱和度,因此可能匹配得更好。
我一直在寻找这个东西,但是没有找到很多答案,所以我决定创建这个小库。
https://github.com/sebastienjouhans/c-sharp-colour-utilities
import matplotlib.colors as mc
import numpy as np
from scipy.spatial import KDTree
import cv2
class ColorNamer:
def __init__(self):
self.clut = {}
self.clut_list = []
self.clut_tree = None
for name in mc.XKCD_COLORS:
rgb = mc.to_rgb(mc.XKCD_COLORS[name])
lab = cv2.cvtColor(np.single([[rgb]]), cv2.COLOR_RGB2Lab)[0][0]
self.clut[tuple(lab)] = name[5:]
self.clut_list = list(self.clut.keys())
self.clut_tree = KDTree(self.clut_list)
def name(self, rgb):
lab = tuple(cv2.cvtColor(np.single([[rgb]]), cv2.COLOR_RGB2Lab)[0][0])
dist, point = self.clut_tree.query(lab, 1)
idx = int(point)
key = self.clut_list[idx]
return self.clut[key]
if __name__ == '__main__':
cn = ColorNamer()
print(cn.name((.3211, .543, .633)))