PageRenderTime 119ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/flow_tracking/calc_flows.cxx

https://github.com/mbsullivan/plus-one
C++ | 304 lines | 175 code | 70 blank | 59 comment | 36 complexity | 4b60d8f7231e17abc4aaabaf9a01ae73 MD5 | raw file
  1. /*
  2. * calc_flows.cxx - Implementation of Calc_Flows functions
  3. * (c) 2008 Michael Sullivan
  4. *
  5. * Version 1.0.0
  6. * Last Revised: 04/29/08
  7. *
  8. * Version History:
  9. * 1.0.0 - Primary implementation (04/29/08)
  10. *
  11. * This program utilizes the Open Computer Vision Library (OpenCV)
  12. *
  13. */
  14. #include "calc_flows.h" // calc_flows header file
  15. #include "img_template.tpl" // provides efficient access to pixels
  16. // Constructors
  17. Calc_Flows::Calc_Flows(){
  18. // Default Constructor
  19. // set up state of machine
  20. ran = false;
  21. where_flow_found = NULL;
  22. num_tracked_points = 0;
  23. lk_flags = 0;
  24. // Points to track
  25. prev_points = NULL;
  26. curr_points = NULL;
  27. swap_points = NULL;
  28. }
  29. Calc_Flows::~Calc_Flows(){
  30. // Destructor
  31. // release all images
  32. for(int i = 0; i < orig_images.size(); i++){
  33. cvReleaseImage(&orig_images[i]);
  34. cvReleaseImage(&gray_images[i]);
  35. cvReleaseImage(&annotated_images[i]);
  36. }
  37. cvReleaseImage(&prev_pyramid);
  38. cvReleaseImage(&curr_pyramid);
  39. cvReleaseImage(&swap_pyramid);
  40. }
  41. // Access Functions
  42. int Calc_Flows::get_size(){
  43. return orig_images.size();
  44. }
  45. // Action Functions
  46. bool Calc_Flows::add(string filename){
  47. // load the image
  48. IplImage *img = NULL, *gray = NULL;
  49. img = cvLoadImage(filename.c_str(),1); // scan in color
  50. if(!img) return false;
  51. // store the image
  52. orig_images.push_back(img);
  53. // convert to grayscale
  54. gray = cvCreateImage(cvGetSize(img), IPL_DEPTH_8U, 1);
  55. cvCvtColor(img, gray, CV_BGR2GRAY);
  56. // store the gray image
  57. gray_images.push_back(gray);
  58. return true;
  59. }
  60. int Calc_Flows::run(){
  61. // Default run function (with default verbosity)
  62. return run(DEFAULT_VERBOSITY);
  63. }
  64. void Calc_Flows::init(IplImage *initial_img){
  65. // set up state of machine for new run
  66. prev_points = (CvPoint2D32f*)cvAlloc(MAX_POINTS_TO_TRACK*sizeof(0)); // initially NULL
  67. curr_points = (CvPoint2D32f*)cvAlloc(MAX_POINTS_TO_TRACK*sizeof(0)); // initially NULL
  68. where_flow_found = (char*)cvAlloc(MAX_POINTS_TO_TRACK); // initially NULL
  69. // setup pyramids for flow
  70. prev_pyramid = cvCreateImage(cvGetSize(initial_img), IPL_DEPTH_8U, 1); // initially NULL
  71. curr_pyramid = cvCreateImage(cvGetSize(initial_img), IPL_DEPTH_8U, 1); // initially NULL
  72. // get initial set for feature detection
  73. IplImage* eig = cvCreateImage(cvGetSize(initial_img), 32, 1);
  74. IplImage* temp = cvCreateImage(cvGetSize(initial_img), 32, 1);
  75. double quality = 0.01;
  76. double min_distance = 10;
  77. num_tracked_points = MAX_POINTS_TO_TRACK;
  78. // detect number of features to track
  79. cvGoodFeaturesToTrack(initial_img, eig, temp, curr_points, &num_tracked_points, quality, min_distance, 0, 3, 0, 0.04 );
  80. cvFindCornerSubPix(initial_img, curr_points, num_tracked_points, cvSize(WINDOW_SIZE,WINDOW_SIZE), cvSize(-1,-1), cvTermCriteria(CV_TERMCRIT_ITER|CV_TERMCRIT_EPS,20,0.03));
  81. // release temporary images
  82. cvReleaseImage(&eig);
  83. cvReleaseImage(&temp);
  84. }
  85. int Calc_Flows::run_webcam(bool verbose){
  86. // capture input from webcam
  87. CvCapture* capture = 0;
  88. capture = cvCaptureFromCAM(0); // capture from default device
  89. if(!capture){
  90. return -1;
  91. }
  92. cvNamedWindow("Webcam_Capture", 0 );
  93. IplImage *image = NULL, *prev_grey = NULL, *grey = NULL;
  94. for(;;){
  95. IplImage* frame = NULL;
  96. frame = cvQueryFrame(capture);
  97. if( !frame )
  98. break;
  99. if(!image){ // initialize data structures the first time
  100. image = cvCreateImage( cvGetSize(frame), 8, 3 );
  101. image->origin = frame->origin;
  102. grey = cvCreateImage( cvGetSize(frame), 8, 1 );
  103. // acquire usable image, grayscale it
  104. cvCopy(frame, image, 0);
  105. cvCvtColor(image, grey, CV_BGR2GRAY);
  106. init(grey);
  107. prev_grey = cvCreateImage(cvGetSize(frame), 8, 1 );
  108. }
  109. else{ // update the reading
  110. cvCopy(frame, image, 0);
  111. cvCvtColor(image, grey, CV_BGR2GRAY);
  112. }
  113. // update pairs with flow information
  114. pair_flow(prev_grey, grey);
  115. // prepare for next captured picture
  116. CV_SWAP(prev_grey, grey, swap_pyramid);
  117. // DEBUGGING OUTPUT
  118. for(int i = 0; i < num_tracked_points; i++){
  119. cvCircle(image, cvPointFrom32f(curr_points[i]), 3, CV_RGB(0,255,0), -1, 8,0);
  120. cout << cvPointFrom32f(curr_points[i]).x << " " << cvPointFrom32f(curr_points[i]).y << endl;
  121. }
  122. // display webcam output
  123. cvShowImage("Webcam_Capture", image);
  124. cvWaitKey(10);
  125. }
  126. return 0;
  127. }
  128. int Calc_Flows::run(bool verbose){
  129. if(orig_images.size() == 0){
  130. cout << " * " << "No images to process!" << endl;
  131. return 0;
  132. }
  133. if(verbose)
  134. cout << endl << " * " << "Calculating optical flow for " << orig_images.size() - 1 << " pairs of images..." << endl;
  135. // used to store (grayscale) input images
  136. IplImage *img1 = NULL, *img2 = NULL, *disp_image = NULL;
  137. // initialize for first frame
  138. img1 = cvCreateImage(cvGetSize(gray_images[0]), 8, 1);
  139. img1->origin = gray_images[0]->origin;
  140. // acquire usable image, grayscale it
  141. cvCopy(gray_images[0], img1, 0);
  142. init(img1);
  143. // DEBUGGING OUTPUT
  144. for(int j = 0; j < num_tracked_points; j++){
  145. //cout << cvPointFrom32f(curr_points[j]).x << " " << cvPointFrom32f(curr_points[j]).y << endl;
  146. }
  147. // run flow algorithm on all remaining images
  148. for(int i = 0; i < orig_images.size() - 1; i++){
  149. // load images
  150. if(i == 0) // necessary?
  151. img1 = cvCreateImage(cvGetSize(gray_images[i]), 8, 1);
  152. else
  153. img1 = cvCloneImage(gray_images[i]);
  154. img2 = cvCloneImage(gray_images[i+1]);
  155. // check that images are consistent
  156. int height1,width1,channels1;
  157. int height2,width2,channels2;
  158. // parse first image data
  159. height1 = img1->height;
  160. width1 = img1->width;
  161. channels1 = img1->nChannels;
  162. // parse second image data
  163. height2 = img2->height;
  164. width2 = img2->width;
  165. channels2 = img2->nChannels;
  166. // check for inequality in image dimensions, channels
  167. if(!img1 || !img2 || height1 != height2 || width1 != width2 || channels1 != channels2){
  168. printf("[ERROR] Images are not same dimensions, number of channels!");
  169. return IMAGE_CONSISTENCY_FAILED;
  170. }
  171. // calculate flow between the two images (results modify private global variables)
  172. if(verbose)
  173. cout << " * " << "Processing optical flow of image pair #" << i << "...";
  174. pair_flow(img1, img2);
  175. // annotate resulting image
  176. annotated_images.push_back(annotate_img(orig_images[i+1])); // animate colored second pair
  177. if(verbose)
  178. cout << "\t\t\t\t[OK]" << endl;
  179. }
  180. // mark as run
  181. ran = true;
  182. return 0;
  183. }
  184. void Calc_Flows::pair_flow(IplImage* img1, IplImage* img2){
  185. // calculate flow and track points (modified Lucas & Kanade algorithm)
  186. cvCalcOpticalFlowPyrLK(img1, img2, prev_pyramid, curr_pyramid, prev_points, curr_points, num_tracked_points, cvSize(WINDOW_SIZE,WINDOW_SIZE), 3, where_flow_found, 0, cvTermCriteria(CV_TERMCRIT_ITER|CV_TERMCRIT_EPS,2000,0.03), lk_flags);
  187. lk_flags |= CV_LKFLOW_PYR_A_READY; // A pyramid will be ready > 1st time
  188. // DEBUGGING OUTPUT
  189. for(int j = 0; j < num_tracked_points; j++){
  190. cout << cvPointFrom32f(curr_points[j]).x << " " << cvPointFrom32f(curr_points[j]).y << endl;
  191. }
  192. // update points
  193. CV_SWAP(prev_pyramid, curr_pyramid, swap_pyramid);
  194. CV_SWAP(prev_points, curr_points, swap_points);
  195. }
  196. void Calc_Flows::animate(string outfolder){
  197. // Default animate function (with default verbosity)
  198. return animate(outfolder, DEFAULT_VERBOSITY);
  199. }
  200. void Calc_Flows::animate(string outfolder, bool verbose){
  201. // make sure state of machine is ready for animation
  202. if(!ran){
  203. cout << " * " << "Optical flow processing has not been performed!" << endl;
  204. return;
  205. }
  206. else if(annotated_images.size() != orig_images.size() - 1){
  207. cout << " * " << "Not all images have been annotated!" << endl;
  208. return;
  209. }
  210. if(verbose)
  211. cout << endl << " * " << "Outputing animation for " << orig_images.size() - 1 << " pairs of images..." << endl;
  212. for(int i = 0; i < orig_images.size() - 1; i++){
  213. if(verbose)
  214. cout << " * " << "Animating image pair #" << i << "...";
  215. // output images
  216. stringstream ostream;
  217. string outfile;
  218. ostream << i << ".png";
  219. outfile = ostream.str();
  220. if(i < 10) outfile = "0" + outfile;
  221. if(i < 100) outfile = "0" + outfile;
  222. outfile = outfolder + outfile;
  223. cvSaveImage(outfile.c_str(),annotated_images[i]); // add the frame to a file
  224. if(verbose)
  225. cout << "\t\t\t\t[OK]" << endl;
  226. }
  227. }
  228. IplImage* Calc_Flows::annotate_img(IplImage* img){
  229. // add circles for each tracked point
  230. for(int i = 0; i < num_tracked_points; i++)
  231. cvCircle(img, cvPointFrom32f(curr_points[i]), 3, CV_RGB(0,255,0), -1, 8,0);
  232. // return annotated image
  233. return img;
  234. }