opencv Camshift

来源:互联网 发布:苏州java软件开发招聘 编辑:程序博客网 时间:2024/06/07 03:58

opencv Camshift

demo:http://download.csdn.net/detail/keen_zuxwang/9861716

对运动物体的跟踪:
背景固定,可用帧差法 然后在计算下连通域 将面积小的去掉即可
背景单一,即你要跟踪的物体颜色和背景色有较大区别 可用基于颜色的跟踪 如CAMSHIFT 鲁棒性都是较好的
背景复杂,如背景中有和前景一样的颜色 就需要用到一些具有预测性的算法 如卡尔曼滤波等 可以和CAMSHIFT结合

反向投影
是一种记录给定图像中的像素点如何适应直方图模型像素分布的方式, 反向投影在某一位置的值就是原图对应位置像素值在原图像中的总数目
(首先计算某一特征的直方图模型,然后使用模型去寻找图像中存在的特征)

反投影直方图的方法
是一种把目标概率分布映射到观测图像的简单方法。其作用是,替换一个输入图像中每一个像素值,使其变成感兴趣区域(ROI)的直方图中对应的概率值

颜色概率模型:利用目标的颜色直方图模型将图像转换为颜色概率分布图
1、将观测图像中每个像素从RGB空间映射到HSV空间,按公式计算H分量大小建立起统计直方图
2、根据直方图进行反向投影运算,即,将观测图像中的每个像素值,用其H分量在已建立的统计直方图中对应的值代替
3、所得到输出图像就是观测图像的颜色概率分布图像

meanshift
meanshift算法是一种密度函数梯度估计的非参数方法,通过迭代寻优找到概率分布的极值来定位目标。计算搜索窗的质心:
调整搜索窗大小,移动搜索窗的中心到质心,如果移动距离大于预设的固定阈值,则重复,直到搜索窗的中心与质心间的移动距离小于预设的固定阈值,或者循环运算的次数达到某一最大值,停止计算。

meanshift原理:
均值漂移算法以迭代的方式锁定概率函数的局部最大值。它主要是寻找预定义窗口中数据点的重心点,或者说加权平均值。该算法将窗口中心移动到数据点的重心处,并重复这个过程直到窗口重心收敛到一个稳定点。
从数学角度上说,meanshift算法利用概率密度的梯度爬升来寻找局部最优。当输入一个图像的范围,然后根据反向投影图和输入的方框进行meanshift迭代,它是向重心移动,即向反向投影图中概率大的地方移动,所以始终会移动到目标上,meanshift算法是一个变步长的梯度上升算法

meanshift的基本思路:
从每个像素开始,首先估计有相似颜色的邻近像素点的密度(局部密度)的梯度,而后利用迭代算法求出局部密度的峰值(即重心点),把能够聚类到同一个峰值点的所有像素点划分成一个区域

基本流程
1、选择跟踪窗口的大小和初始位置。在Mean Shift跟踪算法中,核窗宽(即核函数的定义域的大小,就是搜索窗口的大小)的大小起着非常重要的作用。因为它不但决定了参与Mean Shift迭代的样本数量,而且也反映了跟踪窗口的大小。通常,核窗宽由初始跟踪窗口的尺寸决定,而且在整个跟踪过程中不再发生变化
2、计算跟踪窗口内的质心(或重心)。在离散二维(2D)概率分布图像中,计算某窗口的质心同物理上计算某物体的质心一样,即利用窗口的零阶矩M00和(x,y)的一阶矩(M10,M01)之间的关系,计算得到窗口的质心
这里写图片描述

3、调整跟踪窗口的中心到质心;
4、重复第二步和第三步,直到跟踪窗口中心和质心“会聚”,即每次窗口移动的距离小于一定的阈值

优点
meanshift作为一种高效的模式匹配算法,由于不需要进行全局搜索,而且搜索精度高,已经广泛地应用在各种模式识别、实时可视跟踪等领域,Mean Shift是针对单张图片寻找最优迭代结果。
不足
缺乏必要的模型更新方法;整个跟踪过程中跟踪窗口的大小保持不变,当目标存在尺度变化的时候会导致尺度定位不准确

camshift
将meanshift算法扩展到连续图像序列,就是camshift算法(即Continuously Adaptive Mean-Shift算法,在距离相似性度量的基础之上,又增加了图像灰度相似性的度量)。它将视频的所有帧做meanshift运算,并将上一帧的结果,即搜索窗的大小和中心,作为下一帧meanshift算法搜索窗的初始值。如此迭代下去,就可以实现对目标的快速跟踪。同时,正是由于Camshift针对一个视频序列进行处理,从而保证其可以不断调整窗口的大小,如此一来,当目标的大小发生变化的时候,该算法就可以自适应地调整目标区域继续跟踪。Camshift算法是一种动态变化的非参数密度函数梯度估计方法

