opencv-Python 目标跟踪(一)《Meanshift算法、Camshift算法》

作者:RayChiu_Labloy
版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处


目录

Meanshift算法 

Meanshift算法原理:

追踪一个运动的物体大致流程如下:

视频追踪目标测试:

Camshift算法

 对meanShift的改进:

原理: 

分为三个部分:

1、色彩投影图(反向投影):

2、meanshift

3--camshift


Meanshift算法 

        meanShift,均值漂移,在聚类、图像平滑、分割、跟踪等方面有着广泛的应用,其最初的含义正如其名:偏移的均值向量;但随着理论的发展,meanShift的含义已经发生了很多变化。如今,我们说的meanShift算法,一般是指一个迭代的步骤,即先算出当前点的偏移均值,然后以此为新的起始点,继续移动,直到满足一定的结束条件。

Meanshift算法原理:

         假设你有一堆点集,例如直方图反向投影得到的点集。还有一个小的窗口,这个窗口可能是圆形的,现在要移动这个窗口到点集密度最大的区域当中。

        最开始的窗口是蓝色圆环的区域,命名为C1。蓝色圆环的重音用一个蓝色的矩形标注,命名为C1_o。

        然而,我们发现在这个窗口当中所有点的点集构成的质心在蓝色圆形点处。而且,圆环的型心和质心并不重合。所以,移动蓝色的窗口,使得型心与之前得到的质心重合。在新移动后的圆环的区域当中再次寻找圆环当中所包围点集的质心,然后再次移动,通常情况下,型心和质心是不重合的。不断执行上面的移动过程,直到型心和质心大致重合结束。

        这样,最后圆形的窗口会落到像素分布最大的地方,也就是图中的绿色圈,命名为C2。

        meanshift算法不仅仅限制在二维的图像处理问题当中,同样也可以使用于高维的数据处理。可以通过选取不同的核函数,来改变区域当中偏移向量的权重,最后meanshift算法的过程一定会收敛到某一个位置。

        meanshift算法除了应用在视频追踪当中,在聚类,平滑等等各种涉及到数据以及非监督学习的场合当中均有重要应用,是一个应用广泛的算法。

        假如在二维环境当中,meanshift算法处理的数据是一群离散的二维点集,但是图像是一个矩阵信息,如何在一个视频当中使用meanshift算法来追踪一个运动的物体呢? 

追踪一个运动的物体大致流程如下:

1.首先在图像上使用矩形框或者圆形框选定一个目标区域
2.计算选定好区域的直方图分布。
3.对下一帧图像b同样计算直方图分布。
4.计算图像b当中与选定区域直方图分布最为相似的区域,使用meanshift算法将选定区域沿着最为相似的部分进行移动。(样例当中使用的是直方图反向投影)
5.重复3到4的过程。

视频追踪目标测试:

import cv2
import numpy as np

# 设置初始化的窗口位置
r,h,c,w = 0,200,0,200 # 设置初试窗口位置和大小
track_window = (c,r,w,h)

cap = cv2.VideoCapture(0)
ret, frame= cap.read() 

# 设置追踪的区域
roi = frame[r:r+h, c:c+w]
# roi区域的hsv图像
hsv_roi = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
# 取值hsv值在(0,60,32)到(180,255,255)之间的部分
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)

# 设置终止条件,迭代10次或者至少移动1次
term_crit = ( cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 1 ) 

while(1):
    ret, frame = cap.read()
    frame = cv2.flip(frame,1)
    if ret == True:
        # 计算每一帧的hsv图像
        hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
        # 计算反向投影
        dst = cv2.calcBackProject([hsv],[0],roi_hist,[0,180],1)


        # 调用meanShift算法在dst中寻找目标窗口,找到后返回目标窗口
        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)

    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

cap.release()
cv2.destroyAllWindows()

Camshift算法

 对meanShift的改进:

        如果被追踪的物体迎面过来,由于透视效果,物体会放大,之前设置好的窗口区域大小会不合适, Camshift会调整框的大小。 

原理: 

        camshift利用目标的颜色直方图模型将图像转换为颜色概率分布图,初始化一个搜索窗的大小和位置,并根据上一帧得到的结果自适应调整搜索窗口的位置和大小,从而定位出当前图像中目标的中心位置。

分为三个部分:

1、色彩投影图(反向投影):

(1).RGB颜色空间对光照亮度变化较为敏感,为了减少此变化对跟踪效果的影响,首先将图像从RGB空间转换到HSV空间。

(2).然后对其中的H分量作直方图,在直方图中代表了不同H分量值出现的概率或者像素个数,就是说可以查找出H分量大小为h的概率或者像素个数,即得到了颜色概率查找表。

(3).将图像中每个像素的值用其颜色出现的概率对替换,就得到了颜色概率分布图。这个过程就叫反向投影,颜色概率分布图是一个灰度图像。

2、meanshift

meanshift算法是一种密度函数梯度估计的非参数方法,通过迭代寻优找到概率分布的极值来定位目标。

算法过程为:

(1).在颜色概率分布图中选取搜索窗W

(2).计算零阶距:

计算一阶矩: 

 计算搜索框的质心:

 (3).调整搜索窗大小,宽度为:

长度为1.2s;

