track_ellipse.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560
  1. #include "track_ellipse.h"
  2. #include "track_ellipse_opencl.h"
  3. void ellipsetrack(avi_t *video, double *xc0, double *yc0, int Nc, int R, int Np, int Nf) {
  4. /*
  5. % ELLIPSETRACK tracks cells in the movie specified by 'video', at
  6. % locations 'xc0'/'yc0' with radii R using an ellipse with Np discrete
  7. % points, starting at frame number one and stopping at frame number 'Nf'.
  8. %
  9. % INPUTS:
  10. % video.......pointer to avi video object
  11. % xc0,yc0.....initial center location (Nc entries)
  12. % Nc..........number of cells
  13. % R...........initial radius
  14. % Np..........number of snaxels points per snake
  15. % Nf..........number of frames in which to track
  16. %
  17. % Matlab code written by: DREW GILLIAM (based on code by GANG DONG /
  18. % NILANJAN RAY)
  19. % Ported to C by: MICHAEL BOYER
  20. */
  21. // Compute angle parameter
  22. double *t = (double *) malloc(sizeof(double) * Np);
  23. double increment = (2.0 * PI) / (double) Np;
  24. int i, j;
  25. for (i = 0; i < Np; i++) {
  26. t[i] = increment * (double) i ;
  27. }
  28. // Allocate space for a snake for each cell in each frame
  29. double **xc = alloc_2d_double(Nc, Nf + 1);
  30. double **yc = alloc_2d_double(Nc, Nf + 1);
  31. double ***r = alloc_3d_double(Nc, Np, Nf + 1);
  32. double ***x = alloc_3d_double(Nc, Np, Nf + 1);
  33. double ***y = alloc_3d_double(Nc, Np, Nf + 1);
  34. // Save the first snake for each cell
  35. for (i = 0; i < Nc; i++) {
  36. xc[i][0] = xc0[i];
  37. yc[i][0] = yc0[i];
  38. for (j = 0; j < Np; j++) {
  39. r[i][j][0] = (double) R;
  40. }
  41. }
  42. // Generate ellipse points for each cell
  43. for (i = 0; i < Nc; i++) {
  44. for (j = 0; j < Np; j++) {
  45. x[i][j][0] = xc[i][0] + (r[i][j][0] * cos(t[j]));
  46. y[i][j][0] = yc[i][0] + (r[i][j][0] * sin(t[j]));
  47. }
  48. }
  49. // Allocate arrays so we can break up the per-cell for loop below
  50. double *xci = (double *) malloc(sizeof(double) * Nc);
  51. double *yci = (double *) malloc(sizeof(double) * Nc);
  52. double **ri = alloc_2d_double(Nc, Np);
  53. double *ycavg = (double *) malloc(sizeof(double) * Nc);
  54. int *u1 = (int *) malloc(sizeof(int) * Nc);
  55. int *u2 = (int *) malloc(sizeof(int) * Nc);
  56. int *v1 = (int *) malloc(sizeof(int) * Nc);
  57. int *v2 = (int *) malloc(sizeof(int) * Nc);
  58. MAT **Isub = (MAT **) malloc(sizeof(MAT *) * Nc);
  59. MAT **Ix = (MAT **) malloc(sizeof(MAT *) * Nc);
  60. MAT **Iy = (MAT **) malloc(sizeof(MAT *) * Nc);
  61. MAT **IE = (MAT **) malloc(sizeof(MAT *) * Nc);
  62. // Keep track of the total time spent on computing
  63. // the MGVF matrix and evolving the snakes
  64. long long MGVF_time = 0;
  65. long long snake_time = 0;
  66. // Process each frame sequentially
  67. int frame_num;
  68. for (frame_num = 1; frame_num <= Nf; frame_num++) {
  69. printf("\rProcessing frame %d / %d", frame_num, Nf);
  70. fflush(stdout);
  71. // Get the current video frame and its dimensions
  72. MAT *I = get_frame(video, frame_num, 0, 1);
  73. int Ih = I->m;
  74. int Iw = I->n;
  75. // Initialize the current positions to be equal to the previous positions
  76. for (i = 0; i < Nc; i++) {
  77. xc[i][frame_num] = xc[i][frame_num - 1];
  78. yc[i][frame_num] = yc[i][frame_num - 1];
  79. for (j = 0; j < Np; j++) {
  80. r[i][j][frame_num] = r[i][j][frame_num - 1];
  81. }
  82. }
  83. // Sequentially extract the subimage near each cell
  84. int cell_num;
  85. for (cell_num = 0; cell_num < Nc; cell_num++) {
  86. // Make copies of the current cell's location
  87. xci[cell_num] = xc[cell_num][frame_num];
  88. yci[cell_num] = yc[cell_num][frame_num];
  89. for (j = 0; j < Np; j++) {
  90. ri[cell_num][j] = r[cell_num][j][frame_num];
  91. }
  92. // Add up the last ten y values for this cell
  93. // (or fewer if there are not yet ten previous frames)
  94. ycavg[cell_num] = 0.0;
  95. for (i = (frame_num > 10 ? frame_num - 10 : 0); i < frame_num; i++) {
  96. ycavg[cell_num] += yc[cell_num][i];
  97. }
  98. // Compute the average of the last ten values
  99. // (this represents the expected location of the cell)
  100. ycavg[cell_num] = ycavg[cell_num] / (double) (frame_num > 10 ? 10 : frame_num);
  101. // Determine the range of the subimage surrounding the current position
  102. u1[cell_num] = max(xci[cell_num] - 4.0 * R + 0.5, 0 );
  103. u2[cell_num] = min(xci[cell_num] + 4.0 * R + 0.5, Iw - 1);
  104. v1[cell_num] = max(yci[cell_num] - 2.0 * R + 1.5, 0 );
  105. v2[cell_num] = min(yci[cell_num] + 2.0 * R + 1.5, Ih - 1);
  106. // Extract the subimage
  107. Isub[cell_num] = m_get(v2[cell_num] - v1[cell_num] + 1, u2[cell_num] - u1[cell_num] + 1);
  108. for (i = v1[cell_num]; i <= v2[cell_num]; i++) {
  109. for (j = u1[cell_num]; j <= u2[cell_num]; j++) {
  110. m_set_val(Isub[cell_num], i - v1[cell_num], j - u1[cell_num], m_get_val(I, i, j));
  111. }
  112. }
  113. // Compute the subimage gradient magnitude
  114. Ix[cell_num] = gradient_x(Isub[cell_num]);
  115. Iy[cell_num] = gradient_y(Isub[cell_num]);
  116. IE[cell_num] = m_get(Isub[cell_num]->m, Isub[cell_num]->n);
  117. for (i = 0; i < Isub[cell_num]->m; i++) {
  118. for (j = 0; j < Isub[cell_num]->n; j++) {
  119. double temp_x = m_get_val(Ix[cell_num], i, j);
  120. double temp_y = m_get_val(Iy[cell_num], i, j);
  121. m_set_val(IE[cell_num], i, j, sqrt((temp_x * temp_x) + (temp_y * temp_y)));
  122. }
  123. }
  124. }
  125. // Compute the motion gradient vector flow (MGVF) edgemaps for all cells concurrently
  126. long long MGVF_start_time = get_time();
  127. MAT **IMGVF = MGVF(IE, 1, 1, Nc);
  128. MGVF_time += get_time() - MGVF_start_time;
  129. // Sequentially determine the new location of each cell
  130. for (cell_num = 0; cell_num < Nc; cell_num++) {
  131. // Determine the position of the cell in the subimage
  132. xci[cell_num] = xci[cell_num] - (double) u1[cell_num];
  133. yci[cell_num] = yci[cell_num] - (double) (v1[cell_num] - 1);
  134. ycavg[cell_num] = ycavg[cell_num] - (double) (v1[cell_num] - 1);
  135. // Evolve the snake
  136. long long snake_start_time = get_time();
  137. ellipseevolve(IMGVF[cell_num], &(xci[cell_num]), &(yci[cell_num]), ri[cell_num], t, Np, (double) R, ycavg[cell_num]);
  138. snake_time += get_time() - snake_start_time;
  139. // Compute the cell's new position in the full image
  140. xci[cell_num] = xci[cell_num] + u1[cell_num];
  141. yci[cell_num] = yci[cell_num] + (v1[cell_num] - 1);
  142. // Store the new location of the cell and the snake
  143. xc[cell_num][frame_num] = xci[cell_num];
  144. yc[cell_num][frame_num] = yci[cell_num];
  145. for (j = 0; j < Np; j++) {
  146. r[cell_num][j][frame_num] = 0;
  147. r[cell_num][j][frame_num] = ri[cell_num][j];
  148. x[cell_num][j][frame_num] = xc[cell_num][frame_num] + (ri[cell_num][j] * cos(t[j]));
  149. y[cell_num][j][frame_num] = yc[cell_num][frame_num] + (ri[cell_num][j] * sin(t[j]));
  150. }
  151. // Output the updated center of each cell
  152. // printf("\n%d,%f,%f", cell_num, xci[cell_num], yci[cell_num]);
  153. // Free temporary memory
  154. m_free(Isub[cell_num]);
  155. m_free(Ix[cell_num]);
  156. m_free(Iy[cell_num]);
  157. m_free(IE[cell_num]);
  158. m_free(IMGVF[cell_num]);
  159. }
  160. #ifdef OUTPUT
  161. if (frame_num == Nf)
  162. {
  163. FILE * pFile;
  164. pFile = fopen ("result.txt","w+");
  165. for (cell_num = 0; cell_num < Nc; cell_num++)
  166. fprintf(pFile,"\n%d,%f,%f", cell_num, xci[cell_num], yci[cell_num]);
  167. fclose (pFile);
  168. }
  169. #endif
  170. free(IMGVF);
  171. // Output a new line to visually distinguish the output from different frames
  172. //printf("\n");
  173. }
  174. // Free temporary memory
  175. free_2d_double(xc);
  176. free_2d_double(yc);
  177. free_3d_double(r);
  178. free_3d_double(x);
  179. free_3d_double(y);
  180. free(t);
  181. free(xci);
  182. free(yci);
  183. free_2d_double(ri);
  184. free(ycavg);
  185. free(u1);
  186. free(u2);
  187. free(v1);
  188. free(v2);
  189. free(Isub);
  190. free(Ix);
  191. free(Iy);
  192. free(IE);
  193. // Report average processing time per frame
  194. printf("\n\nTracking runtime (average per frame):\n");
  195. printf("------------------------------------\n");
  196. printf("MGVF computation: %.5f seconds\n", ((float) (MGVF_time)) / (float) (1000*1000*Nf));
  197. printf(" Snake evolution: %.5f seconds\n", ((float) (snake_time)) / (float) (1000*1000*Nf));
  198. }
  199. MAT **MGVF(MAT **IE, double vx, double vy, int Nc) {
  200. /*
  201. % MGVF calculate the motion gradient vector flow (MGVF)
  202. % for the image 'I'
  203. %
  204. % Based on the algorithm in:
  205. % Motion gradient vector flow: an external force for tracking rolling
  206. % leukocytes with shape and size constrained active contours
  207. % Ray, N. and Acton, S.T.
  208. % IEEE Transactions on Medical Imaging
  209. % Volume: 23, Issue: 12, December 2004
  210. % Pages: 1466 - 1478
  211. %
  212. % INPUTS
  213. % I...........image
  214. % vx,vy.......velocity vector
  215. %
  216. % OUTPUT
  217. % IMGVF.......MGVF vector field as image
  218. %
  219. % Matlab code written by: DREW GILLIAM (based on work by GANG DONG /
  220. % NILANJAN RAY)
  221. % Ported to C by: MICHAEL BOYER
  222. */
  223. // Constants
  224. double converge = 0.00001;
  225. double epsilon = 0.0000000001;
  226. // Smallest positive value expressable in double-precision
  227. double eps = pow(2.0, -52.0);
  228. // Maximum number of iterations to compute the MGVF matrix
  229. int iterations = 500;
  230. // Allocate memory for pointers to the MGVF for each cell
  231. MAT **IMGVF = (MAT **) malloc(sizeof(MAT *) * Nc);
  232. // Normalize the sub-image for each cell
  233. int cell_num;
  234. for (cell_num = 0; cell_num < Nc; cell_num++) {
  235. MAT *I = IE[cell_num];
  236. // Find the maximum and minimum values in I
  237. int m = I->m, n = I->n, i, j;
  238. double Imax = m_get_val(I, 0, 0);
  239. double Imin = m_get_val(I, 0, 0);
  240. for (i = 0; i < m; i++) {
  241. for (j = 0; j < n; j++) {
  242. double temp = m_get_val(I, i, j);
  243. if (temp > Imax) Imax = temp;
  244. else if (temp < Imin) Imin = temp;
  245. }
  246. }
  247. // Normalize the images I
  248. double scale = 1.0 / (Imax - Imin + eps);
  249. for (i = 0; i < m; i++) {
  250. for (j = 0; j < n; j++) {
  251. double old_val = m_get_val(I, i, j);
  252. m_set_val(I, i, j, (old_val - Imin) * scale);
  253. }
  254. }
  255. // Allocate memory for this cell's MGVF matrix
  256. IMGVF[cell_num] = m_get(m, n);
  257. }
  258. // Offload the MGVF computation to the GPU
  259. IMGVF_OpenCL(IE, IMGVF, vx, vy, epsilon, iterations, converge, Nc);
  260. return IMGVF;
  261. }
  262. void ellipseevolve(MAT *f, double *xc0, double *yc0, double *r0, double *t, int Np, double Er, double Ey) {
  263. /*
  264. % ELLIPSEEVOLVE evolves a parametric snake according
  265. % to some energy constraints.
  266. %
  267. % INPUTS:
  268. % f............potential surface
  269. % xc0,yc0......initial center position
  270. % r0,t.........initial radii & angle vectors (with Np elements each)
  271. % Np...........number of snaxel points per snake
  272. % Er...........expected radius
  273. % Ey...........expected y position
  274. %
  275. % OUTPUTS
  276. % xc0,yc0.......final center position
  277. % r0...........final radii
  278. %
  279. % Matlab code written by: DREW GILLIAM (based on work by GANG DONG /
  280. % NILANJAN RAY)
  281. % Ported to C by: MICHAEL BOYER
  282. */
  283. // Constants
  284. double deltax = 0.2;
  285. double deltay = 0.2;
  286. double deltar = 0.2;
  287. double converge = 0.1;
  288. double lambdaedge = 1;
  289. double lambdasize = 0.2;
  290. double lambdapath = 0.05;
  291. int iterations = 1000; // maximum number of iterations
  292. int i, j;
  293. // Initialize variables
  294. double xc = *xc0;
  295. double yc = *yc0;
  296. double *r = (double *) malloc(sizeof(double) * Np);
  297. for (i = 0; i < Np; i++) r[i] = r0[i];
  298. // Compute the x- and y-gradients of the MGVF matrix
  299. MAT *fx = gradient_x(f);
  300. MAT *fy = gradient_y(f);
  301. // Normalize the gradients
  302. int fh = f->m, fw = f->n;
  303. for (i = 0; i < fh; i++) {
  304. for (j = 0; j < fw; j++) {
  305. double temp_x = m_get_val(fx, i, j);
  306. double temp_y = m_get_val(fy, i, j);
  307. double fmag = sqrt((temp_x * temp_x) + (temp_y * temp_y));
  308. // Fix 0/0 error
  309. if (fmag > 1E-15) {
  310. m_set_val(fx, i, j, temp_x / fmag);
  311. m_set_val(fy, i, j, temp_y / fmag);
  312. } else {
  313. m_set_val(fx, i, j, 0.0);
  314. m_set_val(fy, i, j, 0.0);
  315. }
  316. /* m_set_val(fx, i, j, temp_x / fmag); */
  317. /* m_set_val(fy, i, j, temp_y / fmag); */
  318. }
  319. }
  320. double *r_old = (double *) malloc(sizeof(double) * Np);
  321. VEC *x = v_get(Np);
  322. VEC *y = v_get(Np);
  323. // Evolve the snake
  324. int iter = 0;
  325. double snakediff = 1.0;
  326. while (iter < iterations && snakediff > converge) {
  327. // Save the values from the previous iteration
  328. double xc_old = xc, yc_old = yc;
  329. for (i = 0; i < Np; i++) {
  330. r_old[i] = r[i];
  331. }
  332. // Compute the locations of the snaxels
  333. for (i = 0; i < Np; i++) {
  334. v_set_val(x, i, xc + r[i] * cos(t[i]));
  335. v_set_val(y, i, yc + r[i] * sin(t[i]));
  336. }
  337. // See if any of the points in the snake are off the edge of the image
  338. double min_x = v_get_val(x, 0), max_x = v_get_val(x, 0);
  339. double min_y = v_get_val(y, 0), max_y = v_get_val(y, 0);
  340. for (i = 1; i < Np; i++) {
  341. double x_i = v_get_val(x, i);
  342. if (x_i < min_x) min_x = x_i;
  343. else if (x_i > max_x) max_x = x_i;
  344. double y_i = v_get_val(y, i);
  345. if (y_i < min_y) min_y = y_i;
  346. else if (y_i > max_y) max_y = y_i;
  347. }
  348. if (min_x < 0.0 || max_x > (double) fw - 1.0 || min_y < 0 || max_y > (double) fh - 1.0) break;
  349. // Compute the length of the snake
  350. double L = 0.0;
  351. for (i = 0; i < Np - 1; i++) {
  352. double diff_x = v_get_val(x, i + 1) - v_get_val(x, i);
  353. double diff_y = v_get_val(y, i + 1) - v_get_val(y, i);
  354. L += sqrt((diff_x * diff_x) + (diff_y * diff_y));
  355. }
  356. double diff_x = v_get_val(x, 0) - v_get_val(x, Np - 1);
  357. double diff_y = v_get_val(y, 0) - v_get_val(y, Np - 1);
  358. L += sqrt((diff_x * diff_x) + (diff_y * diff_y));
  359. // Compute the potential surface at each snaxel
  360. MAT *vf = linear_interp2(f, x, y);
  361. MAT *vfx = linear_interp2(fx, x, y);
  362. MAT *vfy = linear_interp2(fy, x, y);
  363. // Compute the average potential surface around the snake
  364. double vfmean = sum_m(vf ) / L;
  365. double vfxmean = sum_m(vfx) / L;
  366. double vfymean = sum_m(vfy) / L;
  367. // Compute the radial potential surface
  368. int m = vf->m, n = vf->n;
  369. MAT *vfr = m_get(m, n);
  370. for (i = 0; i < n; i++) {
  371. double vf_val = m_get_val(vf, 0, i);
  372. double vfx_val = m_get_val(vfx, 0, i);
  373. double vfy_val = m_get_val(vfy, 0, i);
  374. double x_val = v_get_val(x, i);
  375. double y_val = v_get_val(y, i);
  376. double new_val = (vf_val + vfx_val * (x_val - xc) + vfy_val * (y_val - yc) - vfmean) / L;
  377. m_set_val(vfr, 0, i, new_val);
  378. }
  379. // Update the snake center and snaxels
  380. xc = xc + (deltax * lambdaedge * vfxmean);
  381. yc = (yc + (deltay * lambdaedge * vfymean) + (deltay * lambdapath * Ey)) / (1.0 + deltay * lambdapath);
  382. double r_diff = 0.0;
  383. for (i = 0; i < Np; i++) {
  384. r[i] = (r[i] + (deltar * lambdaedge * m_get_val(vfr, 0, i)) + (deltar * lambdasize * Er)) /
  385. (1.0 + deltar * lambdasize);
  386. r_diff += fabs(r[i] - r_old[i]);
  387. }
  388. // Test for convergence
  389. snakediff = fabs(xc - xc_old) + fabs(yc - yc_old) + r_diff;
  390. // Free temporary matrices
  391. m_free(vf);
  392. m_free(vfx);
  393. m_free(vfy);
  394. m_free(vfr);
  395. iter++;
  396. }
  397. // Set the return values
  398. *xc0 = xc;
  399. *yc0 = yc;
  400. for (i = 0; i < Np; i++)
  401. r0[i] = r[i];
  402. // Free memory
  403. free(r); free(r_old);
  404. v_free( x); v_free( y);
  405. m_free(fx); m_free(fy);
  406. }
  407. // Returns the sum of all of the elements in the specified matrix
  408. double sum_m(MAT *matrix) {
  409. if (matrix == NULL) return 0.0;
  410. int i, j;
  411. double sum = 0.0;
  412. for (i = 0; i < matrix->m; i++)
  413. for (j = 0; j < matrix->n; j++)
  414. sum += m_get_val(matrix, i, j);
  415. return sum;
  416. }
  417. // Returns the sum of all of the elements in the specified vector
  418. double sum_v(VEC *vector) {
  419. if (vector == NULL) return 0.0;
  420. int i;
  421. double sum = 0.0;
  422. for (i = 0; i < vector->dim; i++)
  423. sum += v_get_val(vector, i);
  424. return sum;
  425. }
  426. // Creates a zeroed x-by-y matrix of doubles
  427. double **alloc_2d_double(int x, int y) {
  428. if (x < 1 || y < 1) return NULL;
  429. // Allocate the data and the pointers to the data
  430. double *data = (double *) calloc(x * y, sizeof(double));
  431. double **pointers = (double **) malloc(sizeof(double *) * x);
  432. // Make the pointers point to the data
  433. int i;
  434. for (i = 0; i < x; i++) {
  435. pointers[i] = data + (i * y);
  436. }
  437. return pointers;
  438. }
  439. // Creates a zeroed x-by-y-by-z matrix of doubles
  440. double ***alloc_3d_double(int x, int y, int z) {
  441. if (x < 1 || y < 1 || z < 1) return NULL;
  442. // Allocate the data and the two levels of pointers
  443. double *data = (double *) calloc(x * y * z, sizeof(double));
  444. double **pointers_to_data = (double **) malloc(sizeof(double *) * x * y);
  445. double ***pointers_to_pointers = (double ***) malloc(sizeof(double **) * x);
  446. // Make the pointers point to the data
  447. int i;
  448. for (i = 0; i < x * y; i++) pointers_to_data[i] = data + (i * z);
  449. for (i = 0; i < x; i++) pointers_to_pointers[i] = pointers_to_data + (i * y);
  450. return pointers_to_pointers;
  451. }
  452. // Frees a 2d matrix generated by the alloc_2d_double function
  453. void free_2d_double(double **p) {
  454. if (p != NULL) {
  455. if (p[0] != NULL) free(p[0]);
  456. free(p);
  457. }
  458. }
  459. // Frees a 3d matrix generated by the alloc_3d_double function
  460. void free_3d_double(double ***p) {
  461. if (p != NULL) {
  462. if (p[0] != NULL) {
  463. if (p[0][0] != NULL) free(p[0][0]);
  464. free(p[0]);
  465. }
  466. free(p);
  467. }
  468. }