OpenCV-Python教程:43.meanshift和camshift

meanshift

meanshift 背后的直觉很简单,设想你有一个点集。(可以是一个像素分布,像直方图向后投影)。你得到一个小窗口(可能是个圆)并且你得移动窗口到最大像素强度的地方(或者最多点的数量)

初始化的窗口显示成蓝色圆,名字叫“C1”,它的圆心是蓝方块“C1_o”。但是如果你找窗口里的点的质心,那就在点"C1_r"(用蓝色小圆标记),这是窗口的实际质心,所以它们不匹配。所以移动你的窗口让新窗口的圆圈和前一个的质心匹配。再次,找到新的质心,很可能是还是不匹配,然后再次移动,不断迭代知道窗口的中心和质心落在同一个位置(或者误差可接受)。最后你获得的窗口有最多的像素分布,用绿色的圆标记。叫C2,在图里可以看到他有最多的点。下图展示了整个过程。

所以我们一般传入直方图向后投影图像和初始化的目标位置。当目标移动时,移动被反应在直方图向后投影图像里,作为结果,均值平移算法吧我们的窗口移到有最大亮度的位置。

OpenCV里的均值平移

要在OpenCV里使用均值平移,首先我们需要设置目标,找到他的直方图,这样我们可以为了计算均值平移向后投影目标到每一帧上。我们也需要提供窗口的初始位置。对于直方图,只考虑色调。要避免低光线带来的错误值,低光线的值使用cv2.inRange()函数来丢弃掉。

import numpy as np
import cv2

cap = cv2.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 =  cv2.cvtColor(frame, 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.meanShift(dst, track_window, term_crit)

        # Draw it on image
        x,y,w,h = track_window
        img2 = cv2.rectangle(frame, (x,y), (x+w,y+h), 255,2)
        cv2.imshow('img2',img2)

        k = cv2.waitKey(60) & 0xff
        if k == 27:
            break
        else:
            cv2.imwrite(chr(k)+".jpg",img2)

        else:
            break

cv2.destroyAllWindows()
cap.release()

我使用的视频里的三帧如下:

camshift

你仔细看之前的结果没?有一个问题,我们的窗口总是同样的大小,当汽车从远处走得离摄像头越来越近,这并不好。我们需要让窗口的大小和目标的大小以及旋转相适应。这个解决方案也是来自OpenCV实验室,叫做CAMshift(连续的适应性均值平移)

它首先应用均值平移,当均值平移覆盖后,它更新窗口的大小,

它也计算最适合的椭圆的方向,然后它在使用新的大小的窗口在之前的位置开始进行均值平移,过程不断继续直到到达指定的准确率。

OpenCV里的camshift

和meanshift几乎一样,但是它返回旋转的举行和盒子参数

import numpy as np
import cv2

cap = cv2.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 =  cv2.cvtColor(frame, 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
        if k == 27:
            break
        else:
            cv2.imwrite(chr(k)+".jpg",img2)

    else:
        break

cv2.destroyAllWindows()
cap.release()


END

推荐阅读更多精彩内容