第八章 目标跟踪
8.1 检测移动的目标
基本的运动检测
计算帧之间的差异,或考虑“背景”帧与其他帧之间的差异。
例:
import cv2
import numpy as np
camera = cv2.VideoCapture(0)
es = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(9,4))
kernel = np.ones((5,5),np.uint8)
#通过摄像头读入帧,将第一帧设置为背景
background = None
while(True):
rat,frame = camera.read()
if background is None:
background = cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)
background = cv2.GaussianBlur(background,(21,21),0)
continue
#对帧进行简单处理:转化灰阶、模糊处理(减少噪声影响)
gray_frame = cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)
gray_frame = cv2.GaussianBlur(gray_frame,(21,21),0)
#差分图difference map,
diff = cv2.absdiff(background,gray_frame)
diff = cv2.threshold(diff,25,255,cv2.THRESH_BINARY)[1]
diff = cv2.dilate(diff,es,iterations=2)
#显示矩形
image,cnts,hierarchy = cv2.findContours(diff.copy(),cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)
for c in cnts:
if cv2.contourArea(c)<1500:
continue
(x,y,w,h) = cv2.boundingRect(c)
cv2.rectangle(frame,(x,y),(x+w,y+h),(0,255,0),2)
cv2.imshow("contours",frame)
cv2.imshow("dif",diff)
if cv2.waitKey(82) & 0xff == ord("q"):
break
cv2.destroyAllWindows()
8.2 背景分割器:KNN、MOG2和GMG
OpenCV提供了一个BackgroundSubtractor的类,在分割前景和背景时很方便。BackgroundSubtractor类 可以计算阴影。
例子BackgroundSubtractor
import numpy as np
import cv2
cap = cv2.VideoCapture(0)
mog = cv2.createBackgroundSubtractorMOG2()
while(1):
rat,frame = cap.read()
fgmask = mog.apply(frame)
cv2.imshow('frame',fgmask)
if cv2.waitKey(30) & 0xff == ord("q"):
break
cap.release()
cv2.destroyAllWindows()
例:使用BackgroundSubtractorKNN来实现运动检测
import cv2import numpy as np bs = cv2.createBackgroundSubtractorKNN(detectShadows=True) camera = cv2.VideoCapture('traffic.flv')
# traffic.flv一段公路上的交通情况 while True: rat,frame = camera.read() fgmask = bs.apply(frame) th = cv2.threshold(fgmask.copy(),244,255,cv2.THRESH_BINARY)[1] dilated = cv2.dilate(th,cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(3,3)), iterations = 2) image,contours,hier = cv2.findContours(dilated,cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) for c in contours: if cv2.contourArea(c)>1600: (x,y,w,h) = cv2.boundingRect(c) cv2.rectangle(frame,(x,y),(x+w,y+h),(255,255,0),2) cv2.imshow("mog",fgmask) cv2.imshow("thresh",th) cv2.imshow("detection",frame) if cv2.waitKey(30) & 0xff ==27: break camera.release() cv2.destroyAllWindows()
8.2.1 均值漂移和CAMShift
均值漂移Meanshift是一种目标跟踪算法,该算法寻找概率函数离散样本的最大密度,并且重新计算下一帧的最大密度,该算法给出了目标的移动方向。
均值漂移在追踪视频中感兴趣的区域时非常有用。
例:标记并追踪感兴趣的区域:
import numpy as np
import cv2
cap = cv2.VideoCapture(0)
ret,frame = cap.read()
r,h,c,w = 10,200,10,200
track_window = (c,r,w,h)
roi = frame[r:r+h,c:c+w]
hsv_roi = cv2.cvtColor(frame,cv2.COLOR_BGR2HSV)
mask = cv2.inRange(hsv_roi,np.array((100.,30.,32.)),
np.array((180.,120.,255.)))
roi_hist = cv2.calcHist([hsv_roi],[0],mask,[180],[0,180])
cv2.normalize(roi_hist,roi_hist,0,255,cv2.NORM_MINMAX)
term_crit = (cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT,10,1)
while True:
ret,frame = cap.read()
if ret == True:
hsv = cv2.cvtColor(frame,cv2.COLOR_BGR2HSV)
dst = cv2.calcBackProject([hsv],[0],roi_hist,[0,180],1)
ret,track_window = cv2.meanShift(dst,track_window,term_crit)
x,y,w,h = track_window
img2 = cv2.rectangle(frame,(x,y),(x+w,y+h),255,2)
cv2.imshow('img2',img2)
if cv2.waitKey(60) & 0xff == ord("q"):
break
else:
break
cv2.destroyAllWindows()
cap.release()
如果有颜色范围内的物体进入窗口,就会追踪物体。
8.2.2 彩色直方图
calcHist 函数用来计算图像的彩色直方图。彩色直方图是图像的颜色分布。x轴是色彩值,y轴是像素数量。
calcBackProject 函数 在均值漂移算法中非常重要。称为直方图方向投影,它得到直方图并将其投影到一副图像上,其结果是每个像素属于生成原始直方图的原图像的概率。
import numpy as np
import cv2
cap = cv2.VideoCapture(0)
ret,frame = cap.read()
#标记感兴趣的区域
r,h,c,w = 10,200,10,200
track_window = (c,r,w,h)
#提取roi并将其转换为HSV空间
roi = frame[r:r+h,c:c+w]
hsv_roi = cv2.cvtColor(frame,cv2.COLOR_BGR2HSV)
mask = cv2.inRange(hsv_roi,np.array((100.,30.,32.)),
np.array((180.,120.,255.)))
#roi的直方图
roi_hist = cv2.calcHist([hsv_roi],[0],mask,[180],[0,180])
cv2.normalize(roi_hist,roi_hist,0,255,cv2.NORM_MINMAX)
#均值漂移停止条件 :迭代10次或 中心移动1个像素
term_crit = (cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT,10,1)
while True:
ret,frame = cap.read()
if ret == True:
hsv = cv2.cvtColor(frame,cv2.COLOR_BGR2HSV)
#反向投影
dst = cv2.calcBackProject([hsv],[0],roi_hist,[0,180],1)
ret,track_window = cv2.meanShift(dst,track_window,term_crit)
x,y,w,h = track_window
img2 = cv2.rectangle(frame,(x,y),(x+w,y+h),255,2)
cv2.imshow('img2',img2)
if cv2.waitKey(60) & 0xff == ord("q"):
break
else:
break
cv2.destroyAllWindows()
cap.release()
8.3CAMShift
import numpy as np
import cv2
cap = cv2.VideoCapture(0)
ret,frame = cap.read()
#标记感兴趣的区域
r,h,c,w = 10,200,10,200
track_window = (c,r,w,h)
#提取roi并将其转换为HSV空间
roi = frame[r:r+h,c:c+w]
hsv_roi = cv2.cvtColor(frame,cv2.COLOR_BGR2HSV)
mask = cv2.inRange(hsv_roi,np.array((100.,30.,32.)),
np.array((180.,120.,255.)))
#roi的直方图
roi_hist = cv2.calcHist([hsv_roi],[0],mask,[180],[0,180])
cv2.normalize(roi_hist,roi_hist,0,255,cv2.NORM_MINMAX)
#均值漂移停止条件 :迭代10次或 中心移动1个像素
term_crit = (cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT,10,1)
while True:
ret,frame = cap.read()
if ret == True:
hsv = cv2.cvtColor(frame,cv2.COLOR_BGR2HSV)
#反向投影
dst = cv2.calcBackProject([hsv],[0],roi_hist,[0,180],1)
ret,track_window = cv2.CamShift(dst,track_window,term_crit)
pts = cv2.boxPoints(ret)
pts = np.int0(pts)
img2 = cv2.polylines(frame,[pts],True,255,2)
cv2.imshow('img2',img2)
if cv2.waitKey(60) & 0xff == ord("q"):
break
else:
break
cv2.destroyAllWindows()
cap.release()
8.4 卡尔曼滤波器
卡尔曼滤波器对含有噪声的输入数据流进行递归操作,产生底层系统状态在统计意义上的最优估计。
8.4.1 预测和更新
可将卡尔曼滤波算法分为两个阶段:
预测:使用当前点计算的协方差来估计目标的新位置
更新:记录目标位置,为下一次循环计算修正协方差
例:卡尔曼滤波器预测鼠标轨迹 import cv2, numpy as np measurements = [] predictions = [] frame = np.zeros((800, 800, 3), np.uint8) last_measurement = current_measurement = np.array((2,1), np.float32) last_prediction = current_prediction = np.zeros((2,1), np.float32) def mousemove(event, x, y, s, p): global frame, current_measurement, measurements, last_measurement, current_prediction, last_prediction last_prediction = current_prediction last_measurement = current_measurement current_measurement = np.array([[np.float32(x)],[np.float32(y)]]) kalman.correct(current_measurement) current_prediction = kalman.predict() lmx, lmy = last_measurement[0], last_measurement[1] cmx, cmy = current_measurement[0], current_measurement[1] lpx, lpy = last_prediction[0], last_prediction[1] cpx, cpy = current_prediction[0], current_prediction[1] cv2.line(frame, (lmx, lmy), (cmx, cmy), (0,100,0)) cv2.line(frame, (lpx, lpy), (cpx, cpy), (0,0,200)) cv2.namedWindow("kalman_tracker") cv2.setMouseCallback("kalman_tracker", mousemove); kalman = cv2.KalmanFilter(4,2,1) 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.array([[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]],np.float32) * 0.03 while True: cv2.imshow("kalman_tracker", frame) if (cv2.waitKey(30) & 0xFF) == 27: break if (cv2.waitKey(30) & 0xFF) == ord('q'): cv2.imwrite('kalman.jpg', frame) break cv2.destroyAllWindows()