我正在尝试创建一种算法,该算法将输出一组不同的RGB颜色值,这些值应尽可能不同。 例如:
以下是三种颜色:
接下来的3种颜色是:
接下来的颜色应该在新间隔之间。基本上,我的想法是遍历类似于以下的整个色谱系统间隔:
一组13种颜色应包括1到7之间的颜色,并无限地延续该图案。
我目前正在努力将此图案应用于RGB值的算法,因为这对我来说似乎并不重要。感谢您提出的解决方案。
答案 0 :(得分:1)
首先让我问一下,您是否要保留在sRGB中并浏览每种RGB组合?
或者(这是我的假设),您实际上是否想要彼此“最远”的颜色?由于您使用了“ 不同”一词,因此我将介绍查找颜色差异。
sRGB是引用您的显示/输出的色彩空间。而且,尽管伽玛曲线在感知上是“排序”的,但整体sRGB色彩空间却不是,它的目的是为显示建模而不是人类感知。
要确定各种颜色之间的“最大距离”,您需要一个感知模型,或者使用感知均匀的色彩空间,或者使用颜色外观模型(CAM)。
由于只需要sRGB值,因此使用统一的色彩空间就足够了,例如CIELAB或CIELUV。由于它们使用笛卡尔坐标,因此(L*a*b*)
中两种颜色之间的差异只是欧几里德距离。
如果您要使用极坐标(即色相角),则可以比CIELAB跨一步,进入CIELCh。
我建议Bruce Lindbloom's site进行相关的数学计算。
简化步骤:
将LAB转换为XYZ。
将XYZ转换为sRGB。
将sRGB伽玛曲线添加回每个通道。
如果您不太担心感知均匀性,那么您可以只使用sRGB,尽管结果将不太准确。在这种情况下,您所需要做的就是获取每个通道相对于255的差(即,反转每个通道):
以下是上述两种方法的一些比较:
相同的起始颜色,但使用CIELAB L * C * h *(只需将色相旋转180度,无需调整L *)。
在CIELAB LCh中,只需旋转色相180:
再次在LCh中,但这次还将L *调整为(100-L * firstcolor )
现在,您会注意到这些颜色的色相角发生了很大变化-事实是,虽然LAB是“某种程度的均匀”,但在蓝色区域中却非常蠕动。
看看数字:
它们在色相,色度,a,b方面似乎有很大的不同...但是它们创建相同的十六进制颜色值!因此,是的,即使CIELAB也存在误差(尤其是蓝色)。
如果您想提高准确性,请尝试CIECAM02
答案 1 :(得分:0)
这是我的尝试。您没有指定语言,所以我将用Python编写(未经优化,以便轻松翻译成其他语言)
n_colors = 25
rw, gw, bw = 2, 4, 1
n_global_moves = 32
class Color:
max_weighted_square_distance = (rw * rw + gw * gw + bw * bw) * 255 * 255
def __init__(self, r, g, b):
self.r, self.g, self.b = r, g, b
def weighted_square_distance(self, other):
rd = rw * (self.r - other.r)
gd = gw * (self.g - other.g)
bd = bw * (self.b - other.b)
return rd*rd + gd*gd + bd*bd
def min_weighted_square_distance(self, index, others):
min_wsd = self.max_weighted_square_distance
for i in range(0, len(others)):
if i != index:
wsd = self.weighted_square_distance(others[i])
if min_wsd > wsd:
min_wsd = wsd
return min_wsd
def is_valid(self):
return 0 <= self.r <= 255 and 0 <= self.g <= 255 and 0 <= self.b <= 255
def add(self, other):
return Color(self.r + other.r, self.g + other.g, self.b + other.b)
def __repr__(self):
return f"({self.r}, {self.g}, {self.b})"
colors = [Color(127, 127, 127) for i in range(0, n_colors)]
steps = [Color(dr, dg, db) for dr in [-1, 0, 1]
for dg in [-1, 0, 1]
for db in [-1, 0, 1] if dr or dg or db] # i.e., except 0,0,0
moved = True
global_move_phase = False
global_move_count = 0
while moved or global_move_phase:
moved = False
for index in range(0, len(colors)):
color = colors[index]
if global_move_phase:
min_wsd = -1
else:
min_wsd = color.min_weighted_square_distance(index, colors)
for step in steps:
new_color = color.add(step)
if new_color.is_valid():
new_min_wsd = new_color.min_weighted_square_distance(index, colors)
if min_wsd < new_min_wsd:
min_wsd = new_min_wsd
colors[index] = new_color
moved = True
if not moved:
if global_move_count < n_global_moves:
global_move_count += 1
global_move_phase = True
else:
global_move_phase = False
print(f"n_colors: {n_colors}")
print(f"rw, gw, bw: {rw}, {gw}, {bw}")
print(f"n_global_moves: {n_global_moves}")
print(colors)
首先将颜色全部设置为灰色,即放在颜色立方体的中央,然后以希望最大程度地增大颜色之间的最小距离的方式在颜色立方体中移动。
为节省CPU时间,使用距离的平方代替距离本身,因为距离本身需要计算平方根。
为补偿我们视锥细胞的不同灵敏度,在计算欧几里得距离时,我们在3个颜色方向上使用了不同的权重(rw
,gw
和bw
)。这会使颜色立方体有效地变成颜色长方体。
一次将一种颜色在三个方向中的每个方向上最多移动1个,将其移动到相邻颜色之一,以最大程度地减少与其他颜色的最小距离。这样,全局最小距离被(大约)最大化了。
需要“全局移动”阶段,以克服没有颜色移动的情况,但是强迫所有颜色移动到不比当前颜色差很多的位置时,会导致整体找到更好的配置。随后的常规动作。这最好用3种颜色显示且没有全局移动,将rw
,gw
和bw
都设置为1
:配置变为
[(2, 0, 0), (0, 253, 255), (255, 255, 2)]
同时,通过添加2条全局移动,配置将成为预期的
[(0, 0, 0), (0, 255, 255), (255, 255, 0)]
该算法产生8个解之一。通过将c
替换为255-c
(其中c
为r
,g
或b
,并以所有组合代替$response = array();
$a = 0;
$contacts[$a][0] = $c['contact'];
$contacts[$a][1] = $c['name'];
$contacts[$a][2] = $c['phone'];
$contacts[$a][3] = $c['phone2'];
$contacts[$a][4] = $c['email'];
$contacts[$a][5] = $c['active'];
$a++;
$response['contacts'] = $contacts;
echo json_encode($response);
,产生了另外7个。>
该算法对颜色“距离”进行了简化假设。我不知道颜色长方体的扭曲是否会导致2种颜色之间的感知距离等于欧几里得距离。如果存在,则在此扭曲内移动颜色会产生更好的结果。
我没有校正显示器的gamma,因为它的目的恰恰是为了改变眼睛的亮度,以补偿眼睛在高和低亮度下的不同灵敏度。当然,屏幕伽玛可能无法精确地做到这一点,对伽玛进行(取决于屏幕!)修改会产生更好的结果,但是标准伽玛是一个很好的起点。
这是25种颜色的算法输出:
警告
发布此答案后,我发现了Wikipedia article on color difference。我使用的权重可能适合于组件对亮度的贡献,但不适用于色差的感知。此外,还有一个更好的公式(无需完全更改色彩空间),所以我将不得不修复这两个方面。