demo.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. #include <iostream>
  2. #include <string>
  3. #include <vector>
  4. #include <unordered_map>
  5. #include <stdlib.h>
  6. #include <chrono>
  7. #include <opencv2/core.hpp>
  8. #include <opencv2/imgproc.hpp>
  9. #include <opencv2/highgui.hpp>
  10. #include <opencv2/videoio.hpp>
  11. #include <opencv2/video.hpp>
  12. #include <opencv2/cudaarithm.hpp>
  13. #include <opencv2/cudaimgproc.hpp>
  14. #include <opencv2/cudawarping.hpp>
  15. #include <opencv2/cudaoptflow.hpp>
  16. using namespace cv;
  17. using namespace cv::cuda;
  18. using namespace std;
  19. using namespace std::chrono;
  20. void calculate_optical_flow(string videoFileName, string device)
  21. {
  22. // init map to track time for every stage at each iteration
  23. unordered_map<string, vector<double>> timers;
  24. // init video capture with video
  25. VideoCapture capture(videoFileName);
  26. if (!capture.isOpened())
  27. {
  28. // error in opening the video file
  29. cout << "Unable to open file!" << endl;
  30. return;
  31. }
  32. // get default video FPS
  33. double fps = capture.get(CAP_PROP_FPS);
  34. // get total number of video frames
  35. int num_frames = int(capture.get(CAP_PROP_FRAME_COUNT));
  36. // read the first frame
  37. cv::Mat frame, previous_frame;
  38. capture >> frame;
  39. if (device == "cpu")
  40. {
  41. // resize frame
  42. cv::resize(frame, frame, Size(960, 540), 0, 0, INTER_LINEAR);
  43. // convert to gray
  44. cv::cvtColor(frame, previous_frame, COLOR_BGR2GRAY);
  45. // declare outputs for optical flow
  46. cv::Mat magnitude, normalized_magnitude, angle;
  47. cv::Mat hsv[3], merged_hsv, hsv_8u, bgr;
  48. // set saturation to 1
  49. hsv[1] = cv::Mat::ones(frame.size(), CV_32F);
  50. while (true)
  51. {
  52. // start full pipeline timer
  53. auto start_full_time = high_resolution_clock::now();
  54. // start reading timer
  55. auto start_read_time = high_resolution_clock::now();
  56. // capture frame-by-frame
  57. capture >> frame;
  58. if (frame.empty())
  59. break;
  60. // end reading timer
  61. auto end_read_time = high_resolution_clock::now();
  62. // add elapsed iteration time
  63. timers["reading"].push_back(duration_cast<milliseconds>(end_read_time - start_read_time).count() / 1000.0);
  64. // start pre-process timer
  65. auto start_pre_time = high_resolution_clock::now();
  66. // resize frame
  67. cv::resize(frame, frame, Size(960, 540), 0, 0, INTER_LINEAR);
  68. // convert to gray
  69. cv::Mat current_frame;
  70. cv::cvtColor(frame, current_frame, COLOR_BGR2GRAY);
  71. // end pre-process timer
  72. auto end_pre_time = high_resolution_clock::now();
  73. // add elapsed iteration time
  74. timers["pre-process"].push_back(duration_cast<milliseconds>(end_pre_time - start_pre_time).count() / 1000.0);
  75. // start optical flow timer
  76. auto start_of_time = high_resolution_clock::now();
  77. // calculate optical flow
  78. cv::Mat flow;
  79. calcOpticalFlowFarneback(previous_frame, current_frame, flow, 0.5, 5, 15, 3, 5, 1.2, 0);
  80. // end optical flow timer
  81. auto end_of_time = high_resolution_clock::now();
  82. // add elapsed iteration time
  83. timers["optical flow"].push_back(duration_cast<milliseconds>(end_of_time - start_of_time).count() / 1000.0);
  84. // start post-process timer
  85. auto start_post_time = high_resolution_clock::now();
  86. // split the output flow into 2 vectors
  87. cv::Mat flow_xy[2], flow_x, flow_y;
  88. split(flow, flow_xy);
  89. // get the result
  90. flow_x = flow_xy[0];
  91. flow_y = flow_xy[1];
  92. // convert from cartesian to polar coordinates
  93. cv::cartToPolar(flow_x, flow_y, magnitude, angle, true);
  94. // normalize magnitude from 0 to 1
  95. cv::normalize(magnitude, normalized_magnitude, 0.0, 1.0, NORM_MINMAX);
  96. // get angle of optical flow
  97. angle *= ((1 / 360.0) * (180 / 255.0));
  98. // build hsv image
  99. hsv[0] = angle;
  100. hsv[2] = normalized_magnitude;
  101. merge(hsv, 3, merged_hsv);
  102. // multiply each pixel value to 255
  103. merged_hsv.convertTo(hsv_8u, CV_8U, 255);
  104. // convert hsv to bgr
  105. cv::cvtColor(hsv_8u, bgr, COLOR_HSV2BGR);
  106. // update previous_frame value
  107. previous_frame = current_frame;
  108. // end post pipeline timer
  109. auto end_post_time = high_resolution_clock::now();
  110. // add elapsed iteration time
  111. timers["post-process"].push_back(duration_cast<milliseconds>(end_post_time - start_post_time).count() / 1000.0);
  112. // end full pipeline timer
  113. auto end_full_time = high_resolution_clock::now();
  114. // add elapsed iteration time
  115. timers["full pipeline"].push_back(duration_cast<milliseconds>(end_full_time - start_full_time).count() / 1000.0);
  116. // visualization
  117. imshow("original", frame);
  118. imshow("result", bgr);
  119. int keyboard = waitKey(1);
  120. if (keyboard == 27)
  121. break;
  122. }
  123. }
  124. else
  125. {
  126. // resize frame
  127. cv::resize(frame, frame, Size(960, 540), 0, 0, INTER_LINEAR);
  128. // convert to gray
  129. cv::cvtColor(frame, previous_frame, COLOR_BGR2GRAY);
  130. // upload pre-processed frame to GPU
  131. cv::cuda::GpuMat gpu_previous;
  132. gpu_previous.upload(previous_frame);
  133. // declare cpu outputs for optical flow
  134. cv::Mat hsv[3], angle, bgr;
  135. // declare gpu outputs for optical flow
  136. cv::cuda::GpuMat gpu_magnitude, gpu_normalized_magnitude, gpu_angle;
  137. cv::cuda::GpuMat gpu_hsv[3], gpu_merged_hsv, gpu_hsv_8u, gpu_bgr;
  138. // set saturation to 1
  139. hsv[1] = cv::Mat::ones(frame.size(), CV_32F);
  140. gpu_hsv[1].upload(hsv[1]);
  141. while (true)
  142. {
  143. // start full pipeline timer
  144. auto start_full_time = high_resolution_clock::now();
  145. // start reading timer
  146. auto start_read_time = high_resolution_clock::now();
  147. // capture frame-by-frame
  148. capture >> frame;
  149. if (frame.empty())
  150. break;
  151. // upload frame to GPU
  152. cv::cuda::GpuMat gpu_frame;
  153. gpu_frame.upload(frame);
  154. // end reading timer
  155. auto end_read_time = high_resolution_clock::now();
  156. // add elapsed iteration time
  157. timers["reading"].push_back(duration_cast<milliseconds>(end_read_time - start_read_time).count() / 1000.0);
  158. // start pre-process timer
  159. auto start_pre_time = high_resolution_clock::now();
  160. // resize frame
  161. cv::cuda::resize(gpu_frame, gpu_frame, Size(960, 540), 0, 0, INTER_LINEAR);
  162. // convert to gray
  163. cv::cuda::GpuMat gpu_current;
  164. cv::cuda::cvtColor(gpu_frame, gpu_current, COLOR_BGR2GRAY);
  165. // end pre-process timer
  166. auto end_pre_time = high_resolution_clock::now();
  167. // add elapsed iteration time
  168. timers["pre-process"].push_back(duration_cast<milliseconds>(end_pre_time - start_pre_time).count() / 1000.0);
  169. // start optical flow timer
  170. auto start_of_time = high_resolution_clock::now();
  171. // create optical flow instance
  172. Ptr<cuda::FarnebackOpticalFlow> ptr_calc = cuda::FarnebackOpticalFlow::create(5, 0.5, false, 15, 3, 5, 1.2, 0);
  173. // calculate optical flow
  174. cv::cuda::GpuMat gpu_flow;
  175. ptr_calc->calc(gpu_previous, gpu_current, gpu_flow);
  176. // end optical flow timer
  177. auto end_of_time = high_resolution_clock::now();
  178. // add elapsed iteration time
  179. timers["optical flow"].push_back(duration_cast<milliseconds>(end_of_time - start_of_time).count() / 1000.0);
  180. // start post-process timer
  181. auto start_post_time = high_resolution_clock::now();
  182. // split the output flow into 2 vectors
  183. cv::cuda::GpuMat gpu_flow_xy[2];
  184. cv::cuda::split(gpu_flow, gpu_flow_xy);
  185. // convert from cartesian to polar coordinates
  186. cv::cuda::cartToPolar(gpu_flow_xy[0], gpu_flow_xy[1], gpu_magnitude, gpu_angle, true);
  187. // normalize magnitude from 0 to 1
  188. cv::cuda::normalize(gpu_magnitude, gpu_normalized_magnitude, 0.0, 1.0, NORM_MINMAX, -1);
  189. // get angle of optical flow
  190. gpu_angle.download(angle);
  191. angle *= ((1 / 360.0) * (180 / 255.0));
  192. // build hsv image
  193. gpu_hsv[0].upload(angle);
  194. gpu_hsv[2] = gpu_normalized_magnitude;
  195. cv::cuda::merge(gpu_hsv, 3, gpu_merged_hsv);
  196. // multiply each pixel value to 255
  197. gpu_merged_hsv.cv::cuda::GpuMat::convertTo(gpu_hsv_8u, CV_8U, 255.0);
  198. // convert hsv to bgr
  199. cv::cuda::cvtColor(gpu_hsv_8u, gpu_bgr, COLOR_HSV2BGR);
  200. // send original frame from GPU back to CPU
  201. gpu_frame.download(frame);
  202. // send result from GPU back to CPU
  203. gpu_bgr.download(bgr);
  204. // update previous_frame value
  205. gpu_previous = gpu_current;
  206. // end post pipeline timer
  207. auto end_post_time = high_resolution_clock::now();
  208. // add elapsed iteration time
  209. timers["post-process"].push_back(duration_cast<milliseconds>(end_post_time - start_post_time).count() / 1000.0);
  210. // end full pipeline timer
  211. auto end_full_time = high_resolution_clock::now();
  212. // add elapsed iteration time
  213. timers["full pipeline"].push_back(duration_cast<milliseconds>(end_full_time - start_full_time).count() / 1000.0);
  214. // visualization
  215. imshow("original", frame);
  216. imshow("result", bgr);
  217. int keyboard = waitKey(1);
  218. if (keyboard == 27)
  219. break;
  220. }
  221. }
  222. // release the capture
  223. capture.release();
  224. // destroy all windows
  225. destroyAllWindows();
  226. // print results
  227. cout << "Number of frames: " << num_frames << std::endl;
  228. // elapsed time at each stage
  229. cout << "Elapsed time" << std::endl;
  230. for (auto const& timer : timers)
  231. {
  232. cout << "- " << timer.first << " : " << accumulate(timer.second.begin(), timer.second.end(), 0.0) << " seconds"<< endl;
  233. }
  234. // calculate frames per second
  235. cout << "Default video FPS : " << fps << endl;
  236. float optical_flow_fps = (num_frames - 1) / accumulate(timers["optical flow"].begin(), timers["optical flow"].end(), 0.0);
  237. cout << "Optical flow FPS : " << optical_flow_fps << endl;
  238. float full_pipeline_fps = (num_frames - 1) / accumulate(timers["full pipeline"].begin(), timers["full pipeline"].end(), 0.0);
  239. cout << "Full pipeline FPS : " << full_pipeline_fps << endl;
  240. }
  241. int main( int argc, const char** argv )
  242. {
  243. string videoFileName;
  244. string device;
  245. // parse arguments from command line
  246. if (argc == 3)
  247. {
  248. videoFileName = argv[1];
  249. device = argv[2];
  250. }
  251. else if (argc == 2)
  252. {
  253. videoFileName = argv[1];
  254. device = "cpu";
  255. }
  256. else
  257. {
  258. cout << "Please input video filename." << endl;
  259. cout << "Usage example: ./demo.out video/boat.mp4" << endl;
  260. cout << "If you want to use GPU device instead of CPU, add one more argument." << endl;
  261. cout << "Usage example: ./demo.out video/boat.mp4 gpu" << endl;
  262. return 1;
  263. }
  264. // output passed arguments
  265. cout << "Configuration" << endl;
  266. cout << "- device : "<< device << endl;
  267. cout << "- video file : " << videoFileName << endl;
  268. calculate_optical_flow(videoFileName, device);
  269. return 0;
  270. }