如何用OpenCV Python准确提取矩形尺寸与坐标并分割矩形?
问题解决与脚本优化
一、cv2.boundingRect()绘制不匹配的原因及修复
核心问题分析
绘制不匹配主要源于两个错误:
- 阈值处理颠倒:目标是黑色矩形,默认
cv2.THRESH_BINARY会将黑色区域转为0(黑色)、背景转为255(白色),但cv2.findContours仅识别白色轮廓,导致提取的是背景而非目标矩形。 - 轮廓层级错误:
cv2.RETR_TREE会提取所有嵌套层级的轮廓,可能包含矩形内部杂色小轮廓,造成坐标混乱。
修复代码
# 读取灰度图 img2 = cv2.imread(img, cv2.IMREAD_GRAYSCALE) # 反转阈值,让黑色矩形变为白色轮廓、背景变黑 _, threshold = cv2.threshold(img2, 110, 255, cv2.THRESH_BINARY_INV) # 仅提取最外层轮廓,避免嵌套干扰 contours, _ = cv2.findContours(threshold, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) coordinates = [] for cnt in contours: # 过滤过小轮廓(根据实际场景调整阈值) if cv2.contourArea(cnt) < 50: continue x, y, w, h = cv2.boundingRect(cnt) coordinates.append([x, y, w, h]) # 测试绘制匹配度 test_img = cv2.imread(img) for x, y, w, h in coordinates: # 绘制矩形:左上角(x,y) → 右下角(x+w, y+h) cv2.rectangle(test_img, (x, y), (x+w, y+h), (0, 255, 0), 2) cv2.imwrite("test_match.png", test_img)
二、通过矩形4个角点计算宽高
分两种场景处理:
1. 轴对齐矩形(边平行于坐标轴)
直接取x、y坐标的极值计算:
# 假设corners是包含4个(x,y)元组的列表 x_coords = [p[0] for p in corners] y_coords = [p[1] for p in corners] width = max(x_coords) - min(x_coords) height = max(y_coords) - min(y_coords) # 左上角坐标:(min(x_coords), min(y_coords))
2. 旋转矩形(边不平行于坐标轴)
计算相邻角点的欧氏距离,取两组不同长度作为宽高:
import math def calc_distance(p1, p2): return math.sqrt((p2[0]-p1[0])**2 + (p2[1]-p1[1])**2) # 计算所有两两角点的距离,过滤极小误差值 distances = [] for i in range(4): for j in range(i+1, 4): d = calc_distance(corners[i], corners[j]) if d > 1: distances.append(d) # 去重排序后,短边为宽、长边为高 unique_dists = sorted(list(set([round(d, 2) for d in distances]))) width, height = unique_dists[0], unique_dists[1]
三、优化后的批量分割脚本
修复语法错误、轮廓提取问题,确保分割矩形不超出原范围:
import cv2 import random from datetime import datetime def split_vertical(img_path): # 读取原图与灰度图 image = cv2.imread(img_path) img_gray = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE) # 反转阈值提取黑色矩形轮廓 _, threshold = cv2.threshold(img_gray, 110, 255, cv2.THRESH_BINARY_INV) # 仅提取最外层轮廓 contours, _ = cv2.findContours(threshold, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) coordinates = [] for cnt in contours: # 过滤小轮廓 if cv2.contourArea(cnt) < 50: continue x, y, w, h = cv2.boundingRect(cnt) coordinates.append([x, y, w, h]) # 随机选6个矩形(轮廓不足6个则取全部) select_count = min(6, len(coordinates)) numbers = random.sample(range(len(coordinates)), select_count) for j in numbers: timestamp = datetime.now().strftime('%Y%m%d%H%M%S%f') new_image = image.copy() x, y, width, height = coordinates[j] # 计算分割位置与宽度,确保不超出原矩形 split_offset = random.uniform(0.2, 0.5) # 分割点在原矩形20%-50%位置 split_position = int(x + width * split_offset) max_split_width = x + width - split_position split_width = int(random.uniform(0.03, 0.7) * max_split_width) # 绘制白色分割矩形 cv2.rectangle(new_image, (split_position, y), (split_position + split_width, y + height), (255, 255, 255), -1) # 保存图片 cv2.imwrite(f"data_augmentation/split_vertical_{timestamp}.png", new_image) # 调用示例 # split_vertical("your_image_path.png")
内容的提问来源于stack exchange,提问作者houda
相关产品推荐
相关产品推荐

