OpenCV学习笔记-CamShift
CamShift算法的全称是"Continuously Adaptive Mean-SHIFT",即:连续自适应的MeanShift算法。其基本思想是对视频序列的所有图像帧都作MeanShift运算,并将上一帧的结果(即搜索窗口的中心位置和窗口大小)作为下一帧MeanShift算法的搜索窗口的初始值,如此迭代下去。简单点说,meanShift是针对单张图片寻找最优迭代结果,而camShift则是针对视频序列来处理,并对该序列中的每一帧图片都调用meanShift来寻找最优迭代结果。正是由于camShift针对一个视频序列进行处理,从而保证其可以不断调整窗口的大小,如此一来,当目标的大小发生变化的时候,该算法就可以自适应地调整目标区域继续跟踪。
在OpenCV自带的camShift的例子当中,是通过计算目标在HSV空间下的H分量直方图,通过直方图反向投影得到目标像素的概率分布,然后通过调用OpenCV的CAMSHIFT算法,自动跟踪并调整目标窗口的中心位置与大小。该算法对于简单背景下的单目标跟踪效果较好,但如果被跟踪目标与背景颜色或周围其它目标颜色比较接近,则跟踪效果较差。另外,由于采用颜色特征,所以它对被跟踪目标的形状变化有一定的抵抗能力。
OpenCV自带例子中的camShift算法,可以分为三个部分:
一、计算色彩投影图(反向投影):
(1)为了减少光照变化对目标跟踪的影响,首先将图像从RGB颜色空间转换到HSV颜色空间;
(2)对H分量进行直方图统计,直方图代表了不同H分量取值出现的概率,或者说可以据此查找出H分量的大小为x时的概率或像素个数,即,得到颜色概率查找表;
(3)将图像中每个像素的值用其颜色出现的概率进行替换,由此得到颜色概率分布图;
以上三个步骤称之为反向投影,需要提醒的是,颜色概率分布图是一个灰度图像;
二、meanShift寻优
前面提到过meanShift算法(http://blog.****.net/carson2005/article/details/7337432)是一种非参数概率密度估计方法,它通过不断迭代计算得到最优搜索窗口的位置和大小。
三、camShift跟踪算法
import cv2 as cv import numpy as np from matplotlib import pyplot as plt cap = cv.VideoCapture('img/slow.flv') #读取第一帧图像 ret, frame = cap.read() #设置初始窗口参数 r, h ,c, w = 190, 30, 300, 90 track_window = (c, r, w, h) #设置ROI roi = frame[r:r+h, c:c+w] hsv_roi = cv.cvtColor(roi, cv.COLOR_BGR2HSV) #色调直方图 lower_hsv = np.array([0, 0, 0]) upper_hsv = np.array([255, 255, 46]) # 黑色 mask = cv.inRange(hsv_roi, lowerb=lower_hsv, upperb=upper_hsv) roi_hist = cv.calcHist([hsv_roi], [0], mask, [180], [0, 180]) #归一化 cv.normalize(roi_hist, roi_hist, 0, 255, cv.NORM_MINMAX) #设置迭代终止条件 term_crit = (cv.TERM_CRITERIA_EPS | cv.TERM_CRITERIA_COUNT, 10, 1) while True: ret, frame = cap.read() if ret is True: hsv = cv.cvtColor(frame, cv.COLOR_BGR2HSV) #获得色调直方图的反向投影 dst = cv.calcBackProject([hsv], [0], roi_hist, [0, 180], 1) #返回新的窗口的坐标集合和新的窗口 ret, track_window = cv.CamShift(dst, track_window, term_crit) #ret((center.x, center.y),(h, w), thate) # print(ret) # print(track_window) pts = cv.boxPoints(ret) #返回盒子的四个顶点 pts = np.int0(pts) # print('len pts:', len(pts), pts) #绘制直线,isClose 直线是否闭合 img2 = cv.polylines(frame, [pts], True, (255, 0, 0), 2) cv.imshow('img2', img2) k = cv.waitKey(60) & 0xff if k == 27: break else: break cv.waitKey(0) cv.destroyAllWindows()