在 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
输出:
您可以使用累积总和来完成您想要的:
- 沿水平线计算累计和。
- 您想要的距离的阈值累积总和
n
。
- 与输入逻辑与。
这将取消设置每条线上第一个 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()
我有一个口罩,里面可能有洞。我想从遮罩外侧(而不是孔洞)水平侵蚀一定数量的像素。
诀窍是如果我向内侵蚀 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
输出:
您可以使用累积总和来完成您想要的:
- 沿水平线计算累计和。
- 您想要的距离的阈值累积总和
n
。 - 与输入逻辑与。
这将取消设置每条线上第一个 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()