目标跟踪-粒子滤波算法

http://blog.csdn.net/hujingshuang/article/details/45535423


前言:

粒子滤波广泛的应用于目标跟踪,粒子滤波器是一种序列蒙特卡罗滤波方法,其实质是利用一系列随机抽取的样本(即粒子)来替代状态的后验概率分布。在此不打算介绍和推理繁杂的概率公式,我们来分析Rob Hess源码从而深入理解粒子滤波算法。

试验平台:

VS2010 + opencv2.4.10 + gsl1.8库 + RobHess粒子滤波源码

相关资料:

       Rob Hess粒子滤波的相关代码:http://blogs.oregonstate.edu/hess/code/particles/

       Rob Hess有关粒子滤波的文章:http://web.engr.oregonstate.edu/~afern/papers/cvpr09.pdf

       Rob Hess多目标跟踪效果视频:http://v.youku.com/v_show/id_XOTQ5NDA5ODgw.html

       yangyangcv博主的分析及代码(建议先看):http://www.cnblogs.com/yangyangcv/archive/2010/05/23/1742263.html

下载 yangyangcv提供的代码,配置好后即可使用;本博文也提供下载,见文章末尾


源码分析:

下面用粒子滤波算法来实现单目标的跟踪,在初始时需要手动选取待跟踪目标区域。

步骤一:粒子初始化

1、目标的选取:

在起始帧画面中标记待跟踪的区域(目标物体),在以后的连续帧视频中跟踪目标物体。

2、粒子的定义:

粒子即样本,Rob Hess源码中默认粒子数目PARTICLES=100,下面是粒子的属性(可以看出一个粒子代表的就是一个矩形区域):

[cpp] view plaincopy
  1. typedef struct particle {  
  2.   float x;          /**< 当前x坐标 */  
  3.   float y;          /**< 当前y坐标 */  
  4.   float s;          /**< scale */  
  5.   float xp;         /**< 之前x坐标 */  
  6.   float yp;         /**< 之前y坐标 */  
  7.   float sp;         /**< previous scale */  
  8.   float x0;         /**< x0 */  
  9.   float y0;         /**< y0 */  
  10.   int width;        /**< 粒子宽度 */  
  11.   int height;       /**< 粒子高度 */  
  12.   histogram* histo; /**< 跟踪区域的参考直方图 */  
  13.   float w;          /**< 权重 */  
  14. } particle;  

3、特征提取:

目标跟踪最重要的就是特征,应该选取好的特征(拥有各种不变性的特征当然是最好的);另外考虑算法的效率,目标跟踪一般是实时跟踪,所以对算法实时性有一定的要求。Rob Hess源码提取的是目标的颜色特征(颜色特征对图像本身的尺寸、方向、视角的依赖性较小,从而具有较高的鲁棒性),粒子与目标的直方图越相似,则说明越有可能是目标。

捕捉到一帧图像,标记待跟踪的区域,将其从BGR转化到HSV空间,提取感兴趣部分(目标)的HSV,进行直方图统计并归一化直方图

[cpp] view plaincopy
  1. /* increment appropriate histogram bin for each pixel */  
  2. //计算直方图  
  3. for( r = 0; r < img->height; r++ )  
  4.     for( c = 0; c < img->width; c++ )  
  5.     {  
  6.         bin = histo_bin( /*pixval32f( h, r, c )*/((float*)(h->imageData + h->widthStep*r) )[c],  
  7.                     ((float*)(s->imageData + s->widthStep*r) )[c],  
  8.                     ((float*)(v->imageData + v->widthStep*r) )[c] );  
  9.         hist[bin] += 1;  
  10.     }  