(4).移动搜索窗的中心到质心,如果移动距离大于预设的固定阈值,则重复2)3)4),直到搜索窗的中心与质心间的移动距离小于预设的固定阈值,或者循环运算的次数达到某一最大值,停止计算。

3--camshift

        将meanshift算法扩展到连续图像序列,就是camshift算法。它将视频的所有帧做meanshift运算,并将上一帧的结果,即搜索窗的大小和中心,作为下一帧meanshift算法搜索窗的初始值。如此迭代下去,就可以实现对目标的跟踪。

        算法过程为:

(1).初始化搜索窗

(2).计算搜索窗的颜色概率分布(反向投影)

(3).运行meanshift算法,获得搜索窗新的大小和位置。

(4).在下一帧视频图像中用(3)中的值重新初始化搜索窗的大小和位置,再跳转到(2)继续进行。

        camshift能有效解决目标变形和遮挡的问题,对系统资源要求不高,时间复杂度低,在简单背景下能够取得良好的跟踪效果。但当背景较为复杂,或者有许多与目标颜色相似像素干扰的情况下,会导致跟踪失败。因为它单纯的考虑颜色直方图,忽略了目标的空间分布特性,所以这种情况下需加入对跟踪目标的预测算法。

视频追踪测试:

import cv2
import numpy as np

cap = cv2.VideoCapture(0)

# ret判断是否读到图片 
# frame读取到的当前帧的矩阵 
# 返回的是元组类型,所以也可以加括号 
ret, frame = cap.read()

# 设置跟踪框参数 
r, h, c, w = 0, 100, 0, 100 # simply hardcoded the values 
track_window = (c, r, w, h) 

# 从当前帧中框出一个小框 
roi = frame[r:r + h, c:c + w] 
# RGB转为HSV更好处理 
hsv_roi = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)

# inRange函数设置亮度阈值 
# 去除低亮度的像素点的影响 
# eg. mask = cv2.inRange(hsv, lower_red, upper_red)
# 将低于和高于阈值的值设为0 
mask = cv2.inRange(hsv_roi, np.array((0., 60., 32.)), np.array((180., 255., 255.))) 

# 然后得到框中图像的直方图 
# cv2.calcHist(images, channels, mask, histSize, ranges[, hist[, accumulate ]]) 
# mask 即上文的阈值设置 
# histSize表示这个直方图分成多少份(即多少个直方柱) 
# range是表示直方图能表示像素值的范围 
# 返回直方图 
roi_hist = cv2.calcHist([hsv_roi], [0], mask, [180], [0, 180]) 


# 归一化函数CV2.normalize(src[, dst[, alpha[, beta[, norm_type[, dtype[, mask]]]]]]) 
# 返回dst类型 
# 归一化就是要把需要处理的数据经过处理后(通过某种算法)限制在你需要的一定范围内 
# src - 输入数组 
# dst - 与src大小相同的输出数组 
# alpha - 范围值,  以便在范围归一化的情况下归一化到较低范围边界 
# beta - 范围归一化时的上限范围; 它不用于标准规范化 
# normType - 规范化类型 这里的NORM_MINMAX是数组的数值被平移或缩放到一个指定的范围,线性归一化。 
# dtype - 当为负数时,输出数组与src的类型相同;否则,它具有与src相同的通道数;深度=CV_MAT_DEPTH(dtype) 
# mask - 可选的操作掩码。 
cv2.normalize(roi_hist, roi_hist, 0, 255, cv2.NORM_MINMAX) 

# 设置迭代的终止标准,最多十次迭代 
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) 

    # 反向投影函数(特征提取函数) 
    # 反向投影是一种记录给定图像中的像素点如何适应直方图模型像素分布的方式 
    # 反向投影就是首先计算某一特征的直方图模型,然后使用模型去寻找图像中存在的特征 
    # cv2.calcHist(images, channels, mask, histSize, ranges[, hist[, accumulate]])
    # images:待处理的图像,图像格式为uint8或float32 
    # channels:对应图像需要统计的通道,若是灰度图则为0,彩色图像B、G、R对应0、1、2 
    # mask:掩膜图像。如果统计整幅图像就设置为None,否则这里传入设计的掩膜图像。 
    # histSize表示这个直方图分成多少份(即多少个直方柱) 
    # ranges:像素量化范围,通常为0 - 255。 
    dst = cv2.calcBackProject([hsv], [0], roi_hist, [0, 180], 1) 

    # RotatedRect CamShift(InputArray probImage, Rect&window, TermCriteria criteria。 
    # probImage为输入图像直方图的反向投影图, 
    # window为要跟踪目标的初始位置矩形框, 
    # criteria为算法结束条件。 
    # 函数返回一个有方向角度的矩阵。
    ret, track_window = cv2.CamShift(dst, track_window, term_crit) 

    # Draw it on image 
    pts = cv2.boxPoints(ret) 

    # 类型转换int0()用于索引的整数(same as C ssize_t; normally either int32 or int64) 
    pts = np.int0(pts) 

    # 非填充多边形:polylines() 
    # cv2.polylines(img, pts, isClosed, color[, thickness[, lineType[, shift]]]) 
    # img – 要画的图片 
    # pts – 多边形的顶点 
    # isClosed – 是否闭合线段 
    # color – 颜色 
    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() 

【如果对您有帮助,交个朋友给个一键三连吧,您的肯定是我博客高质量维护的动力!!!】 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值