基于MeanShift的目标跟踪算法及实现(自编函数利用Python实现)

 参考博客

cc(9条消息) 基于MeanShift的目标跟踪算法及实现_taotao1233的博客-CSDN博客_meanshift目标跟踪算法https://blog.csdn.net/jinshengtao/article/details/30258833https://blog.csdn.net/jinshengtao/article/details/30258833(9条消息) MeanShift(均值漂移)MATLAB程序详解_战死为止的博客-CSDN博客_均值漂移matlab代码https://blog.csdn.net/qq_34122861/article/details/89556503       第一篇博客对meanshift目标追踪介绍的比较详细,但仍有不足之处,我参考了这篇论文(基于均值移位的运动目标跟踪研究 - 中国知网),对目标追踪原理做了一点补充,如果你看得懂英文论文,也可以看这篇(IEEE Xplore Full-Text PDF:)。下面的原理补充的地方主要在第四部分“Meanshift迭代过程”,并且前面省略了meanshift基本原理介绍,可参考第一篇博客。最后面有利用python实现的代码。

原理

1、目标模型描述

        通过人工标注的方式在初始帧确定包含跟踪目标的区域。假设其中有n个像素用{zi}-1...n表示其位置,对选中的区域的灰度颜色空间均匀划分,得到由m个相等的区间构成的灰度直方图。目标模型的qu概率密度(u=1,...m)可表示为:

基于MeanShift的目标跟踪算法及实现(自编函数利用Python实现)_第1张图片                                               (1.1)

        其中,zi*表示以目标中心为原点的归一化像素位置,为目标中心坐标。K是核函数,常选用Epanechikov核函数,为

基于MeanShift的目标跟踪算法及实现(自编函数利用Python实现)_第2张图片                                             (1.2)

        b()表示处像素属于哪个直方图区间,u为直方图的颜色索引。δ[b()-u]函数的作用是判断目标区域中像素处的灰度值是否属于直方图中第u个单元,等于为1,否则为0。C是归一化系数。

2、候选模型描述

      在第t帧时,根据第t-1帧的目标中心位置作为搜索窗口的中心,得到候选目标的中心位置坐标f,计算当前帧的候选目标区域直方图。该区域的像素用

{}表示,则候选模型的概率密度为:

                                                        (2.1)

        h为核函数窗口大小,其他参数同目标模型描述。

3、相似性度量

        相似性函数用于描述目标模型和候选目标之间的相似程度。本文采用

Bhattacharyya系数作为相似性函数,其定义为:。相似函

数越大则两个模型越相似。将前一帧中目标的中心位置f0作为搜索窗口的中心,寻找使得相似函数最大的候选区域,即是在本帧中目标的位置。

4、Meanshift迭代过程

        均值漂移的迭代过程,也就是目标位置搜索的过程。为使相似函数最大,对上式进行泰勒展开,取前两项得到Bhattacharyya系数的近似表达:

                                     (4.1)

                                                        (4.2)

        式(4.1)中只有第二项与f有关,相似性最大也就是第二项最大,故第二项可看作候选目标的中心位置坐标f的密度估计,对其求梯度得(为方便计算,式子前的常量均看作C):

        令,得:

 

 基于MeanShift的目标跟踪算法及实现(自编函数利用Python实现)_第3张图片

        式(4.4)括号中的两项都表示了一定的意义。前一项与用核函数g估计的密度

成正比。后一项称为均值移位向量:

基于MeanShift的目标跟踪算法及实现(自编函数利用Python实现)_第4张图片

        它是用核函数g加权的各样本点的均值与核(窗)中心f的差。因此,式(4.4)变为

        式(4.8)指明用核函数g计算得出的点f处的均值移位向量与用核函数K估计出的归一化密度梯度成正比。因此,可用样本的均值移位向量进行归一化密度梯度估计,均值移位向量总是指向密度增长最快的方向。均值移位向量依据局部梯度估计进行调整,它指向了估计密度的“定点”。反复地进行如下两步,就是均值移位过程:

①计算均值移位向量

②用移动窗口

        由于,所以g(x)是常量,可以约掉,最终得到

        总结来说,Meanshift方法就是从f起向两个模型相比相似性最大的方向不断移动,直到最后两次移动距离小于阈值,即找到当前帧的目标位置,并以此作为下一帧的起始搜索窗口中心,如此重复。

5、总体流程

      首先是对目标跟踪的初始化,本文通过手动选取的方式用鼠标选定跟踪目标区域。然后计算核函数加权下的搜索窗口的直方图分布,用同样的方法计算第N帧对应窗口的直方图分布,以两个模板分布的相似性最大为原则,使搜索窗口沿密度增加最大的方向移动,得到目标的真实位置。

MeanShift 算法跟踪步骤如下

①计算目标模板的概率密度{}u=1...m;

②用初始化当前帧的目标位置,计算候选目标模板}u=1...m

③根据式(4.2)计算当前窗口内各点的权重值;

④计算目标的新位置

代码

         本人写程序和open-cv都是新人,看了上面的文章后发现没有python版本的,就按照一摸一样的思路仿写了python的代码,但是是打开笔记本自带摄像头进行MeanShift目标跟踪。希望给萌新一些参考,由于我也是新手,这是我写的第一个比较长的代码,所以会有很多冗余甚至错误,欢迎大家指正。如果看不懂可以参考上面的博客。

