基于Camshift算法的目标追踪

基于Camshift算法的目标追踪

agneto

最近在做无人机的目标追踪,于是了解了下camshift算法。找了好多资料,发现好多论文中的概念没有解释,就在这里整理下,顺便翻译了部分camshift论文内容。

1.公式全部LaTeX手打,写了简单的meanshift算法的matlab实现。
2.原论文的部分翻译在最下面,点目录就可以了。
3.opencv的camshift整个解析我也贴出来,整理了好久。

  • 基于Camshift算法的目标追踪
    • Meanshift算法简介
      • Meanshift的opencv版本
        • back-projection算法
      • Measnshift算法详细介绍
        • 非参数估计
        • 核密度估计
      • Meanshift的matlab实现
      • 过程解释
    • Camshift算法简介
      • Camshift论文部分翻译
        • Camshift算法流程31
        • 连续自适应分布3233
        • moment计算3437
        • 权重矩阵36
      • Opencv实现解析


Meanshift算法简介

Meanshift算法是camshift算法的核心,处理方法可以说几乎相同,camshift仅仅是针对meanshift在连续帧情况下的优化版本罢了。meanshift是在一幅图上通过概率密度不断迭代找到最优解,而camshift是在连续帧情况下,一副一副图采用meanshift时优化了,继承上一帧图像最优解的窗口为起点继续meanshift迭代下一帧而不是新一帧又从头开始搜索。

Meanshift的opencv版本

Created with Raphaël 2.1.0 选中ROI区域,并计算概率直方图 根据直方图进行反向投影算法(backprojection) 计算moment,通过质心进行迭代

这是opencv中meanshift的简要流程图,moment的概念在下面论文那里。
在下面写了代码的解读。

back-projection算法

反向投影算法简要讲就是在计算图像的直方图后将直方图各分区再投影回去作为像素的值。一般讲rgb图像转换为hsv色域再取h(hue)分量进行反向投影。

opencv实现:在histogram.cpp里面,先调用cv::calcBackProject,在里面再根据输入参数格式调用原函数模板calcBackProj_,源码就不在这里贴了。

Measnshift算法详细介绍

上面是opencv版本的meanshift算法,但按照1995年论文(链接)的优化版本,选定roi区域的概率密度计算增加了一个权重函数,根据距离不同贡献也不同。

非参数估计

meanshift算法属于非参数估计,需要的先验知识少,从目前数据进行估计。这里有篇博客(链接,这个部分不翻了网上好多解释)解释得很清楚,里面详细讲解了三种非参数估计方法。要了解meanshift原理需要了解推荐这篇博客里的核密度估计

核密度估计

看完上面那篇博客的介绍,就可以简单了解到,核密度估计与直方图十分相似。先拿公式吓唬一下:

f(x)=1nhi=1nK(xxih)

h是 带宽,K(x)是 核函数。直方图是将值域划分为几个区间进行累加,而核密度估计则是在将值域划分为几个区域(称之为bin)之后,每个数据不再是以1为单位进行累加,而是以每个数据乘以一个核函数K(x)作为权重进行累加。

基于Camshift算法的目标追踪_第1张图片

K(x)一般采用高斯核函数和Epanechnikov核函数。同时顶一个profile函数满足 K:[0,)R ,即 K(x)=k(||x||) ,原式子变成:

f(x)=1nhi=1nk(||xxih||2)

Meanshift的matlab实现

过程解释

计算权重有点特别

Created with Raphaël 2.1.0 选中ROI区域,根据图像位置用epanechnikov核计算位置权重 计算rgb权重,即r*256+g*16+b,反向投影 更新meanshift向量,移动重心
clear all;
close all;
%读入追踪图像
imglocation='/Users/Desktop/img.jpg'  %图像储存位置
I=imread(imglocation);    
[temp,rect]=imcrop(I);      %imcrop用于选取ROI区域
[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));%归一化系数
%计算颜色概率密度
hist1=zeros(1,4096);  
for i=1:a  
    for j=1:b  
        %rgb颜色空间量化为16*16*16 bins  
        q_r=fix(double(temp(i,j,1))/16);  %fix为趋近0取整函数  
        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;        %计算rgb三通道权重累加
        hist1(q_temp+1)= hist1(q_temp+1)+m_wei(i,j);    %计算各bin中每个像素点占的权重  
    end  
