新增摄像头移动后对物体的跟踪
This commit is contained in:
parent
abdecbbace
commit
82e8c1d43f
@ -7,25 +7,40 @@ cap.set(3, 320)
|
|||||||
cap.set(4, 240)
|
cap.set(4, 240)
|
||||||
cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)
|
cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)
|
||||||
|
|
||||||
ret, prev_frame = cap.read()
|
# ===== 核心改动:用背景减法器替代帧差法 =====
|
||||||
prev_gray = cv2.cvtColor(prev_frame, cv2.COLOR_BGR2GRAY)
|
# MOG2: 能适应背景变化,适合移动摄像头场景
|
||||||
prev_gray = cv2.GaussianBlur(prev_gray, (5,5), 0)
|
# 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:
|
while True:
|
||||||
ret, frame = cap.read()
|
ret, frame = cap.read()
|
||||||
if not ret:
|
if not ret:
|
||||||
break
|
break
|
||||||
|
|
||||||
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
|
# 1. 背景减法:得到前景掩码(白色=运动物体)
|
||||||
gray = cv2.GaussianBlur(gray, (5,5), 0)
|
fgmask = fgbg.apply(frame)
|
||||||
|
|
||||||
# 帧差
|
# 2. 形态学处理:去噪 + 填充空洞
|
||||||
diff = cv2.absdiff(prev_gray, gray)
|
fgmask = cv2.morphologyEx(fgmask, cv2.MORPH_OPEN, kernel) # 先开运算去噪点
|
||||||
diff = cv2.convertScaleAbs(diff, alpha=2.5)
|
fgmask = cv2.morphologyEx(fgmask, cv2.MORPH_CLOSE, kernel) # 再闭运算填充空洞
|
||||||
|
|
||||||
_, thresh = cv2.threshold(diff, 20, 255, cv2.THRESH_BINARY)
|
# 3. 找轮廓
|
||||||
|
contours, _ = cv2.findContours(fgmask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
||||||
contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
|
||||||
|
|
||||||
# ===== 核心:选最大目标 =====
|
# ===== 核心:选最大目标 =====
|
||||||
target = None
|
target = None
|
||||||
@ -41,21 +56,34 @@ while True:
|
|||||||
max_area = area
|
max_area = area
|
||||||
target = cnt
|
target = cnt
|
||||||
|
|
||||||
# ===== 只处理一个目标 =====
|
# ===== 处理目标 =====
|
||||||
if target is not None:
|
if target is not None:
|
||||||
x, y, w, h = cv2.boundingRect(target)
|
x, y, w, h = cv2.boundingRect(target)
|
||||||
cx = x + w // 2
|
cx = x + w // 2
|
||||||
cy = y + h // 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.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, (cx, cy), 5, (0, 0, 255), -1)
|
||||||
|
# 画卡尔曼平滑后的位置(黄色)
|
||||||
|
cv2.circle(frame, (pred_x, pred_y), 5, (0, 255, 255), -1)
|
||||||
|
|
||||||
print("唯一目标:", cx, cy, "area:", max_area)
|
print(f"目标位置: ({cx}, {cy}), 面积: {max_area}")
|
||||||
|
else:
|
||||||
|
# 没有检测到目标时,只预测不更新
|
||||||
|
prediction = kalman.predict()
|
||||||
|
# 可选:根据预测位置保持追踪(即使被短暂遮挡)
|
||||||
|
|
||||||
|
# 可选:显示前景掩码(调试用)
|
||||||
|
cv2.imshow("foreground mask", fgmask)
|
||||||
cv2.imshow("tracking", frame)
|
cv2.imshow("tracking", frame)
|
||||||
|
|
||||||
prev_gray = gray.copy()
|
|
||||||
|
|
||||||
if cv2.waitKey(1) & 0xFF == 27:
|
if cv2.waitKey(1) & 0xFF == 27:
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user