import math
import numpy as np
import cv2


def get_tr(img):
    # 定义需要返回的参数
    mouse_params = {'x': None, 'width': None, 'height': None,
                    'y': None, 'temp': None}
    cv2.namedWindow('image')
    # 鼠标框选操作函数
    cv2.setMouseCallback('image', on_mouse, mouse_params)
    cv2.imshow('image', img)
    cv2.waitKey(0)
    return [mouse_params['x'], mouse_params['y'], mouse_params['width'],
            mouse_params['height']], mouse_params['temp']


def on_mouse(event, x, y, flags, param):
    global img, point1
    img2 = img.copy()
    if event == cv2.EVENT_LBUTTONDOWN:  # 左键点击
        point1 = (x, y)
        cv2.circle(img2, point1, 10, (0, 255, 0), 5)
        cv2.imshow('image', img2)
    elif event == cv2.EVENT_MOUSEMOVE and (flags & cv2.EVENT_FLAG_LBUTTON):  # 按住左键拖曳
        cv2.rectangle(img2, point1, (x, y), (255, 0, 0), 5)
        cv2.imshow('image', img2)
    elif event == cv2.EVENT_LBUTTONUP:  # 左键释放
        point2 = (x, y)
        cv2.rectangle(img2, point1, point2, (0, 0, 255), 5)
        cv2.imshow('image', img2)
        # 返回框选矩形左上角点的坐标、矩形宽度、高度以及矩形包含的图像
        param['x'] = min(point1[0], point2[0])
        param['y'] = min(point1[1], point2[1])
        param['width'] = abs(point1[0] - point2[0])
        param['height'] = abs(point1[1] - point2[1])
        param['temp'] = img[param['y']:param['y']+param['height'],
            param['x']:param['x']+param['width']]


def main():
    global img
    cap = cv2.VideoCapture(0)
    # 获取视频第一帧
    ret, frame = cap.read()
    img = frame
    # 框选目标并返回相应信息:rect为四个信息,temp为框选出来的图像
    rect, temp = get_tr(img)
    (a, b, c) = temp.shape
    y = [a/2, b/2]

    # 计算目标图像的权值矩阵
    m_wei = np.zeros((a,b))
    for i in range(a):
        for j in range(b):
            z = (i-y[0])**2 + (j-y[1])**2
            m_wei[i, j] = 1 - z/(y[0]**2 + y[1]**2)

    # 计算目标权值直方图
    C = 1/sum(sum(m_wei))
    hist1 = np.zeros(16**3)
    for i in range(a):
        for j in range(b):
            q_b = math.floor(float(temp[i, j, 0]) / 16)
            q_g = math.floor(float(temp[i, j, 1]) / 16)
            q_r = math.floor(float(temp[i, j, 2]) / 16)
            q_temp1 = q_r*256 + q_g*16 + q_b
            hist1[int(q_temp1)] = hist1[int(q_temp1)] + m_wei[i, j]
    hist1 = hist1*C

    # 接着读取视频并进行目标跟踪
    while(1):
        ret, frame = cap.read()

        if ret == True:
            Img = frame
            num = 0
            Y = [1, 1]

            # mean shift迭代
            while (np.sqrt(Y[0]**2+Y[1]**2) > 0.5) & (num < 20):
                num = num+1

                # 计算候选区域直方图
                temp2 = Img[int(rect[1]):int(rect[1]+rect[3]), int(rect[0]):int(rect[0]+rect[2])]
                hist2 = np.zeros(16**3)
                q_temp2 = np.zeros((a, b))
                for i in range(a):
                    for j in range(b):
                        q_b = math.floor(float(temp2[i, j, 0]) / 16)
                        q_g = math.floor(float(temp2[i, j, 1]) / 16)
                        q_r = math.floor(float(temp2[i, j, 2]) / 16)
                        q_temp2[i, j] = q_r * 256 + q_g * 16 + q_b
                        hist2[int(q_temp2[i, j])] = hist2[int(q_temp2[i, j])] + m_wei[i, j]
                hist2 = hist2*C

                w = np.zeros(16**3)
                for i in range(16**3):
                    if hist2[i] != 0:
                        w[i] = math.sqrt(hist1[i]/hist2[i])
                    else:
                        w[i] = 0

                sum_w = 0
                sum_xw = [0, 0]
                for i in range(a):
                    for j in range(b):
                        sum_w = sum_w + w[int(q_temp2[i, j])]
                        sum_xw = sum_xw + w[int(q_temp2[i, j])]*np.array([i-y[0], j - y[1]])
                Y = sum_xw/sum_w

                # 位置更新
                rect[0] = rect[0] + Y[1]
                rect[1] = rect[1] + Y[0]

            v0 = int(rect[0])
            v1 = int(rect[1])
            v2 = int(rect[2])
            v3 = int(rect[3])
            pt1 = (v0, v1)
            pt2 = (v0+v2, v1+v3)

            # 画矩形
            IMG = cv2.rectangle(Img, pt1, pt2, (0, 0, 255), 2)
            cv2.imshow('IMG', IMG)
            k = cv2.waitKey(60) & 0xff
            if k == 27:
                break
        else:
            break


if __name__ == '__main__':
    main()

结果

你可能感兴趣的:(目标跟踪,python,算法,pycharm,机器学习)