end  
hist1=hist1*C;  
rect(3)=ceil(rect(3));  
rect(4)=ceil(rect(4));  
%读入要跟踪的图像序列
myfile=dir('/Users/Desktop/image/*.jpg');%*是正则匹配  
lengthfile=length(myfile);  

for l=1:lengthfile  
    Im=imread(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];  
            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  

Camshift算法简介

camshift是对于meanshift算法的改进,称为连续自适应的MeanShift算法,CamShift算法的全称是”Continuously Adaptive Mean-SHIFT”,它的基本思想是视频图像的所有帧作MeanShift运算,并将上一帧的结果(即Search Window的中心和大小)作为下一帧MeanShift算法的Search Window的初始值,如此迭代下去。

Camshift论文部分翻译

因为论文第1,2部分是介绍和发展历史,因此不翻了。括号里面是原文section。
论文里面有加上一些自己的解释。

Camshift算法流程(3.1)

Created with Raphaël 2.1.0 设置ROI区域并计算概率密度分布 选择初始窗口(要被追踪的目标) 计算窗口内的颜色概率分布 meanshift迭代算出概率密度图的中心,计算m00矩和中心矩并储存 下一帧图像以步骤4的窗口为中心继续迭代,并把窗口大小设置为步骤3储存的初始时刻的大小,然后从第三步继续。

连续自适应分布(3.2&3.3)

概率分布图(pdf:probability distribution image)的每一个像素对应这个像素在图中的分布概率。最常用的就是反向投影算法(backprojection,上面介绍过了)。
(论文里面讲得比较严谨)反向投影计算的直方图假设分为m个bin,并且有n个像素。 {qu^}u=1...m 表示 直方图标号, {xi^}i=1...n 表示像素的位置,并且定义一个函数 c:R{1...m} xi 像素转换为直方图对应bin的索引:

qu^=i=1nδ[c(xi)u]

解释:这个很好理解,一看冲激函数就应该知道了,在这个bin里面就累计一(无权重)。
bin值( {pu^}u=1...m )的计算用下面公式:
{qu^=min(255max(q̂ )q̂ u),255}u=1...m

moment计算(3.4&3.7)

论文内容:
1.普通矩的计算(离散):

Mmn=xyxnymI(x,y)

