diff --git a/CMakeLists.txt b/CMakeLists.txt index 4a1949c..ae0df50 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,7 +19,7 @@ set(LIBRARY_DEPS ${OpenCV_LIBS}) # BUILD add_subdirectory(sample_template) # Add you directory here -# add_subdirectory(sample_YOUR_NAME) +add_subdirectory(sample_moshkina) # REPORT message( STATUS "") diff --git a/sample_moshkina/CMakeLists.txt b/sample_moshkina/CMakeLists.txt new file mode 100644 index 0000000..7bd850f --- /dev/null +++ b/sample_moshkina/CMakeLists.txt @@ -0,0 +1,7 @@ +set(target "sample_moshkina") + +file(GLOB hdrs "*.hpp") +file(GLOB srcs "*.cpp") + +add_executable(${target} ${srcs} ${hdrs}) +target_link_libraries(${target} ${LIBRARY_DEPS}) diff --git a/sample_moshkina/application.cpp b/sample_moshkina/application.cpp new file mode 100644 index 0000000..cfe6391 --- /dev/null +++ b/sample_moshkina/application.cpp @@ -0,0 +1,208 @@ +#include "application.hpp" +#include "processing.hpp" +#include "ctime" + +#include + +using namespace cv; + +int Application::parseArguments(int argc, const char **argv, + Application::Parameters ¶ms) +{ + 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, guiState.filter); + + if (dst.empty()) + { + return 1; + } + + return 0; +} + +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.grayButtonPlace = Rect(440, display.rows - 60, 120, 40); + guiState.cannyButtonPlace = Rect(580, display.rows - 60, 120, 40); + guiState.rpixelButtonPlace = Rect(720, display.rows - 60, 120, 40); + guiState.spixelButtonPlace = Rect(860, display.rows - 60, 120, 40); + + rectangle(display, guiState.onButtonPlace, + Scalar(128, 128, 128), CV_FILLED); + rectangle(display, guiState.offButtonPlace, + Scalar(128, 128, 128), CV_FILLED); + rectangle(display, guiState.saveButtonPlace, + Scalar(128, 128, 128), CV_FILLED); + rectangle(display, guiState.grayButtonPlace, + Scalar(128, 128, 128), CV_FILLED); + rectangle(display, guiState.cannyButtonPlace, + Scalar(128, 128, 128), CV_FILLED); + rectangle(display, guiState.rpixelButtonPlace, + Scalar(128, 128, 128), CV_FILLED); + rectangle(display, guiState.spixelButtonPlace, + Scalar(128, 128, 128), CV_FILLED); + + putText(display, "median", + Point(guiState.onButtonPlace.x + guiState.onButtonPlace.width / 2 - 50, + guiState.onButtonPlace.y + guiState.onButtonPlace.height / 2 + 10), + FONT_HERSHEY_SIMPLEX, 1.0, Scalar(0, 0, 0), 2); + putText(display, "off", + Point(guiState.offButtonPlace.x + guiState.offButtonPlace.width / 2 - 20, + guiState.offButtonPlace.y + guiState.offButtonPlace.height / 2 + 10), + FONT_HERSHEY_SIMPLEX, 1.0, Scalar(0, 0, 0), 2); + putText(display, "save", + Point(guiState.saveButtonPlace.x + guiState.saveButtonPlace.width / 2 - 35, + guiState.saveButtonPlace.y + guiState.saveButtonPlace.height / 2 + 10), + FONT_HERSHEY_SIMPLEX, 1.0, Scalar(0, 0, 0), 2); + putText(display, "gray", + Point(guiState.grayButtonPlace.x + guiState.grayButtonPlace.width / 2 - 35, + guiState.grayButtonPlace.y + guiState.grayButtonPlace.height / 2 + 10), + FONT_HERSHEY_SIMPLEX, 1.0, Scalar(0, 0, 0), 2); + putText(display, "canny", + Point(guiState.cannyButtonPlace.x + guiState.cannyButtonPlace.width / 2 - 35, + guiState.cannyButtonPlace.y + guiState.cannyButtonPlace.height / 2 + 10), + FONT_HERSHEY_SIMPLEX, 1.0, Scalar(0, 0, 0), 2); + putText(display, "r_pixel", + Point(guiState.rpixelButtonPlace.x + guiState.rpixelButtonPlace.width / 2 - 50, + guiState.rpixelButtonPlace.y + guiState.rpixelButtonPlace.height / 2 + 10), + FONT_HERSHEY_SIMPLEX, 1.0, Scalar(0, 0, 0), 2); + putText(display, "s_pixel", + Point(guiState.spixelButtonPlace.x + guiState.spixelButtonPlace.width / 2 - 50, + guiState.spixelButtonPlace.y + guiState.spixelButtonPlace.height / 2 + 10), + FONT_HERSHEY_SIMPLEX, 1.0, Scalar(0, 0, 0), 2); + + return 0; +} + +int Application::showFrame(const std::string &caption, + const Mat& src, Mat& dst) +{ + + if (guiState.state == OffFilter) + { + src.copyTo(dst); + } + else if (guiState.state == OnFilter) + { + processFrame(src, dst); + } + else + { + return 1; + } + + 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); + + if (guiState.saveState) { + time_t rawtime; + struct tm * timeinfo; + + time (&rawtime); + timeinfo = localtime(&rawtime); + + string name = "result-moshkina-"; + //name = name + std::to_string(rawtime); + std::ostringstream oss; + oss << rawtime; + string str = oss.str(); + + imwrite(name+str+".jpg", display); + guiState.saveState=false; + } + drawButtons(display); + + namedWindow(caption); + imshow(caption, display); + setMouseCallback(caption, onButtonsOnOffClick, &guiState); + char key = waitKey(1); + + return key; +} + +void onButtonsOnOffClick(int eventId, int x, int y, int flags, void *userData) +{ + if (eventId != EVENT_LBUTTONDOWN) + { + return; + } + Application::GUIElementsState *elems = + (Application::GUIElementsState *)userData; + if (onButtonClicked(elems->onButtonPlace, x, y)) + { + elems->state = Application::OnFilter; + elems->filter = MEDIAN; + return; + } + if (onButtonClicked(elems->offButtonPlace, x, y)) + { + elems->state = Application::OffFilter; + return; + } + if (onButtonClicked(elems->saveButtonPlace, x, y)) + { + elems->state = Application::OnFilter; + elems->saveState = true; + return; + } + if (onButtonClicked(elems->grayButtonPlace, x, y)) + { + elems->state = Application::OnFilter; + elems->filter = CVT_CONVERT_GRAY; + return; + } + if (onButtonClicked(elems->cannyButtonPlace, x, y)) + { + elems->state = Application::OnFilter; + elems->filter = CANNY; + return; + } + if (onButtonClicked(elems->rpixelButtonPlace, x, y)) + { + elems->state = Application::OnFilter; + elems->filter = PIXELIZED_ROUND; + return; + } + if (onButtonClicked(elems->spixelButtonPlace, x, y)) + { + elems->state = Application::OnFilter; + elems->filter = PIXELIZED_SQUARE; + return; + } +} + +bool onButtonClicked(cv::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_moshkina/application.hpp b/sample_moshkina/application.hpp new file mode 100644 index 0000000..69cf9ec --- /dev/null +++ b/sample_moshkina/application.hpp @@ -0,0 +1,60 @@ +#pragma once + +#include +#include +#include + +#include "processing.hpp" + +bool onButtonClicked(cv::Rect buttonPlace, int x, int y); +void onButtonsOnOffClick(int eventId, int x, int y, int flags, void *userData); + +class Application +{ + public: + enum WindowState + { + OnFilter, + OffFilter, + savePic, + grayFilter + }; + struct Parameters + { + std::string imgFileName; + }; + struct GUIElementsState + { + WindowState state; + bool saveState; + FilterType filter; + cv::Rect onButtonPlace; + cv::Rect offButtonPlace; + cv::Rect saveButtonPlace; + cv::Rect grayButtonPlace; + cv::Rect cannyButtonPlace; + cv::Rect rpixelButtonPlace; + cv::Rect spixelButtonPlace; + }; + int parseArguments(int argc, const char **argv, Parameters ¶ms); + 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); + friend void onButtonsOnOffClick(int eventId, int x, int y, + int flags, void *userData); + Application() + { + guiState.state = OnFilter; + guiState.saveState=false; + guiState.filter = MEDIAN; + }; + + private: + Processing processor; + GUIElementsState guiState; + + int drawButtons(cv::Mat &display); + + friend bool onButtonClicked(cv::Rect buttonPlace, int x, int y); +}; diff --git a/sample_moshkina/cmake_install.cmake b/sample_moshkina/cmake_install.cmake new file mode 100644 index 0000000..f8e0653 --- /dev/null +++ b/sample_moshkina/cmake_install.cmake @@ -0,0 +1,29 @@ +# Install script for directory: C:/Temp/moshkina/practice2-opencv-intro/sample_moshkina + +# Set the install prefix +IF(NOT DEFINED CMAKE_INSTALL_PREFIX) + SET(CMAKE_INSTALL_PREFIX "C:/Program Files/practice2") +ENDIF(NOT DEFINED CMAKE_INSTALL_PREFIX) +STRING(REGEX REPLACE "/$" "" CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}") + +# Set the install configuration name. +IF(NOT DEFINED CMAKE_INSTALL_CONFIG_NAME) + IF(BUILD_TYPE) + STRING(REGEX REPLACE "^[^A-Za-z0-9_]+" "" + CMAKE_INSTALL_CONFIG_NAME "${BUILD_TYPE}") + ELSE(BUILD_TYPE) + SET(CMAKE_INSTALL_CONFIG_NAME "Release") + ENDIF(BUILD_TYPE) + MESSAGE(STATUS "Install configuration: \"${CMAKE_INSTALL_CONFIG_NAME}\"") +ENDIF(NOT DEFINED CMAKE_INSTALL_CONFIG_NAME) + +# Set the component getting installed. +IF(NOT CMAKE_INSTALL_COMPONENT) + IF(COMPONENT) + MESSAGE(STATUS "Install component: \"${COMPONENT}\"") + SET(CMAKE_INSTALL_COMPONENT "${COMPONENT}") + ELSE(COMPONENT) + SET(CMAKE_INSTALL_COMPONENT) + ENDIF(COMPONENT) +ENDIF(NOT CMAKE_INSTALL_COMPONENT) + diff --git a/sample_moshkina/main.cpp b/sample_moshkina/main.cpp new file mode 100644 index 0000000..43ff041 --- /dev/null +++ b/sample_moshkina/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_moshkina/processing.cpp b/sample_moshkina/processing.cpp new file mode 100644 index 0000000..7eba2f5 --- /dev/null +++ b/sample_moshkina/processing.cpp @@ -0,0 +1,107 @@ +#include "processing.hpp" + +#include + +using namespace cv; +int step=0; +int expansion=0; + +void Processing::processFrame(const cv::Mat& src, cv::Mat& dst, FilterType filter) +{ + src.copyTo(dst); + + if ((3*src.rows/4+step+expansion>src.rows)||(3*src.cols/4+step+expansion>src.cols)) { + step=0; + expansion=0; + } + + cv::Rect region(src.rows/4+step, src.cols/4+step, src.rows/2+expansion, src.cols/2+expansion); + + Mat roi = dst(region); + + const int kSize = 11; + switch (filter) + { + case (MEDIAN): { + medianBlur(roi, roi, kSize); + break; } + case (CVT_CONVERT_GRAY): { + Mat gray; + cvtColor(roi, gray, CV_RGB2GRAY); + Mat grays[3] = {gray, gray, gray}; + merge(grays, 3, roi); + break; } + case (CANNY): { + Mat edges; + cvtColor(roi, edges, CV_RGB2GRAY); + GaussianBlur(edges, edges, Size(7,7), 1.5); + Canny(edges, edges, 0, 50); + Mat edges_arr[3] = {edges, edges, edges}; + merge(edges_arr, 3, roi); + break; + } + case (PIXELIZED_ROUND): { + Mat res = Mat::zeros(roi.size(), CV_8UC3); + Mat cir = Mat::zeros(roi.size(), CV_8UC1); + int bsize = 10; + + for (int i = 0; i < roi.rows; i += bsize) + { + for (int j = 0; j < roi.cols; j += bsize) + { + Rect rect = Rect(j, i, bsize, bsize) & + Rect(0, 0, roi.cols, roi.rows); + + Mat sub_dst(res, rect); + sub_dst.setTo(mean(roi(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, cv::NORM_MINMAX); + + Mat dst_32f; + res.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(roi, CV_8U); + + break; + } + case (PIXELIZED_SQUARE): { + Mat res = Mat::zeros(roi.size(), CV_8UC3); + Mat cir = Mat::zeros(roi.size(), CV_8UC1); + int bsize = 10; + + for (int i = 0; i < roi.rows; i += bsize) + { + for (int j = 0; j < roi.cols; j += bsize) + { + Rect rect = Rect(j, i, bsize, bsize) & + Rect(0, 0, roi.cols, roi.rows); + + Mat sub_dst(roi, rect); + sub_dst.setTo(mean(roi(rect))); + } + } + break; + } + } + + rectangle(dst, region, Scalar(255, 0, 0)); + step++; + expansion++; +} diff --git a/sample_moshkina/processing.hpp b/sample_moshkina/processing.hpp new file mode 100644 index 0000000..ad793ca --- /dev/null +++ b/sample_moshkina/processing.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include + +enum FilterType +{ + MEDIAN, + CVT_CONVERT_GRAY, + PIXELIZED_ROUND, + PIXELIZED_SQUARE, + CANNY +}; + +class Processing +{ + public: + void processFrame(const cv::Mat& src, cv::Mat& dst, FilterType filter); + + +};