【目标跟踪】|Meansift 算法原理及对应代码解释 matlab C

背景原理

MeanShift算法属于核密度估计法,它不需要任何先验知识而完全依靠特征空间中样本点的计算其密度函数值。

无参密度估计方法:不事先规定概率密度函数的结构形式,在某一连续点处的密度函数值 由该点邻域中的若干样本点估计 得出。常用的无参密度估计方法有:直方图法、最近邻域法和核密度估计法。

核密度估计法的原理在直方图法基础上,加了一个用于平滑数据的核函数。

MeanShift的基本思想及物理含义:

在一定范围内的所有采样点相对于基准点的“合运动方向”, 即为meanshift向量的方向
注意,各个分运动需要根据样本点距离基准点的距离,用核函数进行加权。

在这里插入图片描述
比较学术描述:
在这里插入图片描述
在这里插入图片描述
ref
https://blog.csdn.net/jinshengtao/article/details/30258833

核函数也叫窗口函数,在核估计中起到平滑的作用。常用的核函数有:Uniform,Epannechnikov,Gaussian等。
在这里插入图片描述

核密度估计

直方图到核密度估计

需要观察这些样本的分布情况,往往我们会采用直方图的方法来进行直观的展现。
直方图就是密度函数。
绘制直方图时,需要确定bins,如果bins不同,那么最后的直方图会产生很大的差别。如下面的两直方图,右边比左边的直方图多划分了bins,导致最后的结果有很大的差别,左边时双峰的,右边时单峰的。
在这里插入图片描述
同时,直方图展示的分布曲线并不平滑,即在一个bin中的样本具有相等的概率密度

概率密度函数不连续,这同样存在很大的问题。如果我们将这些不连续的区间连续起来,那么这很大程度上便能符合我们的要求,其中一个思想就是对于样本中的某一点的概率密度,如果能把邻域的信息利用起来,那么最后的概率密度就会很大程度上改善不连续的问题

密度函数就是分布函数的一阶导数。通过估计分布函数的一阶导数来估计密度函数呢?一个最简单而有效的估计分布函数的方法是所谓的「经验分布函数(empirical distribution function)」:

在这里插入图片描述

采用直方图的思想。选一个x附近的小区间,数一下在这个区间里面的点的个数,除以总个数,作为X=x处的密度函数值

根据分布函数概念,也可以写作:
在这里插入图片描述
把分布函数用上面的经验分布函数替代,那么上式分子上就是落在[x-h,x+h]区间的点的个数。我们可以把f(x)的估计写成:
在这里插入图片描述

其中 h叫做「窗宽(bandwidth)」
如果记在这里插入图片描述,K就是核函数。

那么估计式可以写为:
在这里插入图片描述
密度函数的积分应该等于1:在这里插入图片描述

基于此,核函数应该有以下特性
(非负、对称、积分为1,符合概率密度性质,并且均值为0)

如下:
在这里插入图片描述

二、基于MeanShift的目标跟踪算法

基于均值漂移的目标跟踪算法通过分别计算目标区域和候选区域内 像素的特征值概率得到关于目标模型和候选模型的描述,
然后利用相似函数 度量 初始帧目标模型和当前帧的候选模版的相似性,选择使相似函数最大的候选模型
得到关于目标模型的Meanshift向量,这个向量正是目标由初始位置向正确位置移动的向量。

由于均值漂移算法的快速收敛性,通过不断迭代计算Meanshift向量,算法最终将收敛到目标的真实位置,达到跟踪的目的。
在这里插入图片描述

2.1 目标模型和候选模型的描述(计算区域内像素的特征值概率)

在这里插入图片描述

其中表述为一维图像,在编程中需要对二维图像转换。

  • zi( 1,2,…,n)对应代码中 i,j,对n累加,即对整个图像像素(i,j)的累加
  • b(zi)为该像素对应的颜色特征值,u 为对应遍历整个颜色特征的序号
整个计算过程分为三步

2.1.1 核函数计算

即1-目标框中每个像素到中心点的距离的平方比上中心点平方和
在这里插入图片描述在这里插入图片描述

 % 核函数