[cpp] view plaincopy
  1. int histo_bin( float h, float s, float v )  
  2. {  
  3.   int hd, sd, vd;  
  4.   
  5.   /* if S or V is less than its threshold, return a "colorless" bin */  
  6.   vd = MIN( (int)(v * NV / V_MAX), NV-1 );  
  7.   if( s < S_THRESH  ||  v < V_THRESH )  
  8.     return NH * NS + vd;  
  9.     
  10.   /* otherwise determine "colorful" bin */  
  11.   hd = MIN( (int)(h * NH / H_MAX), NH-1 );  
  12.   sd = MIN( (int)(s * NS / S_MAX), NS-1 );  
  13.   return sd * NH + hd;  
  14. }  

img是目标矩形区域,h、s、v是个分量,hist就是直方图统计;NV、V_MAX....等是宏定义的固定值。
[cpp] view plaincopy
  1. void normalize_histogram( histogram* histo )//直方图归一化  
  2. {  
  3.   float* hist;  
  4.   float sum = 0, inv_sum;  
  5.   int i, n;  
  6.   
  7.   hist = histo->histo;  
  8.   n = histo->n;  
  9.   
  10.   /* compute sum of all bins and multiply each bin by the sum's inverse */  
  11.   for( i = 0; i < n; i++ )  
  12.     sum += hist[i];  
  13.   inv_sum = 1.0 / sum;  
  14.   for( i = 0; i < n; i++ )  
  15.     hist[i] *= inv_sum;  
  16. }  

4、粒子初始化

根据选定的目标区域来初始化粒子,初始时所有粒子都为等权重,具有同样的属性。

[cpp] view plaincopy
  1. /* create particles at the centers of each of n regions */  
  2. for( i = 0; i < n; i++ )  
  3. {  
  4.     width = regions[i].width;  
  5.     height = regions[i].height;  
  6.     x = regions[i].x + width / 2;<span style="white-space:pre">   </span>//粒子中心  
  7.     y = regions[i].y + height / 2;  
  8.     for( j = 0; j < np; j++ )  
  9.     {  
  10.         particles[k].x0 = particles[k].xp = particles[k].x = x;  
  11.         particles[k].y0 = particles[k].yp = particles[k].y = y;  
  12.         particles[k].sp = particles[k].s = 1.0;  
  13.         particles[k].width = width;  
  14.         particles[k].height = height;  
  15.         particles[k].histo = histos[i];  
  16.         particles[k++].w = 0;  
  17.     }  
  18. }  

步骤二、粒子相似度搜索、计算

5、粒子搜索

在步骤一中,初始化了100个粒子,由于初始帧中指定了目标区域,而该目标会在下一帧中发生偏移,但是相邻帧目标移动得不是太远,所以在目标区域附近随机撒出100个粒子。(此处使用的是二阶动态回归来估计偏移后的粒子位置)

[cpp] view plaincopy
  1. particle transition( particle p, int w, int h, gsl_rng* rng )//随机撒出一个粒子  
  2. {  
  3.   float x, y, s;  
  4.   particle pn;  
  5.   //回归模型的参数即A1、A2、B0等Rob Hess在代码中已设定(我不知道是怎么来的?)  
  6.   /* sample new state using second-order autoregressive dynamics (使用二阶动态回归来自动更新粒子状态)*/  
  7.   x = A1 * ( p.x - p.x0 ) + A2 * ( p.xp - p.x0 ) +  
  8.     B0 * gsl_ran_gaussian( rng, TRANS_X_STD ) + p.x0;  
  9.   pn.x = MAX( 0.0, MIN( (float)w - 1.0, x ) );  
  10.   y = A1 * ( p.y - p.y0 ) + A2 * ( p.yp - p.y0 ) +  
  11.     B0 * gsl_ran_gaussian( rng, TRANS_Y_STD ) + p.y0;  
  12.   pn.y = MAX( 0.0, MIN( (float)h - 1.0, y ) );  
  13.   s = A1 * ( p.s - 1.0 ) + A2 * ( p.sp - 1.0 ) +  
  14.     B0 * gsl_ran_gaussian( rng, TRANS_S_STD ) + 1.0;  
  15.   pn.s = MAX( 0.1, s );  
  16.   pn.xp = p.x;  
  17.   pn.yp = p.y;  
  18.   pn.sp = p.s;  
  19.   pn.x0 = p.x0;  
  20.   pn.y0 = p.y0;  
  21.   pn.width = p.width;  
  22.   pn.height = p.height;  
  23.   pn.histo = p.histo;  
  24.   pn.w = 0;  
  25.   
  26.   return pn;  
  27. }  

