具有最大可区分颜色的颜色图

WRJ*_*WRJ 6 python colors matplotlib colormap

我在Python中使用matplotlib绘制20到50条线之间的任意位置。使用matplotlib的滑动色标后,在绘制了一定数量的线后(在20之前),这些变得难以区分。

虽然我已经看到了Matlab和C#中的一些代码示例,以创建任意数量的颜色的色图,这些色图可以最大程度地彼此区分,但是我在Python中找不到任何东西。

谁能指出我在Python中可以做到这一点的方向?

干杯

Gre*_*0ry 11

我喜欢 @xuancong84 创建的调色板的想法,并稍微修改了他的代码,使其不依赖于 alpha 通道。我把它放在这里供其他人使用,谢谢@xuancong84!

import math

import numpy as np
from matplotlib.colors import ListedColormap
from matplotlib.cm import hsv


def generate_colormap(number_of_distinct_colors: int = 80):
    if number_of_distinct_colors == 0:
        number_of_distinct_colors = 80

    number_of_shades = 7
    number_of_distinct_colors_with_multiply_of_shades = int(math.ceil(number_of_distinct_colors / number_of_shades) * number_of_shades)

    # Create an array with uniformly drawn floats taken from <0, 1) partition
    linearly_distributed_nums = np.arange(number_of_distinct_colors_with_multiply_of_shades) / number_of_distinct_colors_with_multiply_of_shades

    # We are going to reorganise monotonically growing numbers in such way that there will be single array with saw-like pattern
    #     but each saw tooth is slightly higher than the one before
    # First divide linearly_distributed_nums into number_of_shades sub-arrays containing linearly distributed numbers
    arr_by_shade_rows = linearly_distributed_nums.reshape(number_of_shades, number_of_distinct_colors_with_multiply_of_shades // number_of_shades)

    # Transpose the above matrix (columns become rows) - as a result each row contains saw tooth with values slightly higher than row above
    arr_by_shade_columns = arr_by_shade_rows.T

    # Keep number of saw teeth for later
    number_of_partitions = arr_by_shade_columns.shape[0]

    # Flatten the above matrix - join each row into single array
    nums_distributed_like_rising_saw = arr_by_shade_columns.reshape(-1)

    # HSV colour map is cyclic (https://matplotlib.org/tutorials/colors/colormaps.html#cyclic), we'll use this property
    initial_cm = hsv(nums_distributed_like_rising_saw)

    lower_partitions_half = number_of_partitions // 2
    upper_partitions_half = number_of_partitions - lower_partitions_half

    # Modify lower half in such way that colours towards beginning of partition are darker
    # First colours are affected more, colours closer to the middle are affected less
    lower_half = lower_partitions_half * number_of_shades
    for i in range(3):
        initial_cm[0:lower_half, i] *= np.arange(0.2, 1, 0.8/lower_half)

    # Modify second half in such way that colours towards end of partition are less intense and brighter
    # Colours closer to the middle are affected less, colours closer to the end are affected more
    for i in range(3):
        for j in range(upper_partitions_half):
            modifier = np.ones(number_of_shades) - initial_cm[lower_half + j * number_of_shades: lower_half + (j + 1) * number_of_shades, i]
            modifier = j * modifier / upper_partitions_half
            initial_cm[lower_half + j * number_of_shades: lower_half + (j + 1) * number_of_shades, i] += modifier

    return ListedColormap(initial_cm)

Run Code Online (Sandbox Code Playgroud)

这些是我得到的颜色:

from matplotlib import pyplot as plt
import numpy as np

N = 16
M = 7
H = np.arange(N*M).reshape([N,M])
fig = plt.figure(figsize=(10, 10))
ax = plt.pcolor(H, cmap=generate_colormap(N*M))
plt.show()
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明


xua*_*g84 6

最近,我也遇到了同样的问题。所以我创建了以下简单的 Python 代码来为 jupyter notebook matplotlib 生成视觉上可区分的颜色。它在感知上不是完全可区分的,但它比 matplotlib 中的大多数内置颜色图效果更好。

该算法将 HSV 比例分成 2 个块,第一个块的 RGB 值增加,第二个块的 alpha 降低,以便颜色可以混合到白色背景中。

请注意,如果您使用的是 jupyter notebook 以外的任何工具包,则必须确保背景为白色,否则 alpha 混合会不同,生成的颜色也会不同。

此外,颜色的独特性在很大程度上取决于您的计算机屏幕、投影仪等。在一个屏幕上可区分的调色板并不一定意味着在另一个屏幕上。如果您想用于演示,您必须对其进行物理测试。

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap

def generate_colormap(N):
    arr = np.arange(N)/N
    N_up = int(math.ceil(N/7)*7)
    arr.resize(N_up)
    arr = arr.reshape(7,N_up//7).T.reshape(-1)
    ret = matplotlib.cm.hsv(arr)
    n = ret[:,3].size
    a = n//2
    b = n-a
    for i in range(3):
        ret[0:n//2,i] *= np.arange(0.2,1,0.8/a)
    ret[n//2:,3] *= np.arange(1,0.1,-0.9/b)
#     print(ret)
    return ret

N = 16
H = np.arange(N*N).reshape([N,N])
fig = plt.figure(figsize=(10, 10))
ax = plt.pcolor(H, cmap=ListedColormap(generate_colormap(N*N)))
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明