for i=1:a  
    for j=1:b   %目标区域内进行计算
        dist=(i-y(1))^2+(j-y(2))^2;  %距离目标中心越远,距离越大
        m_wei(i,j)=1-dist/h; %epanechnikov profile  
    end  
end  
C=1/sum(sum(m_wei));%归一化系数  

2.1.2 目标区域内每个像素点的颜色特征计算

Mean Shift 最初的方式是采用的颜色直方图的建模方式,有基于RGB模型和基于HSV模型两种方式。基于RGB模型的彩色直方图反映的是图像中每一种(R,G,B)组合出现的次数,因为有R、G、B三种色彩,所以这样的彩色直方图是三维的,彩色直方图的横坐标被分成了256份,所以一共有256256256种色彩。实际操作中为了减小计算量,把256个小区间划分为16个大的区间(划分的bin越多越精确),然后用一个颜色特征值代替三维的特征向量(d=256R+16G+B),将三维的彩色直方图降维成一维的直方图(另外一种降维的方法是将RGB模型转换为HSV模型,只使用其中的H分量,即色调分量)。

for i=1:a  
    for j=1:b  
        %rgb颜色空间量化为16*16*16 bins  
        q_r=fix(double(temp(i,j,1))/16);  %fix为趋近0,向下取整函数   红,将256分成1~16个去间,属于1~16哪个区间bin?
        q_g=fix(double(temp(i,j,2))/16);  %绿
        q_b=fix(double(temp(i,j,3))/16);  %蓝色通道
        q_temp=q_r*256+q_g*16+q_b;            %设置每个像素点红色、绿色、
        蓝色分量所占比重  256 16 1
        color_q(i,j)=q_temp

color_q(i,j)范围属于0-4096

2.1.3 目标区域概率密度计算

目标区域概率密度 表示为hist1, 为 0-4096的种颜色特征的统计值
即 累加4096种颜色特征在各个像素出现的频率。在这里插入图片描述

对应公式,对整个像素区域的颜色特征值进行累加

for i=1:a  
    for j=1:b  
   
        hist1(color_q(i,j)+1) = hist1(color_q(i,j)+1)+m_wei(i,j);    %遍历目标区域每个像素点,计算直方图统计,得到每种颜色特征取值分布,对出现的该种分布累加上该像素点对应的权重  
        
    end  
end  
hist1=hist1*C;  

2.1.4 候选区域特征计算相同

按照上一帧的位置计算
在这里插入图片描述
得到候选区域的特征分布 p
ps
在这里插入图片描述

2.2 相似性度量

在这里插入图片描述

        for i=1:4096  
            if(hist2(i)~=0) %不等于  
                w(i)=sqrt(hist1(i)/hist2(i));  %每个灰度值的相似度度量 向量
            else  
                w(i)=0;  
            end  
        end       

2.3 偏移位置更新

在这里插入图片描述

        for i=1:a;  
            for j=1:b  
                sum_w=sum_w+w(uint32(q_temp1(i,j))+1);  %对全像素的相似度进行累加
                xw=xw+w(uint32(q_temp1(i,j))+1)*[i-y(1)-0.5,j-y(2)-0.5];  %计算移动量,相似度乘以该像素位置-目标位置-0.5
            end  
        end  
        Y=xw/sum_w;  
        %中心点位置更新  
        rect(1)=rect(1)+Y(2);  
        rect(2)=rect(2)+Y(1);  

2.4 迭代

在这里插入图片描述

    %%%%%%%mean shift迭代  
    while((Y(1)^2+Y(2)^2>0.5)&num<20)   %迭代条件  
        num=num+1;  
        temp1=imcrop(Im,rect);  %以上一帧位置,进行裁剪候选区域,
		%。。。。
	end

因此meansift只能在固定大小跟踪,且不能应对快速目标移出。

3 汇总code

3.2 matlab