camshift原理
Camshift算法首先根据跟踪目标颜色概率模型,将视频图像转化为概率分布图像(PDI),并初始化一个矩形搜索窗口,对每一帧PDI图像利用Mean Shift算法搜索目标匹配的最优区域,并根据搜索区域的不变矩估算跟踪目标的中心和大小,保存和输出当前帧搜索结果,并且用当前帧搜索结果作为下一帧图像初始化搜索窗口。如此循环,即可实现对目标的连续跟踪。

算法过程:
1、初始化搜索窗
2、计算搜索窗的颜色概率分布(反向投影)
3、运行meanshift算法,获得搜索窗新的大小和位置。
4、在下一帧视频图像中用上一步中的值重新初始化搜索窗的大小和位置,再跳转到步骤2继续进行。
camshift能有效解决目标变形和遮挡的问题,对系统资源要求不高,时间复杂度低,在简单背景下能够取得良好的跟踪效果。
当背景较为复杂,或者有许多与目标颜色相似像素干扰的情况下,会导致跟踪失败。因为它单纯的考虑颜色直方图,忽略了目标的空间分布特性,所以这种情况下需加入对跟踪目标的预测算法

//Meanshift跟踪算法返回的Box类typedef struct CvBox2D{   CvPoint2D32f center; // 盒子的中心   CvSize2D32f size; // 盒子的长和宽   float angle; // 水平轴与第一个边的夹角,用弧度表示}CvBox2D;typedef struct CvConnectedComp{   double area; // 连通域的面积   float value; // 分割域的灰度缩放值   CvRect rect; // 分割域的 ROI} CvConnectedComp;/* Implements CAMSHIFT algorithm - determines object position, size and orientation    from the object histogram back project (extension of meanshift) */CVAPI(int)  cvCamShift( const CvArr* prob_image, CvRect  window,                         CvTermCriteria criteria, CvConnectedComp* comp,                         CvBox2D* box CV_DEFAULT(NULL) );  

prob_image 色彩概率分布图像
window search window的初始值
criteria 判断搜索是否停止的一个标准
comp 联通域(运算结果,包括新的search window的位置和面积)
box 包含被跟踪物体的最小矩形

JNI: camshift.cpp

