Camshift
Meanshift跟踪里面还有一个问题。我们的窗口的大小是固定的,而汽车由远及近(在视觉上)是一个逐渐变大的过程,固定的窗口是不合适的。所以我们需要根据目标的大小和角度来对窗口的大小和角度进行修订。
OpenCVLabs 为我们带来的解决方案(1988 年):一个被叫做 CAMshift 的算法。
CamShift算法,全称是 Continuously AdaptiveMeanShift Continuously AdaptiveMeanShift,顾名思义,它是对Mean Shift 算法的改进,能够自动调节搜索窗口大小来适应目标的大小,可以跟踪视频中尺寸变化的目标。
这个算法首先要使用 meanshift,meanshift 找到(并覆盖)目标之后,再去调整窗口的大小,技术分享图片 。它还会计算目标对象的最佳外接椭圆的角度,并以此调节窗口角度。然后使用更新后的窗口大小和角度来在原来的位置继续进行 meanshift。重复这个过程知道达到需要的精度。
它也是一种半自动跟踪算法,需要手动标定跟踪目标。基本思想是以视频图像中运动物体的颜色信息作为特征,对输入图像的每一帧分别作 Mean-Shift 运算,并将上一帧的目标中心和搜索窗口大小(核函数带宽)作为下一帧 Mean shift 算法的中心和搜索窗口大小的初始值,如此迭代下去,就可以实现对目标的跟踪。因为在每次搜索前将搜索窗口的位置和大小设置为运动目标当前中心的位置和大小,而运动目标通常在这区域附近,缩短了搜索时间;另外,在目标运动过程中,颜色变化不大,故该算法具有良好的鲁棒性。已被广泛应用到运动人体跟踪,人脸跟踪等领域。
CamShift跟踪流程图
具体步骤:
步骤一:计算目标区域内的颜色直方图。
通常是将输入图像转换到HSV颜色空间,目标区域为初始设定的搜索窗口范围,分离出色调H分量做该区域的色调直方图计算。因为 RGB 颜色空间对光线条件的改变较为敏感,要减小该因素对跟踪效果的影响,CamShift 算法通常采用 HSV 色彩空间进行处理,当然也可以用其它颜色空间计算。这样即得到目标模板的颜色直方图。
步骤二:根据获得的颜色直方图将原始输入图像转化成颜色概率分布图像,
该过程称为“反向投影"。所谓直方图反向投影,就是输入图像在已知目标颜色直方图的条件下的颜色概率密度分布图,包含了目标在当前帧中的相干信息。对于输入图像中的每一个像素,查询目标模型颜色直方图,对于目标区域内的像素,可得到该像素属于目标像素的概率,而对于非目标区域内的像素,该概率为0。
步骤三:Mean Shift迭代过程。
即右边大矩形框内的部分,它是 CamShift 算法的核心,目的在于找到目标中心在当前帧中的位置。首先在颜色概率分布图中选择搜索窗口的大小和初始位置,然后计算搜索窗口的质心位置。设像素点(i, j)位于搜索窗口内,I(i, j)是颜色直方图的反向投影图中该像素点对应的值,定义搜索窗口的零阶矩 M00 和一阶矩M10,M01如下:
接着调整搜索窗口中心到质心。零阶矩反映了搜索窗口尺寸,依据它调整窗口大小,并将搜索窗口的中心移到质心,如果移动距离大于设定的阈值,则重新计算调整后的窗口质心,进行新一轮的窗口位置和尺寸调整。直到窗口中心与质心之间的移动距离小于阈值,或者迭代次数达到某一最大值,认为收敛条件满足,将搜索窗口位置和大小作为下一帧的目标位置输入,开始对下一帧图像进行新的目标搜索
OpenCV 中的 Camshift
Camshift函数的原型为:RotatedRect CamShift(InputArray probImage, Rect& window, TermCriteria criteria)。
其中:
probImage为输入图像直方图的反向投影图,
window为要跟踪目标的初始位置矩形框,
criteria为算法结束条件。
函数返回一个有方向角度的矩阵。
该函数的实现首先是利用meanshift算法计算出要跟踪的中心,
然后调整初始窗口的大小位置和方向角度。
在camshift内部调用了meanshift算法计算目标的重心。
与 Meanshift 基本一样,但是返回的结果是一个带旋转角度的矩形(这是我们的结果),以及这个矩形的参数(被用到下一次迭代过程中)。下面是代码:
import numpy as np
import cv2
cap = cv2.VideoCapture('vtest.avi')
# take first frame of the video
ret,frame = cap.read()
# setup initial location of window
#r,h,c,w = 250,90,400,125 # simply hardcoded the values
r,h,c,w = 238,100,627,70
track_window = (c,r,w,h)
# set up the ROI for tracking
roi = frame[r:r+h, c:c+w]
hsv_roi = cv2.cvtColor(roi, cv2.COLOR_BGR2HSV)
mask = cv2.inRange(hsv_roi, np.array((0., 60.,32.)), np.array((180.,255.,255.)))
roi_hist = cv2.calcHist([hsv_roi],[0],mask,[180],[0,180])
cv2.normalize(roi_hist,roi_hist,0,255,cv2.NORM_MINMAX)
# Setup the termination criteria, either 10 iteration or move by atleast 1 pt
term_crit = ( cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 1 )
while(1):
ret ,frame = cap.read()
if ret == True:
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
dst = cv2.calcBackProject([hsv],[0],roi_hist,[0,180],1)
# apply meanshift to get the new location
ret, track_window = cv2.CamShift(dst, track_window, term_crit)
# Draw it on image
pts = cv2.boxPoints(ret)
pts = np.int0(pts)
img2 = cv2.polylines(frame,[pts],True, 255,2)
cv2.imshow('img2',img2)
#k = cv2.waitKey(60) & 0xff
k = cv2.waitKey(0) & 0xff
if k == 27:
break
else:
cv2.imwrite(chr(k)+".jpg",img2)
else:
break
cv2.destroyAllWindows()
cap.release()
对三帧图像分析的结果如下: