现在的位置: 首页 > 综合 > 正文

目标跟踪学习笔记_2(particle filter初探1)

2012年07月09日 ⁄ 综合 ⁄ 共 11487字 ⁄ 字号 评论关闭

    首先提供几篇关于粒子滤波算法的博客:
http://www.cnblogs.com/yangyangcv/archive/2010/05/23/1742263.html 这篇博客比较通俗易懂,简单的介绍了粒子滤波的基本工作思想和步骤。
http://www.cnblogs.com/lwbaptx/archive/2011/10/20/2218419.html这篇博客用的是opencv1.0,实现的功能是用粒子滤波跟踪鼠标轨迹,有视频演示,效果还不错。
http://blog.csdn.net/yang_xian521/article/details/6928131 这篇博客是用粒子滤波来做视频目标跟踪的,里面也有opencv2.0的代码,有注释,结构比较清晰。我这里就把他的代码和分析贴出来,里面加了我添加了一些注释,这样看起来更加清晰。
   代码和解释如下:

  1 // particle_demo.cpp : 定义控制台应用程序的入口点。
2 //
3
4 #include "stdafx.h"
5 /************************************************************************/
6 /*
7 Description: 基本的粒子滤波目标跟踪
8 Author: Yang Xian
9 Email: yang_xian521@163.com
10 Version: 2011-11-2
11 History:
12 */
13 /************************************************************************/
14 #include <iostream> // for standard I/O
15 #include <string> // for strings
16 #include <iomanip> // for controlling float print precision
17 #include <sstream> // string to number conversion
18
19 #include <opencv2/imgproc/imgproc.hpp>
20 #include <opencv2/core/core.hpp> // Basic OpenCV structures (cv::Mat, Scalar)
21 #include <opencv2/highgui/highgui.hpp> // OpenCV window I/O
22
23 using namespace cv;
24 using namespace std;
25
26 // 以下这些参数对结果影响很大,而且也会根据视频内容,会对结果有很大的影响
27 const int PARTICLE_NUM = 25; // 粒子个数
28 // 粒子放入的相关区域
29 const double A1 = 2.0;
30 const double A2 = -1.0;
31 const double B0 = 1.0;
32 // 高斯随机数sigma参数
33 const double SIGMA_X = 1.0;
34 const double SIGMA_Y = 0.5;
35 const double SIGMA_SCALE = 0.001;
36
37 // 粒子结构体
38 typedef struct particle {
39 double x; // 当前x坐标
40 double y; // 当前y坐标
41 double scale; // 窗口比例系数
42 double xPre; // x坐标预测位置
43 double yPre; // y坐标预测位置
44 double scalePre; // 窗口预测比例系数
45 double xOri; // 原始x坐标
46 double yOri; // 原始y坐标
47 Rect rect; // 原始区域大小
48 MatND hist; // 粒子区域的特征直方图
49 double weight; // 该粒子的权重
50 } PARTICLE;
51
52 Mat hsv; // hsv色彩空间的输入图像
53 Mat roiImage; // 目标区域
54 MatND roiHist; // 目标区域直方图
55 Mat img; // 输出的目标图像
56 PARTICLE particles[PARTICLE_NUM]; // 粒子
57
58 int nFrameNum = 0;
59
60 bool bSelectObject = false; // 区域选择标志
61 bool bTracking = false; // 开始跟踪标志
62 Point origin; // 鼠标按下时的点位置
63 Rect selection;// 感兴趣的区域大小
64
65 // 直方图相关参数,特征的选取也会对结果影响巨大
66 // Quantize the hue to 30 levels
67 // and the saturation to 32 levels
68 // value to 10 levels
69 int hbins = 180, sbins = 256, vbin = 10;
70 int histSize[] = {hbins, sbins, vbin};
71 // hue varies from 0 to 179, see cvtColor
72 float hranges[] = { 0, 180 };
73 // saturation varies from 0 (black-gray-white) to 255 (pure spectrum color)
74 float sranges[] = { 0, 256 };
75 // value varies from 0 (black-gray-white) to 255 (pure spectrum color)
76 float vranges[] = { 0, 256 };
77 const float* ranges[] = {hranges, sranges, vranges};
78 // we compute the histogram from the 0-th and 1-st channels
79 int channels[] = {0, 1, 2};
80
81 // 鼠标响应函数,得到选择的区域,保存在selection
82 void onMouse(int event, int x, int y, int, void*)
83 {
84 if( bSelectObject )
85 {
86 selection.x = MIN(x, origin.x);
87 selection.y = MIN(y, origin.y);
88 selection.width = std::abs(x - origin.x);
89 selection.height = std::abs(y - origin.y);
90
91 selection &= Rect(0, 0, img.cols, img.rows);
92 }
93
94 switch (event)
95 {
96 case CV_EVENT_LBUTTONDOWN:
97 origin = Point(x,y);
98 selection = Rect(x,y,0,0);
99 bSelectObject = true;
100 bTracking = false;
101 break;
102 case CV_EVENT_LBUTTONUP:
103 bSelectObject = false;
104 // if( selection.width > 0 && selection.height > 0 )
105 bTracking = true;
106 nFrameNum = 0;
107 break;
108 }
109 }
110
111 // 快速排序算法排序函数
112 int particle_cmp(const void* p1,const void* p2)
113 {
114 PARTICLE* _p1 = (PARTICLE*)p1;
115 PARTICLE* _p2 = (PARTICLE*)p2;
116
117 if(_p1->weight < _p2->weight)
118 return 1; //按照权重降序排序
119 if(_p1->weight > _p2->weight)
120 return -1;
121 return 0;
122 }
123
124 const char* keys =
125 {
126 "{1| | 0 | camera number}"
127 };
128
129 int main(int argc, const char **argv)//这里char **argv前必须用const,why?
130 {
131 int delay = 30; // 控制播放速度
132 char c; // 键值
133
134 /*读取avi文件*/
135 VideoCapture captRefrnc("IndoorGTTest1.avi"); // 视频文件
136
137 /*打开摄像头*/
138 //VideoCapture captRefrnc;
139 //CommandLineParser parser(argc, argv, keys);//命令解析器函数
140 //int camNum = parser.get<int>("1");
141 //captRefrnc.open(camNum);
142
143 if ( !captRefrnc.isOpened())
144 {
145 return -1;
146 }
147
148 // Windows
149 const char* WIN_RESULT = "Result";
150 namedWindow(WIN_RESULT, CV_WINDOW_AUTOSIZE);
151 //namedWindow(WIN_RESULT, 0);
152 // namedWindow("Result",0);
153 // 鼠标响应函数
154 //setMouseCallback(WIN_RESULT, onMouse, 0);
155 setMouseCallback("Result", onMouse, 0);
156
157 Mat frame; //视频的每一帧图像
158
159 bool paused = false;
160 PARTICLE * pParticles = particles;//particles为可装PARTICLE_NUM个PARTICLE结构体的数组,所以pParticles为指向其数组的指针
161
162 while(true) //Show the image captured in the window and repeat
163 {
164 if(!paused)
165 {
166 captRefrnc >> frame;
167 if(frame.empty())
168 break;
169 }
170
171 frame.copyTo(img); // 接下来的操作都是对src的
172
173
174 // 选择目标后进行跟踪
175 if (bTracking == true)//鼠标操作选完后
176 {
177 if(!paused)
178 {
179 nFrameNum++;//帧数计数器
180 cvtColor(img, hsv, CV_BGR2HSV);
181 Mat roiImage(hsv, selection); // 目标区域,这个构造函数第二个参数表示截取selection部分
182
183 if (nFrameNum == 1) //选择目标后的第一帧需要初始化
184 {
185 // step 1: 提取目标区域特征,难道其目标特征就是其色调的直方图分布?
186 calcHist(&roiImage, 1, channels, Mat(), roiHist, 3, histSize, ranges);
187 normalize(roiHist, roiHist); // 归一化L2
188
189 // step 2: 初始化particle
190 pParticles = particles;
191 for (int i=0; i<PARTICLE_NUM; i++)
192 {
193 pParticles->x = selection.x + 0.5 * selection.width;
194 pParticles->y = selection.y + 0.5 * selection.height;
195 pParticles->xPre = pParticles->x;
196 pParticles->yPre = pParticles->y;
197 pParticles->xOri = pParticles->x;
198 pParticles->yOri = pParticles->y;
199 pParticles->rect = selection;
200 pParticles->scale = 1.0;
201 pParticles->scalePre = 1.0;
202 pParticles->hist = roiHist;
203 pParticles->weight = 0;
204 pParticles++;
205 }
206 }
207 else //不是第一帧
208 {
209 pParticles = particles;
210 RNG rng;//随机数序列产生器
211 for (int i=0; i<PARTICLE_NUM; i++)
212 {
213 // step 3: 求particle的transition,粒子结构中的参数全部更新过
214 double x, y, s;
215
216 pParticles->xPre = pParticles->x;
217 pParticles->yPre = pParticles->y;
218 pParticles->scalePre = pParticles->scale;
219
220 x = A1 * (pParticles->x - pParticles->xOri) + A2 * (pParticles->xPre - pParticles->xOri) +
221 B0 * rng.gaussian(SIGMA_X) + pParticles->xOri;//以当前点为中心产生高斯分布的粒子
222 pParticles->x = std::max(0.0, std::min(x, img.cols-1.0));//其实就是x,只是考虑了边界在内而已
223
224
225 y = A1 * (pParticles->y - pParticles->yOri) + A2 * (pParticles->yPre - pParticles->yOri) +
226 B0 * rng.gaussian(SIGMA_Y) + pParticles->yOri;
227 pParticles->y = std::max(0.0, std::min(y, img.rows-1.0));
228
229 s = A1 * (pParticles->scale - 1.0) + A2 * (pParticles->scalePre - 1.0) +
230 B0 * rng.gaussian(SIGMA_SCALE) + 1.0;
231 pParticles->scale = std::max(0.1, std::min(s, 3.0));
232 // rect参数有待考证
233 pParticles->rect.x = std::max(0, std::min(cvRound(pParticles->x - 0.5 * pParticles->rect.width * pParticles->scale), img.cols-1)); // 0 <= x <= img.rows-1
234 pParticles->rect.y = std::max(0, std::min(cvRound(pParticles->y - 0.5 * pParticles->rect.height * pParticles->scale), img.rows-1)); // 0 <= y <= img.cols-1
235 pParticles->rect.width = std::min(cvRound(pParticles->rect.width * pParticles->scale), img.cols - pParticles->rect.x);
236 pParticles->rect.height = std::min(cvRound(pParticles->rect.height * pParticles->scale), img.rows - pParticles->rect.y);
237 // Ori参数不改变
238
239 // step 4: 求particle区域的特征直方图
240 Mat imgParticle(img, pParticles->rect);
241 calcHist(&imgParticle, 1, channels, Mat(), pParticles->hist, 3, histSize, ranges);
242 normalize(pParticles->hist, pParticles->hist); // 归一化L2
243
244 // step 5: 特征的比对,更新particle权重
245 //compareHist()函数返回2个直方图之间的相似度,因为参数为CV_COMP_INTERSECT,所以返回的是最小直方图值之和
246 pParticles->weight = compareHist(roiHist, pParticles->hist, CV_COMP_INTERSECT);//其差值直接作为其权值
247
248 pParticles++;
249 }
250
251 // step 6: 归一化粒子权重
252 double sum = 0.0;
253 int i;
254
255 pParticles = particles;
256 for(i=0; i<PARTICLE_NUM; i++)
257 {
258 sum += pParticles->weight;
259 pParticles++;
260 }
261 pParticles = particles;
262 for(i=0; i<PARTICLE_NUM; i++)
263 {
264 pParticles->weight /= sum;
265 pParticles++;
266 }
267
268 // step 7: resample根据粒子的权重的后验概率分布重新采样
269 pParticles = particles;
270 // PARTICLE* newParticles = new PARTICLE[sizeof(PARTICLE) * PARTICLE_NUM];
271 PARTICLE newParticles[PARTICLE_NUM];
272 int np, k = 0;
273
274 //qsort()函数为对数组进行快速排序,其中第4个参数表示的是排序是升序还是降序
275 qsort(pParticles, PARTICLE_NUM, sizeof(PARTICLE), &particle_cmp);//这里采用的是降序排列
276 for(int i=0; i<PARTICLE_NUM; i++)
277 {
278 np = cvRound(particles[i].weight * PARTICLE_NUM);//权值高的优先重采样
279 for(int j=0; j<np; j++)
280 {
281 newParticles[k++] = particles[i];
282 if(k == PARTICLE_NUM)//重采样后达到了个数要求则直接跳出
283 goto EXITOUT;
284 }
285 }
286 while(k < PARTICLE_NUM)
287 {
288 newParticles[k++] = particles[0];//个数不够时,将权值最高的粒子重复给
289 }
290
291 EXITOUT:
292 for (int i=0; i<PARTICLE_NUM; i++)
293 {
294 particles[i] = newParticles[i];
295 }
296
297 }// end else
298
299 qsort(pParticles, PARTICLE_NUM, sizeof(PARTICLE), &particle_cmp);
300
301 // step 8: 计算粒子的期望,作为跟踪结果
302 Rect_<double> rectTrackingTemp(0.0, 0.0, 0.0, 0.0);
303 pParticles = particles;
304 for (int i=0; i<PARTICLE_NUM; i++)
305 {
306 rectTrackingTemp.x += pParticles->rect.x * pParticles->weight;//坐标加上权重的偏移值
307 rectTrackingTemp.y += pParticles->rect.y * pParticles->weight;
308 rectTrackingTemp.width += pParticles->rect.width * pParticles->weight;//宽度也要加上权值的偏移值
309 rectTrackingTemp.height += pParticles->rect.height * pParticles->weight;
310 pParticles++;
311 }
312 Rect rectTracking(rectTrackingTemp); // 跟踪结果
313
314 // 显示各粒子的运动
315 for (int i=0; i<PARTICLE_NUM; i++)
316 {
317 rectangle(img, particles[i].rect, Scalar(255,0,0));
318 }
319 // 显示跟踪结果
320 rectangle(img, rectTracking, Scalar(0,0,255), 3);
321
322 }
323 }// end Tracking
324
325 // imshow(WIN_SRC, frame);
326 imshow(WIN_RESULT, img);
327
328 c = (char)waitKey(delay);
329 if( c == 27 )
330 break;
331 switch(c)
332 {
333 case 'p'://暂停键
334 paused = !paused;
335 break;
336 default:
337 ;
338 }
339 }// end while
340 }

 

   但是用他的代码在进行鼠标选定后就出现如下错误。

     我的工程环境:opencv2.2+vs2010

     经过单步调试跟踪后发现,错误的那一行为:

     pParticles->weight = compareHist(roiHist, pParticles->hist, CV_COMP_INTERSECT);

     去掉该行程序可以正常运行,但是完成不了跟踪功能,其目标跟踪框不会移动,只会慢慢收敛到一个点,因为粒子的权重此时没有更新。

