在 OpenCV 中从带有孔的掩码侧面侵蚀 x 像素

Erode x pixels from side of mask with hole in OpenCV

我有一个口罩,里面可能有洞。我想从遮罩外侧(而不是孔洞)水平侵蚀一定数量的像素。

诀窍是如果我向内侵蚀 5px 并且在某个点有一个距离边缘 3px 的洞,我想侵蚀这 3px 然后剩下的 2px 穿过洞。所以我总是每边侵蚀 5px,基本上跳过任何洞。

例如这个面具:

这些灰色区域将被侵蚀:

我可以通过遍历每一行来了解如何做到这一点,例如:

erode_px = 5
for y, row in enumerate(mask):
    idxs = np.nonzero(row)[0]
    if idxs.size:
        if idxs.size < erode_px * 2:
            mask[y] = 0
        else:
            mask[y, :idxs[erode_px]] = 0
            mask[y, idxs[-erode_px]:] = 0

但我正在处理非常大的蒙版,这需要高效。有没有办法在不遍历 Python 中的每一行的情况下实现这一点?最好只使用 OpenCV / numpy。

我得承认,这不是最好的解决方案,我不确定它是否能达到您的目的,但这里有一种方法可以像您描述的那样进行操作:

import numpy as np
from skimage.morphology import closing, erosion
from matplotlib import pyplot as plt

np.random.seed(0)
idxs = np.random.randint(0, 15 ,(2, 30))

mask = np.ones((15, 15))
mask[:2] = 0
mask[-2:] = 0
mask[:, :2] = 0
mask[:, -2:] = 0
mask[idxs[0], idxs[1]] = 0

mask_cls = closing(mask, selem = np.ones((2, 2)))

mask_erd = erosion(mask_cls, selem = np.ones((3, 3)))

mask_erd_out = mask*mask_erd

输出:

您可以使用累积总和来完成您想要的:

  1. 沿水平线计算累计和。
  2. 您想要的距离的阈值累积总和 n
  3. 与输入逻辑与。

这将取消设置每条线上第一个 n 设置的像素。要从右边缘应用操作,翻转矩阵,应用上面的操作,然后翻转结果。

以下代码演示操作:

import numpy as np
import matplotlib.pyplot as plt

def erode_left(mask, n):
   return np.logical_and(np.cumsum(mask, axis=1) > n, mask)

def erode_both(mask, n):
   mask = erode_left(mask, n)
   mask = np.fliplr(erode_left(np.fliplr(mask), n))
   return mask
   

mask = np.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,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0],
                 [0,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0],
                 [0,1,1,1,0,0,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,0],
                 [0,1,1,1,0,0,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,0],
                 [0,1,1,1,0,0,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,0],
                 [0,1,1,1,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0],
                 [0,1,1,1,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0],
                 [0,1,1,1,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0],
                 [0,1,1,1,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0],
                 [0,1,1,1,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0],
                 [0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,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]], dtype=bool)

f, axarr = plt.subplots(2,2)
axarr[0,0].imshow(mask)
axarr[0,0].title.set_text('Input')
axarr[0,1].imshow(erode_left(mask, 5))
axarr[0,1].title.set_text('Eroded left side')
axarr[1,0].imshow(erode_both(mask,5))
axarr[1,0].title.set_text('Eroded both sides')
axarr[1,1].imshow(erode_both(mask,5) + 2*mask)
axarr[1,1].title.set_text('Overlay')
plt.show()