如果匹配颜色,则创建 numpy rgb 数组的布尔掩码

create boolean mask of numpy rgb array if matches color

我想识别位于两种颜色之间的所有黄色像素,例如 [255, 255, 0] 是亮黄色,而 [200, 200, 50] 是中黄色。

c = color_array = np.array([ 
  [255, 255, 0],  # bright yellow
  [200, 200, 50]])  # mid yellow

所以 rgb 范围可以表示为:

(min, max) tuple... 
r (200, 255)
g (200, 255)
b (0, 50)

我有一个 2D(图像的高度 x 图像的宽度)[r, g, b] 数组:

image = np.random.randint(0, 255, (5, 4, 3))
array([[[169, 152,  65],
    [ 46, 123,  39],
    [116, 190, 227],
    [ 95, 138, 243]],
   [[120,  75, 156],
    [ 94, 168, 139],
    [131,   0,   0],
    [233,  43,  28]],
   [[195,  65, 198],
    [ 33, 161, 231],
    [125,  31, 101],
    [ 56, 123, 151]],
   [[118, 124, 220],
    [241, 221, 137],
    [ 71,  65,  23],
    [ 91,  75, 178]],
   [[153, 238,   7],
    [  2, 154,  45],
    [144,  33,  94],
    [ 52, 188,   4]]])

如果 r、g、b 值在颜色数组中的 2 个颜色值之间的范围内,我想生成一个带有 True 的二维数组。

[[T, F, F, T], 
 [T, F, F, T],
  ...       ]]

我一直在努力获得正确的索引。

这是一个不是特别优雅但应该有效的解决方案:

def color_mask(array, r_lim, g_lim, b_lim):
    """
    array : m x n x 3 array of colors
    *_lim are 2-element tuples, where the first element is expected to be <= the second.
    """
    r_mask = ((array[..., 0] >= r_lim[0]) & (array[..., 0] <= r_lim[1]))
    g_mask = ((array[..., 1] >= g_lim[0]) & (array[..., 1] <= g_lim[1]))
    b_mask = ((array[..., 2] >= b_lim[0]) & (array[..., 2] <= b_lim[1]))
    return r_mask & g_mask & b_mask

您可以使用 numpy 的广播规则轻松扩展它以处理最后一个维度中的任意数量的颜色:

def color_mask(array, *lims):
    lims = np.asarray(lims)
    lower = (array >= lims[:, 0])
    upper = (array <= lims[:, 1])
    return np.logical_and.reduce(upper & lower, axis=2)

我可以想出几种方法来解决这个问题:

一种方法是单独比较所有元素:

c = color_array

within_box = np.all(
    np.logical_and(
        np.min(c, axis=0) < image,
        image < np.max(c, axis=0)
    ),
    axis=-1
)

对于

的所有像素,这将是True
200 < R < 255 and 200 < G < 255 and 0 < B < 50

这相当于在 RGB color space(更大的框)中查找由 color_array 定义的小子集(框)内的所有像素。

另一种解决方案 是取 color_array 中两点之间的线:

distance = np.linalg.norm(np.cross(c[1,:] - c[0,:], c[0,:] - image), axis=-1)/np.linalg.norm(c[1,:] - c[0,:])

之后你可以找到距离该线一定距离内的所有像素,即

within_distance = distance < 25

一个第三种解决方案是计算每个像素的欧几里德距离到你的两种颜色的平均值:

distance_to_mean = np.linalg.norm(image - np.mean(c, axis=0), axis=-1)

找到一个限制内的所有像素然后可以解释为在你的两个限制颜色的平均颜色周围找到一个球体中的所有像素。例如。如果你选择的距离是两点之间距离的一半

within_sphere = distance_to_mean < (np.linalg.norm(c) / 2)

你得到落在一个球体中的所有像素,因为两种限制颜色都恰好接触表面的两端

当然,如果您希望所有像素都在感知上与您的两个极限颜色相似,您应该将数据转换为感知颜色space,例如 Lab

import skimage
image_lab = skimage.color.rgb2lab(image / 255)
color_array_lab = skimage.color.rgb2lab(color_array[np.newaxis, ...] / 255)

并在 space 中进行计算。