基于粒子滤波器的目标跟踪算法及实现

代码实现:

运行方式:按P停止,在前景窗口鼠标点击目标,会自动生成外接矩形,再次按P,对该选定目标进行跟踪。

[cpp] view plaincopy

  1. // TwoLevel.cpp : 定义控制台应用程序的入口点。
  2. //
  3. /************************************************************************/
  4. /*参考文献real-time Multiple Objects Tracking with Occlusion Handling in Dynamic Scenes  */
  5. /************************************************************************/
  6. #include "stdafx.h"
  7. #include <cv.h>
  8. #include <cxcore.h>
  9. #include <highgui.h>
  10. #include <math.h>
  11. # include <time.h>
  12. #include <iostream>
  13. using namespace std;
  14. #define B(image,x,y) ((uchar*)(image->imageData + image->widthStep*(y)))[(x)*3]       //B
  15. #define G(image,x,y) ((uchar*)(image->imageData + image->widthStep*(y)))[(x)*3+1] //G
  16. #define R(image,x,y) ((uchar*)(image->imageData + image->widthStep*(y)))[(x)*3+2] //R
  17. #define S(image,x,y) ((uchar*)(image->imageData + image->widthStep*(y)))[(x)]
  18. #define  Num 10  //帧差的间隔
  19. #define  T 40    //Tf
  20. #define Re 30     //
  21. #define ai 0.08   //学习率
  22. #define CONTOUR_MAX_AREA 10000
  23. #define CONTOUR_MIN_AREA 50
  24. # define R_BIN      8  /* 红色分量的直方图条数 */
  25. # define G_BIN      8  /* 绿色分量的直方图条数 */
  26. # define B_BIN      8  /* 兰色分量的直方图条数 */
  27. # define R_SHIFT    5  /* 与上述直方图条数对应 */
  28. # define G_SHIFT    5  /* 的R、G、B分量左移位数 */
  29. # define B_SHIFT    5  /* log2( 256/8 )为移动位数 */
  30. /*
  31. 采用Park and Miller方法产生[0,1]之间均匀分布的伪随机数
  32. 算法详细描述见:
  33. [1] NUMERICAL RECIPES IN C: THE ART OF SCIENTIFIC COMPUTING.
  34. Cambridge University Press. 1992. pp.278-279.
  35. [2] Park, S.K., and Miller, K.W. 1988, Communications of the ACM,
  36. vol. 31, pp. 1192–1201.
  37. */
  38. #define IA 16807
  39. #define IM 2147483647
  40. #define AM (1.0/IM)
  41. #define IQ 127773
  42. #define IR 2836
  43. #define MASK 123459876
  44. typedef struct __SpaceState {  /* 状态空间变量 */
  45. int xt;               /* x坐标位置 */
  46. int yt;               /* x坐标位置 */
  47. float v_xt;           /* x方向运动速度 */
  48. float v_yt;           /* y方向运动速度 */
  49. int Hxt;              /* x方向半窗宽 */
  50. int Hyt;              /* y方向半窗宽 */
  51. float at_dot;         /* 尺度变换速度 */
  52. } SPACESTATE;
  53. bool pause=false;//是否暂停
  54. bool track = false;//是否跟踪
  55. IplImage *curframe=NULL;
  56. IplImage *pBackImg=NULL;
  57. IplImage *pFrontImg=NULL;
  58. IplImage *pTrackImg =NULL;
  59. unsigned char * img;//把iplimg改到char*  便于计算
  60. int xin,yin;//跟踪时输入的中心点
  61. int xout,yout;//跟踪时得到的输出中心点
  62. int Wid,Hei;//图像的大小
  63. int WidIn,HeiIn;//输入的半宽与半高
  64. int WidOut,HeiOut;//输出的半宽与半高
  65. long ran_seed = 802163120; /* 随机数种子,为全局变量,设置缺省值 */
  66. float DELTA_T = (float)0.05;    /* 帧频,可以为30,25,15,10等 */
  67. int POSITION_DISTURB = 15;      /* 位置扰动幅度   */
  68. float VELOCITY_DISTURB = 40.0;  /* 速度扰动幅值   */
  69. float SCALE_DISTURB = 0.0;      /* 窗宽高扰动幅度 */
  70. float SCALE_CHANGE_D = (float)0.001;   /* 尺度变换速度扰动幅度 */
  71. int NParticle = 75;       /* 粒子个数   */
  72. float * ModelHist = NULL; /* 模型直方图 */
  73. SPACESTATE * states = NULL;  /* 状态数组 */
  74. float * weights = NULL;   /* 每个粒子的权重 */
  75. int nbin;                 /* 直方图条数 */
  76. float Pi_Thres = (float)0.90; /* 权重阈值   */
  77. float Weight_Thres = (float)0.0001;  /* 最大权重阈值,用来判断是否目标丢失 */
  78. /*
  79. 设置种子数
  80. 一般利用系统时间来进行设置,也可以直接传入一个long型整数
  81. */
  82. long set_seed( long setvalue )
  83. {
  84. if ( setvalue != 0 ) /* 如果传入的参数setvalue!=0,设置该数为种子 */
  85. ran_seed = setvalue;
  86. else                 /* 否则,利用系统时间为种子数 */
  87. {
  88. ran_seed = time(NULL);
  89. }
  90. return( ran_seed );
  91. }
  92. /*
  93. 计算一幅图像中某个区域的彩色直方图分布
  94. 输入参数:
  95. int x0, y0:           指定图像区域的中心点
  96. int Wx, Hy:           指定图像区域的半宽和半高
  97. unsigned char * image:图像数据,按从左至右,从上至下的顺序扫描,
  98. 颜色排列次序:RGB, RGB, ...
  99. (或者:YUV, YUV, ...)
  100. int W, H:             图像的宽和高
  101. 输出参数:
  102. float * ColorHist:    彩色直方图,颜色索引按:
  103. i = r * G_BIN * B_BIN + g * B_BIN + b排列
  104. int bins:             彩色直方图的条数R_BIN*G_BIN*B_BIN(这里取8x8x8=512)
  105. */
  106. void CalcuColorHistogram( int x0, int y0, int Wx, int Hy,
  107. unsigned char * image, int W, int H,
  108. float * ColorHist, int bins )
  109. {
  110. int x_begin, y_begin;  /* 指定图像区域的左上角坐标 */
  111. int y_end, x_end;
  112. int x, y, i, index;
  113. int r, g, b;
  114. float k, r2, f;
  115. int a2;
  116. for ( i = 0; i < bins; i++ )     /* 直方图各个值赋0 */
  117. ColorHist[i] = 0.0;
  118. /* 考虑特殊情况:x0, y0在图像外面,或者,Wx<=0, Hy<=0 */
  119. /* 此时强制令彩色直方图为0 */
  120. if ( ( x0 < 0 ) || (x0 >= W) || ( y0 < 0 ) || ( y0 >= H )
  121. || ( Wx <= 0 ) || ( Hy <= 0 ) ) return;
  122. x_begin = x0 - Wx;               /* 计算实际高宽和区域起始点 */
  123. y_begin = y0 - Hy;
  124. if ( x_begin < 0 ) x_begin = 0;
  125. if ( y_begin < 0 ) y_begin = 0;
  126. x_end = x0 + Wx;
  127. y_end = y0 + Hy;
  128. if ( x_end >= W ) x_end = W-1;
  129. if ( y_end >= H ) y_end = H-1;
  130. a2 = Wx*Wx+Hy*Hy;                /* 计算核函数半径平方a^2 */
  131. f = 0.0;                         /* 归一化系数 */
  132. for ( y = y_begin; y <= y_end; y++ )
  133. for ( x = x_begin; x <= x_end; x++ )
  134. {
  135. r = image[(y*W+x)*3] >> R_SHIFT;   /* 计算直方图 */
  136. g = image[(y*W+x)*3+1] >> G_SHIFT; /*移位位数根据R、G、B条数 */
  137. b = image[(y*W+x)*3+2] >> B_SHIFT;
  138. index = r * G_BIN * B_BIN + g * B_BIN + b;
  139. r2 = (float)(((y-y0)*(y-y0)+(x-x0)*(x-x0))*1.0/a2); /* 计算半径平方r^2 */
  140. k = 1 - r2;   /* 核函数k(r) = 1-r^2, |r| < 1; 其他值 k(r) = 0 */
  141. f = f + k;
  142. ColorHist[index] = ColorHist[index] + k;  /* 计算核密度加权彩色直方图 */
  143. }
  144. for ( i = 0; i < bins; i++ )     /* 归一化直方图 */
  145. ColorHist[i] = ColorHist[i]/f;
  146. return;
  147. }
  148. /*
  149. 计算Bhattacharyya系数
  150. 输入参数:
  151. float * p, * q:      两个彩色直方图密度估计
  152. int bins:            直方图条数
  153. 返回值:
  154. Bhattacharyya系数
  155. */
  156. float CalcuBhattacharyya( float * p, float * q, int bins )
  157. {
  158. int i;
  159. float rho;
  160. rho = 0.0;
  161. for ( i = 0; i < bins; i++ )
  162. rho = (float)(rho + sqrt( p[i]*q[i] ));
  163. return( rho );
  164. }
  165. /*# define RECIP_SIGMA  3.98942280401  / * 1/(sqrt(2*pi)*sigma), 这里sigma = 0.1 * /*/
  166. # define SIGMA2       0.02           /* 2*sigma^2, 这里sigma = 0.1 */
  167. float CalcuWeightedPi( float rho )
  168. {
  169. float pi_n, d2;
  170. d2 = 1 - rho;
  171. //pi_n = (float)(RECIP_SIGMA * exp( - d2/SIGMA2 ));
  172. pi_n = (float)(exp( - d2/SIGMA2 ));
  173. return( pi_n );
  174. }
  175. /*
  176. 采用Park and Miller方法产生[0,1]之间均匀分布的伪随机数
  177. 算法详细描述见:
  178. [1] NUMERICAL RECIPES IN C: THE ART OF SCIENTIFIC COMPUTING.
  179. Cambridge University Press. 1992. pp.278-279.
  180. [2] Park, S.K., and Miller, K.W. 1988, Communications of the ACM,
  181. vol. 31, pp. 1192–1201.
  182. */
  183. float ran0(long *idum)
  184. {
  185. long k;
  186. float ans;
  187. /* *idum ^= MASK;*/      /* XORing with MASK allows use of zero and other */
  188. k=(*idum)/IQ;            /* simple bit patterns for idum.                 */
  189. *idum=IA*(*idum-k*IQ)-IR*k;  /* Compute idum=(IA*idum) % IM without over- */
  190. if (*idum < 0) *idum += IM;  /* flows by Schrage’s method.               */
  191. ans=AM*(*idum);          /* Convert idum to a floating result.            */
  192. /* *idum ^= MASK;*/      /* Unmask before return.                         */
  193. return ans;
  194. }
  195. /*
  196. 获得一个[0,1]之间均匀分布的随机数
  197. */
  198. float rand0_1()
  199. {
  200. return( ran0( &ran_seed ) );
  201. }
  202. /*
  203. 获得一个x - N(u,sigma)Gaussian分布的随机数
  204. */
  205. float randGaussian( float u, float sigma )
  206. {
  207. float x1, x2, v1, v2;
  208. float s = 100.0;
  209. float y;
  210. /*
  211. 使用筛选法产生正态分布N(0,1)的随机数(Box-Mulles方法)
  212. 1. 产生[0,1]上均匀随机变量X1,X2
  213. 2. 计算V1=2*X1-1,V2=2*X2-1,s=V1^2+V2^2
  214. 3. 若s<=1,转向步骤4,否则转1
  215. 4. 计算A=(-2ln(s)/s)^(1/2),y1=V1*A, y2=V2*A
  216. y1,y2为N(0,1)随机变量
  217. */
  218. while ( s > 1.0 )
  219. {
  220. x1 = rand0_1();
  221. x2 = rand0_1();
  222. v1 = 2 * x1 - 1;
  223. v2 = 2 * x2 - 1;
  224. s = v1*v1 + v2*v2;
  225. }
  226. y = (float)(sqrt( -2.0 * log(s)/s ) * v1);
  227. /*
  228. 根据公式
  229. z = sigma * y + u
  230. 将y变量转换成N(u,sigma)分布
  231. */
  232. return( sigma * y + u );
  233. }
  234. /*
  235. 初始化系统
  236. int x0, y0:        初始给定的图像目标区域坐标
  237. int Wx, Hy:        目标的半宽高
  238. unsigned char * img:图像数据,RGB形式
  239. int W, H:          图像宽高
  240. */
  241. int Initialize( int x0, int y0, int Wx, int Hy,
  242. unsigned char * img, int W, int H )
  243. {
  244. int i, j;
  245. float rn[7];
  246. set_seed( 0 ); /* 使用系统时钟作为种子,这个函数在 */
  247. /* 系统初始化时候要调用一次,且仅调用1次 */
  248. //NParticle = 75; /* 采样粒子个数 */
  249. //Pi_Thres = (float)0.90; /* 设置权重阈值 */
  250. states = new SPACESTATE [NParticle]; /* 申请状态数组的空间 */
  251. if ( states == NULL ) return( -2 );
  252. weights = new float [NParticle];     /* 申请粒子权重数组的空间 */
  253. if ( weights == NULL ) return( -3 );
  254. nbin = R_BIN * G_BIN * B_BIN; /* 确定直方图条数 */
  255. ModelHist = new float [nbin]; /* 申请直方图内存 */
  256. if ( ModelHist == NULL ) return( -1 );
  257. /* 计算目标模板直方图 */
  258. CalcuColorHistogram( x0, y0, Wx, Hy, img, W, H, ModelHist, nbin );
  259. /* 初始化粒子状态(以(x0,y0,1,1,Wx,Hy,0.1)为中心呈N(0,0.4)正态分布) */
  260. states[0].xt = x0;
  261. states[0].yt = y0;
  262. states[0].v_xt = (float)0.0; // 1.0
  263. states[0].v_yt = (float)0.0; // 1.0
  264. states[0].Hxt = Wx;
  265. states[0].Hyt = Hy;
  266. states[0].at_dot = (float)0.0; // 0.1
  267. weights[0] = (float)(1.0/NParticle); /* 0.9; */
  268. for ( i = 1; i < NParticle; i++ )
  269. {
  270. for ( j = 0; j < 7; j++ ) rn[j] = randGaussian( 0, (float)0.6 ); /* 产生7个随机高斯分布的数 */
  271. states[i].xt = (int)( states[0].xt + rn[0] * Wx );
  272. states[i].yt = (int)( states[0].yt + rn[1] * Hy );
  273. states[i].v_xt = (float)( states[0].v_xt + rn[2] * VELOCITY_DISTURB );
  274. states[i].v_yt = (float)( states[0].v_yt + rn[3] * VELOCITY_DISTURB );
  275. states[i].Hxt = (int)( states[0].Hxt + rn[4] * SCALE_DISTURB );
  276. states[i].Hyt = (int)( states[0].Hyt + rn[5] * SCALE_DISTURB );
  277. states[i].at_dot = (float)( states[0].at_dot + rn[6] * SCALE_CHANGE_D );
  278. /* 权重统一为1/N,让每个粒子有相等的机会 */
  279. weights[i] = (float)(1.0/NParticle);
  280. }
  281. return( 1 );
  282. }
  283. /*
  284. 计算归一化累计概率c‘_i
  285. 输入参数:
  286. float * weight:    为一个有N个权重(概率)的数组
  287. int N:             数组元素个数
  288. 输出参数:
  289. float * cumulateWeight: 为一个有N+1个累计权重的数组,
  290. cumulateWeight[0] = 0;
  291. */
  292. void NormalizeCumulatedWeight( float * weight, float * cumulateWeight, int N )
  293. {
  294. int i;
  295. for ( i = 0; i < N+1; i++ )
  296. cumulateWeight[i] = 0;
  297. for ( i = 0; i < N; i++ )
  298. cumulateWeight[i+1] = cumulateWeight[i] + weight[i];
  299. for ( i = 0; i < N+1; i++ )
  300. cumulateWeight[i] = cumulateWeight[i]/ cumulateWeight[N];
  301. return;
  302. }
  303. /*
  304. 折半查找,在数组NCumuWeight[N]中寻找一个最小的j,使得
  305. NCumuWeight[j] <=v
  306. float v:              一个给定的随机数
  307. float * NCumuWeight:  权重数组
  308. int N:                数组维数
  309. 返回值:
  310. 数组下标序号
  311. */
  312. int BinearySearch( float v, float * NCumuWeight, int N )
  313. {
  314. int l, r, m;
  315. l = 0;  r = N-1;   /* extreme left and extreme right components‘ indexes */
  316. while ( r >= l)
  317. {
  318. m = (l+r)/2;
  319. if ( v >= NCumuWeight[m] && v < NCumuWeight[m+1] ) return( m );
  320. if ( v < NCumuWeight[m] ) r = m - 1;
  321. else l = m + 1;
  322. }
  323. return( 0 );
  324. }
  325. /*
  326. 重新进行重要性采样
  327. 输入参数:
  328. float * c:          对应样本权重数组pi(n)
  329. int N:              权重数组、重采样索引数组元素个数
  330. 输出参数:
  331. int * ResampleIndex:重采样索引数组
  332. */
  333. void ImportanceSampling( float * c, int * ResampleIndex, int N )
  334. {
  335. float rnum, * cumulateWeight;
  336. int i, j;
  337. cumulateWeight = new float [N+1]; /* 申请累计权重数组内存,大小为N+1 */
  338. NormalizeCumulatedWeight( c, cumulateWeight, N ); /* 计算累计权重 */
  339. for ( i = 0; i < N; i++ )
  340. {
  341. rnum = rand0_1();       /* 随机产生一个[0,1]间均匀分布的数 */
  342. j = BinearySearch( rnum, cumulateWeight, N+1 ); /* 搜索<=rnum的最小索引j */
  343. if ( j == N ) j--;
  344. ResampleIndex[i] = j;   /* 放入重采样索引数组 */
  345. }
  346. delete cumulateWeight;
  347. return;
  348. }
  349. /*
  350. 样本选择,从N个输入样本中根据权重重新挑选出N个
  351. 输入参数:
  352. SPACESTATE * state:     原始样本集合(共N个)
  353. float * weight:         N个原始样本对应的权重
  354. int N:                  样本个数
  355. 输出参数:
  356. SPACESTATE * state:     更新过的样本集
  357. */
  358. void ReSelect( SPACESTATE * state, float * weight, int N )
  359. {
  360. SPACESTATE * tmpState;
  361. int i, * rsIdx;
  362. tmpState = new SPACESTATE[N];
  363. rsIdx = new int[N];
  364. ImportanceSampling( weight, rsIdx, N ); /* 根据权重重新采样 */
  365. for ( i = 0; i < N; i++ )
  366. tmpState[i] = state[rsIdx[i]];//temState为临时变量,其中state[i]用state[rsIdx[i]]来代替
  367. for ( i = 0; i < N; i++ )
  368. state[i] = tmpState[i];
  369. delete[] tmpState;
  370. delete[] rsIdx;
  371. return;
  372. }
  373. /*
  374. 传播:根据系统状态方程求取状态预测量
  375. 状态方程为: S(t) = A S(t-1) + W(t-1)
  376. W(t-1)为高斯噪声
  377. 输入参数:
  378. SPACESTATE * state:      待求的状态量数组
  379. int N:                   待求状态个数
  380. 输出参数:
  381. SPACESTATE * state:      更新后的预测状态量数组
  382. */
  383. void Propagate( SPACESTATE * state, int N)
  384. {
  385. int i;
  386. int j;
  387. float rn[7];
  388. /* 对每一个状态向量state[i](共N个)进行更新 */
  389. for ( i = 0; i < N; i++ )  /* 加入均值为0的随机高斯噪声 */
  390. {
  391. for ( j = 0; j < 7; j++ ) rn[j] = randGaussian( 0, (float)0.6 ); /* 产生7个随机高斯分布的数 */
  392. state[i].xt = (int)(state[i].xt + state[i].v_xt * DELTA_T + rn[0] * state[i].Hxt + 0.5);
  393. state[i].yt = (int)(state[i].yt + state[i].v_yt * DELTA_T + rn[1] * state[i].Hyt + 0.5);
  394. state[i].v_xt = (float)(state[i].v_xt + rn[2] * VELOCITY_DISTURB);
  395. state[i].v_yt = (float)(state[i].v_yt + rn[3] * VELOCITY_DISTURB);
  396. state[i].Hxt = (int)(state[i].Hxt+state[i].Hxt*state[i].at_dot + rn[4] * SCALE_DISTURB + 0.5);
  397. state[i].Hyt = (int)(state[i].Hyt+state[i].Hyt*state[i].at_dot + rn[5] * SCALE_DISTURB + 0.5);
  398. state[i].at_dot = (float)(state[i].at_dot + rn[6] * SCALE_CHANGE_D);
  399. cvCircle(pTrackImg,cvPoint(state[i].xt,state[i].yt),3, CV_RGB(0,255,0),-1);
  400. }
  401. return;
  402. }
  403. /*
  404. 观测,根据状态集合St中的每一个采样,观测直方图,然后
  405. 更新估计量,获得新的权重概率
  406. 输入参数:
  407. SPACESTATE * state:      状态量数组
  408. int N:                   状态量数组维数
  409. unsigned char * image:   图像数据,按从左至右,从上至下的顺序扫描,
  410. 颜色排列次序:RGB, RGB, ...
  411. int W, H:                图像的宽和高
  412. float * ObjectHist:      目标直方图
  413. int hbins:               目标直方图条数
  414. 输出参数:
  415. float * weight:          更新后的权重
  416. */
  417. void Observe( SPACESTATE * state, float * weight, int N,
  418. unsigned char * image, int W, int H,
  419. float * ObjectHist, int hbins )
  420. {
  421. int i;
  422. float * ColorHist;
  423. float rho;
  424. ColorHist = new float[hbins];
  425. for ( i = 0; i < N; i++ )
  426. {
  427. /* (1) 计算彩色直方图分布 */
  428. CalcuColorHistogram( state[i].xt, state[i].yt,state[i].Hxt, state[i].Hyt,
  429. image, W, H, ColorHist, hbins );
  430. /* (2) Bhattacharyya系数 */
  431. rho = CalcuBhattacharyya( ColorHist, ObjectHist, hbins );
  432. /* (3) 根据计算得的Bhattacharyya系数计算各个权重值 */
  433. weight[i] = CalcuWeightedPi( rho );
  434. }
  435. delete ColorHist;
  436. return;
  437. }
  438. /*
  439. 估计,根据权重,估计一个状态量作为跟踪输出
  440. 输入参数:
  441. SPACESTATE * state:      状态量数组
  442. float * weight:          对应权重
  443. int N:                   状态量数组维数
  444. 输出参数:
  445. SPACESTATE * EstState:   估计出的状态量
  446. */
  447. void Estimation( SPACESTATE * state, float * weight, int N,
  448. SPACESTATE & EstState )
  449. {
  450. int i;
  451. float at_dot, Hxt, Hyt, v_xt, v_yt, xt, yt;
  452. float weight_sum;
  453. at_dot = 0;
  454. Hxt = 0;    Hyt = 0;
  455. v_xt = 0;   v_yt = 0;
  456. xt = 0;     yt = 0;
  457. weight_sum = 0;
  458. for ( i = 0; i < N; i++ ) /* 求和 */
  459. {
  460. at_dot += state[i].at_dot * weight[i];
  461. Hxt += state[i].Hxt * weight[i];
  462. Hyt += state[i].Hyt * weight[i];
  463. v_xt += state[i].v_xt * weight[i];
  464. v_yt += state[i].v_yt * weight[i];
  465. xt += state[i].xt * weight[i];
  466. yt += state[i].yt * weight[i];
  467. weight_sum += weight[i];
  468. }
  469. /* 求平均 */
  470. if ( weight_sum <= 0 ) weight_sum = 1; /* 防止被0除,一般不会发生 */
  471. EstState.at_dot = at_dot/weight_sum;
  472. EstState.Hxt = (int)(Hxt/weight_sum + 0.5 );
  473. EstState.Hyt = (int)(Hyt/weight_sum + 0.5 );
  474. EstState.v_xt = v_xt/weight_sum;
  475. EstState.v_yt = v_yt/weight_sum;
  476. EstState.xt = (int)(xt/weight_sum + 0.5 );
  477. EstState.yt = (int)(yt/weight_sum + 0.5 );
  478. return;
  479. }
  480. /************************************************************
  481. 模型更新
  482. 输入参数:
  483. SPACESTATE EstState:   状态量的估计值
  484. float * TargetHist:    目标直方图
  485. int bins:              直方图条数
  486. float PiT:             阈值(权重阈值)
  487. unsigned char * img:   图像数据,RGB形式
  488. int W, H:              图像宽高
  489. 输出:
  490. float * TargetHist:    更新的目标直方图
  491. ************************************************************/
  492. # define ALPHA_COEFFICIENT      0.2     /* 目标模型更新权重取0.1-0.3 */
  493. int ModelUpdate( SPACESTATE EstState, float * TargetHist, int bins, float PiT,
  494. unsigned char * img, int W, int H )
  495. {
  496. float * EstHist, Bha, Pi_E;
  497. int i, rvalue = -1;
  498. EstHist = new float [bins];
  499. /* (1)在估计值处计算目标直方图 */
  500. CalcuColorHistogram( EstState.xt, EstState.yt, EstState.Hxt,
  501. EstState.Hyt, img, W, H, EstHist, bins );
  502. /* (2)计算Bhattacharyya系数 */
  503. Bha  = CalcuBhattacharyya( EstHist, TargetHist, bins );
  504. /* (3)计算概率权重 */
  505. Pi_E = CalcuWeightedPi( Bha );
  506. if ( Pi_E > PiT )
  507. {
  508. for ( i = 0; i < bins; i++ )
  509. {
  510. TargetHist[i] = (float)((1.0 - ALPHA_COEFFICIENT) * TargetHist[i]
  511. + ALPHA_COEFFICIENT * EstHist[i]);
  512. }
  513. rvalue = 1;
  514. }
  515. delete EstHist;
  516. return( rvalue );
  517. }
  518. /*
  519. 系统清除
  520. */
  521. void ClearAll()
  522. {
  523. if ( ModelHist != NULL ) delete [] ModelHist;
  524. if ( states != NULL ) delete [] states;
  525. if ( weights != NULL ) delete [] weights;
  526. return;
  527. }
  528. /**********************************************************************
  529. 基于彩色直方图的粒子滤波算法总流程
  530. 输入参数:
  531. unsigned char * img: 图像数据,RGB形式
  532. int W, H:            图像宽高
  533. 输出参数:
  534. int &xc, &yc:        找到的图像目标区域中心坐标
  535. int &Wx_h, &Hy_h:    找到的目标的半宽高
  536. float &max_weight:   最大权重值
  537. 返回值:
  538. 成功1,否则-1
  539. 基于彩色直方图的粒子滤波跟踪算法的完整使用方法为:
  540. (1)读取彩色视频中的1帧,并确定初始区域,以此获得该区域的中心点、
  541. 目标的半高、宽,和图像数组(RGB形式)、图像高宽参数。
  542. 采用初始化函数进行初始化
  543. int Initialize( int x0, int y0, int Wx, int Hy,
  544. unsigned char * img, int W, int H )
  545. (2)循环调用下面函数,直到N帧图像结束
  546. int ColorParticleTracking( unsigned char * image, int W, int H,
  547. int & xc, int & yc, int & Wx_h, int & Hy_h )
  548. 每次调用的输出为:目标中心坐标和目标的半高宽
  549. 如果函数返回值<0,则表明目标丢失。
  550. (3)清除系统各个变量,结束跟踪
  551. void ClearAll()
  552. **********************************************************************/
  553. int ColorParticleTracking( unsigned char * image, int W, int H,
  554. int & xc, int & yc, int & Wx_h, int & Hy_h,
  555. float & max_weight)
  556. {
  557. SPACESTATE EState;
  558. int i;
  559. /* 选择:选择样本,并进行重采样 */
  560. ReSelect( states, weights, NParticle );
  561. /* 传播:采样状态方程,对状态变量进行预测 */
  562. Propagate( states, NParticle);
  563. /* 观测:对状态量进行更新 */
  564. Observe( states, weights, NParticle, image, W, H,
  565. ModelHist, nbin );
  566. /* 估计:对状态量进行估计,提取位置量 */
  567. Estimation( states, weights, NParticle, EState );
  568. xc = EState.xt;
  569. yc = EState.yt;
  570. Wx_h = EState.Hxt;
  571. Hy_h = EState.Hyt;
  572. /* 模型更新 */
  573. ModelUpdate( EState, ModelHist, nbin, Pi_Thres, image, W, H );
  574. /* 计算最大权重值 */
  575. max_weight = weights[0];
  576. for ( i = 1; i < NParticle; i++ )
  577. max_weight = max_weight < weights[i] ? weights[i] : max_weight;
  578. /* 进行合法性检验,不合法返回-1 */
  579. if ( xc < 0 || yc < 0 || xc >= W || yc >= H ||
  580. Wx_h <= 0 || Hy_h <= 0 ) return( -1 );
  581. else
  582. return( 1 );
  583. }
  584. //把iplimage 转到img 数组中,BGR->RGB
  585. void IplToImge(IplImage* src, int w,int h)
  586. {
  587. int i,j;
  588. for ( j = 0; j < h; j++ ) // 转成正向图像
  589. for ( i = 0; i < w; i++ )
  590. {
  591. img[ ( j*w+i )*3 ] = R(src,i,j);
  592. img[ ( j*w+i )*3+1 ] = G(src,i,j);
  593. img[ ( j*w+i )*3+2 ] = B(src,i,j);
  594. }
  595. }
  596. void mouseHandler(int event, int x, int y, int flags, void* param)//在这里要注意到要再次调用cvShowImage,才能显示方框
  597. {
  598. CvMemStorage* storage = cvCreateMemStorage(0);
  599. CvSeq * contours;
  600. IplImage* pFrontImg1 = 0;
  601. int centerX,centerY;
  602. int delt = 10;
  603. pFrontImg1=cvCloneImage(pFrontImg);//这里也要注意到如果在 cvShowImage("foreground",pFrontImg1)中用pFrontImg产效果,得重新定义并复制
  604. switch(event){
  605. case CV_EVENT_LBUTTONDOWN:
  606. //printf("laskjfkoasfl\n");
  607. //寻找轮廓
  608. if(pause)
  609. {
  610. cvFindContours(pFrontImg,storage,&contours,sizeof(CvContour),CV_RETR_EXTERNAL,
  611. CV_CHAIN_APPROX_SIMPLE);
  612. //在原场景中绘制目标轮廓的外接矩形
  613. for (;contours;contours = contours->h_next)
  614. {
  615. CvRect r = ((CvContour*)contours)->rect;
  616. if(x>r.x&&x<(r.x+r.width)&&y>r.y&&r.y<(r.y+r.height))
  617. {
  618. if (r.height*r.width>CONTOUR_MIN_AREA && r.height*r.width<CONTOUR_MAX_AREA)
  619. {
  620. centerX = r.x+r.width/2;//得到目标中心点
  621. centerY = r.y+r.height/2;
  622. WidIn = r.width/2;//得到目标半宽与半高
  623. HeiIn = r.height/2;
  624. xin = centerX;
  625. yin = centerY;
  626. cvRectangle(pFrontImg1,cvPoint(r.x,r.y),cvPoint(r.x+r.width,r.y+r.height),cvScalar(255,255,255),2,8,0);
  627. //Initial_MeanShift_tracker(centerX,centerY,WidIn,HeiIn,img,Wid,Hei,1./delt);  //初始化跟踪变量
  628. /* 初始化跟踪器 */
  629. Initialize( centerX, centerY, WidIn, HeiIn, img, Wid, Hei );
  630. track = true;//进行跟踪
  631. cvShowImage("foreground",pFrontImg1);
  632. return;
  633. }
  634. }
  635. }
  636. }
  637. break;
  638. case CV_EVENT_LBUTTONUP:
  639. printf("Left button up\n");
  640. break;
  641. }
  642. }
  643. //void on_mouse(int event, int x, int y, int flags, void *param)
  644. //{
  645. //  if(!image)
  646. //      return ;
  647. //  if(image->origin)
  648. //  {
  649. //      image->origin = 0;
  650. //      y = image->height - y;
  651. //  }
  652. //  if(selecting) //正在选择物体
  653. //  {
  654. //      selection.x = MIN(x,origin.x);
  655. //      selection.y = MIN(y,origin.y);
  656. //      selection.width = selection.x + CV_IABS(x - origin.x);
  657. //      selection.height = selection.y + CV_IABS(y - origin.y);
  658. //
  659. //      selection.x = MAX(selection.x ,0);
  660. //      selection.y = MAX(selection.y,0);
  661. //      selection.width = MIN(selection.width,image->width);
  662. //      selection.height = MIN(selection.height,image->height);
  663. //      selection.width -= selection.x;
  664. //      selection.height -= selection.y;
  665. //  }
  666. //  switch(event)
  667. //  {
  668. //  case CV_EVENT_LBUTTONDOWN:
  669. //      origin = cvPoint(x,y);
  670. //      selection = cvRect(x,y,0,0);
  671. //      selecting = 1;
  672. //      break;
  673. //  case CV_EVENT_LBUTTONUP:
  674. //      selecting = 0;
  675. //      if(selection.width >0 && selection.height >0)
  676. //          selected = 1;
  677. //      break;
  678. //  }
  679. //}
  680. void main()
  681. {
  682. int FrameNum=0;  //帧号
  683. int k=0;
  684. CvCapture *capture = cvCreateFileCapture("test.avi");
  685. char res1[20],res2[20];
  686. //CvCapture *capture = cvCreateFileCapture("test1.avi");
  687. //CvCapture *capture = cvCreateFileCapture("camera1_mov.avi");
  688. IplImage* frame[Num]; //用来存放图像
  689. int i,j;
  690. uchar key = false;      //用来设置暂停
  691. float rho_v;//表示相似度
  692. float max_weight;
  693. int sum=0;    //用来存放两图像帧差后的值
  694. for (i=0;i<Num;i++)
  695. {
  696. frame[i]=NULL;
  697. }
  698. IplImage *curFrameGray=NULL;
  699. IplImage *frameGray=NULL;
  700. CvMat *Mat_D,*Mat_F;   //动态矩阵与帧差后矩阵
  701. int row ,col;
  702. cvNamedWindow("video",1);
  703. cvNamedWindow("background",1);
  704. cvNamedWindow("foreground",1);
  705. cvNamedWindow("tracking",1);
  706. cvSetMouseCallback("tracking",mouseHandler,0);//响应鼠标
  707. while (capture)
  708. {
  709. curframe=cvQueryFrame(capture); //抓取一帧
  710. if(FrameNum<Num)
  711. {
  712. if(FrameNum==0)//第一帧时初始化过程
  713. {
  714. curFrameGray=cvCreateImage(cvGetSize(curframe),IPL_DEPTH_8U,1);
  715. frameGray=cvCreateImage(cvGetSize(curframe),IPL_DEPTH_8U,1);
  716. pBackImg=cvCreateImage(cvGetSize(curframe),IPL_DEPTH_8U,1);
  717. pFrontImg=cvCreateImage(cvGetSize(curframe),IPL_DEPTH_8U,1);
  718. pTrackImg = cvCreateImage(cvGetSize(curframe),IPL_DEPTH_8U,3);
  719. cvSetZero(pFrontImg);
  720. cvCvtColor(curframe,pBackImg,CV_RGB2GRAY);
  721. row=curframe->height;
  722. col=curframe->width;
  723. Mat_D=cvCreateMat(row,col,CV_32FC1);
  724. cvSetZero(Mat_D);
  725. Mat_F=cvCreateMat(row,col,CV_32FC1);
  726. cvSetZero(Mat_F);
  727. Wid = curframe->width;
  728. Hei = curframe->height;
  729. img = new unsigned char [Wid * Hei * 3];
  730. }
  731. frame[k]=cvCloneImage(curframe);  //把前num帧存入到图像数组
  732. pTrackImg = cvCloneImage(curframe);
  733. }
  734. else
  735. {
  736. k=FrameNum%Num;
  737. pTrackImg = cvCloneImage(curframe);
  738. IplToImge(curframe,Wid,Hei);
  739. cvCvtColor(curframe,curFrameGray,CV_RGB2GRAY);
  740. cvCvtColor(frame[k],frameGray,CV_RGB2GRAY);
  741. for(i=0;i<curframe->height;i++)
  742. for(j=0;j<curframe->width;j++)
  743. {
  744. sum=S(curFrameGray,j,i)-S(frameGray,j,i);
  745. sum=sum<0 ? -sum : sum;
  746. if(sum>T)   //文献中公式(1)
  747. {
  748. CV_MAT_ELEM(*Mat_F,float,i,j)=1;
  749. }
  750. else
  751. {
  752. CV_MAT_ELEM(*Mat_F,float,i,j)=0;
  753. }
  754. if(CV_MAT_ELEM(*Mat_F,float,i,j)!=0)//文献中公式(2)
  755. CV_MAT_ELEM(*Mat_D,float,i,j)=Re;
  756. else{
  757. if(CV_MAT_ELEM(*Mat_D,float,i,j)!=0)
  758. CV_MAT_ELEM(*Mat_D,float,i,j)=CV_MAT_ELEM(*Mat_D,float,i,j)-1;
  759. }
  760. if(CV_MAT_ELEM(*Mat_D,float,i,j)==0.0)
  761. {
  762. //文献中公式(3)
  763. S(pBackImg,j,i)=(uchar)((1-ai)*S(pBackImg,j,i)+ai*S(curFrameGray,j,i));
  764. }
  765. sum=S(curFrameGray,j,i)-S(pBackImg,j,i);//背景差分法
  766. sum=sum<0 ? -sum : sum;
  767. if(sum>40)
  768. {
  769. S(pFrontImg,j,i)=255;
  770. }
  771. else
  772. S(pFrontImg,j,i)=0;
  773. }
  774. frame[k]=cvCloneImage(curframe);
  775. }
  776. FrameNum++;
  777. k++;
  778. cout<<FrameNum<<endl;
  779. //进行形态学滤波,去噪
  780. cvDilate(pFrontImg, pFrontImg, 0, 2);
  781. cvErode(pFrontImg, pFrontImg, 0, 3);
  782. cvDilate(pFrontImg, pFrontImg, 0, 1);
  783. if(track)
  784. {
  785. /* 跟踪一帧 */
  786. rho_v = ColorParticleTracking( img, Wid, Hei, xout, yout, WidOut, HeiOut, max_weight);
  787. /* 画框: 新位置为蓝框 */
  788. if ( rho_v > 0 && max_weight > 0.0001 )  /* 判断是否目标丢失 */
  789. {
  790. cvRectangle(pFrontImg,cvPoint(xout - WidOut,yout - HeiOut),cvPoint(xout+WidOut,yout+HeiOut),cvScalar(255,255,255),2,8,0);
  791. cvRectangle(pTrackImg,cvPoint(xout - WidOut,yout - HeiOut),cvPoint(xout+WidOut,yout+HeiOut),cvScalar(255,255,255),2,8,0);
  792. xin = xout; yin = yout;
  793. WidIn = WidOut; HeiIn = HeiOut;
  794. /*draw_rectangle( pBuffer, Width, Height, xo, Height-yo-1, wo, ho, 0x00ff0000, 2 );
  795. xb = xo; yb = yo;
  796. wb = wo; hb = ho;*/
  797. }
  798. }
  799. cvShowImage("video",curframe);
  800. cvShowImage("foreground",pFrontImg);
  801. cvShowImage("background",pBackImg);
  802. cvShowImage("tracking",pTrackImg);
  803. /*sprintf(res1,"fore%d.jpg",FrameNum);
  804. cvSaveImage(res1,pFrontImg);
  805. sprintf(res2,"ground%d.jpg",FrameNum);
  806. cvSaveImage(res2,pBackImg);*/
  807. cvSetMouseCallback("foreground",mouseHandler,0);//响应鼠标
  808. key = cvWaitKey(1);
  809. if(key == ‘p‘) pause = true;
  810. while(pause)
  811. if(cvWaitKey(0)==‘p‘)
  812. pause = false;
  813. }
  814. cvReleaseImage(&curFrameGray);
  815. cvReleaseImage(&frameGray);
  816. cvReleaseImage(&pBackImg);
  817. cvReleaseImage(&pFrontImg);
  818. cvDestroyAllWindows();
  819. //  Clear_MeanShift_tracker();
  820. ClearAll();
  821. }