function [] = select()  
close all;  
clear all;  
%%%%%%%%%%%%%%%%%%根据一幅目标全可见的图像圈定跟踪目标%%%%%%%%%%%%%%%%%%%%%%%  
I=imread('img/4.jpg');  
figure(1);  
imshow(I);  
  
  
[temp,rect]=imcrop(I);  %rect 左上角定点横纵坐标+长宽
[a,b,c]=size(temp);         %a:row,b:col  
  
  
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%计算目标图像的权值矩阵%%%%%%%%%%%%%%%%%%%%%%%  
y(1)=a/2;  
y(2)=b/2;  
tic_x=rect(1)+rect(3)/2;  
tic_y=rect(2)+rect(4)/2;  %在图像中 目标中心坐标
m_wei=zeros(a,b);%权值矩阵   跟目标区域大小相同
h=y(1)^2+y(2)^2 ;%带宽  四分之一个目标区域大小的窗口
  
 % 核函数
for i=1:a  
    for j=1:b   %目标区域内进行计算
        dist=(i-y(1))^2+(j-y(2))^2;  %目标区域内进行计算,距离目标中心越远,距离越大
        m_wei(i,j)=1-dist/h; %epanechnikov profile  
    end  
end  
C=1/sum(sum(m_wei));%归一化系数  
  
  
%计算目标权值直方图qu  目标模型 
%hist1=C*wei_hist(temp,m_wei,a,b);%target model  
hist1=zeros(1,4096); %颜色空间量化为16*16*16 bins(256*256*256降维)
for i=1:a  
    for j=1:b  
        %rgb颜色空间量化为16*16*16 bins  
        q_r=fix(double(temp(i,j,1))/16);  %fix为趋近0,向下取整函数   红,将256分成1~16个去间,属于1~16哪个区间bin?
        q_g=fix(double(temp(i,j,2))/16);  %绿
        q_b=fix(double(temp(i,j,3))/16);  %蓝色通道
        q_temp=q_r*256+q_g*16+q_b;            %设置每个像素点红色、绿色、蓝色分量所占比重  256 16 1
        hist1(q_temp+1)= hist1(q_temp+1)+m_wei(i,j);    %遍历目标区域每个像素点,计算直方图统计,得到每种颜色特征取值分布,对出现的该种分布累加上该像素点对应的权重  
        
    end  
