Camshift算法原理(opencv源码剖析)(上)

(转载请注明出处http://hi.baidu.com/wcp12%5F4/blog/item/09541cfb05344f016c22eb44.html

因为最近要写的论文的思想和camshift类似,所以把camshift算法的前前后后仔细再看了看。参考了网上的资料,camshift原论文,《学习opencv》,opencv源码等。网上的很多资料大都限于对opencv那几个函数调用的粗略的讲解,我想还是有必要写点东西总结一下。

概述:Mean shift算法是Fukunaga于1975年提出的,其含义即偏移的均值向量。随着Mean shift理论的发展,它的含义也发生了变化。现在一般是指一个迭代的步骤,即先算出当前点的偏移均值,移动该点到其偏移均值,然后以此为新的起始点,继续移动,直到满足一定的条件结束。Cheng Yizong定义了一族核函数 ,将Mean shift算法引入到计算机视觉领域。Bradski G R对Mean shift算法进行改进,发展建立了Camshift算法,将Mean shift方法扩展应用到了目标跟踪中来。(摘录自一个ppt)

算法描述:

(1)初始化搜索窗口,得到该区域HSV颜色空间H分量的直方图.。得到的直方图横坐标为0-255的灰度值(H分量的值),纵坐标为搜索窗口中某个灰度值的像素个数。

(2)反向投影:利用(1)的直方图计算整幅图像的(关于该搜索窗口中颜色的)概率分布。例如:在整幅图中,假设某个像素的H分量值为10,而到(1)的直方图中查找H=10对应的纵坐标值为50(代表搜索窗口中有50个H值为10的像素),则将整幅图中该像素的值置为50。对所有像素对应查找赋值便得到了反向投影图。(实际计算中不必计算整幅图像,只需计算比搜索窗口大一些的范围)

(3)利用meanshift算法迭代找到概率分布图的重心。0阶矩计算出图像"质量",1阶矩/0阶矩 计算出图像的质心。

I(x,y) is the pixel (probability) value at position (x,y) in the image, and x and y range over the search window

(4)调整搜索窗口大小,用来初始化下一帧中的搜索窗口。返回到(2)循环实现跟踪

For 2D color probability distributions where the maximum pixel value is 255, we set window size s to

  s只是搜索窗口一边的长度,camshift论文中是这样设置的:for tracking faces, we set window width to s and window length to 1.2s since faces are somewhat elliptical.

camshift论文中如下描述

How to Calculate the Continuously Adaptive Mean Shift Algorithm
1. Choose the initial location of the search window.
2. Mean Shift as above (one or many iterations); store
the zeroth moment.
3. Set the search window size equal to a function of the
zeroth moment found in Step 2.
4. Repeat Steps 2 and 3 until convergence (mean
location moves less than a preset threshold).

以上来自camshift论文。步骤2中用到了meanshift,该论文中如下描述:

How to Calculate the Mean Shift Algorithm

1. Choose a search window size.
2. Choose the initial location of the search window.
3. Compute the mean location in the search window.
4. Center the search window at the mean location
computed in Step 3.
5. Repeat Steps 3 and 4 until convergence (or until the
mean location moves less than a preset threshold).

opencv源码:

(ps:看这种一层套一层的代码还是用source insight方便,比用VC看爽多了)

/***************************** B A C K   P R O J E C T  反向投影 *****************************/

// Calculates back project for one or more 8u arrays 8位图(0-255)
static CvStatus CV_STDCALL
    icvCalcBackProject_8u_C1R( uchar** img, int step, uchar* dst, int dstStep,
                               CvSize size, const CvHistogram* hist )
{
    const int small_hist_size = 1<<12;
    int* tab = 0;
    int is_sparse = CV_IS_SPARSE_HIST(hist);//判断hist的矩阵是否为空等
    int dims, histsize[CV_MAX_DIM];
    int i, x;
    CvStatus status;//枚举类型

    dims = cvGetDims( hist->bins, histsize );//返回直方图数组维数dims、矩阵的长宽histsize

    tab = (int*)cvStackAlloc( dims*256*sizeof(int));//8位所以是256
    status = icvCalcHistLookupTables8u( hist, dims, histsize, tab );
    if( status < 0 )
        return status;

    if( !is_sparse )//hist没有问题
    {
        int total = 1;
        CvMatND* mat = (CvMatND*)(hist->bins);
        float* bins = mat->data.fl;//从hist矩阵中取出数据
        uchar* buffer = 0;

        for( i = 0; i < dims; i++ )
            total *= histsize[i];//总大小

        if( dims <= 3 && total >= -ICV_HIST_DUMMY_IDX )
            return CV_BADSIZE_ERR; // too big histogram//返回错误

        if( dims > 1 && total <= small_hist_size && CV_IS_MAT_CONT(mat->type))
        {
            buffer = (uchar*)cvAlloc(total);//hist
            if( !buffer )
                return CV_OUTOFMEM_ERR;
            for( i = 0; i < total; i++ )
            {
                int v = cvRound(bins[i]);//
                buffer[i] = CV_CAST_8U(v);将数值约束在0-255之间
            }
        }

        switch( dims )//hist维数
        {
        case 1:
            {
            uchar tab1d[256];//hist直方图
            for( i = 0; i < 256; i++ )//将直方图的值拷贝到tab1d[]中
            {
                int idx = tab[i];//tab数组大小为维数*256。(0-255)映射到数组中的实际位置
                if( idx >= 0 )
                {
                    int v = cvRound(bins[idx]);//V应该是直方图灰度值为i的像素个数?
                    tab1d[i] = CV_CAST_8U(v);//将数值约束在0-255之间
                }
                else
                    tab1d[i] = 0;
            }
   //逐行扫描
            for( ; size.height--; img[0] += step, dst += dstStep )//uchar类型,+ 一个step、dstStep(深度)跳到下一个像素
            {
                uchar* ptr = img[0];//源图
                for( x = 0; x <= size.width - 4; x += 4 )//拷贝源图到目标图
                {
               //反向投影
                //得到每个像素的灰度(0-255)在hist[0-255]中对应的值,存到目标图
                    uchar v0 = tab1d[ptr[x]];
                    uchar v1 = tab1d[ptr[x+1]];

                    dst[x] = v0;
                    dst[x+1] = v1;

                    v0 = tab1d[ptr[x+2]];
                    v1 = tab1d[ptr[x+3]];

                    dst[x+2] = v0;
                    dst[x+3] = v1;
                }

                for( ; x < size.width; x++ )//末尾
                    dst[x] = tab1d[ptr[x]];
            }
            }
            break;
        case 2:
            for( ; size.height--; img[0] += step, img[1] += step, dst += dstStep )
            {
                uchar* ptr0 = img[0];
                uchar* ptr1 = img[1];

                if( buffer )
                {
                    for( x = 0; x < size.width; x++ )
                    {
                        int v0 = ptr0[x];
                        int v1 = ptr1[x];
                        int idx = tab[v0] + tab[256+v1];
                        int v = 0;

                        if( idx >= 0 )
                            v = buffer[idx];

                        dst[x] = (uchar)v;
                    }
                }
                else
                {
                    for( x = 0; x < size.width; x++ )
                    {
                        int v0 = ptr0[x];
                        int v1 = ptr1[x];
                        int idx = tab[v0] + tab[256+v1];
                        int v = 0;

                        if( idx >= 0 )
                        {
                            v = cvRound(bins[idx]);
                            v = CV_CAST_8U(v);
                        }

                        dst[x] = (uchar)v;
                    }
                }
            }
            break;
        case 3:
            for( ; size.height--; img[0] += step, img[1] += step,
                                  img[2] += step, dst += dstStep )
            {
                uchar* ptr0 = img[0];
                uchar* ptr1 = img[1];
                uchar* ptr2 = img[2];

                if( buffer )
                {
                    for( x = 0; x < size.width; x++ )
                    {
                        int v0 = ptr0[x];
                        int v1 = ptr1[x];
                        int v2 = ptr2[x];
                        int idx = tab[v0] + tab[256+v1] + tab[512+v2];
                        int v = 0;

                        if( idx >= 0 )
                            v = buffer[idx];

                        dst[x] = (uchar)v;
                    }
                }
                else
                {
                    for( x = 0; x < size.width; x++ )
                    {
                        int v0 = ptr0[x];
                        int v1 = ptr1[x];
                        int v2 = ptr2[x];
                        int idx = tab[v0] + tab[256+v1] + tab[512+v2];
                        int v = 0;

                        if( idx >= 0 )
                        {
                            v = cvRound(bins[idx]);
                            v = CV_CAST_8U(v);
                        }
                        dst[x] = (uchar)v;
                    }
                }
            }
            break;
        default://大于3维
            for( ; size.height--; dst += dstStep )
            {
                if( buffer )
                {
                    for( x = 0; x < size.width; x++ )
                    {
                        uchar* binptr = buffer;
                        int v = 0;

                        for( i = 0; i < dims; i++ )
                        {
                            int idx = tab[i*256 + img[i][x]];
                            if( idx < 0 )
                                break;
                            binptr += idx;
                        }
                        
                        if( i == dims )
                            v = binptr[0];

                        dst[x] = (uchar)v;
                    }
                }
                else
                {
                    for( x = 0; x < size.width; x++ )
                    {
                        float* binptr = bins;
                        int v = 0;

                        for( i = 0; i < dims; i++ )
                        {
                            int idx = tab[i*256 + img[i][x]];
                            if( idx < 0 )
                                break;
                            binptr += idx;
                        }

                        if( i == dims )
                        {
                            v = cvRound( binptr[0] );
                            v = CV_CAST_8U(v);
                        }

                        dst[x] = (uchar)v;
                    }
                }

                for( i = 0; i < dims; i++ )
                    img[i] += step;
            }
        }

        cvFree( &buffer );
    }
    else//hist
    {
        CvSparseMat* mat = (CvSparseMat*)(hist->bins);
        int node_idx[CV_MAX_DIM];

        for( ; size.height--; dst += dstStep )
        {
            for( x = 0; x < size.width; x++ )
            {
                int v = 0;

                for( i = 0; i < dims; i++ )
                {
                    int idx = tab[i*256 + img[i][x]];
                    if( idx < 0 )
                        break;
                    node_idx[i] = idx;
                }
                if( i == dims )
                {
                    float* bin = (float*)cvPtrND( mat, node_idx, 0, 1, 0 );
                    v = cvRound(bin[0]);
                    v = CV_CAST_8U(v);
                }

                dst[x] = (uchar)v;
            }

            for( i = 0; i < dims; i++ )
                img[i] += step;
        }
    }

    return CV_OK;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值