OpenCV帧差法车辆检测:同一车辆多次标记的解决咨询
问题描述
我正在使用帧差法检测和跟踪车辆,但部分车辆被多次标记。试过用膨胀(dilate)函数解决,但会导致噪声问题严重,求实现每辆车仅用一个矩形标记,直到其离开画面的方法。
相关截图
- 多帧截图:

- 背景与当前帧的差异图:

现有代码
import cv2 import argparse import numpy import math from tracker import * from get_background import get_background parser = argparse.ArgumentParser() parser.add_argument('-i', '--input', help='path to the input video', required=True) parser.add_argument('-c', '--consecutive-frames', default=4, type=int, dest='consecutive_frames', help='path to the input video') args = vars(parser.parse_args()) cap = cv2.VideoCapture(args['input']) # get the video frame height and width frame_width = int(cap.get(3)) frame_height = int(cap.get(4)) save_name = f"outputs/{args['input'].split('/')[-1]}" # define codec and create VideoWriter object out = cv2.VideoWriter( save_name, cv2.VideoWriter_fourcc(*'mp4v'), 10, (frame_width, frame_height) ) #object_detector = cv2.createBackgroundSubtractorMOG2(history=100, varThreshold=40) # get the background model background = get_background(args['input']) # convert the background model to grayscale format background = cv2.cvtColor(background, cv2.COLOR_BGR2GRAY) frame_count = 0 consecutive_frame = args['consecutive_frames'] ########################################################################################### kernel_size = 3 gauss_img = cv2.bilateralFilter(background, 9, 300,300) canny_img = cv2.Canny(gauss_img, 100, 200) #canny_img = cv2.erode(canny_img, None, iterations=1) lines = cv2.HoughLinesP(canny_img, rho=1, theta=math.pi / 180, threshold=15, minLineLength=50, maxLineGap=5) line_img = numpy.zeros((background.shape[0], background.shape[1], 3), dtype=numpy.uint8) for points in lines: # Extracted points nested in the list x1, y1, x2, y2 = points[0] # Draw the lines joing the points # On the original image cv2.line(line_img, (x1, y1), (x2, y2), (0, 255, 0), 2) # Maintain a simples lookup list for points #lines_list.append([(x1, y1), (x2, y2)]) cv2.imshow("lines", line_img) #cv2.imshow("canny", canny_img) if cv2.waitKey(100) & 0xFF == ord('q'): pass ########################################################################################### # 24.08.2022 object tracking tracker = EuclideanDistTracker() ### detections = [] while (cap.isOpened()): ret, frame = cap.read() if ret == True: frame_count += 1 orig_frames = [] orig_frame = frame.copy() orig_frames.append(orig_frame) # IMPORTANT STEP: convert the frame to grayscale first gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) if frame_count % consecutive_frame == 0 or frame_count == 1: frame_diff_list = [] # find the difference between current frame and base frame frame_diff = cv2.absdiff(gray, background) # thresholding to convert the frame to binary #ret, thres = cv2.threshold(frame_diff, 50, 255, cv2.THRESH_BINARY) #frame_diff = cv2.GaussianBlur(frame_diff, (3, 3), 0) ret, thres = cv2.threshold(frame_diff, 50, 255, cv2.THRESH_OTSU) cv2.imshow('frame_diff', frame_diff) out.write(frame_diff) if cv2.waitKey(100) & 0xFF == ord('q'): break # ... makes the detection of contours a bit easier kernel = numpy.ones((9,9), numpy.uint8) # bu gerekli mi erode_frame = cv2.erode(thres, kernel, iterations=1) # (thres, None, iterations=1) # append the final result into the `frame_diff_list` frame_diff_list.append(erode_frame) # cv2.imshow("lines", line_img) # #cv2.imshow("canny", canny_img) # if cv2.waitKey(100) & 0xFF == ord('q'): # break # if we have reached `consecutive_frame` number of frames if len(frame_diff_list) == consecutive_frame: # add all the frames in the `frame_diff_list` sum_frames = sum(frame_diff_list) # find the contours around the white segmented areas contours, hierarchy = cv2.findContours(sum_frames, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # draw the contours, not strictly necessary for i, cnt in enumerate(contours): cv2.drawContours(frame, contours, i, (0, 0, 255), 3) for contour in contours: # continue through the loop if contour area is less than 500... # ... helps in removing noise detection if cv2.contourArea(contour) < 300: continue # get the xmin, ymin, width, and height coordinates from the contours (x, y, w, h) = cv2.boundingRect(contour) # draw the bounding boxes cv2.rectangle(orig_frame, (x, y), (x + w, y + h), (0, 255, 0), 2) detections.append([x, y, w, h]) ### cv2.imshow('Detected Objects', orig_frame) out.write(orig_frame) if cv2.waitKey(100) & 0xFF == ord('q'): break else: break cap.release() cv2.destroyAllWindows()
解决方案
以下几种方法可解决同一车辆被多次标记的问题,同时避免膨胀操作带来的噪声:
1. 轮廓合并(IOU匹配)
提取轮廓生成bounding box后,计算框之间的交并比(IOU),合并重叠度高的框:
def merge_boxes(boxes, iou_threshold=0.3): if not boxes: return [] # 按x坐标排序 boxes = sorted(boxes, key=lambda x: x[0]) merged = [boxes[0]] for current in boxes[1:]: last = merged[-1] # 计算IOU x1 = max(last[0], current[0]) y1 = max(last[1], current[1]) x2 = min(last[0]+last[2], current[0]+current[2]) y2 = min(last[1]+last[3], current[1]+current[3]) if x2 > x1 and y2 > y1: area_inter = (x2-x1)*(y2-y1) area_last = last[2]*last[3] area_current = current[2]*current[3] iou = area_inter / (area_last + area_current - area_inter) if iou >= iou_threshold: # 合并框:取最小x、y,最大宽高 new_x = min(last[0], current[0]) new_y = min(last[1], current[1]) new_w = max(last[0]+last[2], current[0]+current[2]) - new_x new_h = max(last[1]+last[3], current[1]+current[3]) - new_y merged[-1] = [new_x, new_y, new_w, new_h] else: merged.append(current) else: merged.append(current) return merged
在代码中替换原检测框绘制逻辑:
# 替换原contour遍历部分 temp_boxes = [] for contour in contours: if cv2.contourArea(contour) < 300: continue (x, y, w, h) = cv2.boundingRect(contour) temp_boxes.append([x, y, w, h]) # 合并重叠框 merged_boxes = merge_boxes(temp_boxes) # 绘制合并后的框并更新detections for box in merged_boxes: x, y, w, h = box cv2.rectangle(orig_frame, (x, y), (x + w, y + h), (0, 255, 0), 2) detections.append([x, y, w, h])
2. 改进背景差分预处理
- 对帧差图先做高斯模糊再阈值化,减少噪声干扰:
# 替换原阈值化代码 frame_diff = cv2.GaussianBlur(frame_diff, (5,5), 0) ret, thres = cv2.threshold(frame_diff, 50, 255, cv2.THRESH_OTSU)
- 使用形态学开运算(先腐蚀后膨胀)替代单纯腐蚀,既能去除小噪声,又能避免轮廓过度破碎:
kernel = numpy.ones((5,5), numpy.uint8) opening_frame = cv2.morphologyEx(thres, cv2.MORPH_OPEN, kernel, iterations=1) frame_diff_list.append(opening_frame)
3. 正确启用跟踪器
你已初始化EuclideanDistTracker,但未真正用它关联前后帧的检测框。修改循环逻辑,让跟踪器维护车辆ID和框:
# 在获得merged_boxes后添加: tracked_objects = tracker.update(merged_boxes) for obj_id, box in tracked_objects.items(): x, y, w, h = box cv2.rectangle(orig_frame, (x, y), (x + w, y + h), (0, 255, 0), 2) cv2.putText(orig_frame, f"ID: {obj_id}", (x, y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0,255,0), 2)
跟踪器会基于欧氏距离匹配前后帧的框,同一车辆会被分配固定ID,不会重复标记。
内容的提问来源于stack exchange,提问作者questionhead
相关产品推荐
相关产品推荐

