diff --git a/samples/gpu/pyrlk_optical_flow.cpp b/samples/gpu/pyrlk_optical_flow.cpp index f13487b622..fa6b376362 100644 --- a/samples/gpu/pyrlk_optical_flow.cpp +++ b/samples/gpu/pyrlk_optical_flow.cpp @@ -1,13 +1,14 @@ #include #include -#include "opencv2/core.hpp" +#include #include -#include "opencv2/imgproc.hpp" -#include "opencv2/highgui.hpp" -#include "opencv2/video.hpp" -#include "opencv2/cudaoptflow.hpp" -#include "opencv2/cudaimgproc.hpp" +#include +#include +#include +#include +#include +#include using namespace std; using namespace cv; @@ -66,6 +67,132 @@ static void drawArrows(Mat& frame, const vector& prevPts, const vector< } } +inline bool isFlowCorrect(Point2f u) +{ + return !cvIsNaN(u.x) && !cvIsNaN(u.y) && fabs(u.x) < 1e9 && fabs(u.y) < 1e9; +} + +static Vec3b computeColor(float fx, float fy) +{ + static bool first = true; + + // relative lengths of color transitions: + // these are chosen based on perceptual similarity + // (e.g. one can distinguish more shades between red and yellow + // than between yellow and green) + const int RY = 15; + const int YG = 6; + const int GC = 4; + const int CB = 11; + const int BM = 13; + const int MR = 6; + const int NCOLS = RY + YG + GC + CB + BM + MR; + static Vec3i colorWheel[NCOLS]; + + if (first) + { + int k = 0; + + for (int i = 0; i < RY; ++i, ++k) + colorWheel[k] = Vec3i(255, 255 * i / RY, 0); + + for (int i = 0; i < YG; ++i, ++k) + colorWheel[k] = Vec3i(255 - 255 * i / YG, 255, 0); + + for (int i = 0; i < GC; ++i, ++k) + colorWheel[k] = Vec3i(0, 255, 255 * i / GC); + + for (int i = 0; i < CB; ++i, ++k) + colorWheel[k] = Vec3i(0, 255 - 255 * i / CB, 255); + + for (int i = 0; i < BM; ++i, ++k) + colorWheel[k] = Vec3i(255 * i / BM, 0, 255); + + for (int i = 0; i < MR; ++i, ++k) + colorWheel[k] = Vec3i(255, 0, 255 - 255 * i / MR); + + first = false; + } + + const float rad = sqrt(fx * fx + fy * fy); + const float a = atan2(-fy, -fx) / (float)CV_PI; + + const float fk = (a + 1.0f) / 2.0f * (NCOLS - 1); + const int k0 = static_cast(fk); + const int k1 = (k0 + 1) % NCOLS; + const float f = fk - k0; + + Vec3b pix; + + for (int b = 0; b < 3; b++) + { + const float col0 = colorWheel[k0][b] / 255.0f; + const float col1 = colorWheel[k1][b] / 255.0f; + + float col = (1 - f) * col0 + f * col1; + + if (rad <= 1) + col = 1 - rad * (1 - col); // increase saturation with radius + else + col *= .75; // out of range + + pix[2 - b] = static_cast(255.0 * col); + } + + return pix; +} + +static void drawOpticalFlow(const Mat_& flowx, const Mat_& flowy, Mat& dst, float maxmotion = -1) +{ + dst.create(flowx.size(), CV_8UC3); + dst.setTo(Scalar::all(0)); + + // determine motion range: + float maxrad = maxmotion; + + if (maxmotion <= 0) + { + maxrad = 1; + for (int y = 0; y < flowx.rows; ++y) + { + for (int x = 0; x < flowx.cols; ++x) + { + Point2f u(flowx(y, x), flowy(y, x)); + + if (!isFlowCorrect(u)) + continue; + + maxrad = max(maxrad, sqrt(u.x * u.x + u.y * u.y)); + } + } + } + + for (int y = 0; y < flowx.rows; ++y) + { + for (int x = 0; x < flowx.cols; ++x) + { + Point2f u(flowx(y, x), flowy(y, x)); + + if (isFlowCorrect(u)) + dst.at(y, x) = computeColor(u.x / maxrad, u.y / maxrad); + } + } +} + +static void showFlow(const char* name, const GpuMat& d_flow) +{ + GpuMat planes[2]; + cuda::split(d_flow, planes); + + Mat flowx(planes[0]); + Mat flowy(planes[1]); + + Mat out; + drawOpticalFlow(flowx, flowy, out, 10); + + imshow(name, out); +} + template inline T clamp (T x, T a, T b) { return ((x) > (a) ? ((x) < (b) ? (x) : (b)) : (a)); @@ -80,15 +207,16 @@ template inline T mapValue(T x, T a, T b, T c, T d) int main(int argc, const char* argv[]) { const char* keys = - "{ h help | | print help message }" + "{ h help | | print help message }" "{ l left | ../data/pic1.png | specify left image }" "{ r right | ../data/pic2.png | specify right image }" - "{ gray | | use grayscale sources [PyrLK Sparse] }" - "{ win_size | 21 | specify windows size [PyrLK] }" - "{ max_level | 3 | specify max level [PyrLK] }" - "{ iters | 30 | specify iterations count [PyrLK] }" - "{ points | 4000 | specify points count [GoodFeatureToTrack] }" - "{ min_dist | 0 | specify minimal distance between points [GoodFeatureToTrack] }"; + "{ flow | sparse | specify flow type [PyrLK] }" + "{ gray | | use grayscale sources [PyrLK Sparse] }" + "{ win_size | 21 | specify windows size [PyrLK] }" + "{ max_level | 3 | specify max level [PyrLK] }" + "{ iters | 30 | specify iterations count [PyrLK] }" + "{ points | 4000 | specify points count [GoodFeatureToTrack] }" + "{ min_dist | 0 | specify minimal distance between points [GoodFeatureToTrack] }"; CommandLineParser cmd(argc, argv, keys); @@ -108,6 +236,22 @@ int main(int argc, const char* argv[]) return -1; } + string flow_type = cmd.get("flow"); + bool is_sparse = true; + if (flow_type == "sparse") + { + is_sparse = true; + } + else if (flow_type == "dense") + { + is_sparse = false; + } + else + { + cerr << "please specify 'sparse' or 'dense' as flow type" << endl; + return -1; + } + bool useGray = cmd.has("gray"); int winSize = cmd.get("win_size"); int maxLevel = cmd.get("max_level"); @@ -124,9 +268,6 @@ int main(int argc, const char* argv[]) return -1; } - namedWindow("PyrLK [Sparse]", WINDOW_NORMAL); - namedWindow("PyrLK [Dense] Flow Field", WINDOW_NORMAL); - cout << "Image size : " << frame0.cols << " x " << frame0.rows << endl; cout << "Points count : " << points << endl; @@ -138,43 +279,53 @@ int main(int argc, const char* argv[]) cv::cvtColor(frame1, frame1Gray, COLOR_BGR2GRAY); // goodFeaturesToTrack - GpuMat d_frame0Gray(frame0Gray); GpuMat d_prevPts; Ptr detector = cuda::createGoodFeaturesToTrackDetector(d_frame0Gray.type(), points, 0.01, minDist); - detector->detect(d_frame0Gray, d_prevPts); - // Sparse - - Ptr d_pyrLK = cuda::SparsePyrLKOpticalFlow::create( - Size(winSize, winSize), maxLevel, iters); - GpuMat d_frame0(frame0); GpuMat d_frame1(frame1); GpuMat d_frame1Gray(frame1Gray); GpuMat d_nextPts; GpuMat d_status; + GpuMat d_flow(frame0.size(), CV_32FC2); - d_pyrLK->calc(useGray ? d_frame0Gray : d_frame0, useGray ? d_frame1Gray : d_frame1, d_prevPts, d_nextPts, d_status); + if (is_sparse) + { + // Sparse + Ptr d_pyrLK_sparse = cuda::SparsePyrLKOpticalFlow::create( + Size(winSize, winSize), maxLevel, iters); + d_pyrLK_sparse->calc(useGray ? d_frame0Gray : d_frame0, useGray ? d_frame1Gray : d_frame1, d_prevPts, d_nextPts, d_status); - // Draw arrows + // Draw arrows + vector prevPts(d_prevPts.cols); + download(d_prevPts, prevPts); - vector prevPts(d_prevPts.cols); - download(d_prevPts, prevPts); + vector nextPts(d_nextPts.cols); + download(d_nextPts, nextPts); - vector nextPts(d_nextPts.cols); - download(d_nextPts, nextPts); + vector status(d_status.cols); + download(d_status, status); - vector status(d_status.cols); - download(d_status, status); + namedWindow("PyrLK [Sparse]", WINDOW_NORMAL); + drawArrows(frame0, prevPts, nextPts, status, Scalar(255, 0, 0)); + imshow("PyrLK [Sparse]", frame0); + } + else + { + // Dense + Ptr d_pyrLK_dense = cuda::DensePyrLKOpticalFlow::create( + Size(winSize, winSize), maxLevel, iters); + d_pyrLK_dense->calc(d_frame0Gray, d_frame1Gray, d_flow); - drawArrows(frame0, prevPts, nextPts, status, Scalar(255, 0, 0)); + // Draw flows + namedWindow("PyrLK [Dense] Flow Field", WINDOW_NORMAL); + showFlow("PyrLK [Dense] Flow Field", d_flow); + } - imshow("PyrLK [Sparse]", frame0); - - waitKey(); + waitKey(0); return 0; -} +} \ No newline at end of file