diff --git a/modules/dnn_modern/CMakeLists.txt b/modules/dnn_modern/CMakeLists.txt index 1be8e8268..0f77508f9 100644 --- a/modules/dnn_modern/CMakeLists.txt +++ b/modules/dnn_modern/CMakeLists.txt @@ -15,6 +15,23 @@ list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) # MODULE REQUIREMENTS # ---------------------------------------------------------------------------- +set(TINY_DNN_CPP_PATH "${OpenCV_BINARY_DIR}/3rdparty/tinydnn") +set(TINY_DNN_CPP_ROOT "${TINY_DNN_CPP_PATH}/tiny-dnn-1.0.0a3") +ocv_download(FILENAME "v1.0.0a3.tar.gz" + HASH "adb1c512e09ca2c7a6faef36f9c53e59" + URL + "${OPENCV_TINY_DNN_URL}" + "$ENV{OPENCV_TINY_DNN_URL}" + "https://github.com/tiny-dnn/tiny-dnn/archive/" + DESTINATION_DIR "${TINY_DNN_CPP_PATH}" + STATUS TINY_DNN_DOWNLOAD_SUCCESS + ID "tiny-dnn" + UNPACK RELATIVE_URL) + +if(NOT TINY_DNN_DOWNLOAD_SUCCESS) + message(STATUS "Failed to download tiny-dnn sources") +endif() + find_package(TinyDNN QUIET) include(CheckCXXCompilerFlag) @@ -59,12 +76,22 @@ if(TINYDNN_USE_NNPACK) list(APPEND REQUIRED_LIBRARIES ${NNPACK_LIB}) endif() -# we need to disable seializer unless we import cereal -add_definitions(-DCNN_NO_SERIALIZATION) +# we need to disable seializer unless we import cereal and we gonna use caffe converter +add_definitions(-DCNN_NO_SERIALIZATION -DCNN_USE_CAFFE_CONVERTER) # NOTE: In case that proto files already exist, # this is not needed anymore. -find_package(Protobuf) +find_package(Protobuf QUIET) + +if(DEFINED PROTOBUF_PROTOC_EXECUTABLE AND EXISTS ${PROTOBUF_PROTOC_EXECUTABLE}) + execute_process(COMMAND ${PROTOBUF_PROTOC_EXECUTABLE} caffe.proto --cpp_out=./ + WORKING_DIRECTORY ${TINYDNN_INCLUDE_DIRS}/tiny_dnn/io/caffe) +else() + message(STATUS "The protocol buffer compiler is not found (PROTOBUF_PROTOC_EXECUTABLE='${PROTOBUF_PROTOC_EXECUTABLE}')") + ocv_module_disable(dnn_modern) + return() +endif() + list(APPEND REQUIRED_LIBRARIES ${PROTOBUF_LIBRARIES}) #### @@ -76,7 +103,6 @@ list(APPEND REQUIRED_LIBRARIES ${PROTOBUF_LIBRARIES}) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) -message(STATUS "C++11 support has been enabled by default.") # Unix if(CMAKE_COMPILER_IS_GNUCXX OR MINGW OR @@ -138,7 +164,9 @@ endif() # DNN-MODERN MODULE # ---------------------------------------------------------------------------- -ocv_define_module(dnn_modern opencv_core opencv_imgproc opencv_imgcodecs WRAP python) +ocv_define_module(dnn_modern opencv_core opencv_imgproc WRAP python) ocv_target_link_libraries(${the_module} ${REQUIRED_LIBRARIES}) ocv_target_include_directories(${the_module} ${TINYDNN_INCLUDE_DIRS}) -target_compile_options(${the_module} PRIVATE "-Wno-error=non-virtual-dtor") +ocv_warnings_disable(CMAKE_CXX_FLAGS + -Wnon-virtual-dtor -Wunused-parameter -Wshadow -Wundef + /wd4265 /wd4100 /wd4458 /wd4244 /wd4456) diff --git a/modules/dnn_modern/README.md b/modules/dnn_modern/README.md index a61665476..6588734bd 100644 --- a/modules/dnn_modern/README.md +++ b/modules/dnn_modern/README.md @@ -1,9 +1,8 @@ Modern Deep Learning Module =========================== -The module is wrapper to [tiny-dnn](https://github.com/tiny-dnn/tiny-dnn) - -A header only, dependency-free deep learning framework in C++11 +The module is wrapper to [tiny-dnn](https://github.com/tiny-dnn/tiny-dnn), +a header only, dependency-free deep learning framework in C++11. Installation ------------ @@ -12,19 +11,24 @@ Installation - System under Unix or Windows - C++11 compiler - tiny-dnn headers + - protoc compiler (part of the [protobuf](https://developers.google.com/protocol-buffers/docs/overview) library) **How to install tiny-dnn?** + CMake will try to download a certain version of tiny-dnn which was tested to build with OpenCV. + If the latest version is needed, location of the tiny-dnn folder can be specified manually: - Download tiny-dnn project somewhere in your system - +- Download tiny-dnn project somewhere in your system +``` cd /opt git clone https://github.com/tiny-dnn/tiny-dnn.git +``` - Run your OpenCV CMake pointing to your tiny-dnn headers location - +- Run your OpenCV CMake pointing to your tiny-dnn headers location +``` cd /opt/opencv/build cmake -DTINYDNN_ROOT=/opt/tiny-dnn .. make -j4 +``` **Extra** @@ -42,3 +46,5 @@ Installation Check project site for installation: [https://github.com/Maratyszcza/NNPACK](https://github.com/Maratyszcza/NNPACK) cmake -DTINYDNN_USE_NNPACK=ON .. // not supported yet for Caffe loader + +See detailed module API documentation in http://docs.opencv.org/trunk/d1/df7/group__dnn__modern.html diff --git a/modules/dnn_modern/cmake/FindTinyDNN.cmake b/modules/dnn_modern/cmake/FindTinyDNN.cmake index ebfd720d4..ac4dcaa96 100644 --- a/modules/dnn_modern/cmake/FindTinyDNN.cmake +++ b/modules/dnn_modern/cmake/FindTinyDNN.cmake @@ -19,6 +19,7 @@ set(TINYDNN_INCLUDE_SEARCH_PATHS $ENV{TINYDNN_ROOT} ${TINYDNN_ROOT} ${TINYDNN_ROOT}/tiny_dnn + ${TINY_DNN_CPP_ROOT} ) find_path(TINYDNN_INCLUDE_DIR diff --git a/modules/dnn_modern/include/opencv2/dnn_modern.hpp b/modules/dnn_modern/include/opencv2/dnn_modern.hpp index db9a65408..335989034 100644 --- a/modules/dnn_modern/include/opencv2/dnn_modern.hpp +++ b/modules/dnn_modern/include/opencv2/dnn_modern.hpp @@ -46,25 +46,33 @@ #define __OPENCV_DNN_M_HPP__ #include "opencv2/core.hpp" -#include "opencv2/imgcodecs.hpp" -#include "opencv2/imgproc.hpp" /** @defgroup dnn_modern Deep Learning Modern Module -@{ - -Base class for tiny-dnn converter - -@} + * This module is based on the [tiny-dnn](https://github.com/tiny-dnn/tiny-dnn) framework. + * The module uses tiny-dnn to load and run pre-trained Caffe models. + * tiny-dnn's converter only supports single input/single output network without branches. */ + namespace cv { namespace dnn2 { +//! @addtogroup dnn_modern +//! @{ + +/** @brief Base class for tiny-dnn converter. + */ class CV_EXPORTS_W BaseConverter { public: virtual ~BaseConverter() {}; - virtual void eval(const cv::InputArray image, std::vector* results) = 0; + + /** + @brief Evaluates single model output on single model input. + @param image input image. + @param results output form model. + */ + CV_WRAP virtual void eval(InputArray image, std::vector& results) = 0; }; /** @brief Class implementing the CaffeConverter. @@ -77,20 +85,22 @@ class CV_EXPORTS_W CaffeConverter : public BaseConverter { public: /** - @param model_file path to the prototxt file. - @param trained_file path to the caffemodel file. - @param mean_file path to binaryproto file. + @brief Creates a CaffeConverter object. + + @param model_file path to the prototxt file. + @param trained_file path to the caffemodel file. + @param mean_file path to binaryproto file. */ - CV_WRAP static Ptr create(const cv::String& model_file, - const cv::String& trained_file, - const cv::String& mean_file=cv::String()); + CV_WRAP static Ptr create(const String& model_file, + const String& trained_file, + const String& mean_file = String()); - virtual void eval(const cv::InputArray image, std::vector* results) = 0; + CV_WRAP virtual void eval(InputArray image, CV_OUT std::vector& results) = 0; }; +//! @} } // namespace dnn2 } // namespace cv - #endif /* End of file. */ diff --git a/modules/dnn_modern/samples/simple_test.cpp b/modules/dnn_modern/samples/simple_test.cpp index 71b0ab761..965a2afbe 100644 --- a/modules/dnn_modern/samples/simple_test.cpp +++ b/modules/dnn_modern/samples/simple_test.cpp @@ -1,8 +1,10 @@ #include +#include #include #include + using namespace std; using namespace cv; using namespace cv::dnn2; @@ -79,7 +81,7 @@ int main(int argc, char* argv[]) { // inference ! vector scores; - caffe_ptr->eval(img, &scores); + caffe_ptr->eval(img, scores); // retrieve n labels const int n = 5; diff --git a/modules/dnn_modern/src/caffe_converter.cpp b/modules/dnn_modern/src/caffe_converter.cpp index 07c201fd2..b8762892c 100644 --- a/modules/dnn_modern/src/caffe_converter.cpp +++ b/modules/dnn_modern/src/caffe_converter.cpp @@ -43,9 +43,10 @@ */ #include "precomp.hpp" +#include -#define CNN_USE_CAFFE_CONVERTER #include +#include using namespace tiny_dnn; using namespace tiny_dnn::activation; @@ -59,9 +60,9 @@ namespace dnn2 { */ class CaffeConverter_Impl : public CaffeConverter { public: - explicit CaffeConverter_Impl(const cv::String& model_file, - const cv::String& trained_file, - const cv::String& mean_file) { + explicit CaffeConverter_Impl(const String& model_file, + const String& trained_file, + const String& mean_file) { net_ = create_net_from_caffe_prototxt(model_file); reload_weight_from_caffe_protobinary(trained_file, net_.get()); @@ -73,31 +74,31 @@ class CaffeConverter_Impl : public CaffeConverter { ~CaffeConverter_Impl() {} - virtual void eval(const cv::InputArray image, std::vector* results); + virtual void eval(InputArray image, std::vector& results); private: - cv::Mat compute_mean(const string& mean_file, const size_t width, + Mat compute_mean(const string& mean_file, const size_t width, const size_t height); - cv::ColorConversionCodes get_cvt_codes(const int src_channels, + ColorConversionCodes get_cvt_codes(const int src_channels, const int dst_channels); - void preprocess(const cv::Mat& img, const cv::Mat& mean, - const int num_channels, const cv::Size& geometry, - vector* input_channels); + void preprocess(const Mat& img, const Mat& mean, + const int num_channels, const Size& geometry, + vector* input_channels); - cv::Mat mean_; + Mat mean_; std::shared_ptr> net_; }; -cv::Mat +Mat CaffeConverter_Impl::compute_mean(const string& mean_file, const size_t width, const size_t height) { caffe::BlobProto blob; ::detail::read_proto_from_binary(mean_file, &blob); - vector channels; + vector channels; auto data = blob.mutable_data()->mutable_data(); const size_t offset = blob.height() * blob.width(); @@ -106,69 +107,69 @@ CaffeConverter_Impl::compute_mean(const string& mean_file, channels.emplace_back(blob.height(), blob.width(), CV_32FC1, data); } - cv::Mat mean; - cv::merge(channels, mean); + Mat meanChannel; + merge(channels, meanChannel); - return cv::Mat(cv::Size(width, height), mean.type(), cv::mean(mean)); + return Mat(Size(width, height), meanChannel.type(), mean(meanChannel)); } -cv::ColorConversionCodes +ColorConversionCodes CaffeConverter_Impl::get_cvt_codes(const int src_channels, const int dst_channels) { assert(src_channels != dst_channels); if (dst_channels == 3) { - return src_channels == 1 ? cv::COLOR_GRAY2BGR : cv::COLOR_BGRA2BGR; + return src_channels == 1 ? COLOR_GRAY2BGR : COLOR_BGRA2BGR; } else if (dst_channels == 1) { - return src_channels == 3 ? cv::COLOR_BGR2GRAY : cv::COLOR_BGRA2GRAY; + return src_channels == 3 ? COLOR_BGR2GRAY : COLOR_BGRA2GRAY; } else { throw runtime_error("unsupported color code"); } } -void CaffeConverter_Impl::preprocess(const cv::Mat& img, - const cv::Mat& mean, +void CaffeConverter_Impl::preprocess(const Mat& img, + const Mat& mean, const int num_channels, - const cv::Size& geometry, - vector* input_channels) { - cv::Mat sample; + const Size& geometry, + vector* input_channels) { + Mat sample; // convert color if (img.channels() != num_channels) { - cv::cvtColor(img, sample, + cvtColor(img, sample, get_cvt_codes(img.channels(), num_channels)); } else { sample = img; } // resize - cv::Mat sample_resized; - cv::resize(sample, sample_resized, geometry); + Mat sample_resized; + resize(sample, sample_resized, geometry); - cv::Mat sample_float; + Mat sample_float; sample_resized.convertTo(sample_float, num_channels == 3 ? CV_32FC3 : CV_32FC1); // subtract mean if (mean.size().width > 0) { - cv::Mat sample_normalized; - cv::subtract(sample_float, mean, sample_normalized); - cv::split(sample_normalized, *input_channels); + Mat sample_normalized; + subtract(sample_float, mean, sample_normalized); + split(sample_normalized, *input_channels); } else { - cv::split(sample_float, *input_channels); + split(sample_float, *input_channels); } } -void CaffeConverter_Impl::eval(const cv::InputArray image, - std::vector* results) { - const cv::Mat img = image.getMat(); +void CaffeConverter_Impl::eval(InputArray image, + std::vector& results) { + const Mat img = image.getMat(); const size_t channels = (*net_)[0]->in_data_shape()[0].depth_; const size_t width = (*net_)[0]->in_data_shape()[0].width_; const size_t height = (*net_)[0]->in_data_shape()[0].height_; - vector input_channels; + vector input_channels; vector inputvec(width*height*channels); for (size_t i = 0; i < channels; i++) { @@ -177,7 +178,7 @@ void CaffeConverter_Impl::eval(const cv::InputArray image, } // subtract mean from input - preprocess(img, mean_, 3, cv::Size(width, height), &input_channels); + preprocess(img, mean_, 3, Size(width, height), &input_channels); const vector vec(inputvec.begin(), inputvec.end()); @@ -185,17 +186,17 @@ void CaffeConverter_Impl::eval(const cv::InputArray image, auto result = net_->predict(vec); // allocate output - results->clear(); - results->reserve(result.size()); + results.clear(); + results.reserve(result.size()); for (size_t i = 0; i < result.size(); i++) { - results->push_back(result[i]); + results.push_back(result[i]); } } -Ptr CaffeConverter::create(const cv::String& model_file, - const cv::String& trained_file, - const cv::String& mean_file) { +Ptr CaffeConverter::create(const String& model_file, + const String& trained_file, + const String& mean_file) { return makePtr(model_file, trained_file, mean_file); }