diff --git a/include/tracking.hpp b/include/tracking.hpp index f80004c..86e1ab4 100644 --- a/include/tracking.hpp +++ b/include/tracking.hpp @@ -4,6 +4,7 @@ #include #include "opencv2/core/core.hpp" +#include "opencv2/opencv.hpp" class Tracker { public: @@ -11,3 +12,14 @@ class Tracker { virtual bool Init(const cv::Mat &frame, const cv::Rect &roi) = 0; virtual cv::Rect Track(const cv::Mat &frame) = 0; }; + +class MedianFlowTracker : public Tracker +{ + public: + virtual bool Init(const cv::Mat &frame, const cv::Rect &roi); + virtual cv::Rect Track(const cv::Mat &frame); + + protected: + cv::Rect position_; + cv::Mat frame_; +}; \ No newline at end of file diff --git a/samples/tracking_demo.cpp b/samples/tracking_demo.cpp new file mode 100644 index 0000000..6a30bc6 --- /dev/null +++ b/samples/tracking_demo.cpp @@ -0,0 +1,109 @@ +#include +#include +#include "tracking.hpp" + +using namespace std; +using namespace cv; + +struct MouseCallbackState +{ + bool is_selection_started; + bool is_selection_finished; + Point point_first; + Point point_second; +}; + +void OnMouse(int event, int x, int y, int flags, void* param) +{ + MouseCallbackState* mouse = (MouseCallbackState*)param; + + switch (event) + { + case EVENT_LBUTTONDOWN: + + mouse->is_selection_started = true; + mouse->is_selection_finished = false; + mouse->point_first.x = x; + mouse->point_first.y = y; + break; + + case EVENT_LBUTTONUP: + + mouse->is_selection_started = false; + mouse->is_selection_finished = true; + if (mouse->point_first.x != x && mouse->point_first.y != y) + { + mouse->point_second.x = x; + mouse->point_second.y = y; + } + break; + + case EVENT_MOUSEMOVE: + + if (mouse->is_selection_finished == false) + { + mouse->point_second.x = x; + mouse->point_second.y = y; + } + break; + } + +} +int main(int argc, const char** argv) +{ + //init + VideoCapture capture; + Mat frame; + Rect roi; + MedianFlowTracker track; + char c; + MouseCallbackState p; + std::string vidName = "C:\\MyProjects\\itseez-ss-2016-practice\\test\\test_data\\video\\pedestrians.mpg"; + bool flag = capture.open(vidName); + namedWindow("Track"); + setMouseCallback("Track", OnMouse, &p); + + if (flag) + { + flag = capture.read(frame); + + if (frame.empty()) + { + cout<< " --(!) No captured frame -- Break!"; + return -1; + } + + imshow("Track", frame); + // select obj + while (1) + { + c = waitKey(1000); + roi = Rect(p.point_first, p.point_second); + if ((p.point_first.x < p.point_second.x) && (p.point_first.y < p.point_second.y) && !roi.empty()) break; + } + rectangle(frame, roi, Scalar(255, 0, 255));//draw rect + imshow("Track", frame); + + waitKey(1000); + + if (!track.Init(frame, roi)) + { + cout << "Init error"; + return -1; + } + + while (capture.read(frame)) + { + roi = track.Track(frame); + rectangle(frame, roi, Scalar(255, 0, 255));//draw rect + imshow("Track", frame); + c = waitKey(33); + if (c == 27) break; + } + capture.release(); + } + else + cout << "--(!)Error opening video capture" << endl; + waitKey(0); + return 0; +} \ No newline at end of file diff --git a/src/tracking.cpp b/src/tracking.cpp index 26cbe06..0d0b87c 100644 --- a/src/tracking.cpp +++ b/src/tracking.cpp @@ -1,13 +1,95 @@ #include "tracking.hpp" #include +#include using std::string; using std::shared_ptr; using namespace cv; -shared_ptr Tracker::CreateTracker(const string &name) { - std::cerr << "Failed to create tracker with name '" << name << "'" - << std::endl; - return nullptr; +shared_ptr Tracker::CreateTracker(const string &name) +{ + if (name == "median_flow") + { + return std::make_shared(); + } + else + { + std::cerr << "Failed to create tracker with name '" << name << "'" + << std::endl; + return nullptr; + } + +} + +bool MedianFlowTracker::Init(const cv::Mat &frame, const cv::Rect &roi) +{ + if (frame.empty()) return false; + if (roi.empty()) return false; + frame_ = frame; + position_ = roi; + return true; +} + +cv::Rect MedianFlowTracker::Track(const cv::Mat & frame) +{ + //init + TermCriteria termcrit(TermCriteria::COUNT | TermCriteria::EPS, 20, 0.03); + Size subPixWinSize(10, 10), winSize(31, 31); + Mat tmp, tmp_gray, next_gray, prev_gray; + std::vector features_prev, features_next; // Mat? + std::vector status; + std::vector err; + tmp = frame_(position_); + //frame_(position_).copyTo(tmp); + + // find features + cvtColor(tmp, tmp_gray, COLOR_BGR2GRAY); + goodFeaturesToTrack(tmp_gray, features_prev, 100 , 0.01, 10); + assert(features_prev.size() > 0); + + //calc optical flow + cvtColor(frame_, prev_gray, COLOR_BGR2GRAY); + cvtColor(frame, next_gray, COLOR_BGR2GRAY); + calcOpticalFlowPyrLK(prev_gray, next_gray, features_prev, features_next, status, err, + winSize, 3, termcrit, 0, 0.001); + + //filtration bad points + + size_t i, k; // counters + + for( i = k = 0; i < features_next.size(); i++ ) + { + if (!status[i]) + continue; + features_prev[k] = features_prev[i]; + features_next[k++] = features_next[i]; + + } + features_prev.resize(k); // new points + features_next.resize(k); + + //find median X and Y offsets + std::vector offsetX; + std::vector offsetY; + Point2f newObjCenter; + + for (i = 0; i < features_next.size(); i++) + { + offsetX.push_back( features_next[i].x - features_prev[i].x); + offsetY.push_back( features_next[i].y - features_prev[i].y); + } + + float medianX , medianY; + + std::sort(offsetX.begin(), offsetX.end()); + std::sort(offsetY.begin(), offsetY.end()); + + medianX = (int)offsetX[offsetX.size()/2]; + medianY = (int)offsetY[offsetY.size()/2]; + + // new rect position + Rect newPos(medianX - position_.width / 2, medianY - position_.height / 2, position_.width, position_.height); + + return newPos; }