pyrlk_optical_flow.cpp 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. #include <iostream>
  2. #include <vector>
  3. #include <opencv2/core.hpp>
  4. #include <opencv2/core/utility.hpp>
  5. #include <opencv2/imgproc.hpp>
  6. #include <opencv2/highgui.hpp>
  7. #include <opencv2/video.hpp>
  8. #include <opencv2/cudaoptflow.hpp>
  9. #include <opencv2/cudaimgproc.hpp>
  10. #include <opencv2/cudaarithm.hpp>
  11. using namespace std;
  12. using namespace cv;
  13. using namespace cv::cuda;
  14. static void download(const GpuMat& d_mat, vector<Point2f>& vec)
  15. {
  16. vec.resize(d_mat.cols);
  17. Mat mat(1, d_mat.cols, CV_32FC2, (void*)&vec[0]);
  18. d_mat.download(mat);
  19. }
  20. static void download(const GpuMat& d_mat, vector<uchar>& vec)
  21. {
  22. vec.resize(d_mat.cols);
  23. Mat mat(1, d_mat.cols, CV_8UC1, (void*)&vec[0]);
  24. d_mat.download(mat);
  25. }
  26. static void drawArrows(Mat& frame, const vector<Point2f>& prevPts, const vector<Point2f>& nextPts, const vector<uchar>& status, Scalar line_color = Scalar(0, 0, 255))
  27. {
  28. for (size_t i = 0; i < prevPts.size(); ++i)
  29. {
  30. if (status[i])
  31. {
  32. int line_thickness = 1;
  33. Point p = prevPts[i];
  34. Point q = nextPts[i];
  35. double angle = atan2((double) p.y - q.y, (double) p.x - q.x);
  36. double hypotenuse = sqrt( (double)(p.y - q.y)*(p.y - q.y) + (double)(p.x - q.x)*(p.x - q.x) );
  37. if (hypotenuse < 1.0)
  38. continue;
  39. // Here we lengthen the arrow by a factor of three.
  40. q.x = (int) (p.x - 3 * hypotenuse * cos(angle));
  41. q.y = (int) (p.y - 3 * hypotenuse * sin(angle));
  42. // Now we draw the main line of the arrow.
  43. line(frame, p, q, line_color, line_thickness);
  44. // Now draw the tips of the arrow. I do some scaling so that the
  45. // tips look proportional to the main line of the arrow.
  46. p.x = (int) (q.x + 9 * cos(angle + CV_PI / 4));
  47. p.y = (int) (q.y + 9 * sin(angle + CV_PI / 4));
  48. line(frame, p, q, line_color, line_thickness);
  49. p.x = (int) (q.x + 9 * cos(angle - CV_PI / 4));
  50. p.y = (int) (q.y + 9 * sin(angle - CV_PI / 4));
  51. line(frame, p, q, line_color, line_thickness);
  52. }
  53. }
  54. }
  55. inline bool isFlowCorrect(Point2f u)
  56. {
  57. return !cvIsNaN(u.x) && !cvIsNaN(u.y) && fabs(u.x) < 1e9 && fabs(u.y) < 1e9;
  58. }
  59. static Vec3b computeColor(float fx, float fy)
  60. {
  61. static bool first = true;
  62. // relative lengths of color transitions:
  63. // these are chosen based on perceptual similarity
  64. // (e.g. one can distinguish more shades between red and yellow
  65. // than between yellow and green)
  66. const int RY = 15;
  67. const int YG = 6;
  68. const int GC = 4;
  69. const int CB = 11;
  70. const int BM = 13;
  71. const int MR = 6;
  72. const int NCOLS = RY + YG + GC + CB + BM + MR;
  73. static Vec3i colorWheel[NCOLS];
  74. if (first)
  75. {
  76. int k = 0;
  77. for (int i = 0; i < RY; ++i, ++k)
  78. colorWheel[k] = Vec3i(255, 255 * i / RY, 0);
  79. for (int i = 0; i < YG; ++i, ++k)
  80. colorWheel[k] = Vec3i(255 - 255 * i / YG, 255, 0);
  81. for (int i = 0; i < GC; ++i, ++k)
  82. colorWheel[k] = Vec3i(0, 255, 255 * i / GC);
  83. for (int i = 0; i < CB; ++i, ++k)
  84. colorWheel[k] = Vec3i(0, 255 - 255 * i / CB, 255);
  85. for (int i = 0; i < BM; ++i, ++k)
  86. colorWheel[k] = Vec3i(255 * i / BM, 0, 255);
  87. for (int i = 0; i < MR; ++i, ++k)
  88. colorWheel[k] = Vec3i(255, 0, 255 - 255 * i / MR);
  89. first = false;
  90. }
  91. const float rad = sqrt(fx * fx + fy * fy);
  92. const float a = atan2(-fy, -fx) / (float)CV_PI;
  93. const float fk = (a + 1.0f) / 2.0f * (NCOLS - 1);
  94. const int k0 = static_cast<int>(fk);
  95. const int k1 = (k0 + 1) % NCOLS;
  96. const float f = fk - k0;
  97. Vec3b pix;
  98. for (int b = 0; b < 3; b++)
  99. {
  100. const float col0 = colorWheel[k0][b] / 255.0f;
  101. const float col1 = colorWheel[k1][b] / 255.0f;
  102. float col = (1 - f) * col0 + f * col1;
  103. if (rad <= 1)
  104. col = 1 - rad * (1 - col); // increase saturation with radius
  105. else
  106. col *= .75; // out of range
  107. pix[2 - b] = static_cast<uchar>(255.0 * col);
  108. }
  109. return pix;
  110. }
  111. static void drawOpticalFlow(const Mat_<float>& flowx, const Mat_<float>& flowy, Mat& dst, float maxmotion = -1)
  112. {
  113. dst.create(flowx.size(), CV_8UC3);
  114. dst.setTo(Scalar::all(0));
  115. // determine motion range:
  116. float maxrad = maxmotion;
  117. if (maxmotion <= 0)
  118. {
  119. maxrad = 1;
  120. for (int y = 0; y < flowx.rows; ++y)
  121. {
  122. for (int x = 0; x < flowx.cols; ++x)
  123. {
  124. Point2f u(flowx(y, x), flowy(y, x));
  125. if (!isFlowCorrect(u))
  126. continue;
  127. maxrad = max(maxrad, sqrt(u.x * u.x + u.y * u.y));
  128. }
  129. }
  130. }
  131. for (int y = 0; y < flowx.rows; ++y)
  132. {
  133. for (int x = 0; x < flowx.cols; ++x)
  134. {
  135. Point2f u(flowx(y, x), flowy(y, x));
  136. if (isFlowCorrect(u))
  137. dst.at<Vec3b>(y, x) = computeColor(u.x / maxrad, u.y / maxrad);
  138. }
  139. }
  140. }
  141. static void showFlow(const char* name, const GpuMat& d_flow)
  142. {
  143. GpuMat planes[2];
  144. cuda::split(d_flow, planes);
  145. Mat flowx(planes[0]);
  146. Mat flowy(planes[1]);
  147. Mat out;
  148. drawOpticalFlow(flowx, flowy, out, 10);
  149. imshow(name, out);
  150. }
  151. template <typename T> inline T clamp (T x, T a, T b)
  152. {
  153. return ((x) > (a) ? ((x) < (b) ? (x) : (b)) : (a));
  154. }
  155. template <typename T> inline T mapValue(T x, T a, T b, T c, T d)
  156. {
  157. x = clamp(x, a, b);
  158. return c + (d - c) * (x - a) / (b - a);
  159. }
  160. int main(int argc, const char* argv[])
  161. {
  162. const char* keys =
  163. "{ h help | | print help message }"
  164. "{ l left | ../data/pic1.png | specify left image }"
  165. "{ r right | ../data/pic2.png | specify right image }"
  166. "{ flow | sparse | specify flow type [PyrLK] }"
  167. "{ gray | | use grayscale sources [PyrLK Sparse] }"
  168. "{ win_size | 21 | specify windows size [PyrLK] }"
  169. "{ max_level | 3 | specify max level [PyrLK] }"
  170. "{ iters | 30 | specify iterations count [PyrLK] }"
  171. "{ points | 4000 | specify points count [GoodFeatureToTrack] }"
  172. "{ min_dist | 0 | specify minimal distance between points [GoodFeatureToTrack] }";
  173. CommandLineParser cmd(argc, argv, keys);
  174. if (cmd.has("help") || !cmd.check())
  175. {
  176. cmd.printMessage();
  177. cmd.printErrors();
  178. return 0;
  179. }
  180. string fname0 = cmd.get<string>("left");
  181. string fname1 = cmd.get<string>("right");
  182. if (fname0.empty() || fname1.empty())
  183. {
  184. cerr << "Missing input file names" << endl;
  185. return -1;
  186. }
  187. string flow_type = cmd.get<string>("flow");
  188. bool is_sparse = true;
  189. if (flow_type == "sparse")
  190. {
  191. is_sparse = true;
  192. }
  193. else if (flow_type == "dense")
  194. {
  195. is_sparse = false;
  196. }
  197. else
  198. {
  199. cerr << "please specify 'sparse' or 'dense' as flow type" << endl;
  200. return -1;
  201. }
  202. bool useGray = cmd.has("gray");
  203. int winSize = cmd.get<int>("win_size");
  204. int maxLevel = cmd.get<int>("max_level");
  205. int iters = cmd.get<int>("iters");
  206. int points = cmd.get<int>("points");
  207. double minDist = cmd.get<double>("min_dist");
  208. Mat frame0 = imread(fname0);
  209. Mat frame1 = imread(fname1);
  210. if (frame0.empty() || frame1.empty())
  211. {
  212. cout << "Can't load input images" << endl;
  213. return -1;
  214. }
  215. cout << "Image size : " << frame0.cols << " x " << frame0.rows << endl;
  216. cout << "Points count : " << points << endl;
  217. cout << endl;
  218. Mat frame0Gray;
  219. cv::cvtColor(frame0, frame0Gray, COLOR_BGR2GRAY);
  220. Mat frame1Gray;
  221. cv::cvtColor(frame1, frame1Gray, COLOR_BGR2GRAY);
  222. // goodFeaturesToTrack
  223. GpuMat d_frame0Gray(frame0Gray);
  224. GpuMat d_prevPts;
  225. Ptr<cuda::CornersDetector> detector = cuda::createGoodFeaturesToTrackDetector(d_frame0Gray.type(), points, 0.01, minDist);
  226. detector->detect(d_frame0Gray, d_prevPts);
  227. GpuMat d_frame0(frame0);
  228. GpuMat d_frame1(frame1);
  229. GpuMat d_frame1Gray(frame1Gray);
  230. GpuMat d_nextPts;
  231. GpuMat d_status;
  232. GpuMat d_flow(frame0.size(), CV_32FC2);
  233. if (is_sparse)
  234. {
  235. // Sparse
  236. Ptr<cuda::SparsePyrLKOpticalFlow> d_pyrLK_sparse = cuda::SparsePyrLKOpticalFlow::create(
  237. Size(winSize, winSize), maxLevel, iters);
  238. d_pyrLK_sparse->calc(useGray ? d_frame0Gray : d_frame0, useGray ? d_frame1Gray : d_frame1, d_prevPts, d_nextPts, d_status);
  239. // Draw arrows
  240. vector<Point2f> prevPts(d_prevPts.cols);
  241. download(d_prevPts, prevPts);
  242. vector<Point2f> nextPts(d_nextPts.cols);
  243. download(d_nextPts, nextPts);
  244. vector<uchar> status(d_status.cols);
  245. download(d_status, status);
  246. namedWindow("PyrLK [Sparse]", WINDOW_NORMAL);
  247. drawArrows(frame0, prevPts, nextPts, status, Scalar(255, 0, 0));
  248. imshow("PyrLK [Sparse]", frame0);
  249. }
  250. else
  251. {
  252. // Dense
  253. Ptr<cuda::DensePyrLKOpticalFlow> d_pyrLK_dense = cuda::DensePyrLKOpticalFlow::create(
  254. Size(winSize, winSize), maxLevel, iters);
  255. d_pyrLK_dense->calc(d_frame0Gray, d_frame1Gray, d_flow);
  256. // Draw flows
  257. namedWindow("PyrLK [Dense] Flow Field", WINDOW_NORMAL);
  258. showFlow("PyrLK [Dense] Flow Field", d_flow);
  259. }
  260. waitKey(0);
  261. return 0;
  262. }