IplImage  *image = 0;IplImage  *hsv = 0;IplImage  *hue = 0;IplImage  *mask = 0;IplImage  *backproject = 0;IplImage  *histimg = 0;      //用HSV中的Hue分量进行跟踪CvConnectedComp track_comp;  //连接部件CvHistogram *hist = 0;       //直方图类int backproject_mode = 0;int select_object    = 0;signed int track_object = 0;int bin_w;CvPoint origin;CvRect  selection;CvRect  track_window;CvBox2D track_box;int vmin=10, vmax=256, smin = 30;//将Hue量转换成RGB量CvScalar hsv2rgb( float hue ){    int rgb[3], p, sector;    static const int sector_data[][3]={{0,2,1}, {1,2,0}, {1,0,2}, {2,0,1}, {2,1,0}, {0,1,2}};    hue *= 0.033333333333333333333333333333333f;    sector = cvFloor(hue);    p = cvRound(255*(hue - sector));    p ^= sector & 1 ? 255 : 0;    rgb[sector_data[sector][0]] = 255;    rgb[sector_data[sector][1]] = 0;    rgb[sector_data[sector][2]] = p;    return cvScalar(rgb[2], rgb[1], rgb[0],0);}JNIEXPORT int JNICALL Java_com_example_camshift_MainActivity_doSelectRect(JNIEnv *env, jclass clz,        jint imgx, jint imgy){    LOGD("          doSelectRect start       !");    selection.x = imgx-60;    selection.y = imgy-60;    selection.width  = 120;    selection.height = 120;    select_object = 1;    track_object  = 0;    LOGD("         doSelectRect %d, %d      !", imgx,imgy);}void DrawBox(IplImage* img, CvBox2D box){   CvPoint2D32f points[4];   cvBoxPoints(box,points);   CvPoint pt[4];   for (int i=0; i<4; i++)   {     pt[i].x = (int)points[i].x;     pt[i].y = (int)points[i].y;   }   cvLine( img, pt[0], pt[1],CV_RGB(255,0,0), 2, 8, 0 );   cvLine( img, pt[1], pt[2],CV_RGB(255,0,0), 2, 8, 0 );   cvLine( img, pt[2], pt[3],CV_RGB(255,0,0), 2, 8, 0 );   cvLine( img, pt[3], pt[0],CV_RGB(255,0,0), 2, 8, 0 );}int hist_size   = 256;    //直方图尺寸int hist_height = 256;float range[] = {0,255};  //灰度级的范围float* ranges[]={range};JNIEXPORT jlong JNICALL Java_com_example_camshift_MainActivity_doCamshift(JNIEnv *env, jclass clz, jlong imageGray){    int i;    Mat imageMat = Mat(*(Mat*)imageGray);    IplImage  temp_src = imageMat;    IplImage* frame = &temp_src;    if(!image )//初始建立缓冲区    {         LOGD("            doCamshift init        !");         image = cvCreateImage(cvGetSize(frame), 8, 3);         image->origin = frame->origin;         hsv  = cvCreateImage(cvGetSize(frame), 8, 3);         hue  = cvCreateImage(cvGetSize(frame), 8, 1);         mask = cvCreateImage(cvGetSize(frame), 8, 1);         //分配掩膜图像空间         backproject = cvCreateImage(cvGetSize(frame), 8, 1);         //创建一维直方图,统计图像在[0 255]像素的均匀分布         hist = cvCreateHist(1,&hist_size,CV_HIST_ARRAY,ranges,1);         //分配直方图空间         histimg = cvCreateImage(cvGetSize(frame), 8, 3);//cvSize(320,200)         //分配用于直方图显示的空间         cvZero(histimg); //置背景为黑色    }    cvCvtColor(frame, image, CV_BGRA2BGR);    cvCvtColor(image, hsv, CV_BGR2HSV); //把图像从RGB表色系转为HSV表色系    if(track_object>0) //track_object非零,表示有需要跟踪的物体    {         LOGD("       -------  doCamshift tracking-------     !");         int _vmin = vmin, _vmax = vmax;         cvInRangeS(hsv, cvScalar(0, smin, MIN(_vmin,_vmax), 0),                            cvScalar(256, 256, MAX(_vmin,_vmax), 0), mask);         //制作掩膜板,只处理像素值为H:0~180/256,S:smin~256,V:vmin~vmax之间的部分         cvSplit(hsv, hue, 0, 0, 0);//分离H分量         cvCalcBackProject(&hue, backproject, hist);//计算hue的反向投影图         cvAnd(backproject, mask, backproject, 0 );//得到掩膜内的反向投影         cvCamShift(backproject, track_window,                            cvTermCriteria( CV_TERMCRIT_EPS | CV_TERMCRIT_ITER, 10, 1 ),                            &track_comp, &track_box );         //使用MeanShift算法对backproject中的内容进行搜索,返回跟踪结果         if(track_comp.rect.width>0 && track_comp.rect.height>0){            track_window = track_comp.rect;//得到跟踪结果的矩形框         }         //if( backproject_mode )         //    cvCvtColor( backproject, image, CV_GRAY2BGR );         if( image->origin )             track_box.angle = -track_box.angle;         //画出跟踪结果的位置         //cvEllipseBox( frame , track_box, CV_RGB(255,0,0), 3, CV_AA, 0 );         DrawBox(frame, track_box);         //Mat mtx(frame,0);         Mat *hist0 = new Mat(frame);//mtx         return (jlong) hist0;     }     else if(track_object < 0)//如果需要跟踪的物体还没有进行属性提取,则进行选取框类的图像属性提取     {        float max_val = 0.f;        cvSetImageROI(hue, selection); //设置原选择框为ROI        cvSetImageROI(mask, selection);//设置掩膜板选择框为ROI        cvCalcHist(&hue, hist, 0, mask);//得到选择框内且满足掩膜板内的直方图        cvGetMinMaxHistValue(hist, 0, &max_val, 0, 0);        //cvConvertScale(hist->bins, hist->bins, max_val?255./max_val:0., 0 );// 对直方图的数值转为0~255        cvResetImageROI( hue );//去除ROI        cvResetImageROI( mask );//去除ROI        track_window = selection;        track_object = 1;//置track_object为1,表明属性提取完成        cvZero(histimg);        LOGD("            doCamshift select %f         !", max_val);        int scale = 2;         //分别将每个直方块的值绘制到图中        for(i=0;i<hist_size;i++)        {        float bin_val = cvGetReal1D(hist->bins,i); //像素i的概率   cvQueryHistValue_1D        int intensity = cvRound(bin_val*hist_height/max_val);  //要绘制的高度        cvRectangle(histimg,            cvPoint(i*scale,hist_height-1),            cvPoint((i+1)*scale - 1, hist_height - intensity),            CV_RGB(255,255,255));        }        Mat mtx(histimg,0);        Mat *hist0 = new Mat(mtx);        return (jlong) hist0;    }    if( select_object && selection.width > 0 && selection.height > 0 ) //如果正处于物体选择,画出选择框    {        cvSetImageROI(image, selection);        cvXorS(image, cvScalarAll(255), image, 0);        cvResetImageROI(image);        select_run++;        if(select_run>=1){           select_run = 0;           select_object = 0;           track_object  = -1;           track_window = selection;           LOGD("          doSelectRect %d       !",track_object);        }        Mat mtx(image,0);        Mat *hist0 = new Mat(mtx);        return (jlong) hist0;     }     Mat *hist0 = new Mat(imageMat);     return (jlong) hist0;}

框选区域:
这里写图片描述

直方块的值绘制:
这里写图片描述

CamShift()检出:
这里写图片描述

原创粉丝点击