使用OpenCV在python中检测图图像中的方形符号

to94eoyn  于 2022-11-24  发布在  Python
关注(0)|答案(1)|浏览(238)

我正在尝试检测P&ID中的方形符号(一个图表)图像文件。我尝试了使用轮廓的教程,但这种方法似乎不适用于这样的图表图像。使用霍夫线,我能够标记这些正方形的垂直边缘,但我不知道如何使用这些边缘检测正方形。图像中的所有正方形都有相同的尺寸,但不同图像的尺寸可能不一样,因此模板匹配对我不起作用。
我的代码使用霍夫线:

import cv2 as cv
import numpy as np
import math

img = cv.imread('test_img.jpg')
img_gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
img_display = img.copy()
ret,thresh = cv.threshold(img_gray,250,255,cv.THRESH_BINARY)
image_inverted = cv.bitwise_not(thresh)

linesP = cv.HoughLinesP(image_inverted, 1, np.pi / 1, 50, None, 50, 2)

if linesP is not None:
    for i in range(0, len(linesP)):
        l = linesP[i][0]
        length = math.sqrt((l[2] - l[0])**2 + (l[3] - l[1])**2)
        if length < 100:
            cv.line(img_display, (l[0], l[1]), (l[2], l[3]), (0,0,255), 1, cv.LINE_AA)
            
cv.imwrite('img_display.png', img_display)

输入图像:

输出图像:

在上面的代码中,我将它设置为只检测垂直线,因为它不能可靠地检测水平线。

ql3eal8s

ql3eal8s1#

如果你知道线条是水平的还是垂直的,你可以通过组合腐 eclipse 和膨胀来过滤它们(docs描述了它的工作原理)。
在分离水平线和垂直线之后,你可以根据大小过滤它们。最后你可以填充所有剩余的闭合轮廓,然后再次使用腐 eclipse /删除来提取更大的形状。
这比使用Hough线变换更可靠,并且可以更好地控制提取的确切内容。
下面是一个演示:

import numpy as np
import cv2 as cv

min_length = 29
max_length = 150

# erode and dilate with rectangular kernel of given dimensions
def erode_dilate(image, dim):
    kernel = cv.getStructuringElement(cv.MORPH_RECT, dim)
    result = cv.erode(image, kernel)
    result = cv.dilate(result, kernel)
    return result

# get contours and filter by max_size
def filter_contours(image, max_size):
    contours, _ = cv.findContours(image, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
    dims = [(cnt, cv.boundingRect(cnt)) for cnt in contours]
    contours = [cnt for cnt, (x, y, w, h) in dims if w <= max_size and h <= max_size]
    return contours

# read image and get inverted threshold mask
img = cv.imread('test_img.jpg')
img_gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
ret, thres = cv.threshold(img_gray, 250, 255, cv.THRESH_BINARY_INV)

# extract horizontal lines
thres_h = erode_dilate(thres, (min_length, 1))
cv.imshow("horizontal", thres_h)

# extract vertical lines
thres_v = erode_dilate(thres, (1, min_length))
cv.imshow("vertical", thres_v)

# filter lines by max_length and draw them back to res
res = np.zeros_like(thres)
cntrs_h = filter_contours(thres_h, max_length)
cv.drawContours(res, cntrs_h, -1, 255, cv.FILLED)
cntrs_v = filter_contours(thres_v, max_length)
cv.drawContours(res, cntrs_v, -1, 255, cv.FILLED)
cv.imshow("filtered horizontal + vertical", res)

# fill remaining shapes
cntrs = filter_contours(res, max_length)
for c in cntrs:
    cv.drawContours(res, [c], -1, 255, cv.FILLED)
cv.imshow("filled", res)

# extract larger shapes
res = erode_dilate(res, (min_length, min_length))
cv.imshow("squares", res)

# draw contours of detected shapes on original image
cntrs = filter_contours(res, max_length)
cv.drawContours(img, cntrs, -1, (0, 0, 255), 2)

cv.imshow("output", img)
cv.waitKey(-1)

cv.destroyAllWindows()

输出量:

相关问题