6、相似度计算

然后计算这100个粒子hsv空间直方图与目标hsv空间直方图相似成度,马氏距离(Battacharyya)来度量两个粒子的相似度系数。

[cpp] view plaincopy
  1. //计算粒子与跟踪区域直方图相似程度,越相似则权值越大  
  2. particles[j].w = likelihood( hsv_frame, cvRound(particles[j].y),  
  3.             cvRound( particles[j].x ),  
  4.             cvRound( particles[j].width * s ),  
  5.             cvRound( particles[j].height * s ),  
  6.             particles[j].histo );  

[cpp] view plaincopy
  1. float histo_dist_sq( histogram* h1, histogram* h2 )//两个粒子的  
  2. {  
  3.   float* hist1, * hist2;  
  4.   float sum = 0;  
  5.   int i, n;  
  6.   
  7.   n = h1->n;  
  8.   hist1 = h1->histo;  
  9.   hist2 = h2->histo;  
  10.   
  11.   /* 
  12.     According the the Battacharyya similarity coefficient, 
  13.      
  14.     D = \sqrt{ 1 - \sum_1^n{ \sqrt{ h_1(i) * h_2(i) } } }//马氏距离公式 
  15.   */  
  16.   for( i = 0; i < n; i++ )  
  17.     sum += sqrt( hist1[i]*hist2[i] );  
  18.   return 1.0 - sum;  
  19. }  

步骤三、重采样

7、重采样

由步骤二知,经过一次搜索后,粒子的权重会发生改变,离目标距离远的粒子权重小,距离近的权重大。那么经过多帧的跟踪后,有的粒子权重会变得相当的小,也就是与目标不相似了,即粒子退化现象,这种粒子我们要抛弃,那么什么时候该抛弃它呢?就得设一个权重阈值,凡是权重低于阈值的粒子就抛弃。OK,原来有100个粒子,然后总会有被抛弃的粒子,似的粒子总数不满100个,此时就要找一些新的粒子来补充,那么就用最大权值来填充。(比如现在抛弃了20个粒子,我们就复制20个权值最大的粒子,放到里面填满100个。)——这就是重采样的过程。

[cpp] view plaincopy
  1. particle* resample( particle* particles, int n )  
  2. {  
  3.   particle* new_particles;  
  4.   int i, j, np, k = 0;  
  5.   
  6.   qsort( particles, n, sizeof( particle ), &particle_cmp );//根据权重进行排序  
  7.   new_particles = malloc( n * sizeof( particle ) );  
  8.     for( i = 0; i < n; i++ )  
  9.     {  
  10.         np = cvRound( particles[i].w * n );//淘汰弱权值样本,保留阈值以上样本  
  11.         for( j = 0; j < np; j++ )  
  12.         {  
  13.             new_particles[k++] = particles[i];  
  14.             if( k == n )  
  15.             goto exit;  
  16.         }  
  17.     }  
  18.   while( k < n )  
  19.     new_particles[k++] = particles[0];//复制大权值样本以填充满样本空间  
  20.   
  21.  exit:  
  22.   return new_particles;  
  23. }  

8、更新粒子

将重采样后的100个粒子更新到步骤一4中。至此完成了一次粒子滤波。(需要注意的是,经过一次迭代后,步骤一4中的粒子权值已经不是等权值了)

9、小结

先初始化(完成1、2、3、4),再不停的迭代5、6、7、8过程(即:5—>6—>7—>8—>5......)。

以上是粒子滤波的全部过程,以及一些核心源码,可以认为权重最大的粒子是目标物体。值得关注的是,重采样会降低粒子的多样性(因为是许多粒子是直接复制过来的),这样也会对目标跟踪产生影响,有兴趣的可以继续研究改进算法以保证粒子的多样性。

