使用数组进行 Numpy 过滤

Posted

技术标签:

【中文标题】使用数组进行 Numpy 过滤【英文标题】:Numpy filtering using array 【发布时间】:2021-04-17 05:05:01 【问题描述】:

我知道之前有人问过这个问题,但对于我的特定用例似乎没有任何内容。

我有一个 numpy 数组 obs,它代表彩色图像,形状为 (252, 288, 3)

我想将不是纯黑色的每个像素都转换为纯白色。

我尝试过的是obs[obs != [0, 0, 0]] = [255, 255, 255],但它给出了以下异常:

ValueError: NumPy boolean array indexing assignment cannot assign 3 input values to the 807 output values where the mask is true

结果与obs[obs[:, :] != [0, 0, 0]] = [255, 255, 255] 相同。另外,(obs[:, :] != [0, 0, 0]).shape(252, 288, 3),我不明白为什么它不是简单的 (252, 288)(布尔矩阵)。

我考虑过使用obs[obs != 0] = 255,但这不会产生我想要的效果,因为纯绿色的像素 ([0, 255, 0]) 将被处理组件明智并且在过滤后仍然是[0, 255, 0],而不是实际上是白色的 ([255, 255, 255])。

为什么我到现在为止尝试过的方法都不起作用,我应该怎么做?

【问题讨论】:

这不是你想要的吗? obs[obs != 0] = 255 @Stepan 我已经在问题中解释了这一点。我已经更正了一些错字,现在应该更清楚了。 毫无疑问,单通道(灰度)甚至布尔(真/假)结果足以代表您所需的黑白输出,而不需要 3 倍的 RAM,而 RGB 结果只包含黑白的? 我的回答或其他人是否解决了您的问题?如果是这样,请考虑接受它作为您的答案 - 通过单击计票旁边的空心对勾/复选标记。如果没有,请说出什么不起作用,以便我或其他人可以进一步帮助您。谢谢。 meta.stackexchange.com/questions/5234/… 【参考方案1】:

根据您实际想要做什么,有多种可能性。让我们先编写设置代码(所有可能性都通用),这样您就可以明白我的意思了。

#!/usr/bin/env python3

import numpy as np

# Make a repeatable random image
np.random.seed(764)
obs = np.random.randint(0,32,(252,288,3), dtype=np.uint8)

出于测试目的,此图像在以下位置恰好有纯黑色像素:

obs[ 21, 267]
obs[ 28, 252]
obs[ 69, 127]
obs[ 98,   0]
obs[124, 210]
obs[133,  98]
obs[160,  81]
obs[167,  48]
obs[217, 237]

现在,假设您想要一个新的纯真/假黑色像素布尔掩码,您可以使用:

mask = obs.any(axis=-1)

该解决方案具有以下特点:

time: 876 µs
mask.shape: (252,288)
mask.nbytes: 72576
mask.dtype: 'bool'

您随后可以像这样使用和重复使用该掩码:

# Make masked pixels red
obs[mask,:] = [255,0,0]

# Make unmasked pixels cyan
obs[~mask,:] = [0,255,255]


现在假设您想要一个新的灰度图像,具有黑白像素,您可以使用:

grey = obs.any(axis=-1) * np.uint8(255)

该解决方案具有以下特点:

time: 887 µs
grey.shape: (252,288)
grey.nbytes: 72576
grey.dtype: np.uint8

现在假设您想要将现有的“obs”就地更改为纯黑白(但仍然是 RGB):

obs[obs.any(axis=-1),:] = [255,255,255]

该解决方案具有以下特点:

time: 1.98 ms
obs.shape: (252,288,3)
obs.nbytes: 217728
obs.dtype: np.uint8

【讨论】:

【参考方案2】:

obs[obs != [0, 0, 0]] 这样的布尔索引返回一个一维数组,其中包含obs 中满足给定条件的所有元素。 看下面的例子:

obs = np.array([
 [[88, 0,99],
  [ 0, 0, 0]],
 [[ 0, 0, 0],
  [88,77,66]]
])

obs != [0, 0, 0] 返回一个布尔数组:

array([[[ True, False,  True],
        [False, False, False]],
       [[False, False, False],
        [ True,  True,  True]]])

然后obs[obs != [0, 0, 0]] 返回一个一维数组,其中包含掩码为True 的所有元素:array([88, 99, 88, 77, 66])

所以你需要where 来测试是否有any 颜色分量不等于0:

np.where(obs.any(axis=-1, keepdims=True), 255, obs)

结果:

array([[[255, 255, 255],
        [  0,   0,   0]],
       [[  0,   0,   0],
        [255, 255, 255]]])

请注意,您需要keepdims=True 才能启用广播到obs 的原始形状。否则,您必须通过 np.where(obs.any(-1)[...,np.newaxis], 255, obs)np.where(np.atleast_3d(obs.any(-1)), 255, obs) 添加丢失的维度,这不太优雅。

【讨论】:

以上是关于使用数组进行 Numpy 过滤的主要内容,如果未能解决你的问题,请参考以下文章

如何过滤数组中不需要的值以进行绘图?使用numpy数组的matplotlib中的ValueError

使用 numpy 从过滤后的排序数组返回索引

过滤 2D numpy 数组

Python numpy 按条件过滤二维数组

如何高斯过滤(模糊)浮点numpy数组

具有 numpy 的数组的高效阈值过滤器