91 lines
3.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import cv2
import numpy as np
cap = cv2.VideoCapture(0)
cap.set(3, 320)
cap.set(4, 240)
cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)
# ===== 核心改动:用背景减法器替代帧差法 =====
# MOG2: 能适应背景变化,适合移动摄像头场景
# history: 保留多少帧来建立背景模型(越大适应越慢,但越稳定)
# varThreshold: 判断前景的灵敏度(越小越敏感,但噪声越多)
fgbg = cv2.createBackgroundSubtractorMOG2(history=100, varThreshold=25, detectShadows=False)
# 可选用KNN背景减法器效果类似速度稍快
# fgbg = cv2.createBackgroundSubtractorKNN(history=100, dist2Threshold=400, detectShadows=False)
# 形态学核(用于去噪)
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
# 用于平滑追踪的卡尔曼滤波器(可选,减少抖动)
kalman = cv2.KalmanFilter(4, 2)
kalman.measurementMatrix = np.array([[1,0,0,0], [0,1,0,0]], np.float32)
kalman.transitionMatrix = np.array([[1,0,1,0], [0,1,0,1], [0,0,1,0], [0,0,0,1]], np.float32)
kalman.processNoiseCov = np.eye(4, dtype=np.float32) * 0.03
kalman.measurementNoiseCov = np.eye(2, dtype=np.float32) * 0.5
kalman.statePost = np.zeros((4,1), np.float32)
while True:
ret, frame = cap.read()
if not ret:
break
# 1. 背景减法:得到前景掩码(白色=运动物体)
fgmask = fgbg.apply(frame)
# 2. 形态学处理:去噪 + 填充空洞
fgmask = cv2.morphologyEx(fgmask, cv2.MORPH_OPEN, kernel) # 先开运算去噪点
fgmask = cv2.morphologyEx(fgmask, cv2.MORPH_CLOSE, kernel) # 再闭运算填充空洞
# 3. 找轮廓
contours, _ = cv2.findContours(fgmask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# ===== 核心:选最大目标 =====
target = None
max_area = 0
for cnt in contours:
area = cv2.contourArea(cnt)
if area < 300: # 过滤噪声
continue
if area > max_area:
max_area = area
target = cnt
# ===== 处理目标 =====
if target is not None:
x, y, w, h = cv2.boundingRect(target)
cx = x + w // 2
cy = y + h // 2
# 卡尔曼滤波预测/更新(可选,让追踪更平滑)
measurement = np.array([[np.float32(cx)], [np.float32(cy)]])
kalman.correct(measurement)
prediction = kalman.predict()
pred_x, pred_y = int(prediction[0]), int(prediction[1])
# 画原始检测框
cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2)
cv2.circle(frame, (cx, cy), 5, (0, 0, 255), -1)
# 画卡尔曼平滑后的位置(黄色)
cv2.circle(frame, (pred_x, pred_y), 5, (0, 255, 255), -1)
print(f"目标位置: ({cx}, {cy}), 面积: {max_area}")
else:
# 没有检测到目标时,只预测不更新
prediction = kalman.predict()
# 可选:根据预测位置保持追踪(即使被短暂遮挡)
# 可选:显示前景掩码(调试用)
cv2.imshow("foreground mask", fgmask)
cv2.imshow("tracking", frame)
if cv2.waitKey(1) & 0xFF == 27:
break
cap.release()
cv2.destroyAllWindows()