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

基于OpenCV与Python的矩形内线段检测、编号及矫正方案问询

基于OpenCV的带线段矩形处理优化方案

问题描述

  • 需处理含内部线段的矩形图片:按内部线段长度(越短编号越小)在对应矩形下方标注1-4编号
  • 曾尝试霍夫直线变换(产生大量伪直线)和轮廓检测,寻求更优实现
  • 额外需求:将倾斜的矩形及内部线段矫正为水平垂直状态

现有代码的不足

  1. 依赖Canny边缘检测易产生杂边缘,导致误检测无关轮廓
  2. 通过层级索引判断内轮廓的逻辑不够精准,可能把矩形边框的缺口误判为线段
  3. 标注位置直接取线段轮廓的起始点,不符合“矩形下方标注”的需求
  4. 未实现矩形矫正功能

优化后的实现方案

一、精准检测内部线段并标注编号

核心思路:先定位每个矩形外轮廓,再在矩形区域内提取线段,计算长度后排序标注

import cv2
import numpy as np
import matplotlib.pyplot as plt

# 1. 图像预处理
image = cv2.imread('rects.png')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 阈值分割(比Canny更适合高对比度的黑白图,减少伪边缘)
_, thresh = cv2.threshold(gray, 200, 255, cv2.THRESH_BINARY_INV)

# 2. 检测矩形外轮廓(只找最外层大轮廓)
contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
rect_contours = []
for cnt in contours:
    # 轮廓近似为多边形,筛选出矩形(4个顶点)
    epsilon = 0.02 * cv2.arcLength(cnt, True)
    approx = cv2.approxPolyDP(cnt, epsilon, True)
    if len(approx) == 4:
        rect_contours.append(approx)

# 3. 提取每个矩形内部的线段并计算长度
line_info_list = []
for rect in rect_contours:
    # 获取矩形的边界框,用于裁剪区域
    x, y, w, h = cv2.boundingRect(rect)
    rect_roi = thresh[y:y+h, x:x+w]
    
    # 检测矩形内部的线段轮廓(只找内层轮廓)
    line_contours, _ = cv2.findContours(rect_roi, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE)
    for line_cnt in line_contours:
        # 筛选线段:轮廓近似为2个点(直线段),且长度大于最小阈值(过滤噪点)
        epsilon_line = 0.01 * cv2.arcLength(line_cnt, True)
        line_approx = cv2.approxPolyDP(line_cnt, epsilon_line, True)
        if len(line_approx) == 2:
            # 计算线段长度(欧氏距离)
            pt1 = line_approx[0][0]
            pt2 = line_approx[1][0]
            length = np.linalg.norm(pt1 - pt2)
            # 记录线段所属矩形的底部中心(用于标注位置)、长度
            rect_bottom_center = (x + w//2, y + h + 30)  # 矩形下方30像素处
            line_info_list.append((length, rect_bottom_center))

# 4. 按线段长度升序排序,分配1-4编号
line_info_list.sort()
for idx, (length, pos) in enumerate(line_info_list[:4]):
    cv2.putText(image, str(idx+1), pos, cv2.FONT_HERSHEY_SIMPLEX, 1.2, (0, 0, 255), 3)

# 可视化结果
plt.figure(figsize=(10,8))
plt.imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
plt.axis('off')
plt.show()

二、矩形矫正(透视变换)

通过透视变换将倾斜矩形转为水平垂直,步骤如下:

def rectify_rectangle(image, rect_approx):
    # 对矩形顶点排序:按左上角、右上角、右下角、左下角的顺序
    pts = rect_approx.reshape(4, 2)
    pts = np.float32(pts)
    
    # 计算目标矩形的宽度和高度
    width = int(max(np.linalg.norm(pts[0]-pts[1]), np.linalg.norm(pts[2]-pts[3])))
    height = int(max(np.linalg.norm(pts[0]-pts[3]), np.linalg.norm(pts[1]-pts[2])))
    
    # 目标点坐标(水平垂直的矩形)
    dst_pts = np.float32([[0, 0], [width-1, 0], [width-1, height-1], [0, height-1]])
    
    # 计算透视变换矩阵并执行变换
    M = cv2.getPerspectiveTransform(pts, dst_pts)
    rectified = cv2.warpPerspective(image, M, (width, height))
    return rectified

# 对每个矩形执行矫正
for idx, rect in enumerate(rect_contours):
    rectified_rect = rectify_rectangle(image, rect)
    # 可将矫正后的矩形保存或展示
    # cv2.imwrite(f'rectified_{idx}.png', rectified_rect)
    plt.imshow(cv2.cvtColor(rectified_rect, cv2.COLOR_BGR2RGB))
    plt.axis('off')
    plt.show()

优化点说明

  • 用阈值分割替代Canny:针对高对比度的黑白图,有效减少伪边缘和杂轮廓
  • 先定位矩形外轮廓:确保只处理目标矩形内部的线段,避免误检测其他区域
  • 线段长度用欧氏距离计算:比弧长更精准,因为线段是直线
  • 标注位置固定在矩形下方中心:符合需求,视觉更规整
  • 透视变换矫正:通过排序矩形顶点,精准将倾斜矩形转为水平垂直

内容的提问来源于stack exchange,提问作者Ashutosh Chapagain

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 02:07:33