翻译自:https://docs.opencv.org/master/db/df8/tutorial_py_meanshift.html
目录
1 Meanshift
1.1 原理
1.2 OpenCV中的Meashift
2 CAMSHIFT
2.1 原理
2.2 OpenCV 中的 Camshift
Meanshift 算法的基本原理是和很简单的。假设我们有一堆点(比如直方 图反向投影得到的点),和一个小的圆形窗口,我们要完成的任务就是将这个窗 口移动到最大灰度密度处(或者是点最多的地方)。如下图所示:
初始窗口是蓝色的“C1”,它的圆心为蓝色方框“C1_o”,而窗口中所有点质心却是“C1_r”(小的蓝色圆圈),很明显圆心和点的质心没有重合。所以移动圆 心 C1_o 到质心 C1_r,这样我们就得到了一个新的窗口。这时又可以找到新 窗口内所有点的质心,大多数情况下还是不重合的,所以重复上面的操作:将 新窗口的中心移动到新的质心。就这样不停的迭代操作直到窗口的中心和其所 包含点的质心重合为止(或者有一点小误差)。按照这样的操作我们的窗口最终 会落在像素值(和)最大的地方。如上图所示“C2”是窗口的最后位址,我们可 以看出来这个窗口中的像素点最多。整个过程如下图所示:
通常情况下我们要使用直方图方向投影得到的图像和目标对象的起始位置。当目标对象的移动会反映到直方图反向投影图中。就这样,meanshift 算法就 把我们的窗口移动到图像中灰度密度最大的区域了。
要在 OpenCV 中使用 Meanshift 算法首先我们要对目标对象进行设置,计算目标对象的直方图,这样在执行 meanshift 算法时我们就可以将目标对 象反向投影到每一帧中去了。另外我们还需要提供窗口的起始位置。在这里我 们值计算 H(Hue)通道的直方图,同样为了避免低亮度造成的影响,我们使用函数 cv2.inRange() 将低亮度的值忽略掉。
获取掩膜将低亮度的值忽略掉
dst=cv2.inRange(src, lowerb, upperb)
对于单通道:
CamShift算法:
retval, window=cv2.meanShift(probImage, window, criteria)
获得矩形顶点
points = cv2.boxPoints(box)
import numpy as np
import cv2 as cv
cap = cv.VideoCapture('slow.flv')
# 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
track_window = (c,r,w,h)
# set up the ROI for tracking
roi = frame[r:r+h, c:c+w]
hsv_roi = cv.cvtColor(roi, cv.COLOR_BGR2HSV)
mask = cv.inRange(hsv_roi, np.array((0., 60.,32.)), np.array((180.,255.,255.)))
roi_hist = cv.calcHist([hsv_roi],[0],mask,[180],[0,180])
cv.normalize(roi_hist,roi_hist,0,255,cv.NORM_MINMAX)
# Setup the termination criteria, either 10 iteration or move by atleast 1 pt
term_crit = ( cv.TERM_CRITERIA_EPS | cv.TERM_CRITERIA_COUNT, 10, 1 )
while(1):
ret ,frame = cap.read()
if ret == True:
hsv = cv.cvtColor(frame, cv.COLOR_BGR2HSV)
dst = cv.calcBackProject([hsv],[0],roi_hist,[0,180],1)
# apply meanshift to get the new location
ret, track_window = cv.meanShift(dst, track_window, term_crit)
# Draw it on image
x,y,w,h = track_window
img2 = cv.rectangle(frame, (x,y), (x+w,y+h), 255,2)
cv.imshow('img2',img2)
k = cv.waitKey(60) & 0xff
if k == 27:
break
else:
cv.imwrite(chr(k)+".jpg",img2)
else:
break
cv.destroyAllWindows()
cap.release()
下面是我使用 meanshift 算法对一个视频前三帧分析的结果:
你认真看上面的结果了吗?这里面还有一个问题。我们的窗口的大小是固定的,而汽车由远及近(在视觉上)是一个逐渐变大的过程,固定的窗口是不 合适的。所以我们需要根据目标的大小和角度来对窗口的大小和角度进行修订。 OpenCVLabs 为我们带来的解决方案(1988 年):一个被叫做 CAMshift 的 算法。 这个算法首先要使用 meanshift,meanshift 找到(并覆盖)目标之后,再去调整窗口的大小,。它还会计算目标对象的最佳外接椭圆的角度,并以此调节窗口角度。然后使用更新后的窗口大小和角度来在原来的位 置继续进行 meanshift。重复这个过程知道达到需要的精度。
与 Meanshift 基本一样,但是返回的结果是一个带旋转角度的矩形(这是我们的结果),以及这个矩形的参数(被用到下一次迭代过程中)。
用到的函数;
获取掩膜
dst=cv2.inRange(src, lowerb, upperb)
对于单通道:
CamShift算法:
retval, window=cv2.CamShift(probImage, window, criteria)
获得矩形顶点
points = cv2.boxPoints(box)
下面是代码:
import numpy as np
import cv2 as cv
cap = cv.VideoCapture('slow.flv')
# 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
track_window = (c,r,w,h)
# set up the ROI for tracking
roi = frame[r:r+h, c:c+w]
hsv_roi = cv.cvtColor(roi, cv.COLOR_BGR2HSV)
mask = cv.inRange(hsv_roi, np.array((0., 60.,32.)), np.array((180.,255.,255.)))
roi_hist = cv.calcHist([hsv_roi],[0],mask,[180],[0,180])
cv.normalize(roi_hist,roi_hist,0,255,cv.NORM_MINMAX)
# Setup the termination criteria, either 10 iteration or move by atleast 1 pt
term_crit = ( cv.TERM_CRITERIA_EPS | cv.TERM_CRITERIA_COUNT, 10, 1 )
while(1):
ret ,frame = cap.read()
if ret == True:
hsv = cv.cvtColor(frame, cv.COLOR_BGR2HSV)
dst = cv.calcBackProject([hsv],[0],roi_hist,[0,180],1)
# apply meanshift to get the new location
ret, track_window = cv.CamShift(dst, track_window, term_crit)
# Draw it on image
pts = cv.boxPoints(ret)
pts = np.int0(pts)
img2 = cv.polylines(frame,[pts],True, 255,2)
cv.imshow('img2',img2)
k = cv.waitKey(60) & 0xff
if k == 27:
break
else:
cv.imwrite(chr(k)+".jpg",img2)
else:
break
cv.destroyAllWindows()
cap.release()
对三帧图像分析的结果如下: