我正在尝试创建一个算法来输出一组不同的 RGB 颜色值,这些颜色值应该尽可能不同。 例如:
按照一组 3 种颜色:
- (255, 0, 0) [红色]
- (0, 255, 0) [绿色]
- (0, 0, 255) [蓝色]
接下来的 3 种颜色是:
- (255, 255, 0) [黄色]
- (0, 255, 255) [青色]
- (255, 0, 255) [紫色]
接下来的颜色应该在新间隔之间。基本上,我的想法是遍历与此类似的整个色谱系统间隔:
一组 13 种颜色应该包括 1 到 7 之间的颜色,无限地延续该模式。
我目前正在努力将这种模式应用于 RGB 值的算法,因为它对我来说似乎并不微不足道。我很感谢能给我指出解决方案的任何提示。
最佳答案
Wikipedia article on color difference值得一读,article on a “low-cost approximation” 也值得一读通过其中链接的 CompuPhase。我将基于后者进行尝试。
您没有指定语言,所以我将使用未优化的 Python 编写它(引用文章中已经存在的整数优化除外),以便于翻译成其他语言。
n_colors = 25
n_global_moves = 32
class Color:
max_weighted_square_distance = (((512 + 127) * 65025) >> 8) + 4 * 65025 + (((767 - 127) * 65025) >> 8)
def __init__(self, r, g, b):
self.r, self.g, self.b = r, g, b
def weighted_square_distance(self, other):
rm = (self.r + other.r) // 2 # integer division
dr = self.r - other.r
dg = self.g - other.g
db = self.b - other.b
return (((512 + rm) * dr*dr) >> 8) + 4 * dg*dg + (((767 - rm) * db*db) >> 8)
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:
best_min_wsd = -1
else:
best_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 best_min_wsd < new_min_wsd:
best_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"n_global_moves: {n_global_moves}")
print(colors)
首先将颜色全部设置为灰色,即放在 RGB 颜色立方体的中心,然后在颜色立方体中移动,希望使颜色之间的最小距离最大化。
为了节省 CPU 时间,使用距离的平方而不是距离本身,后者需要计算平方根。
颜色一次移动一个,在 3 个方向的每个方向上最多移动 1 个,到相邻颜色之一,使与其他颜色的最小距离最大化。通过这样做,全局最小距离(近似)最大化。
为了克服没有颜色会移动的情况,需要“全局移动”阶段,但是迫使所有颜色移动到一个不比当前位置差多少的位置会导致整体找到一个更好的配置随后的常规 Action 。使用 3 种颜色和没有全局移动可以最好地看到这一点,将加权平方距离修改为简单的rd*rd+gd*gd+bd*bd
:配置变为
[(2, 0, 0), (0, 253, 255), (255, 255, 2)]
同时,通过添加 2 个全局移动,配置变为预期的配置
[(0, 0, 0), (0, 255, 255), (255, 255, 0)]
该算法仅产生几种可能的解决方案中的一种。不幸的是,由于使用的度量不是欧几里得,因此不可能简单地翻转 8 种可能组合中的 3 维(即替换 r
→255-r
和/或g
和/或 b
) 相同以获得等效解决方案。最好按照尝试颜色移动步骤的顺序引入随机性,并改变随机种子。
我没有更正显示器的 gamma ,因为它的目的恰恰是改变亮度的间距,以补偿眼睛在高亮度和低亮度下的不同敏感度。当然,屏幕 Gamma 曲线偏离理想值,并且(取决于系统!) Gamma 修改会产生更好的结果,但标准 Gamma 是一个很好的起点。
这是 25 种颜色的算法输出:
请注意,前 8 种颜色(底部一行和上面一行的前 3 种颜色)靠近 RGB 立方体的角(它们不在角上,因为不-欧几里德度量)。
关于algorithm - 生成具有最高多样性的 RGB 颜色集,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56338701/