over!

以上是对粒子滤波的个人见解,至于理论上看着比较复杂的理论公式推导没有体现出来(其实有些概率论知识我还是没看懂),再次膜拜一下Rob Hess大婶,牛的一逼(他也实现了Lowe的SIFT算法并开源代码出来)。

本文所需材料都已打包上传,点击此处下载(配置gsl,请百度):http://download.csdn.net/detail/hujingshuang/8669945

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/313141.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Magicodes.Pay,打造开箱即用的统一支付库,已提供ABP模块封装

Magicodes.Pay&#xff0c;打造开箱即用的统一支付库&#xff0c;已提供ABP模块封装简介Magicodes.Pay&#xff0c;是心莱科技团队提供的统一支付库&#xff0c;相关库均使用.NET标准库编写&#xff0c;支持.NET Framework以及.NET Core。目前已提供Abp模块的封装&#xff0c;支…

在.NET Core 3.0中发布单个Exe文件(PublishSingleFile)

假设我有一个简单的“ Hello World”控制台应用程序&#xff0c;我想发送给朋友来运行。朋友没有安装.NET Core&#xff0c;所以我知道我需要为他构建一个独立的应用程序。很简单&#xff0c;我只需在项目目录中运行以下命令&#xff1a;dotnet publish -r win-x64 -c Release …

python import 问题

https://my.oschina.net/leejun2005/blog/109679 python中&#xff0c;每个py文件被称之为模块&#xff0c;每个具有__init__.py文件的目录被称为包。只要模块或者包所在的目录在sys.path中&#xff0c;就可以使用import 模块或import 包来使用。 如果想使用非当前模块中的…

.NET如何写正确的“抽奖”——数组乱序算法

.NET如何写正确的“抽奖”——数组乱序算法数组乱序算法常用于抽奖等生成临时数据操作。就拿年会抽奖来说&#xff0c;如果你的算法有任何瑕疵&#xff0c;造成了任何不公平&#xff0c;在年会现场 code review时&#xff0c;搞不好不能活着走出去。这个算法听起来很简单&#…

maximum mean discrepancy

http://blog.csdn.net/a1154761720/article/details/51516273 MMD&#xff1a;maximum mean discrepancy。最大平均差异。最先提出的时候用于双样本的检测&#xff08;two-sample test&#xff09;问题&#xff0c;用于判断两个分布p和q是否相同。它的基本假设是&#xff1a;如…

FineUICore基础版部署到docker实战

文 | 蒙古海军司令 合作者FineUI用了好多年&#xff0c;最近出了FineUICore版本&#xff0c;一直没时间是试一下docker&#xff0c;前几天买了一个腾讯云服务器&#xff0c;1核2g&#xff0c;装了centos7.6&#xff0c;开始的时候主要是整个个人博客&#xff0c;在腾讯云安装了…

Hebbian principle理解

http://blog.csdn.net/mao_xiao_feng/article/details/53350798 Hebbian principle 目前图像领域的深度学习&#xff0c;是使用更深的网络提升representation power&#xff0c;从而提高准确率&#xff0c;但是这会导致网络需要更新的参数爆炸式增长&#xff0c;导致两个严重的…

2019全球Microsoft 365开发者训练营(北京站)

Microsoft365介绍&#xff1a;Microsoft365不仅仅是Office 365&#xff0c;它还包括Windows 10操作系统&#xff0c;以及诸多企业级移动和安全应用。它是一套可用于从小型到集团化企业的办公、协作、沟通的企业信息化解决方案。在2017年7月11日举行的Inspire年度合作伙伴大会上…

caffe/common.cu error: function atomicadd has already been defined

http://blog.csdn.NET/houqiqi/article/details/46469981 1, 下载matio(http://sourceforge.NET/projects/matio/) 2,&#xff0c;安装 $ tar zxf matio-X.Y.Z.tar.gz $ cd matio-X.Y.Z $ ./configure $ make $ make check $ make install sudo ldconfig (如果不执行&#x…