实验结果:

时间: 2024-07-30 07:55:49

基于粒子滤波器的目标跟踪算法及实现的相关文章

opencv学习之基于背景提取等目标跟踪算法#20190704

/* *********************************************************************************************************************** 任务目标: 基于背景提取的目标跟踪算法实践及代码分析. ***************************************************************************************************

基于mean shift的目标跟踪算法

Mean shift 算法是一种半自动跟踪方法在起始跟踪帧通过手工确定搜索窗口来选择运动目标计算核函数加权下的搜索窗口的直方图分布用同样的方法计算当前帧对应窗口的直方图分布以两个分布的相似性最大为原则使搜索窗口沿密度增加最大的方向移动目标的真实位置. 加权直方图 传统直方图仅仅统计落入直方图区间的像素的个数,而加权直方图进一步考虑了像素与目标中心的距离,远离目标中心的像素对直方图的贡献较小. 带空间位置信息的加权直方图的思想就是:在计算直方图时,给每个点赋予一定的权值,权值的大小根据它离中心点的

基于粒子滤波的目标追踪

基于粒子滤波的目标追踪 particle filter object tracking 读"K. Nummiaro, E. Koller-Meier, L. Van Gool. An adaptive color-based particle filter[J], Image and Vision Computing, 2002"笔记 粒子滤波中最重要的一个过程就是重要性重采样, Sampling Importance Resampling (SIR). 这篇博客基于粒子滤波的物体跟踪

基于MeanShift的目标跟踪算法及实现

一.简介 首先扯扯无参密度估计理论,无参密度估计也叫做非参数估计,属于数理统计的一个分支,和参数密度估计共同构成了概率密度估计方法.参数密度估计方法要求特征空间服从一个已知的概率密度函数,在实际的应用中这个条件很难达到.而无参数密度估计方法对先验知识要求最少,完全依靠训练数据进行估计,并且可以用于任意形状的密度估计.所以依靠无参密度估计方法,即不事先规定概率密度函数的结构形式,在某一连续点处的密度函数值可由该点邻域中的若干样本点估计得出.常用的无参密度估计方法有:直方图法.最近邻域法和核密度估计

目标跟踪算法综述

转自  https://www.zhihu.com/question/26493945 作者:YaqiLYU 第一部分:目标跟踪速览 先跟几个SOTA的tracker混个脸熟,大概了解一下目标跟踪这个方向都有些什么.一切要从2013年的那个数据库说起..如果你问别人近几年有什么比较niubility的跟踪算法,大部分人都会扔给你吴毅老师的论文,OTB50和OTB100(OTB50这里指OTB-2013,OTB100这里指OTB-2015,50和100分别代表视频数量,方便记忆): Wu Y, L

视频目标跟踪算法综述

视频跟踪:基于对比度分析的目标跟踪.基于匹配的目标跟踪和基于运动检测的目标跟踪      基于对比度分析的目标跟踪:主要利用目标和背景的对比度差异实现目标的检测与跟踪.这类算法按照跟踪参考点的不同可以分为边缘跟踪# 形心跟踪和质心 跟踪等.这类算法不适合复杂背景中的目标跟踪"但在空中背景下的目标跟踪中非常有效. 基于匹配的目标跟踪:主要通过前后帧之间的特征匹配实现目标的定位.   特征匹配:特征是目标可区别与其他事物的属性, 具有可区分性.可靠性.独立性和稀疏性.基于匹配的目标跟踪算法需要提取目

挑战目标跟踪算法极限,SiamRPN系列算法解读

商汤科技智能视频团队首次开源其目标跟踪研究平台 PySOT.PySOT 包含了商汤科技 SiamRPN 系列算法,以及刚被 CVPR2019 收录为 Oral 的 SiamRPN++.此篇文章将解读目标跟踪最强算法 SiamRPN 系列. 背景 由于存在遮挡.光照变化.尺度变化等一些列问题,单目标跟踪的实际落地应用一直都存在较大的挑战.过去两年中,商汤智能视频团队在孪生网络上做了一系列工作,包括将检测引入跟踪后实现第一个高性能孪生网络跟踪算法的 SiamRPN(CVPR 18),更好地利用训练数

寻找目标跟踪算法(1)

目标跟踪算法 从知乎的一个帖子开始 计算机视觉中,目前有哪些经典的目标跟踪算法?(https://www.zhihu.com/question/26493945) 跟踪算法比较 Visual Tracker Benchmark:http://www.visual-tracking.net/ 经典算法: Mean-shift, Particle Filter, Ensemble Tracking,TLD, 压缩感知跟踪,KCF Tracker及其改进 KCF(opencv里集成了) http://

4. 基于深度学习的目标检测算法的综述(转)

4. 基于深度学习的目标检测算法的综述(转) 原文链接:https://www.cnblogs.com/zyly/p/9250195.html 目录 一 相关研究 1.选择性搜索(Selective Search) 2.OverFeat 二.基于区域提名的方法 1.R-CNN 2.SPP-Net 3.Fast R-CNN 4.Faster R-CNN 5.R-FCN 三 端对端的方法 1.YOLO 2.SSD 四 总结 在前面几节中,我们已经介绍了什么是目标检测,以及如何进行目标检测,还提及了滑