diff --git a/CMakeLists.txt b/CMakeLists.txt index 4a1949c..23a9cbf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,6 +18,7 @@ set(LIBRARY_DEPS ${OpenCV_LIBS}) # BUILD add_subdirectory(sample_template) +add_subdirectory(sample_zhiltsov) # Add you directory here # add_subdirectory(sample_YOUR_NAME) diff --git a/sample_zhiltsov/CMakeLists.txt b/sample_zhiltsov/CMakeLists.txt new file mode 100644 index 0000000..c5fe81b --- /dev/null +++ b/sample_zhiltsov/CMakeLists.txt @@ -0,0 +1,7 @@ +set(target "sample_zhiltsov") + +file(GLOB hdrs "*.hpp") +file(GLOB srcs "*.cpp") + +add_executable(${target} ${srcs} ${hdrs}) +target_link_libraries(${target} ${LIBRARY_DEPS}) diff --git a/sample_zhiltsov/application.cpp b/sample_zhiltsov/application.cpp new file mode 100644 index 0000000..3d8325e --- /dev/null +++ b/sample_zhiltsov/application.cpp @@ -0,0 +1,169 @@ +#include "application.hpp" +#include "processing.hpp" + +#include +#include +#include +#include + +using namespace cv; + +Application::Application() +{ + guiState.state = WindowState::OnFilter; +} + +int Application::parseArguments(int argc, const char** argv, Application::Parameters& params) +{ + if (argc < 2) + { + return 1; + } + params.imgFileName = std::string(argv[1]); + return 0; +} + +int Application::getFrame(const std::string& fileName, Mat& src) +{ + src = imread(fileName); + if (src.empty()) + { + return 1; + } + return 0; +} + +int Application::processFrame(const Mat& src, Mat& dst) +{ + processor.processFrame(src, dst); + + if (dst.empty()) + { + return 1; + } + + return 0; +} + +void create_button(const std::string& text, const Rect& rect, Mat& display) +{ + rectangle(display, rect, Scalar(128, 128, 128), CV_FILLED); + putText(display, text, + Point(rect.x + rect.width / 2 - 15, + rect.y + rect.height / 2 + 10), + FONT_HERSHEY_SIMPLEX, 1.0, CV_RGB(0, 0, 0), 2); +} + +int Application::drawButtons(Mat& display) +{ + guiState.onButtonPlace = Rect(20, display.rows - 60, 120, 40); + guiState.offButtonPlace = Rect(160, display.rows - 60, 120, 40); + guiState.saveButtonPlace = Rect(300, display.rows - 60, 120, 40); + + guiState.filterBlurButtonPlace = Rect(20, display.rows - 120, 120, 40); + guiState.filterCannyButtonPlace = Rect(160, display.rows - 120, 120, 40); + guiState.filterPixelizeButtonPlace = Rect(300, display.rows - 120, 120, 40); + guiState.filterGrayscaleButtonPlace = Rect(440, display.rows - 120, 120, 40); + + create_button("on", guiState.onButtonPlace, display); + create_button("off", guiState.offButtonPlace, display); + create_button("save", guiState.saveButtonPlace, display); + create_button("Canny", guiState.filterCannyButtonPlace, display); + create_button("Blur", guiState.filterBlurButtonPlace, display); + create_button("Pixelize", guiState.filterPixelizeButtonPlace, display); + create_button("Grayscale", guiState.filterGrayscaleButtonPlace, display); + + return 0; +} + +int Application::showFrame(const std::string &caption, const Mat& src, Mat& dst) +{ + switch (guiState.state) + { + case WindowState::OffFilter: + src.copyTo(dst); + break; + + case WindowState::OnFilter: + processFrame(src, dst); + break; + + default: { throw "Unexpected window state."; } + } + + Mat display(src.rows, src.cols + dst.cols, src.type()); + Mat srcRoi = display(Rect(0, 0, src.cols, src.rows)); + src.copyTo(srcRoi); + Mat dstRoi = display(Rect(src.cols, 0, dst.cols, dst.rows)); + dst.copyTo(dstRoi); + + drawButtons(display); + + namedWindow(caption); + imshow(caption, display); + + buttonClickHandleArgs.dstBuf = &dst; + setMouseCallback(caption, onButtonClick, this); + + char key = waitKey(1); + + return key; +} + +void Application::onButtonClick(int eventId, int x, int y, int flsgs, void* userData) +{ + if (eventId != EVENT_LBUTTONDOWN) + { + return; + } + Application* app = static_cast(userData); + + if (isButtonClicked(app->guiState.onButtonPlace, x, y)) + { + app->guiState.state = WindowState::OnFilter; + return; + } + if (isButtonClicked(app->guiState.offButtonPlace, x, y)) + { + app->guiState.state = WindowState::OffFilter; + return; + } + if (isButtonClicked(app->guiState.saveButtonPlace, x, y)) + { + std::stringstream ss; + ss << clock() << ".png"; + imwrite(ss.str(), *app->buttonClickHandleArgs.dstBuf); + return; + } + if (isButtonClicked(app->guiState.filterBlurButtonPlace, x, y)) + { + app->processor.setFilterType(Processing::FilterType::Blur); + return; + } + if (isButtonClicked(app->guiState.filterCannyButtonPlace, x, y)) + { + app->processor.setFilterType(Processing::FilterType::Canny); + return; + } + if (isButtonClicked(app->guiState.filterGrayscaleButtonPlace, x, y)) + { + app->processor.setFilterType(Processing::FilterType::Grayscale); + return; + } + if (isButtonClicked(app->guiState.filterPixelizeButtonPlace, x, y)) + { + app->processor.setFilterType(Processing::FilterType::Pixelize); + return; + } +} + +bool Application::isButtonClicked(const Rect& buttonPlace, int x, int y) +{ + if (x < buttonPlace.x || x > buttonPlace.x + buttonPlace.width || + y < buttonPlace.y || y > buttonPlace.y + buttonPlace.height) + { + return false; + } + return true; +} + diff --git a/sample_zhiltsov/application.hpp b/sample_zhiltsov/application.hpp new file mode 100644 index 0000000..f03f9ff --- /dev/null +++ b/sample_zhiltsov/application.hpp @@ -0,0 +1,59 @@ +#pragma once + +#include +#include +#include + +#include "processing.hpp" + + +class Application +{ +public: + struct WindowState + { + enum + { + OnFilter, + OffFilter + }; + }; + typedef int WindowState_t; + + struct Parameters + { + std::string imgFileName; + }; + struct GUIElementsState + { + WindowState_t state; + cv::Rect onButtonPlace; + cv::Rect offButtonPlace; + cv::Rect saveButtonPlace; + cv::Rect filterGrayscaleButtonPlace; + cv::Rect filterPixelizeButtonPlace; + cv::Rect filterCannyButtonPlace; + cv::Rect filterBlurButtonPlace; + }; + int parseArguments(int argc, const char** argv, Parameters& params); + int getFrame(const std::string& fileName, cv::Mat& src); + int processFrame(const cv::Mat& src, cv::Mat& dst); + int showFrame(const std::string& caption, + const cv::Mat& src, cv::Mat& dst); + + Application(); +private: + Processing processor; + GUIElementsState guiState; + struct ButtonClickHandleArgs + { + cv::Mat* dstBuf; + }; + ButtonClickHandleArgs buttonClickHandleArgs; + + + int drawButtons(cv::Mat &display); + + static void onButtonClick(int eventId, int x, int y, int flags, void* userData); + static bool isButtonClicked(const cv::Rect& buttonPlace, int x, int y); +}; diff --git a/sample_zhiltsov/main.cpp b/sample_zhiltsov/main.cpp new file mode 100644 index 0000000..43ff041 --- /dev/null +++ b/sample_zhiltsov/main.cpp @@ -0,0 +1,44 @@ +#include +#include + +#include "application.hpp" + +using namespace std; +using namespace cv; + +enum ErrorCode { + OK, + WRONG_ARGUMENTS, + WRONG_INPUT, + CANT_PROCESS +}; + +int main(int argc, const char **argv) +{ + Application app; + Application::Parameters params; + + if (app.parseArguments(argc, argv, params) != 0) + { + cout << "sample_template " << endl; + cout << " - image name for filtering" << endl; + return WRONG_ARGUMENTS; + } + + Mat src; + if (app.getFrame(params.imgFileName, src) != 0) + { + cout << "Error: \'src\' image is null or empty!" << endl; + return WRONG_INPUT; + } + + const std::string caption = "OpenCV Sample"; + char key = 0; + Mat dst(src.rows, src.cols, src.type()); + while (key != 27) // Esc + { + key = app.showFrame(caption, src, dst); + } + + return OK; +} diff --git a/sample_zhiltsov/processing.cpp b/sample_zhiltsov/processing.cpp new file mode 100644 index 0000000..9e89d15 --- /dev/null +++ b/sample_zhiltsov/processing.cpp @@ -0,0 +1,129 @@ +#include "processing.hpp" + +#include +#include + +using namespace cv; + +Processing::Processing(const FilterType_t& filterType_) : + filterType(filterType_) +{} + +void Processing::setFilterType(const FilterType_t& value) +{ + filterType = value; +} + +void Processing::updateRoiPosition(const cv::Mat& src) +{ + region.x = src.rows/4 + region.x * std::abs(sin((double)clock())) * 0.1; + region.y = src.cols/4 + region.y * std::abs(cos((double)clock())) * 0.1; + + region.width = src.rows/2 + region.width * std::abs(sin((double)clock())) * 0.1; + region.height = src.cols/2 + region.height * std::abs(cos((double)clock())) * 0.1; +} + +void Processing::processFrame(const cv::Mat& src, cv::Mat& dst) +{ + src.copyTo(dst); + Mat roi = dst(region); + + switch (filterType) { + case FilterType::Blur: + applyBlurFilter(roi, roi); + break; + + case FilterType::Grayscale: + applyGrayscaleFilter(roi, roi); + break; + + case FilterType::Pixelize: + applyPixelizeFilter(roi, roi); + break; + + case FilterType::Canny: + applyCannyFilter(roi, roi); + break; + + default: { throw "Unexpected filter type."; } + } + + rectangle(dst, region, CV_RGB(255, 0, 0)); +} + +/* + * Copy-pasted from: http://opencv-code.com/tutorials/photo-to-colored-dot-patterns-with-opencv/ +*/ +void Processing::applyPixelizeFilter(const Mat& src, Mat& dst) +{ + Mat cir = cv::Mat::zeros(src.size(), CV_8UC1); + const int bsize = 10; + + for (int i = 0; i < src.rows; i += bsize) + { + for (int j = 0; j < src.cols; j += bsize) + { + Rect rect = Rect(j, i, bsize, bsize) & + Rect(0, 0, src.cols, src.rows); + + Mat sub_dst(dst, rect); + sub_dst.setTo(cv::mean(src(rect))); + + circle( + cir, + Point(j+bsize/2, i+bsize/2), + bsize/2-1, + CV_RGB(255,255,255), -1, CV_AA + ); + } + } + + Mat cir_32f; + cir.convertTo(cir_32f, CV_32F); + normalize(cir_32f, cir_32f, 0, 1, NORM_MINMAX); + + Mat dst_32f; + dst.convertTo(dst_32f, CV_32F); + + std::vector channels; + split(dst_32f, channels); + for (int i = 0; i < channels.size(); ++i) + channels[i] = channels[i].mul(cir_32f); + + merge(channels, dst_32f); + dst_32f.convertTo(dst, CV_8U); +} + +void Processing::applyCannyFilter(const Mat& src, Mat& dst) +{ + const Size kSize(11, 11); + GaussianBlur(src, dst, kSize, 5, 10); + + Mat newRoi = dst.clone(); + cvtColor(dst, newRoi, CV_BGR2GRAY); + Canny(newRoi, newRoi, 10, 1); + + std::vector channels; + channels.push_back(newRoi); + channels.push_back(newRoi); + channels.push_back(newRoi); + merge(channels, dst); +} + +void Processing::applyGrayscaleFilter(const Mat& src, Mat& dst) +{ + Mat newRoi = src.clone(); + cvtColor(newRoi, newRoi, CV_BGR2GRAY); + + std::vector channels; + channels.push_back(newRoi); + channels.push_back(newRoi); + channels.push_back(newRoi); + merge(channels, dst); +} + +void Processing::applyBlurFilter(const Mat& src, Mat& dst) +{ + const int kSize = 11; + medianBlur(src, dst, kSize); +} \ No newline at end of file diff --git a/sample_zhiltsov/processing.hpp b/sample_zhiltsov/processing.hpp new file mode 100644 index 0000000..d809f96 --- /dev/null +++ b/sample_zhiltsov/processing.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include + + +class Processing +{ +public: + /* Wanted scoped enum here */ + struct FilterType + { + enum + { + Grayscale, + Pixelize, + Canny, + Blur + }; + }; + typedef int FilterType_t; + + + Processing(const FilterType_t& filterType = FilterType::Blur); + + void processFrame(const cv::Mat& src, cv::Mat& dst); + + void setFilterType(const FilterType_t& filterType); +private: + cv::Rect region; + FilterType_t filterType; + + void applyPixelizeFilter(const cv::Mat& src, cv::Mat& dst); + void applyCannyFilter(const cv::Mat& src, cv::Mat& dst); + void applyGrayscaleFilter(const cv::Mat& src, cv::Mat& dst); + void applyBlurFilter(const cv::Mat& src, cv::Mat& dst); + + void updateRoiPosition(const cv::Mat& src); +};