end  
hist1=hist1*C;  
rect(3)=ceil(rect(3));  
rect(4)=ceil(rect(4));  %向上取整
  
  
  
  
%%%%%%%%%%%%%%%%%%%%%%%%%读取序列图像  
myfile=dir('H:\code\1doctor_code\matlab_code\tracking\img\*.jpg');  
lengthfile=length(myfile);  
  
  
for l=1:lengthfile  
    Im=imread(strcat('H:\code\1doctor_code\matlab_code\tracking\img\',myfile(l).name));  
    num=0;  
    Y=[2,2];  
      
      
    %%%%%%%mean shift迭代  
    while((Y(1)^2+Y(2)^2>0.5)&num<20)   %迭代条件  
        num=num+1;  
        temp1=imcrop(Im,rect);  %以上一帧位置,进行裁剪候选区域,
        %计算侯选区域直方图(概率密度)  
        %hist2=C*wei_hist(temp1,m_wei,a,b);%target candidates pu  
        hist2=zeros(1,4096);  
        for i=1:a  
            for j=1:b  
                q_r=fix(double(temp1(i,j,1))/16);  
                q_g=fix(double(temp1(i,j,2))/16);  
                q_b=fix(double(temp1(i,j,3))/16);  
                q_temp1(i,j)=q_r*256+q_g*16+q_b;  
                hist2(q_temp1(i,j)+1)= hist2(q_temp1(i,j)+1)+m_wei(i,j);  
            end  
        end  
        hist2=hist2*C;  
        figure(2);  
        subplot(1,2,1);  
        plot(hist2);  
        hold on;  
          
        w=zeros(1,4096);  
        for i=1:4096  
            if(hist2(i)~=0) %不等于  
                w(i)=sqrt(hist1(i)/hist2(i));  %每个灰度值的相似度度量 向量
            else  
                w(i)=0;  
            end  
        end       
        %变量初始化  
        sum_w=0;  
        xw=[0,0];  
        for i=1:a;  
            for j=1:b  
                sum_w=sum_w+w(uint32(q_temp1(i,j))+1);  %对全像素的相似度进行累加
                xw=xw+w(uint32(q_temp1(i,j))+1)*[i-y(1)-0.5,j-y(2)-0.5];  %计算移动量,相似度乘以该像素位置-目标位置-0.5
            end  
        end  
        Y=xw/sum_w;  
        %中心点位置更新  
        rect(1)=rect(1)+Y(2);  
        rect(2)=rect(2)+Y(1);  
    end  
      
      
    %%%跟踪轨迹矩阵%%%  
    tic_x=[tic_x;rect(1)+rect(3)/2];  
    tic_y=[tic_y;rect(2)+rect(4)/2];  
      
    v1=rect(1);  
    v2=rect(2);  
    v3=rect(3);  
    v4=rect(4);  
    %%%显示跟踪结果%%%  
    subplot(1,2,2);  
    imshow(uint8(Im));  
    title('目标跟踪结果及其运动轨迹');  
    hold on;  
    plot([v1,v1+v3],[v2,v2],[v1,v1],[v2,v2+v4],[v1,v1+v3],[v2+v4,v2+v4],[v1+v3,v1+v3],[v2,v2+v4],'LineWidth',2,'Color','r');  
    plot(tic_x,tic_y,'LineWidth',2,'Color','b');  
      
      
end   

3.2 C code

#include “stdafx.h”  
#include “cv.h”  
#include “highgui.h”  
#define  u_char unsigned char  
#define  DIST 0.5  
#define  NUM 20  
  
//全局变量  
bool pause = false;  
bool is_tracking = false;  
CvRect drawing_box;  
IplImage *current;  
double *hist1, *hist2;  
double *m_wei;                                                                  //权值矩阵  
double C = 0.0;                                                                //归一化系数  
  
void init_target(double *hist1, double *m_wei, IplImage *current)  
{  
    IplImage *pic_hist = 0;  
    int t_h, t_w, t_x, t_y;  
    double h, dist;  
    int i, j;  
    int q_r, q_g, q_b, q_temp;  
      
    t_h = drawing_box.height;  
    t_w = drawing_box.width;  
    t_x = drawing_box.x;  
    t_y = drawing_box.y;  
  
    h = pow(((double)t_w)/2,2) + pow(((double)t_h)/2,2);            //带宽  
    pic_hist = cvCreateImage(cvSize(300,200),IPL_DEPTH_8U,3);     //生成直方图图像  
  
    //初始化权值矩阵和目标直方图  
    for (i = 0;i < t_w*t_h;i++)  
    {  
        m_wei[i] = 0.0;  
    }  
  
    for (i=0;i<4096;i++)  
    {  
        hist1[i] = 0.0;  
    }  
  
    for (i = 0;i < t_h; i++)  
    {  
        for (j = 0;j < t_w; j++)  
        {  
            dist = pow(i - (double)t_h/2,2) + pow(j - (double)t_w/2,2);  
            m_wei[i * t_w + j] = 1 - dist / h;   
            //printf(“%f\n”,m_wei[i * t_w + j]);  
            C += m_wei[i * t_w + j] ;  
        }  
    }  
  
    //计算目标权值直方  
    for (i = t_y;i < t_y + t_h; i++)  
    {  
        for (j = t_x;j < t_x + t_w; j++)  
        {  
            //rgb颜色空间量化为16*16*16 bins  
            q_r = ((u_char)current->imageData[i * current->widthStep + j * 3 + 2]) / 16;  
            q_g = ((u_char)current->imageData[i * current->widthStep + j * 3 + 1]) / 16;  
            q_b = ((u_char)current->imageData[i * current->widthStep + j * 3 + 0]) / 16;  
            q_temp = q_r * 256 + q_g * 16 + q_b;  
            hist1[q_temp] =  hist1[q_temp] +  m_wei[(i - t_y) * t_w + (j - t_x)] ;  
        }  
    }  
  
    //归一化直方图  
    for (i=0;i<4096;i++)  
    {  
        hist1[i] = hist1[i] / C;  
        //printf(“%f\n”,hist1[i]);  
    }  
  
    //生成目标直方图  
    double temp_max=0.0;  
  
    for (i = 0;i < 4096;i++)         //求直方图最大值,为了归一化  
    {  
        //printf(“%f\n”,val_hist[i]);  
        if (temp_max < hist1[i])  
        {  
            temp_max = hist1[i];  
        }  
    }  
    //画直方图  
    CvPoint p1,p2;  
    double bin_width=(double)pic_hist->width/4096;  
    double bin_unith=(double)pic_hist->height/temp_max;  
  
    for (i = 0;i < 4096; i++)  
    {  
        p1.x = i * bin_width;  
        p1.y = pic_hist->height;  
        p2.x = (i + 1)*bin_width;  
        p2.y = pic_hist->height - hist1[i] * bin_unith;  
        //printf(“%d,%d,%d,%d\n”,p1.x,p1.y,p2.x,p2.y);  
        cvRectangle(pic_hist,p1,p2,cvScalar(0,255,0),-1,8,0);  
    }  
    cvSaveImage(”hist1.jpg”,pic_hist);  
    cvReleaseImage(&pic_hist);  
}  
  
void MeanShift_Tracking(IplImage *current)  
{  
    int num = 0, i = 0, j = 0;  
    int t_w = 0, t_h = 0, t_x = 0, t_y = 0;  
    double *w = 0, *hist2 = 0;  
    double sum_w = 0, x1 = 0, x2 = 0,y1 = 2.0, y2 = 2.0;  
    int q_r, q_g, q_b;  
    int *q_temp;  
    IplImage *pic_hist = 0;  
  
    t_w = drawing_box.width;  
    t_h = drawing_box.height;  
      
    pic_hist = cvCreateImage(cvSize(300,200),IPL_DEPTH_8U,3);     //生成直方图图像  
    hist2 = (double *)malloc(sizeof(double)*4096);  
    w = (double *)malloc(sizeof(double)*4096);  
    q_temp = (int *)malloc(sizeof(int)*t_w*t_h);  
  
    while ((pow(y2,2) + pow(y1,2) > 0.5)&& (num < NUM))  
    {  
        num++;  
        t_x = drawing_box.x;  
        t_y = drawing_box.y;  
        memset(q_temp,0,sizeof(int)*t_w*t_h);  
        for (i = 0;i<4096;i++)  
        {  
            w[i] = 0.0;  
            hist2[i] = 0.0;  
        }  
  
        for (i = t_y;i < t_h + t_y;i++)  
        {  
            for (j = t_x;j < t_w + t_x;j++)  
            {  
                //rgb颜色空间量化为16*16*16 bins  
                q_r = ((u_char)current->imageData[i * current->widthStep + j * 3 + 2]) / 16;  
                q_g = ((u_char)current->imageData[i * current->widthStep + j * 3 + 1]) / 16;  
                q_b = ((u_char)current->imageData[i * current->widthStep + j * 3 + 0]) / 16;  
                q_temp[(i - t_y) *t_w + j - t_x] = q_r * 256 + q_g * 16 + q_b;  
                hist2[q_temp[(i - t_y) *t_w + j - t_x]] =  hist2[q_temp[(i - t_y) *t_w + j - t_x]] +  m_wei[(i - t_y) * t_w + j - t_x] ;  
            }  
        }  
  
        //归一化直方图  
        for (i=0;i<4096;i++)  
        {  
            hist2[i] = hist2[i] / C;  
            //printf(“%f\n”,hist2[i]);  
        }  
        //生成目标直方图  
        double temp_max=0.0;  
  
        for (i=0;i<4096;i++)         //求直方图最大值,为了归一化  
        {  
            if (temp_max < hist2[i])  
            {  
                temp_max = hist2[i];  
            }  
        }  
        //画直方图  
        CvPoint p1,p2;  
        double bin_width=(double)pic_hist->width/(4368);  
        double bin_unith=(double)pic_hist->height/temp_max;  
  
        for (i = 0;i < 4096; i++)  
        {  
            p1.x = i * bin_width;  
            p1.y = pic_hist->height;  
            p2.x = (i + 1)*bin_width;  
            p2.y = pic_hist->height - hist2[i] * bin_unith;  
            cvRectangle(pic_hist,p1,p2,cvScalar(0,255,0),-1,8,0);  
        }  
        cvSaveImage(”hist2.jpg”,pic_hist);  
      
        for (i = 0;i < 4096;i++)  
        {  
            if (hist2[i] != 0)  
            {  
                w[i] = sqrt(hist1[i]/hist2[i]);  
            }else  
            {  
                w[i] = 0;  
            }  
        }  
              
        sum_w = 0.0;  
        x1 = 0.0;  
        x2 = 0.0;  
  
        for (i = 0;i < t_h; i++)  
        {  
            for (j = 0;j < t_w; j++)  
            {  
                //printf(“%d\n”,q_temp[i * t_w + j]);  
                sum_w = sum_w + w[q_temp[i * t_w + j]];  
                x1 = x1 + w[q_temp[i * t_w + j]] * (i - t_h/2);  
                x2 = x2 + w[q_temp[i * t_w + j]] * (j - t_w/2);  
            }  
        }  
        y1 = x1 / sum_w;  
        y2 = x2 / sum_w;  
          
        //中心点位置更新  
        drawing_box.x += y2;  
        drawing_box.y += y1;  
  
        //printf(“%d,%d\n”,drawing_box.x,drawing_box.y);  
    }  
    free(hist2);  
    free(w);  
    free(q_temp);  
    //显示跟踪结果  
    cvRectangle(current,cvPoint(drawing_box.x,drawing_box.y),cvPoint(drawing_box.x+drawing_box.width,drawing_box.y+drawing_box.height),CV_RGB(255,0,0),2);  
    cvShowImage(”Meanshift”,current);  
    //cvSaveImage(“result.jpg”,current);  
    cvReleaseImage(&pic_hist);  
}  
  
void onMouse( int event, int x, int y, int flags, void *param )  
{  
    if (pause)  
    {  
        switch(event)  
        {  
        case CV_EVENT_LBUTTONDOWN:   
            //the left up point of the rect  
            drawing_box.x=x;  
            drawing_box.y=y;  
            break;  
        case CV_EVENT_LBUTTONUP:  
            //finish drawing the rect (use color green for finish)  
            drawing_box.width=x-drawing_box.x;  
            drawing_box.height=y-drawing_box.y;  
            cvRectangle(current,cvPoint(drawing_box.x,drawing_box.y),cvPoint(drawing_box.x+drawing_box.width,drawing_box.y+drawing_box.height),CV_RGB(255,0,0),2);  
            cvShowImage(”Meanshift”,current);  
              
            //目标初始化  
            hist1 = (double *)malloc(sizeof(double)*16*16*16);  
            m_wei =  (double *)malloc(sizeof(double)*drawing_box.height*drawing_box.width);  
            init_target(hist1, m_wei, current);  
            is_tracking = true;  
            break;  
        }  
        return;  
    }  
}  
  
  
  
int _tmain(int argc, _TCHAR* argv[])  
{  
    CvCapture *capture=cvCreateFileCapture(”test.avi”);  
    current = cvQueryFrame(capture);  
    char res[20];  
    int nframe = 0;  
  
    while (1)  
    {     
    /*  sprintf(res,”result%d.jpg”,nframe); 
        cvSaveImage(res,current); 
        nframe++;*/  
        if(is_tracking)  
        {  
            MeanShift_Tracking(current);  
        }  
  
        int c=cvWaitKey(1);  
        //暂停  
        if(c == ‘p’)   
        {  
            pause = true;  
            cvSetMouseCallback( ”Meanshift”, onMouse, 0 );  
        }  
        while(pause){  
            if(cvWaitKey(0) == ‘p’)  
                pause = false;  
        }  
        cvShowImage(”Meanshift”,current);  
        current = cvQueryFrame(capture); //抓取一帧  
    }  
  
    cvNamedWindow(”Meanshift”,1);  
    cvReleaseCapture(&capture);  
    cvDestroyWindow(”Meanshift”);  
    return 0;  
}  
  • 6
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值