0

有没有办法在 OpenCV 中执行类似于腐蚀的过程,如果它的任何邻居都不为零,而不是要求它的所有邻居都非零,它会保留给定的像素?

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

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

它可以反转图像,执行腐蚀,然后将其反转回来吗?

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

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

4

2 回答 2

4

实现目标的一种简单方法是与正方形 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 有没有这样的功能,我对这些库了解不够。但 DIPlib 确实(披露:我是作者):

import diplib as dip

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

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

于 2021-09-03T04:44:31.133 回答
2

假设你有

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 时,我得到了一些有趣的结果。我确信这些可以使用。

于 2021-09-03T10:14:25.917 回答