解释:定义可以看出来其实就是期望的计算。
2.特别矩的含义:
M00 :质量
M10M00 :质心的x坐标( xc
M01M00 :质心的y坐标( yc
[M20M11M11M02]

根据上面定义可以看出这个是协方差矩阵
3.密度方向和窗口大小
定义三个变量a,b和c
a=M20M00xc2

b=2(M11M00xcyc)

c=M02M00yc2

方向:
θ=12tan1(bac)

距离新质心的距离:
l1=(a+c)+b2+(a+c)22

l2=(a+c)b2+(ac)22

解释:对照上面协方差矩阵特征值计算应该可以理解。

权重矩阵(3.6)

就是上面的核密度估计,在直方图统计基础上加了一个权重。

Opencv实现解析

CV_IMPL int
cvCamShift( const void* imgProb, CvRect windowIn,
            CvTermCriteria criteria,
            CvConnectedComp* _comp,
            CvBox2D* box )
{
    const int TOLERANCE = 10;
    CvMoments moments;
    double m00 = 0, m10, m01, mu20, mu11, mu02, inv_m00;
    double a, b, c, xc, yc;
    double rotate_a, rotate_c;
    double theta = 0, square;
    double cs, sn;
    double length = 0, width = 0;
    int itersUsed = 0;
    CvConnectedComp comp;
    CvMat  cur_win, stub, *mat = (CvMat*)imgProb;

    comp.rect = windowIn;

    mat = cvGetMat( mat, &stub );

    itersUsed = cvMeanShift( mat, windowIn, criteria, &comp );
    windowIn = comp.rect;

    windowIn.x -= TOLERANCE;
    if( windowIn.x < 0 )
        windowIn.x = 0;

    windowIn.y -= TOLERANCE;
    if( windowIn.y < 0 )
        windowIn.y = 0;

    windowIn.width += 2 * TOLERANCE;
    if( windowIn.x + windowIn.width > mat->width )
        windowIn.width = mat->width - windowIn.x;

    windowIn.height += 2 * TOLERANCE;
    if( windowIn.y + windowIn.height > mat->height )
        windowIn.height = mat->height - windowIn.y;

    cvGetSubRect( mat, &cur_win, windowIn );

    /* 计算moment,mu是中心矩 */
    cvMoments( &cur_win, &moments );

    m00 = moments.m00;
    m10 = moments.m10;
    m01 = moments.m01;
    mu11 = moments.mu11;
    mu20 = moments.mu20;
    mu02 = moments.mu02;

    if( fabs(m00) < DBL_EPSILON )
        return -1;

    inv_m00 = 1. / m00;
    xc = cvRound( m10 * inv_m00 + windowIn.x );
    yc = cvRound( m01 * inv_m00 + windowIn.y );
    a = mu20 * inv_m00;
    b = mu11 * inv_m00;
    c = mu02 * inv_m00;

    /* 计算长宽,公式在上面论文 */
    square = sqrt( 4 * b * b + (a - c) * (a - c) );

    /* 计算方向 */
    theta = atan2( 2 * b, a - c + square );

    cs = cos( theta );
    sn = sin( theta );

    rotate_a = cs * cs * mu20 + 2 * cs * sn * mu11 + sn * sn * mu02;
    rotate_c = sn * sn * mu20 - 2 * cs * sn * mu11 + cs * cs * mu02;
    length = sqrt( rotate_a * inv_m00 ) * 4;
    width = sqrt( rotate_c * inv_m00 ) * 4;


    if( length < width )
    {
        double t;

        CV_SWAP( length, width, t );
        CV_SWAP( cs, sn, t );
        theta = CV_PI*0.5 - theta;
    }

    /* Saving results */
    if( _comp || box )
    {
        int t0, t1;
        int _xc = cvRound( xc );
        int _yc = cvRound( yc );

        t0 = cvRound( fabs( length * cs ));
        t1 = cvRound( fabs( width * sn ));

        t0 = MAX( t0, t1 ) + 2;
        comp.rect.width = MIN( t0, (mat->width - _xc) * 2 );

        t0 = cvRound( fabs( length * sn ));
        t1 = cvRound( fabs( width * cs ));

        t0 = MAX( t0, t1 ) + 2;
        comp.rect.height = MIN( t0, (mat->height - _yc) * 2 );

        comp.rect.x = MAX( 0, _xc - comp.rect.width / 2 );
        comp.rect.y = MAX( 0, _yc - comp.rect.height / 2 );

        comp.rect.width = MIN( mat->width - comp.rect.x, comp.rect.width );
        comp.rect.height = MIN( mat->height - comp.rect.y, comp.rect.height );
        comp.area = (float) m00;
    }

    if( _comp )
        *_comp = comp;

    if( box )
    {
        box->size.height = (float)length;
        box->size.width = (float)width;
        box->angle = (float)((CV_PI*0.5+theta)*180./CV_PI);
        while(box->angle < 0)
            box->angle += 360;
        while(box->angle >= 360)
            box->angle -= 360;
        if(box->angle >= 180)
            box->angle -= 180;
        box->center = cvPoint2D32f( comp.rect.x + comp.rect.width*0.5f,
                                    comp.rect.y + comp.rect.height*0.5f);
    }

    return itersUsed;
}

你可能感兴趣的:(跟踪算法)