以下是一种使用传统图像处理方法的潜在方案:
获取二进制图像。 我们加载图像,将其转换为灰度图像,进行高斯模糊,然后自适应阈值以获取黑/白二进制图像。然后我们使用轮廓面积过滤来消除小噪声。在此阶段,我们还创建了两个空白掩模。
检测水平和垂直线条。 现在我们通过创建一个水平形状的卷积核并执行形态学操作来隔离水平线条。要检测垂直线条,我们也是这样做的,但使用垂直形状的卷积核。我们将检测到的线条绘制到单独的掩模上。
找到交点。 这个想法是,如果我们组合水平和垂直掩模,交点将会是角落。我们可以对两个掩模执行按位与操作。最后,我们找到每个交点的重心,并通过绘制圆圈来突出显示角落。
这里是一个流程可视化图:
输入图片
->
二值化图片
![enter image description here](https://istack.dev59.com/LmFTA.webp)
检测到水平线 -> 水平掩模
![enter image description here](https://istack.dev59.com/Gtk0Z.webp)
检测到垂直线 ->
垂直掩码
![enter image description here](https://istack.dev59.com/8VVjx.webp)
按位与两个掩码
->
检测交点
->
角落
->
清理角落。
![enter image description here](https://istack.dev59.com/Ie8EY.webp)
结果并不完美,但非常接近。问题在于由于倾斜的图像,垂直掩模上的噪声。如果图像没有倾斜并且居中,结果将是理想的。您可以通过微调内核大小或迭代次数来获得更好的结果。
代码
import cv2
import numpy as np
image = cv2.imread('1.png')
original = image.copy()
horizontal_mask = np.zeros(image.shape, dtype=np.uint8)
vertical_mask = np.zeros(image.shape, dtype=np.uint8)
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (3,3), 0)
thresh = cv2.adaptiveThreshold(blur, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 23, 7)
cnts = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
area = cv2.contourArea(c)
if area < 150:
cv2.drawContours(thresh, [c], -1, 0, -1)
dilate_horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (10,1))
dilate_horizontal = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, dilate_horizontal_kernel, iterations=1)
horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (40,1))
detected_lines = cv2.morphologyEx(dilate_horizontal, cv2.MORPH_OPEN, horizontal_kernel, iterations=1)
cnts = cv2.findContours(detected_lines, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
cv2.drawContours(image, [c], -1, (36,255,12), 2)
cv2.drawContours(horizontal_mask, [c], -1, (255,255,255), 2)
horizontal_mask = cv2.cvtColor(horizontal_mask,cv2.COLOR_BGR2GRAY)
cnts = cv2.findContours(horizontal_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
area = cv2.contourArea(c)
if area > 1000 or area < 100:
cv2.drawContours(horizontal_mask, [c], -1, 0, -1)
dilate_vertical_kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (1,7))
dilate_vertical = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, dilate_vertical_kernel, iterations=1)
vertical_kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (1,2))
detected_lines = cv2.morphologyEx(dilate_vertical, cv2.MORPH_OPEN, vertical_kernel, iterations=4)
cnts = cv2.findContours(detected_lines, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
cv2.drawContours(image, [c], -1, (36,255,12), 2)
cv2.drawContours(vertical_mask, [c], -1, (255,255,255), 2)
vertical_mask = cv2.cvtColor(vertical_mask,cv2.COLOR_BGR2GRAY)
combined = cv2.bitwise_and(horizontal_mask, vertical_mask)
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (2,2))
combined = cv2.morphologyEx(combined, cv2.MORPH_OPEN, kernel, iterations=1)
cnts = cv2.findContours(combined, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
try:
M = cv2.moments(c)
cx = int(M['m10']/M['m00'])
cy = int(M['m01']/M['m00'])
cv2.circle(original, (cx, cy), 3, (36,255,12), -1)
except ZeroDivisionError:
pass
cv2.imshow('thresh', thresh)
cv2.imshow('horizontal_mask', horizontal_mask)
cv2.imshow('vertical_mask', vertical_mask)
cv2.imshow('combined', combined)
cv2.imshow('original', original)
cv2.imshow('image', image)
cv2.waitKey()