问题描述
我只想提取右侧的红色像素:属于 1px 垂直线的像素,但不提取到任何较粗的线或具有超过 1 个相邻黑色像素的其他区域。图像是双色调的。
到目前为止,我已经尝试了具有垂直(10px,这是我的目的)和水平内核的形态 OPEN
并采用了差异,但这需要一个尴尬的转变并留下一些“斑点”:
vertical_kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(1,10))
vertical_mask1 = cv2.morphologyEx(thresh,cv2.MORPH_OPEN,vertical_kernel,iterations=1)
horz_kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(2,1))
horz_mask = cv2.morphologyEx(thresh,horz_kernel,iterations=1)
M = np.float32([[1,-1],[0,1,1]])
rows,cols = horz_mask.shape
vertical_mask = cv2.warpAffine(horz_mask,M,(cols,rows))
result = cv2.bitwise_and(thresh,cv2.bitwise_not(horz_mask))
隔离 1px 线(且仅 1px 线)的正确方法是什么?
在一般情况下,对于其他内核,这个问题是:如何找到图像中位于内核“适合内部”区域的所有像素(然后进行减法以获得我想要的结果)?
>解决方法
这基本上是(二进制)template matching。您需要从您的“内核”中获取适当的模板。对于较大的“内核”,这也可能涉及为这些模板使用掩码,参见。 cv2.matchTemplate
。
单像素垂直线最重要的特征是什么?当前像素的左右邻居必须是0
。因此,要匹配的模板是 [0,1,0]
。通过使用 TemplateMatchMode
cv2.TM_SQDIFF_NORMED
,完美匹配将导致 0
数组中的值接近 result
。
您可以屏蔽这些位置,并根据模板的大小进行扩张。然后,您使用 bitwise_and
提取属于您的模板的实际像素。
这是一些带有一些模板(“内核”)的代码:
import cv2
import numpy as np
img = cv2.imread('AapJk.png',cv2.IMREAD_GRAYSCALE)[:,:50]
vert_line = np.array([[0,0]],np.uint8)
cross = np.array([[0,0],[1,1],[0,np.uint8)
corner = np.array([[0,1]],np.uint8)
for i_k,k in enumerate([vert_line,cross,corner]):
m,n = k.shape
img_tmp = 1 - img // 255
mask = cv2.matchTemplate(img_tmp,k,cv2.TM_SQDIFF_NORMED) < 10e-6
mask = cv2.dilate(mask.astype(np.uint8),np.ones((m,n)),anchor=(n-1,m-1))
m,n = mask.shape
mask = cv2.bitwise_and(img_tmp[:m,:n],mask)
out = cv2.cvtColor(img,cv2.COLOR_GRAY2BGR)
roi = out[:m,:n]
roi[mask.astype(bool),:] = [0,255]
cv2.imwrite('{}.png'.format(i_k),out)
竖线:
交叉:
右下角 3 x 3
:
较大的模板(“内核”)很可能需要额外的掩码,具体取决于应考虑或不考虑多少或哪些相邻像素。
----------------------------------------
System information
----------------------------------------
Platform: Windows-10-10.0.19041-SP0
Python: 3.9.1
PyCharm: 2021.1.3
NumPy: 1.20.3
OpenCV: 4.5.2
----------------------------------------