删除所有具有全零邻居的像素

Remove all pixels with all-zero neighbors

有没有一种方法可以在 OpenCV 中执行类似于侵蚀的过程,如果 ANY 的邻居不为零,则保留给定像素,而不是要求其所有邻居非零?

这里,邻居,我指的是具有 abs(x1-x2)+abs(y1-y2)==1 的任何像素,但这很容易通过侵蚀内核进行控制。

当然,我总是可以使用 for 循环并从头开始实现这种行为,但我更喜欢 OpenCV 可以通过其库提供的速度。

反转图像,执行侵蚀,然后反转回来是否有效?

我的另一个想法是与一个中心为空的内核进行卷积,然后将所有值裁剪到 0 到 1 的范围内。我会为此使用 scipy.ndimage.convolve

我正在使用类型为 np.float32(即值 0.0 或 1.0)且形状为 (512,512) 的二进制 NumPy 数组。

实现目标的一种简单方法是与 1 的 3x3 正方形核进行卷积。对于每个像素,您现在知道其邻域(包括其自身)中有多少前景像素。将此阈值设置为 2 (>= 2) 以获得邻域中至少有 2 个前景像素的所有像素。最后,与原始图像的逻辑与将给出具有至少一个前景邻居的所有前景像素。

这是一个例子:

import scipy.ndimage
import numpy as np

img = np.array([[0., 0., 0., 0., 0., 0., 0., 1., 1., 1.],
                [0., 1., 0., 0., 0., 0., 0., 1., 1., 1.],
                [0., 0., 0., 0., 0., 0., 0., 0., 1., 1.],
                [0., 0., 0., 0., 0., 0., 1., 0., 1., 1.],
                [0., 0., 0., 0., 0., 0., 1., 0., 0., 0.],
                [0., 0., 1., 0., 0., 0., 0., 0., 0., 0.],
                [0., 1., 1., 0., 0., 0., 0., 0., 0., 0.],
                [0., 1., 1., 0., 1., 0., 0., 0., 0., 0.],
                [0., 1., 1., 0., 0., 0., 0., 0., 0., 0.],
                [0., 1., 1., 0., 0., 0., 0., 0., 0., 0.],
                [0., 1., 1., 0., 0., 0., 0., 0., 0., 0.],
                [0., 0., 0., 1., 0., 0., 0., 1., 1., 0.],
                [0., 0., 0., 0., 1., 1., 0., 0., 0., 0.],
                [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
                [0., 0., 0., 0., 0., 0., 0., 0., 0., 1.],
                [0., 0., 0., 0., 0., 0., 0., 1., 0., 0.]], dtype=np.float32)
tmp = scipy.ndimage.convolve(img, np.ones((3,3)), mode='constant')
out = np.logical_and(tmp >= 2, img).astype(np.float32)

输出为:

[[0. 0. 0. 0. 0. 0. 0. 1. 1. 1.]
 [0. 0. 0. 0. 0. 0. 0. 1. 1. 1.]
 [0. 0. 0. 0. 0. 0. 0. 0. 1. 1.]
 [0. 0. 0. 0. 0. 0. 1. 0. 1. 1.]
 [0. 0. 0. 0. 0. 0. 1. 0. 0. 0.]
 [0. 0. 1. 0. 0. 0. 0. 0. 0. 0.]
 [0. 1. 1. 0. 0. 0. 0. 0. 0. 0.]
 [0. 1. 1. 0. 0. 0. 0. 0. 0. 0.]
 [0. 1. 1. 0. 0. 0. 0. 0. 0. 0.]
 [0. 1. 1. 0. 0. 0. 0. 0. 0. 0.]
 [0. 1. 1. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 1. 0. 0. 0. 1. 1. 0.]
 [0. 0. 0. 0. 1. 1. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]]

当然有些图像库会有专门为此设计的功能。不知道OpenCV或者ndimage或者scikit-image有没有这样的功能,我对这些库不是很了解。 But DIPlib does(披露:我是作者):

import diplib as dip

out = img - dip.GetSinglePixels(img > 0)

img > 0部分是将浮点数数组转换为逻辑数组,DIPlib期望二进制图像。对于 512x512 图像,这大约是其他解决方案的 5 倍。

假设你有

src = np.uint8([
       [  0,   0,   0,   0,   0,   0,   0,   0],
       [  0,   0,   0,   0,   0,   0,   0,   0],
       [  0,   0, 255,   0,   0,   0,   0,   0],
       [  0,   0,   0,   0,   0,   0,   0,   0],
       [  0,   0,   0,   0,   0,   0,   0,   0],
       [  0,   0,   0,   0,   0, 255,   0,   0],
       [  0,   0,   0,   0,   0,   0, 255,   0],
       [  0,   0,   0,   0,   0,   0,   0,   0]])

(你可以弄清楚如何从花车中获取它)

您正在寻找非零像素,其中 并非 所有邻居都为 0。您可以从 构造 所需结果 几个 操作。

你可以用MORPH_HITMISS作为正条件(所有邻居都是0),然后结合否定条件。

你会使用这个内核:

kernel = np.int8([ # 0 means "don't care", all others have to match
    [-1, -1, -1],
    [-1, +1, -1],
    [-1, -1, -1],
])

neighbors_all_zero = cv.morphologyEx(src=src, op=cv.MORPH_HITMISS, kernel=kernel)

result = src & ~neighbors_all_zero

结果:

array([[  0,   0,   0,   0,   0,   0,   0,   0],
       [  0,   0,   0,   0,   0,   0,   0,   0],
       [  0,   0,   0,   0,   0,   0,   0,   0],
       [  0,   0,   0,   0,   0,   0,   0,   0],
       [  0,   0,   0,   0,   0,   0,   0,   0],
       [  0,   0,   0,   0,   0, 255,   0,   0],
       [  0,   0,   0,   0,   0,   0, 255,   0],
       [  0,   0,   0,   0,   0,   0,   0,   0]], dtype=uint8)

注意值本身。 OpenCV 有时假设掩码为 0 或 255,有时为 0 且非零。当我只使用 0 和 1 而不是 0 和 255 时,我得到了一些有趣的结果。我相信这些可以使用。