如何使用Python OpenCV检测正方形的角落?

4
在下面的图片中,我正在使用OpenCV Harris角点检测程序仅检测正方形(和外部正方形内的小正方形)的角点。然而,我也检测到了图像侧面的数字的角点。我需要一种方法来忽略数字,在执行OpenCV角点检测时只集中在正方形上。代码、输入图像和输出图像如下所示:
import cv2 as cv
img = cv.imread(filename)
gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
gray = np.float32(gray)
dst = cv.cornerHarris(gray, 2, 3, 0.04)
dst = cv.dilate(dst,None)
# Threshold for an optimal value, it may vary depending on the image.
img[dst>0.01*dst.max()]=[0,0,255]
cv.imshow('dst', img)

输入图像

enter image description here

Harris角点检测器输出

enter image description here


这并不是那么简单,你仍然需要一些预处理。你可能首先想要矫正透视。该操作将使水平和垂直线都变得直。这将简化事情。如果这是你正在分析的唯一图像,你可以简单地裁剪感兴趣区域并硬编码裁剪坐标。如果这是某个自动化任务的一部分,将接收类似的图像,则更强大的方法是创建一个“网格”的掩模 - 可能使用一对水平和垂直结构元素来创建掩模。 - stateMachine
针对这个特定图像的另一种方法是:通过Otsu阈值处理图像,然后在图像的左上角进行泛洪填充,以黑色填充,这将消除所有周围的黑色斑点。接下来,应用一个长宽比/面积斑点过滤器来过滤掉剩余的数字。希望最终只剩下网格线。 - stateMachine
1个回答

7
以下是一种使用传统图像处理方法的潜在方案:
  1. 获取二进制图像。 我们加载图像,将其转换为灰度图像,进行高斯模糊,然后自适应阈值以获取黑/白二进制图像。然后我们使用轮廓面积过滤来消除小噪声。在此阶段,我们还创建了两个空白掩模。

  2. 检测水平和垂直线条。 现在我们通过创建一个水平形状的卷积核并执行形态学操作来隔离水平线条。要检测垂直线条,我们也是这样做的,但使用垂直形状的卷积核。我们将检测到的线条绘制到单独的掩模上。

  3. 找到交点。 这个想法是,如果我们组合水平和垂直掩模,交点将会是角落。我们可以对两个掩模执行按位与操作。最后,我们找到每个交点的重心,并通过绘制圆圈来突出显示角落。


这里是一个流程可视化图:
输入图片 -> 二值化图片

enter image description here enter image description here

检测到水平线 -> 水平掩模

enter image description here enter image description here

检测到垂直线 -> 垂直掩码

enter image description here enter image description here

按位与两个掩码 -> 检测交点 -> 角落 -> 清理角落。

enter image description here enter image description here enter image description here enter image description here

结果并不完美,但非常接近。问题在于由于倾斜的图像,垂直掩模上的噪声。如果图像没有倾斜并且居中,结果将是理想的。您可以通过微调内核大小或迭代次数来获得更好的结果。
代码
import cv2
import numpy as np

# Load image, create horizontal/vertical masks, Gaussian blur, Adaptive threshold
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)

# Remove small noise on thresholded image
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)

# Detect horizontal lines
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)

# Remove extra horizontal lines using contour area filtering
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)

# Detect vertical 
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)

# Find intersection points
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)

# Highlight corners
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:
    # Find centroid and draw center point
    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()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接