查找资料了很久,函数compareHist()的用法并没有错,也不知道是哪里错了。先工作暂时弄到这里,只要对粒子滤波有个感性认识即可。等过段时间再来真正学粒子滤波算法时完成该演示。

 

修改时间:2012.05.08:

    过了这么久,重新学习粒子滤波时,想解决上面那个遗留下来的问题。事实证明C/C++内存搞死人,上面那个问题debug了2天也不懂哪里出错了。自己又用了不少时间理解程序后敲了一遍代码。那个问题暂时解决了,但是跟踪起来根本无效过。修改后的代码如下:

  1 // particle_tracking.cpp : 定义控制台应用程序的入口点。
2 //
3
4 #include "stdafx.h"
5 #include <opencv2/core/core.hpp>
6 #include "opencv2/imgproc/imgproc.hpp"
7 #include <opencv2/highgui/highgui.hpp>
8 #include <stdio.h>
9 #include <iostream>
10
11 using namespace cv;
12 using namespace std;
13
14 Rect select;
15 bool select_flag=false;
16 bool tracking=false;//跟踪标志位
17 bool select_show=false;
18 Point origin;
19 Mat frame,hsv;
20 int after_select_frames=0;//选择矩形区域完后的帧计数
21
22 /****rgb空间用到的变量****/
23 //int hist_size[]={16,16,16};//rgb空间各维度的bin个数
24 //float rrange[]={0,255.0};
25 //float grange[]={0,255.0};
26 //float brange[]={0,255.0};
27 //const float *ranges[] ={rrange,grange,brange};//range相当于一个二维数组指针
28
29 /****hsv空间用到的变量****/
30 int hist_size[]={16,16,16};
31 float hrange[]={0,180.0};
32 float srange[]={0,256.0};
33 float vrange[]={0,256.0};
34 const float *ranges[]={hrange,srange,vrange};
35
36 int channels[]={0,1,2};
37
38 /****有关粒子窗口变化用到的相关变量****/
39 int A1=2;
40 int A2=-1;
41 int B0=1;
42 double sigmax=1.0;
43 double sigmay=0.5;
44 double sigmas=0.001;
45
46 /****定义使用粒子数目宏****/
47 #define PARTICLE_NUMBER 50 //如果这个数设定太大,比如100,则在运行时将会出现错误
48
49 /****定义粒子结构体****/
50 typedef struct particle
51 {
52 int orix,oriy;//原始粒子坐标
53 int x,y;//当前粒子的坐标
54 double scale;//当前粒子窗口的尺寸
55 int prex,prey;//上一帧粒子的坐标
56 double prescale;//上一帧粒子窗口的尺寸
57 Rect rect;//当前粒子矩形窗口
58 Mat hist;//当前粒子窗口直方图特征
59 double weight;//当前粒子权值
60 }PARTICLE;
61
62 PARTICLE particles[PARTICLE_NUMBER];
63
64 /************************************************************************************************************************/
65 /**** 如果采用这个onMouse()函数的话,则可以画出鼠标拖动矩形框的4种情形 ****/
66 /************************************************************************************************************************/
67 void onMouse(int event,int x,int y,int,void*)
68 {
69 //Point origin;//不能在这个地方进行定义,因为这是基于消息响应的函数,执行完后origin就释放了,所以达不到效果。
70 if(select_flag)
71 {
72 select.x=MIN(origin.x,x);//不一定要等鼠标弹起才计算矩形框,而应该在鼠标按下开始到弹起这段时间实时计算所选矩形框
73 select.y=MIN(origin.y,y);
74 select.width=abs(x-origin.x);//算矩形宽度和高度
75 select.height=abs(y-origin.y);
76 select&=Rect(0,0,frame.cols,frame.rows);//保证所选矩形框在视频显示区域之内
77
78 // rectangle(frame,select,Scalar(0,0,255),3,8,0);//显示手动选择的矩形框
79 }
80 if(event==CV_EVENT_LBUTTONDOWN)
81 {
82 select_flag=true;//鼠标按下的标志赋真值
83 tracking=false;
84 select_show=true;
85 after_select_frames=0;//还没开始选择,或者重新开始选择,计数为0
86 origin=Point(x,y);//保存下来单击是捕捉到的点
87 select=Rect(x,y,0,0);

抱歉!评论已关闭.