基于OpenCV与Python的矩形内线段检测、编号及矫正方案问询
基于OpenCV的带线段矩形处理优化方案
问题描述
- 需处理含内部线段的矩形图片:按内部线段长度(越短编号越小)在对应矩形下方标注1-4编号
- 曾尝试霍夫直线变换(产生大量伪直线)和轮廓检测,寻求更优实现
- 额外需求:将倾斜的矩形及内部线段矫正为水平垂直状态
现有代码的不足
- 依赖Canny边缘检测易产生杂边缘,导致误检测无关轮廓
- 通过层级索引判断内轮廓的逻辑不够精准,可能把矩形边框的缺口误判为线段
- 标注位置直接取线段轮廓的起始点,不符合“矩形下方标注”的需求
- 未实现矩形矫正功能
优化后的实现方案
一、精准检测内部线段并标注编号
核心思路:先定位每个矩形外轮廓,再在矩形区域内提取线段,计算长度后排序标注
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
相关产品推荐
相关产品推荐

