You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用OpenCV在P&ID图纸中检测方形符号?

检测P&ID图像中的方形符号(OpenCV实现)

问题背景

需要用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)

输入图像:
输入图像

输出图像(仅标记垂直线):
输出图像

解决方案

基于同一图像内方形尺寸一致的特点,结合霍夫线检测的垂直/水平线条,通过聚类和匹配来定位方形:

1. 核心思路

  • 优化霍夫线参数,同时检测垂直和水平线条,通过角度过滤筛选目标线条
  • 统计垂直边的x坐标差值,确定当前图像的方形边长
  • 对垂直边按x坐标聚类,匹配对应的上下水平边,验证后绘制方形

2. 改进后代码

import cv2 as cv
import numpy as np
from collections import defaultdict

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,
    rho=1,
    theta=np.pi / 180,
    threshold=40,
    minLineLength=30,
    maxLineGap=5
)

vertical_lines = []
horizontal_lines = []

if linesP is not None:
    for line in linesP:
        x1, y1, x2, y2 = line[0]
        # 计算线条与水平轴的夹角
        angle = np.arctan2(y2 - y1, x2 - x1) * 180 / np.pi
        line_length = np.linalg.norm((x2 - x1, y2 - y1))

        # 筛选垂直线(角度接近±90°,长度在合理范围)
        if abs(abs(angle) - 90) < 10 and 30 < line_length < 100:
            vertical_lines.append((x1, y1, x2, y2, line_length))
            cv.line(img_display, (x1, y1), (x2, y2), (0, 0, 255), 1, cv.LINE_AA)
        # 筛选水平线(角度接近0°或180°,长度在合理范围)
        elif (abs(angle) < 10 or abs(abs(angle) - 180) < 10) and 30 < line_length < 100:
            horizontal_lines.append((x1, y1, x2, y2, line_length))
            cv.line(img_display, (x1, y1), (x2, y2), (0, 255, 0), 1, cv.LINE_AA)

# 统计垂直边x坐标差值,确定方形边长
x_coords = []
for x1, y1, x2, y2, _ in vertical_lines:
    x_coords.extend([x1, x2])

x_diffs = []
for i in range(len(x_coords) - 1):
    diff = abs(x_coords[i] - x_coords[i+1])
    if 20 < diff < 100:  # 限定边长范围
        x_diffs.append(diff)

if x_diffs:
    # 取出现频率最高的差值作为方形边长
    square_side = max(set(x_diffs), key=x_diffs.count)
    print(f"检测到的方形边长:{square_side}")

    # 按x坐标聚类垂直边(误差容忍±5)
    vertical_groups = defaultdict(list)
    for line in vertical_lines:
        x1, y1, x2, y2, _ = line
        line_x = int((x1 + x2) / 2)
        # 按边长分组,确保同一组是同一方形的左右边
        group_key = round(line_x / square_side) * square_side
        vertical_groups[group_key].append(line)

    # 遍历每组垂直边,匹配对应的水平边
    for group_x, lines in vertical_groups.items():
        if len(lines) != 2:
            continue  # 一个方形对应两条垂直边

        # 获取垂直边的y范围(方形的上下边界)
        y_min = min(min(y1, y2) for x1, y1, x2, y2, _ in lines)
        y_max = max(max(y1, y2) for x1, y1, x2, y2, _ in lines)

        # 查找匹配的顶部和底部水平边
        top_line = None
        bottom_line = None
        for h_line in horizontal_lines:
            hx1, hy1, hx2, hy2, _ = h_line
            h_y = int((hy1 + hy2) / 2)
            h_x_min = min(hx1, hx2)
            h_x_max = max(hx1, hx2)

            # 匹配顶部水平边:y接近y_min,x覆盖方形左右边界
            if abs(h_y - y_min) < 5 and h_x_min <= group_x and h_x_max >= group_x + square_side:
                top_line = h_line
            # 匹配底部水平边:y接近y_max,x覆盖方形左右边界
            if abs(h_y - y_max) < 5 and h_x_min <= group_x and h_x_max >= group_x + square_side:
                bottom_line = h_line

        if top_line and bottom_line:
            # 绘制方形边框
            cv.rectangle(img_display, (group_x, y_min), (group_x + square_side, y_max), (255, 0, 0), 2)

# 保存结果
cv.imwrite('img_squares_detected.png', img_display)

3. 参数调整说明

  • threshold:霍夫线检测的阈值,值越高检测到的线条越精准
  • minLineLength/maxLineGap:过滤过短或断裂的线条
  • 角度误差范围(abs(abs(angle)-90) <10):根据图像线条的倾斜程度调整
  • 边长范围(20 < diff <100):根据实际方形尺寸调整

内容的提问来源于stack exchange,提问作者Aditya Patil

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 02:15:32