文档下载链接

Mean shift作为一种跟踪算法经常被用到。它是一种无参数密度估计寻找局部极值的迭代逼近算法。

Mean shift直观描述

其中红点为特征点,蓝色为检测区域,黑点为检测区域中心,黑色虚线箭头为中心点到特征点向量,黄色箭头为检测区域内中心点到所有特征点向量和,是一个向量,这里称其为Mean shift向量(漂移向量)。

目标跟踪python算法 目标跟踪经典算法_直方图


经过一次迭代,中心点向最优区域移动,移动量为上一漂移向量。

目标跟踪python算法 目标跟踪经典算法_目标跟踪python算法_02


目标跟踪python算法 目标跟踪经典算法_权重_03


经过多次迭代,最终可以获取最优相似区域,即漂移向量值小于某阈值。

目标跟踪python算法 目标跟踪经典算法_直方图_04


Mean shift算法原理

Mean shift算法基本思想

目标跟踪python算法 目标跟踪经典算法_目标跟踪python算法_05


Mean shift算法模型实例(这里直接以视频目标跟踪为例)

目标模型:

目标跟踪python算法 目标跟踪经典算法_i++_06


候选模型:

首先候选模型与目标模型具有相同窗口大小和相同的核函数,同时用同样的特征来表示。

目标跟踪python算法 目标跟踪经典算法_权重_07


相似性判断:

目标跟踪python算法 目标跟踪经典算法_i++_08


目标定位:

目标跟踪python算法 目标跟踪经典算法_i++_09


目标跟踪python算法 目标跟踪经典算法_直方图_10


Mean shift算法步骤(以二维图像跟踪为例,其特征为颜色直方图)

创建目标模型:

目标跟踪python算法 目标跟踪经典算法_权重_11


创建候选模型:

目标跟踪python算法 目标跟踪经典算法_权重_12


目标跟踪python算法 目标跟踪经典算法_i++_13

代码:

