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

时间:2022-05-15 21:24:26

I want to identify all the yellow pixels that lie between two colours, for example [255, 255, 0] is bright yellow, and [200, 200, 50] is a mid yellow.

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

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

So the rgb ranges could be represented as :

所以rgb范围可以表示为:

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

I have a 2D (height of image x width of image) [r, g, b] array:

我有一个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]]])

I'd like to produce a 2D array with True if the r,g,b values are in the range between the 2 color values in the color arrays.

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

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

I've been struggling to get the indexing right.

我一直在努力让索引正确。

2 个解决方案

#1


1  

I can imagine several ways to tackle this:

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

The one way would be to compare all elements individually:

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

c = color_array

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

This will be True for all pixels where

对于所有像素,这将为True

200 < R < 255 and 200 < G < 255 and 0 < B < 50

This is equivalent to looking for all pixels inside a small subset (a box), defined by color_array, in the RGB color space (a bigger box).

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

An alternative solution would be to take the line between the two points in color_array, and calculate each pixel's individual euclidean distance to that line:

另一种解决方案是在color_array中取两点之间的线,并计算每个像素与该线的个别欧氏距离:

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

Afterwards you can find all pixels that are within a certain distance to that line, i.e.

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

within_distance = distance < 25

A third solution is to calculate the euclidean distance of each pixel to the mean value of your two colors:

第三种解决方案是计算每个像素的欧氏距离与两种颜色的平均值:

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

finding all pixels within a limit can then interpreted as finding all pixels in a sphere around the average color of your two limit colors. E.g. if you chose the distance to be half the distance between the two points

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

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

you get all pixels that fall in a sphere for wich both limiting colors exactly touch the opposite ends of the surface.

你得到落在球体中的所有像素,两种限制颜色都准确地接触到表面的两端。

And of course if you want all pixels that are perceptually similar to your two limit colors, you should convert your data to a perceptual color space, like Lab

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

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

and do the computations in that space instead.

而是在那个空间中进行计算。

#2


0  

Here is a solution that is not particularly elegant, but should work:

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

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

You could easily extend this to handle arbitrary numbers of colors in the last dimension using numpy's broadcasting rules:

您可以使用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)

#1


1  

I can imagine several ways to tackle this:

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

The one way would be to compare all elements individually:

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

c = color_array

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

This will be True for all pixels where

对于所有像素,这将为True

200 < R < 255 and 200 < G < 255 and 0 < B < 50

This is equivalent to looking for all pixels inside a small subset (a box), defined by color_array, in the RGB color space (a bigger box).

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

An alternative solution would be to take the line between the two points in color_array, and calculate each pixel's individual euclidean distance to that line:

另一种解决方案是在color_array中取两点之间的线,并计算每个像素与该线的个别欧氏距离:

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

Afterwards you can find all pixels that are within a certain distance to that line, i.e.

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

within_distance = distance < 25

A third solution is to calculate the euclidean distance of each pixel to the mean value of your two colors:

第三种解决方案是计算每个像素的欧氏距离与两种颜色的平均值:

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

finding all pixels within a limit can then interpreted as finding all pixels in a sphere around the average color of your two limit colors. E.g. if you chose the distance to be half the distance between the two points

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

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

you get all pixels that fall in a sphere for wich both limiting colors exactly touch the opposite ends of the surface.

你得到落在球体中的所有像素,两种限制颜色都准确地接触到表面的两端。

And of course if you want all pixels that are perceptually similar to your two limit colors, you should convert your data to a perceptual color space, like Lab

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

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

and do the computations in that space instead.

而是在那个空间中进行计算。

#2


0  

Here is a solution that is not particularly elegant, but should work:

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

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

You could easily extend this to handle arbitrary numbers of colors in the last dimension using numpy's broadcasting rules:

您可以使用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)