微软备战 RPA 市场,Power Platform,Ready GO!

最大赌注就在刚刚&#xff0c;微软在 Microsoft Ignite 2019 大会上&#xff0c;首席执行官萨蒂亚纳德拉&#xff08;Satya Nadella&#xff09;宣布了 Microsoft Power Platform 新平台的发布&#xff0c;并且说到&#xff1a;在与Azure合作方面&#xff0c;微软365&#xff0…

matlab将struct和cell转换成matrices

http://blog.csdn.net/mushiheng/article/details/51525639 之前将数组或者矩阵保存为一个mat格式的文件&#xff0c;在进行load命令读取时&#xff1a; s1load(qiyipuzong.mat)&#xff1b; 得到的s1是struct类型的数据&#xff0c;而我想要的是一个矩阵或者数组。 经过搜…

C# 8 新特性 - 只读struct成员

从C# 8开始&#xff0c;我们可以在struct的成员上使用readonly修饰符。 为struct的成员添加readonly修饰符就表示告诉编译器和开发者该成员不可以修改struct的状态。 看下面这个例子&#xff1a; 这里的ToString()方法不会修改Point这个struct的状态&#xff0c;所以我们可以在…

anaconda2/bin/../lib/libstdc++.so.6: version `GLIBCXX_3.4.20' not found Import No module named googl

http://blog.csdn.net/reallocing1/article/details/53066065 conda install libgcc No module named google protobuf conda install protobuf

.NET Core 3.0 中间件 Middleware

中间件官网文档解释&#xff1a;中间件是一种装配到应用管道以处理请求和响应的软件 每个中间件&#xff1a;选择是否将请求传递到管道中的下一个组件。可在管道中的下一个组件前后执行工作。使用 IApplicationBuilder 创建中间件管道ASP.NET Core 请求管道包含一系列请求委托&…

spyder 护眼背景

Tools->Preferences->Syntax coloring->SpyderDark IPython Console->Dark Background

重磅!微软发布 Visual Studio Online:Web 版 VS Code + 云开发环境

今天&#xff08;北京时间 2019 年 11 月 4 日&#xff09;&#xff0c;在 Microsoft Ignite 2019 大会上&#xff0c;微软正式发布了 Visual Studio Online 公开预览版&#xff01;概览Visual Studio Online 提供了由云服务支撑的开发环境。无论是一个长期项目&#xff0c;或是…

python打印数组中期望元素的位置

http://blog.csdn.net/goofysong/article/details/52160012 print np.where(arrthe number you want) import numpy as np np.set_printoptions(thresholdnp.inf)

Ubuntu Linux将支持所有树莓派设备

Canonical 近期公开了对 Raspberry Pi 4 的支持计划&#xff0c;并表示将支持所有 Raspberry Pi 设备。随着 Ubuntu Server 19.10 版本的发布&#xff0c;Canonical 宣布正式支持 Raspberry Pi 4&#xff0c;Raspberry Pi 4 性能强大&#xff0c;但成本较低&#xff0c;可以在边…

ubuntu分解压缩包

https://blog.gtwang.org/linux/split-large-tar-into-multiple-files-of-certain-size/ 使用 split 分割檔案 如果要將一個大檔案分割成許多個小檔案&#xff0c;可以使用 split 配合 -b 參數指定每個小檔案的大小&#xff0c;並指定輸出檔名的開頭名稱&#xff1a; split …

面试官:你连RESTful都不知道我怎么敢要你?

加个“星标★”&#xff0c;每天11.50&#xff0c;好文必达全文约4000字&#xff0c;预计阅读时间8分钟面试官&#xff1a;了解RESTful吗&#xff1f;01 前言回归正题&#xff0c;看过很多RESTful相关的文章总结&#xff0c;参齐不齐&#xff0c;结合工作中的使用&#xff0c;非…