如何剔除OpenCV图像目标检测结果中的重叠矩形框?
我使用OpenCV库检测图像中的目标,检测输入图效果如下:
为实现该检测功能,我编写了如下代码:
import matplotlib.pyplot as plt import cv2 # source data img_file= "Fig1.jpg" # create an OpenCV image img= cv2.imread(img_file) # convert color image to grey image gray_img=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) im_gauss = cv2.GaussianBlur(gray_img, (5, 5), 0) ret, thresh = cv2.threshold(im_gauss, 127, 255, 0) # get contours contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) margin = 40 # calculate area and filter for con in contours: area = cv2.contourArea(con) if 100 < area < 500: x,y,w,h = cv2.boundingRect(con) cv2.rectangle(img, (x-margin, y-margin), (x + w+margin, y + h+margin), (0,255,0), 2) plt.imshow(img, cmap='gray')
上述代码运行后,可得到标注了检测目标的结果图,效果如下:
目前检测生成的部分矩形标注框存在重叠,并非预期的输出效果。目标是剔除所有重叠的矩形框,最终输出仅保留无重叠矩形的结果图。
解决方法
要剔除重叠的矩形框,直接用OpenCV自带的非极大值抑制(NMS)算法就行,不用自己写复杂的矩形相交判断逻辑。整体调整思路很简单:
- 遍历轮廓的时候别急着画框,先把所有符合面积要求的框的坐标、对应参数统一存到列表里
- 调用
cv2.dnn.NMSBoxes对所有存好的框做过滤,通过设置交并比阈值判定哪些框是重叠的,算法会自动优先保留面积最大的框,把和它重叠的其他框全删掉 - 只对过滤后剩下的不重叠的框执行绘制就行
- 顺带把原代码里matplotlib显示OpenCV图像的色彩偏差问题修好,不然显示出来的图颜色不对
修改后的完整可运行代码如下:
import matplotlib.pyplot as plt import cv2 # 基础参数配置 img_file= "Fig1.jpg" margin = 40 min_contour_area = 100 max_contour_area = 500 # 重叠判定阈值:两个框交并比超过该值就判定为重叠,会被自动剔除 iou_threshold = 0.2 # 读取图像 img = cv2.imread(img_file) # 转换色彩空间避免matplotlib显示颜色偏差 img_show = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # 图像预处理 gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) im_gauss = cv2.GaussianBlur(gray_img, (5, 5), 0) ret, thresh = cv2.threshold(im_gauss, 127, 255, 0) # 提取轮廓 contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) # 收集所有符合面积要求的矩形框 bbox_list = [] score_list = [] for con in contours: area = cv2.contourArea(con) if min_contour_area < area < max_contour_area: x,y,w,h = cv2.boundingRect(con) # 计算加边距的矩形坐标,适配NMS输入格式 bbox_x = x - margin bbox_y = y - margin bbox_w = w + 2 * margin bbox_h = h + 2 * margin bbox_list.append([bbox_x, bbox_y, bbox_w, bbox_h]) # 统一给所有框设相同置信度,NMS会优先保留面积最大的框 score_list.append(1.0) # 执行NMS过滤重叠框 nms_keep_index = cv2.dnn.NMSBoxes( bboxes=bbox_list, scores=score_list, score_threshold=0.5, nms_threshold=iou_threshold ) # 绘制过滤后的无重叠框 if len(nms_keep_index) > 0: for idx in nms_keep_index.flatten(): x, y, w, h = bbox_list[idx] cv2.rectangle(img_show, (x, y), (x + w, y + h), (0, 255, 0), 2) plt.imshow(img_show) plt.show()
调参提示
- 代码里的
iou_threshold是重叠判定的阈值,两个框的交并比超过这个值就会被判定为重叠,可以根据实际效果调整:数值越小,对重叠的判定越严格,哪怕离得近的框也会被删掉;数值越大,只有几乎完全重合的框才会被剔除 - 如果想多保留一些距离近但不怎么重叠的框,就把这个值调高一点
内容的提问来源于stack exchange,提问作者Ravi
相关产品推荐
相关产品推荐

