diff --git a/include/image_processing.hpp b/include/image_processing.hpp index ca1f116..02cd5b7 100644 --- a/include/image_processing.hpp +++ b/include/image_processing.hpp @@ -4,6 +4,10 @@ #include #include "opencv2/core/core.hpp" +#include + +using namespace std; +using namespace cv; class ImageProcessor { public: @@ -15,4 +19,16 @@ class ImageProcessor { const int kernelSize) = 0; virtual cv::Mat Pixelize(const cv::Mat &src, const cv::Rect &roi, const int kDivs) = 0; +}; + +class ImageProcessorImpl : public ImageProcessor { +public: + virtual cv::Mat CvtColor(const cv::Mat &src, const cv::Rect &roi); + virtual cv::Mat Filter(const cv::Mat &src, const cv::Rect &roi, + const int size); + virtual cv::Mat DetectEdges(const cv::Mat &src, const cv::Rect &roi, + const int filter_size, const int low_threshold, + const int ratio, const int kernel_size); + virtual cv::Mat Pixelize(const cv::Mat &src, const cv::Rect &roi, + const int divs); }; \ No newline at end of file diff --git a/include/tracking.hpp b/include/tracking.hpp index f80004c..d6ed266 100644 --- a/include/tracking.hpp +++ b/include/tracking.hpp @@ -4,6 +4,9 @@ #include #include "opencv2/core/core.hpp" +#include "opencv2/imgproc/imgproc.hpp" +#include "opencv2/video/video.hpp" +#include "opencv2/imgproc/imgproc.hpp" class Tracker { public: @@ -11,3 +14,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/imgproc_demo.cpp b/samples/imgproc_demo.cpp new file mode 100644 index 0000000..48049cd --- /dev/null +++ b/samples/imgproc_demo.cpp @@ -0,0 +1,179 @@ +#include +#include + +#include "opencv2/core.hpp" +#include "opencv2/highgui.hpp" +#include +#include + +using namespace std; +using namespace cv; + +const char* kAbout = + "This is an empty application that can be treated as a template for your " + "own doing-something-cool applications."; + +const char* kOptions = + "{ @image | | image to process }" + "{ v video | | video to process }" + "{ gray | | convert ROI to gray scale}" + "{ h ? help usage | | print help message }"; + + +struct MouseCallbackState { + bool is_selection_started; + bool is_selection_finished; + Point point_first; + Point point_second; + ImageProcessorImpl processor; + Mat img; + Rect roi; + bool grayOk; + string kSrcWindowName; +}; + +struct ProgState { + MouseCallbackState mouseState; + bool GrayOk; +}; + +void OnMouse(int event, int x, int y, int flags, void* params ) +{ + + //state. + MouseCallbackState* state = (MouseCallbackState*)params; + if (event == EVENT_LBUTTONDOWN) + { + state->is_selection_started = true; + state->is_selection_finished = false; + state->point_first.x = x; + state->point_first.y = y; + } + + + if (event == EVENT_LBUTTONUP) + { + state->is_selection_started = false; + state->is_selection_finished = true; + state->point_second.x = x; + state->point_second.y = y; + + state->img = state->processor.CvtColor(state->img, Rect (state->point_second, state->point_first)); + imshow(state->kSrcWindowName, state->img); + } + else if (event == EVENT_RBUTTONDOWN) + { + //cout << "Right button of the mouse is clicked - position (" << x << ", " << y << ")" << endl; + } + else if (event == EVENT_MBUTTONDOWN) + { + //cout << "Middle button of the mouse is clicked - position (" << x << ", " << y << ")" << endl; + } + else if (event == EVENT_MOUSEMOVE) + { + if (state->is_selection_finished == false) + { + state->point_second.x = x; + state->point_second.y = y; + } + + } +} + + +void DstImgCallback(int event, int x, int y, int flags, void* params) +{ + + //state. + MouseCallbackState* state = (MouseCallbackState*)params; + if (event == EVENT_LBUTTONDOWN) + { + state->is_selection_started = true; + state->is_selection_finished = false; + state->point_first.x = x; + state->point_first.y = y; + } + +} + + +int main(int argc, const char** argv) { + // Parse command line arguments. + CommandLineParser parser(argc, argv, kOptions); + parser.about(kAbout); + + // If help option is given, print help message and exit. + if (parser.get("help")) { + parser.printMessage(); + return 0; + } + + // Do something cool. + cout << "This is empty template sample." << endl; + + // Read image. + Mat img = imread(parser.get(0)); + if (img.empty()) { + cout << "Failed to open image file '" + parser.get(0) + "'." + << endl; + return 0; + } + + + + const string kSrcWindowName = "Source image"; + + const int kWaitKeyDelay = 1; + namedWindow(kSrcWindowName, WINDOW_NORMAL); + resizeWindow(kSrcWindowName, 640, 480); + ImageProcessorImpl processor; + MouseCallbackState state; + state.img = img; + state.processor = processor; + state.kSrcWindowName = kSrcWindowName; + setMouseCallback(kSrcWindowName, OnMouse, &state); + imshow(kSrcWindowName, img); + bool grayOk = false; + const int threshold = parser.get("gray"); + try { + grayOk = true; + + } + + catch (const std::exception& ex) { + cout << ex.what() << endl; + return 0; + } + state.grayOk = grayOk; + + imshow(kSrcWindowName, state.img); + /* + const string kDstWindowName = "Destination image"; + namedWindow(kDstWindowName, WINDOW_NORMAL); + resizeWindow(kDstWindowName, 640, 480); + setMouseCallback(kDstWindowName, DstImgCallback, &state); + + if (grayOk) { + + }*/ + + + //waitKey(kWaitKeyDelay); + waitKey(0); + + //Create a window + //namedWindow("My Window", 1); + + //set the callback function for any mouse event + //setMouseCallback("My Window", CallBackFunc, NULL); + + //show the image + //imshow("My Window", img); + + // Wait until user press some key + //waitKey(0); + + return 0; +} + + diff --git a/samples/template_demo.cpp b/samples/template_demo.cpp index 3b9eb74..7ace08e 100644 --- a/samples/template_demo.cpp +++ b/samples/template_demo.cpp @@ -7,27 +7,27 @@ using namespace std; using namespace cv; const char* kAbout = - "This is an empty application that can be treated as a template for your " - "own doing-something-cool applications."; +"This is an empty application that can be treated as a template for your " +"own doing-something-cool applications."; const char* kOptions = - "{ v video | | video to process }" - "{ h ? help usage | | print help message }"; +"{ v video | | video to process }" +"{ h ? help usage | | print help message }"; int main(int argc, const char** argv) { - // Parse command line arguments. - CommandLineParser parser(argc, argv, kOptions); - parser.about(kAbout); + // Parse command line arguments. + CommandLineParser parser(argc, argv, kOptions); + parser.about(kAbout); - // If help option is given, print help message and exit. - if (parser.get("help")) { - parser.printMessage(); - return 0; - } + // If help option is given, print help message and exit. + if (parser.get("help")) { + parser.printMessage(); + return 0; + } - // Do something cool. - cout << "This is empty template sample." << endl; + // Do something cool. + cout << "This is empty template sample." << endl; - return 0; + return 0; } diff --git a/samples/tracking_demo.cpp b/samples/tracking_demo.cpp new file mode 100644 index 0000000..2f3a68a --- /dev/null +++ b/samples/tracking_demo.cpp @@ -0,0 +1,191 @@ +#include +#include + +#include "opencv2/core.hpp" +#include "opencv2/highgui.hpp" +#include +#include +#include + +using namespace std; +using namespace cv; + +const char* kAbout = +"This is an empty application that can be treated as a template for your " +"own doing-something-cool applications."; + +const char* kOptions = +"{ @image | | image to process }" +"{ v video | | video to process }" +"{ gray | | convert ROI to gray scale}" +"{ h ? help usage | | print help message }"; + + +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* params) +{ + + //state. + MouseCallbackState* state = (MouseCallbackState*)params; + if (event == EVENT_LBUTTONDOWN) + { + state->is_selection_started = true; + state->is_selection_finished = false; + state->point_first.x = x; + state->point_first.y = y; + } + + + if (event == EVENT_LBUTTONUP) + { + state->is_selection_started = false; + state->is_selection_finished = true; + state->point_second.x = x; + state->point_second.y = y; + } + if (event == EVENT_RBUTTONDOWN) + { + //cout << "Right button of the mouse is clicked - position (" << x << ", " << y << ")" << endl; + } + if (event == EVENT_MBUTTONDOWN) + { + //cout << "Middle button of the mouse is clicked - position (" << x << ", " << y << ")" << endl; + } + if (event == EVENT_MOUSEMOVE) + { + if (state->is_selection_finished == false) + { + state->point_second.x = x; + state->point_second.y = y; + } + + } +} + + +void DstImgCallback(int event, int x, int y, int flags, void* params) +{ + + //state. + MouseCallbackState* state = (MouseCallbackState*)params; + if (event == EVENT_LBUTTONDOWN) + { + state->is_selection_started = true; + state->is_selection_finished = false; + state->point_first.x = x; + state->point_first.y = y; + } + +} + + +int main(int argc, const char** argv) { + // Parse command line arguments. + CommandLineParser parser(argc, argv, kOptions); + parser.about(kAbout); + + // If help option is given, print help message and exit. + if (parser.get("help")) { + parser.printMessage(); + return 0; + } + + bool grayOk = false; + if (parser.has("gray")) + grayOk = true; + + // Do something cool. + cout << "This is empty template sample." << endl; + + // Read image. + Mat img = imread(parser.get(0)); + if (img.empty()) { + cout << "Failed to open image file '" + parser.get(0) + "'." + << endl; + return 0; + } + + VideoCapture videoCap("C:\\Users\\iss2016\\itseez-ss-2016-practice\\test\\test_data\\video\\logo.mp4"); // open the video file for reading + + + //if (!videoCap.isOpened()) // if not success, exit program + //{ + // cout << "Cannot open the video file" << endl; + // return -1; + //} + + + + const string kSrcWindowName = "Source image"; + + const int kWaitKeyDelay = 1; + namedWindow(kSrcWindowName, WINDOW_NORMAL); + resizeWindow(kSrcWindowName, 640, 480); + MedianFlowTracker tracker; + ImageProcessorImpl processor; + MouseCallbackState state; + state.is_selection_finished = true; + state.is_selection_started = true; + setMouseCallback(kSrcWindowName, OnMouse, &state); + bool detectChanges = state.is_selection_finished; + char c; + Mat frame; + bool bSuccess = true;// = videoCap.read(frame); // read a new frame from video + + videoCap >> frame; + if (frame.empty()) + { + return -1; + } + + //if (!bSuccess) //if not success, break loop + //{ + // cout << "Cannot read the frame from video file" << endl; + // return 0; + //} + imshow(kSrcWindowName, frame); + for (;;) + { + + + //bSuccess = videoCap.read(frame); // read a new frame from video + + /*if (!bSuccess) //if not success, break loop + { + cout << "Cannot read the frame from video file" << endl; + break; + }*/ + + //img = tracker.Init(img, Rect(state.point_second, state.point_first)); + if ((detectChanges != state.is_selection_finished) & (state.is_selection_finished)) + { + if (!tracker.Init(frame, Rect(state.point_second, state.point_first))) + break; + rectangle(frame, Rect(state.point_second, state.point_first), Scalar(0, 128, 0), 2, 8, 0); + imshow(kSrcWindowName, frame); + + } + detectChanges = state.is_selection_finished; + + if (!state.is_selection_started) + { + tracker.Track(frame); + videoCap >> frame; + } + + c = waitKey(30); + if (c == 27) { // если нажата ESC - выходим + break; + } + } + return 0; +} + + diff --git a/src/image_processing.cpp b/src/image_processing.cpp new file mode 100644 index 0000000..f6e4933 --- /dev/null +++ b/src/image_processing.cpp @@ -0,0 +1,41 @@ +#include +#include +using namespace std; +using namespace cv; +Mat ImageProcessorImpl::CvtColor(const cv::Mat &src, const cv::Rect &roi) +{ + Mat src_copy, src_copy_roi, dst_gray_roi, dst_roi; + src.copyTo(src_copy); + src_copy_roi = src_copy(roi); + + cvtColor(src_copy_roi, dst_gray_roi, COLOR_BGR2GRAY); + + vector channels; + channels.push_back(dst_gray_roi); + channels.push_back(dst_gray_roi); + channels.push_back(dst_gray_roi); + merge(channels, dst_roi); +// cvtColor(dst_roi, dst_roi, COLOR_GRAY2BGR); + dst_roi.copyTo(src_copy_roi); + + return src_copy; +} +Mat ImageProcessorImpl::Filter(const cv::Mat &src, const cv::Rect &roi, + const int kSize) +{ + Mat src_copy; + return src_copy; +} +Mat ImageProcessorImpl::DetectEdges(const cv::Mat &src, const cv::Rect &roi, + const int filterSize, const int lowThreshold, const int ratio, + const int kernelSize) +{ + Mat src_copy; + return src_copy; +} +Mat ImageProcessorImpl::Pixelize(const cv::Mat &src, const cv::Rect &roi, + const int kDivs) +{ + Mat src_copy; + return src_copy; +} \ No newline at end of file diff --git a/src/tracking.cpp b/src/tracking.cpp index 26cbe06..0ef6d3c 100644 --- a/src/tracking.cpp +++ b/src/tracking.cpp @@ -4,10 +4,141 @@ using std::string; using std::shared_ptr; +using namespace std; using namespace cv; shared_ptr Tracker::CreateTracker(const string &name) { - std::cerr << "Failed to create tracker with name '" << name << "'" + + if (name == "median_flow") { + return std::make_shared(); + } + std::cerr << "Failed to create tracker with name '" << name << "'" << std::endl; - return nullptr; + return nullptr; +} + + +bool MedianFlowTracker::Init(const Mat &frame, const Rect &roi) +{ + if (!frame.empty()) + { + this->frame_ = frame; + } + else + { + std::cerr << "Error, frame is empty." + << std::endl; + return false; + } + + if (roi.size() != Size(0,0)) + { + this->position_ = roi; + } + else + { + std::cerr << "Error, roi is undefined." + << std::endl; + return false; + } + return true; } + + +Rect MedianFlowTracker::Track(const cv::Mat &frame) +{ + Mat frame_roi_gray; + vector prev_points_roi; + vector prev_points; + vector post_points; + vector good_new_points; + vector good_old_points; + vector differents; + vector status; + vector pointToDelete; + vector err; + TermCriteria termcrit(TermCriteria::COUNT | TermCriteria::EPS, 20, 0.03); + int maxPoints = 10; + cvtColor(this->frame_(position_), frame_roi_gray, CV_BGR2GRAY); + // ! ! ! Не забыть преобразовать координаты roi в координаты frame + goodFeaturesToTrack(frame_roi_gray, prev_points_roi, maxPoints, + 0.01, 10, Mat(), 3, false, 0.04); + // Не забыть преобразовать координаты roi в координаты frame ! ! ! + + + // begin roi xy -> frame xy + for (int i = 0; i < prev_points_roi.size(); i++) + { + prev_points.push_back(prev_points_roi[i] + Point2f(position_.x, position_.y)); + } + // end roi xy -> frame xy + + + calcOpticalFlowPyrLK(frame_, frame, prev_points, post_points, + status, err, Size(31, 31), 3, termcrit, 0, 0.001); + for (int i = 0; i < status.size(); i++) + { + if (status[i]!=0) + { + good_new_points.push_back(post_points[i]); + good_old_points.push_back(prev_points[i]); + differents.push_back(post_points[i] - prev_points[i]); + } + } + + struct sort_class_x + { + bool operator() (Point2f i, Point2f j) + { + return (i.xposition_.x + medXY.x; + newPosition.y = this->position_.y + medXY.y; + return newPosition; +} \ No newline at end of file diff --git a/src/workaround.cpp b/src/workaround.cpp index 2eba458..edbca9e 100644 --- a/src/workaround.cpp +++ b/src/workaround.cpp @@ -7,4 +7,21 @@ using namespace std; void MatrixProcessor::Threshold(unsigned char* const data, const int width, const int height, const int threshold) { // TODO: Add thresholding logic here. + int i = 0; + int j = 0; + while (i < height) + { + + while (j < width) + { + + if (data[i * width + j] < threshold) + data[i * width + j] = 0; + j += 1; + + } + + j = 0; + i += 1; + } }