【问题标题】:create boolean mask of numpy rgb array if matches color如果匹配颜色,则创建 numpy rgb 数组的布尔掩码
【发布时间】:2018-05-27 21:37:36
【问题描述】:

我想识别位于两种颜色之间的所有黄色像素,例如 [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 的 2D 数组。

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

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

【问题讨论】:

  • 你能让你的数组说5x4而不是5x3吗?这样可以更清楚地说明第 3 维是颜色。
  • 为什么color_array是4维的?
  • 你能清楚地说明在范围内是什么意思吗?是255 >= r >= 200 and 255 >= g >= 200 and 0 <= g <= 50的意思吗?
  • 刚刚更改了尺寸,同意这更清楚。所以这将产生一个 5x4 布尔数组,其中颜色匹配如下:如果 r、g、b 颜色为 200
  • @BlueShrapnel。为了清楚起见,您想匹配 所有 条件,而不是 任何 或“至少两个”或类似的奇怪的东西,对吗?

标签: python arrays numpy rgb


【解决方案1】:

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

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

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_arraycalculate each pixel's individual euclidean distance to that line 中的两点之间画线:

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)

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

当然,如果您希望所有像素在感知上类似于与您的两种极限颜色,您应该将数据转换为感知颜色空间,例如 Lab

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

并改为在该空间中进行计算。

【讨论】:

  • 谢谢,我正在研究您的建议,看看我是否理解它们的工作原理。
  • 非常喜欢球体方法,也很容易概念化。
  • 我在球体方法中添加了一个距离测量,因为球体正好被两种限制颜色所跨越。
【解决方案2】:

这是一个不是特别优雅但应该可以工作的解决方案:

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)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-02
    • 2021-04-07
    • 1970-01-01
    • 2014-02-01
    • 1970-01-01
    • 2020-04-23
    相关资源
    最近更新 更多