OpenCV如何为指定上下目标轮廓绘制外接bounding box
问题描述
需求是为检测到的1个顶部轮廓、1个底部轮廓绘制整体外接bounding box:
原有基础实现代码在无干扰场景可以正常运行,效果见无干扰运行效果,代码如下:
import cv2 import numpy as np img = cv2.imread('light2.png') hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) mask = cv2.inRange(hsv, (0, 0, 46), (179, 255, 255)) kernel = np.ones((5,5),np.uint8) mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel) mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel) contours, hierarchy = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) try: hierarchy = hierarchy[0] except: hierarchy = [] height, width, _ = img.shape min_x, min_y = width, height max_x = max_y = 0 for contour, hier in zip(contours, hierarchy): (x, y, w, h) = cv2.boundingRect(contour) min_x, max_x = min(x, min_x), max(x+w, max_x) min_y, max_y = min(y, min_y), max(y+h, max_y) if w > 80 and h > 80: cv2.rectangle(img, (x, y), (x+w, y+h), (255, 0, 0), 2) if max_x - min_x > 0 and max_y - min_y > 0: cv2.rectangle(img, (min_x, min_y), (max_x, max_y), (255, 0, 0), 2)
原有代码逻辑缺陷:遍历所有轮廓时无差别更新全局外接框的坐标边界,当场景存在无关轮廓时,会把干扰轮廓也纳入计算范围,最终得到范围错误的外接框,错误效果见错误运行效果。
解决思路
核心是先从所有检测到的轮廓里精准筛选出顶部、底部两个目标轮廓,再仅基于这两个轮廓计算整体外接框,不要把无关轮廓纳入坐标计算:
- 第一轮遍历轮廓时,根据目标轮廓的几何特征过滤干扰项:可以用轮廓面积、宽高比、矩形度等特征做筛选,比如目标是横向长条状光带,宽高比会远大于零散小干扰,设置对应宽高比阈值即可排除大部分干扰
- 筛选出的有效轮廓按垂直坐标(轮廓顶部y值)排序,y值最小的是顶部目标轮廓,y值最大的是底部目标轮廓
- 仅用筛选出的两个目标轮廓的坐标,计算整体外接框的min_x、min_y、max_x、max_y,绘制最终外框
修正后参考代码
import cv2 import numpy as np img = cv2.imread('light2.png') hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) mask = cv2.inRange(hsv, (0, 0, 46), (179, 255, 255)) kernel = np.ones((5,5),np.uint8) mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel) mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel) contours, hierarchy = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) height, width, _ = img.shape valid_rects = [] # 第一轮遍历:筛选符合特征的目标轮廓 for contour in contours: x, y, w, h = cv2.boundingRect(contour) # 阈值根据实际场景调整:过滤小尺寸、宽高比不符合横向长条特征的干扰 if w > 80 and h > 20 and w/h > 3: valid_rects.append((x, y, w, h)) # 绘制单个目标轮廓的边框 cv2.rectangle(img, (x, y), (x+w, y+h), (255, 0, 0), 2) # 取上下两个目标轮廓计算整体外框 if len(valid_rects) >= 2: # 按轮廓顶部y值升序排序 valid_rects.sort(key=lambda r: r[1]) top_rect = valid_rects[0] bottom_rect = valid_rects[-1] # 仅用两个目标轮廓计算全局边界 min_x = min(top_rect[0], bottom_rect[0]) min_y = top_rect[1] max_x = max(top_rect[0] + top_rect[2], bottom_rect[0] + bottom_rect[2]) max_y = bottom_rect[1] + bottom_rect[3] # 绘制整体外接框 cv2.rectangle(img, (min_x, min_y), (max_x, max_y), (0, 255, 0), 2) cv2.imshow('result', img) cv2.waitKey(0) cv2.destroyAllWindows()
提示:代码中的尺寸、宽高比阈值可根据实际场景调整,如果存在和目标尺寸接近的干扰,可增加轮廓面积、轮廓占外接矩形比例(矩形度)等筛选条件进一步提升准确率。
内容的提问来源于stack exchange,提问作者waka_linux
相关产品推荐
相关产品推荐