#include <iostream>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
#define  u_char unsigned char
#define  DIST 0.5       // 偏移向量阈值
#define  NUM 20         // 迭代次数阈值
//全局变量
bool leftButtonDownFlag=false;  //左键单击后的标志位
bool leftButtonUpFlag=false;    //左键单击后松开的标志位
Point Point_s;                  //矩形框起点
Point Point_e;                  //矩形框鼠标左键弹起来的终点
Point processPoint;             //矩形框移动的终点
bool  tracking = false;
double *m_hist, *hist, *sim;    // 模板直方图、当前直方图、直方图相似度
Mat w, hist_ind;                // 目标模板权值矩阵,候选模板直方图索引
double C = 0.0;                 // 权重和
void onMouse( int event, int x, int y, int flags, void *param )
{
    if(event==CV_EVENT_LBUTTONDOWN)
    {
        tracking = false;
        leftButtonDownFlag = true; //标志位
        leftButtonUpFlag = false;
        processPoint=Point(x,y);  //设置左键按下点的矩形起点
        Point_s=processPoint;
    }
    else if(event == CV_EVENT_MOUSEMOVE && leftButtonDownFlag)
    {
        processPoint=Point(x,y);
    }
    else if(event==CV_EVENT_LBUTTONUP && leftButtonDownFlag)
    {
        leftButtonDownFlag=false;
        processPoint=Point(x,y);
        Point_e=processPoint;
        leftButtonUpFlag = true;
        tracking = true;
    }
}
void init_target(Mat w, Mat mould)
{
    double h, dist;
    int q_r, q_g, q_b, q_temp;
    h = pow(((double)mould.cols)/2,2) + pow(((double)mould.rows)/2,2);                  //带宽
    //初始化权值矩阵和目标直方图
    for (int i=0;i<4096;i++)
    {
        m_hist[i] = 0.0;
    }
    for (int i = 0;i < mould.rows; i++)
    {
        for (int j = 0;j < mould.cols; j++)
        {
            dist = pow(i - (double)mould.rows/2,2) + pow(j - (double)mould.cols/2,2);
            w.at<double>(i, j) = 1 - dist / h;          // 目标点权重
            C += w.at<double>(i, j);                    // 权重和,用于归一化
        }
    }
    //计算目标权值直方
   for (int i = 0;i < mould.rows; i++)
   {
       for (int j = 0;j < mould.cols; j++)
       {
           //rgb颜色空间量化为16*16*16 bins
           q_r = mould.at<Vec3b>(i, j)[2]/16;
           q_g = mould.at<Vec3b>(i, j)[1]/16;
           q_b = mould.at<Vec3b>(i, j)[0]/16;
           q_temp = q_r * 256 + q_g * 16 + q_b;
           m_hist[q_temp] =  m_hist[q_temp] + w.at<double>(i, j);          // 颜色权重直方图
       }
   }
   //归一化直方图
   for (int i=0;i<4096;i++)
   {
       m_hist[i] = m_hist[i] / C;
   }
}
void MeanShift_Tracking(Mat img, Rect &rect)
{
    int num = 0;
    double sum_sim = 0, y_temp = 0, x_temp = 0, y = 2.0, x = 2.0;
    int q_r, q_g, q_b;
    while ((pow(x,2) + pow(y,2) > DIST)&& (num < NUM))       // 当移动距离大于0.5 或 循环NUM次结束循环
    {
        num++;
        hist_ind = Mat::zeros(rect.height, rect.width, CV_32SC1);       // 候选窗口直方图索引初始化
        for (int i = 0;i<4096;i++)
        {
            sim[i] = 0.0;
            hist[i] = 0.0;
        }
        for (int i = rect.y;i < rect.y + rect.height;i++)
        {
            for (int j = rect.x;j < rect.x + rect.width;j++)
            {
                //rgb颜色空间量化为16*16*16 bins
                q_r = img.at<Vec3b>(i, j)[2]/16;
                q_g = img.at<Vec3b>(i, j)[1]/16;
                q_b = img.at<Vec3b>(i, j)[0]/16;
                hist_ind.at<int>(i - rect.y, j - rect.x) = q_r * 256 + q_g * 16 + q_b;          //获取点对应直方图索引
                hist[hist_ind.at<int>(i - rect.y, j - rect.x)] =  hist[hist_ind.at<int>(i - rect.y, j - rect.x)] + w.at<double>(i - rect.y, j - rect.x);    // 获取候选窗口直方图
            }
        }
        //归一化直方图
        for (int i=0;i<4096;i++)
        {
            hist[i] = hist[i] / C;
        }
        for (int i = 0;i < 4096;i++)
        {
            if (hist[i] != 0)
            {
                sim[i] = sqrt(m_hist[i]/hist[i]);         // 计算特征相似度
            }else
            {
                sim[i] = 0;
            }
        }
        sum_sim = 0.0;
        y_temp = 0.0;
        x_temp = 0.0;
        // Mean shift向量
        for (int i = 0;i < rect.height; i++)
        {
            for (int j = 0;j < rect.width; j++)
            {
                sum_sim = sum_sim + sim[hist_ind.at<int>(i, j)];
                y_temp = y_temp + sim[hist_ind.at<int>(i, j)] * (i - rect.height/2);       // 每个点相对中心点的偏移向量
                x_temp = x_temp + sim[hist_ind.at<int>(i, j)] * (j - rect.width/2);
            }
        }
        y = y_temp / sum_sim;
        x = x_temp / sum_sim;
        //中心点偏移两,即位置更新
        rect.x += x;
        rect.y += y;
    }
    circle(img, Point(rect.x + rect.width/2, rect.y + rect.height/2), 5, Scalar(0, 0, 255), -1);        // 绘制更新点
    rectangle(img, rect, Scalar(0, 255, 0), 3, 8, 0);                                                   //显示跟踪结果,框出
}
int main()
{
    Mat img_mould, frame, mould;
    Rect rect;
    m_hist = (double *)malloc(sizeof(double)*16*16*16);         // 目标直方图
    hist = (double *)malloc(sizeof(double)*4096);               // 候选直方图
    sim = (double *)malloc(sizeof(double)*4096);                // 直方图相似度
    //打开摄像头或者特定视频
    VideoCapture cap;
    cap.open(0);//或cap.open("文件名")
    //读入视频是否为空
    if (!cap.isOpened())
    {
        return -1;
    }
    namedWindow("输出视频", 1);
    setMouseCallback("输出视频", onMouse, 0);//鼠标回调函数,响应鼠标以选择跟踪区域
    while (1)
    {
        cap >> frame;
        if (frame.empty())
        {
            return -1;
        }
        if(tracking && leftButtonUpFlag)
        {
            leftButtonUpFlag = false;
            rect.x = Point_s.x;
            rect.y = Point_s.y;
            rect.width = Point_e.x - Point_s.x;
            rect.height = Point_e.y - Point_s.y;
            img_mould = frame.clone();
            mould = Mat(img_mould, rect);//目标图像
            //目标初始化
            w = Mat::zeros(rect.height, rect.width, CV_64FC1);      // 初始化目标模板权重
            init_target(w, mould);                                  // 获取目标图像直方图
        }
        if(leftButtonDownFlag)                                      // 绘制截取目标窗口
        {
            rect.x = Point_s.x;
            rect.y = Point_s.y;
            rect.width = processPoint.x - Point_s.x;
            rect.height = processPoint.y - Point_s.y;
            rectangle(frame, rect, Scalar(0, 255, 0), 3, 8, 0);
        }
        if(tracking)
        {
            MeanShift_Tracking(frame, rect);                        // 目标跟踪
        }
        imshow("输出视频", frame);
        waitKey(10);
    }
    return 0;
}

Mean shift优缺点:
优点:
1、 算法复杂度小
2、 是无参数算法,易于与其他算法集成
3、 采用加权直方图建模,对目标小角度旋转、轻微变形和部分遮挡不敏感
缺点:
1、 搜索窗的核函数带宽保持不变
2、 缺乏必要的模板更新算法
3、 目标的运动不能过快
缺点解决方案
1、对应带宽窗口不变可以用基于边界力计算带宽变化的方法、camshift算法来解决
2、模板更新和用实时模板更新、双系数模板更新来解决
3、目标移动速度可以用卡尔曼滤波来改善