连续提供最大对比度颜色的算法

Algorithm to successively deliver colors of maximum contrast

我需要生成用于突出显示内容类型的颜色。一个文档中可能有10s的颜色深浅。

我的用例是我希望告知科学家如何从输入文档中提取数据。颜色编码的背景阴影(带有描述性)工具提示将用于突出显示导入的块。这是一个模拟输入文件:

这里是一个模拟版本,显示了突出显示哪些字段已导入后的样子。每个突出显示的块上的工具提示将提供有关识别它的工具的更多详细信息,以及推送到数据库的值(带单位):

每次遇到新类型的内容,我都需要生成新的颜色。该颜色应与现有颜色形成最大对比。显然,我们走得越远,反差就越小。

在尝试想象一个解决方案时,我想象了一个色轮。我们从一种颜色开始。为了使下一种颜色具有最大对比度,它将与色轮上的第一种颜色相反。

对于每个连续的颜色,算法必须在色轮上寻找最大的 "unoccupied" 弧并在其中点生成颜色。

这看起来像任何现有的颜色生成策略吗?

如果是这样,是否有任何记录的算法来实现它?

(我的目标环境是 Python,但这似乎只是一个实现细节)

啊,我想到了解决这个问题的替代策略。

可以将颜色编码推迟到过程结束,而不是即时生成颜色。

一旦我们知道需要多少种颜色,我们就可以在 HSB/HSV 色谱中的整个色调中生成那么多均匀分布的排列。我认为这将提供最大的对比。

您希望颜色等距,并且彼此之间的距离尽可能远,白色和黑色已按使用方式插入。

一个简单且好用的指标是mean-red

formula

formula

formula

formula

formula

如果你是即时进行的,你必须重新计算你添加的每种新颜色的颜色位置,如果你事先知道有多少种颜色,你可以计算它们所有的位置立刻。

这是 python 中的示例实现:

import numpy as np
from itertools import combinations
from scipy.optimize import minimize, Bounds

BLACK_AND_WHITE = np.array((0.0, 0.0, 0.0, 255.0, 255.0, 255.0))

我们认为数组中的三个连续数字代表一种颜色,给定两个这样的三元组,并使用上面定义的距离,我们的距离函数是

def distance(c1, c2):
    r = (c1[0] + c2[0]) / 2.0
    coeffs = np.array(((2.0 + r/256), 4, (2.0 + (255 - r)/256.0)))
    diff = c1 - c2
    return np.sqrt(np.sum(coeffs * diff ** 2))

我们想要最大化所有颜色对之间的最小距离,这与最小化最小距离的负值是一样的。为了得到这些对,我们使用 combinations(..., 2) 来做到这一点,为了让它迭代三元组,我们重塑颜色数组,使每一行都包含一种颜色:

def cost_function(x):
    colors = np.concatenate((BLACK_AND_WHITE, x)).reshape(-1, 3)
    return -min(mean_red_distance(color_pairs[0], color_pairs[1]) for color_pairs in combinations(colors, 2))

现在是时候最小化我们的成本函数了,颜色允许在 0 到 255 之间:

def get_new_colors_after_adding(existing_colors):
    if len(existing_colors):
        guess = np.mod(existing_colors.reshape(-1, 3)[0] + np.array((100, 100, 100)), 256)
    else:
        guess = np.array((0, 255, 255))
    guess = np.concatenate((guess, existing_colors))
    # let all colors range between 0 and 255
    result = minimize(cost_function, guess, bounds=Bounds(0, 255))
    if not result.success:
        raise ValueError('Failed adding new color')
    return result.x

最后,我们在每一步添加 10 种颜色并打印生成的三元组:

if __name__ == '__main__':
    # start with no colors
    existing_colors = np.empty(0, dtype=np.int32)
    # example of consequently adding colors.
    for i in range(10):
        existing_colors = get_new_colors_after_adding(existing_colors)
        print(np.round(existing_colors.reshape(-1, 3)).astype(np.int))