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

问题描述 投票:0回答:2

我正在尝试使用 OpenCV 检测 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)

输入图片:

input image

输出图像:

output image

在上面的代码中,我将其设置为仅检测垂直线,因为它无法可靠地检测水平线。

python opencv hough-transform
2个回答
1
投票

如果您知道线条是水平的或垂直的,您可以通过组合侵蚀和扩张来过滤它们(文档描述了它的工作原理)。

分离水平线和垂直线后,您可以按大小过滤它们。最后,您可以填充所有剩余的闭合轮廓,并再次使用侵蚀/删除来提取更大的形状。

这比使用霍夫线变换更可靠,并且可以让您更好地控制到底提取的内容。

这是一个演示:

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()

输出:

enter image description here


0
投票

效果非常好!如何提取方块内的文字?尝试了 pytesseract 但没有取得太大成功。

© www.soinside.com 2019 - 2024. All rights reserved.