Commit 5348ba8a authored by Maksim Shabunin's avatar Maksim Shabunin

Merge pull request #1075 from sovrasov:dnn_modern_update

parents 470c073f 12bdc2af
...@@ -15,6 +15,23 @@ list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) ...@@ -15,6 +15,23 @@ list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
# MODULE REQUIREMENTS # 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) find_package(TinyDNN QUIET)
include(CheckCXXCompilerFlag) include(CheckCXXCompilerFlag)
...@@ -59,12 +76,22 @@ if(TINYDNN_USE_NNPACK) ...@@ -59,12 +76,22 @@ if(TINYDNN_USE_NNPACK)
list(APPEND REQUIRED_LIBRARIES ${NNPACK_LIB}) list(APPEND REQUIRED_LIBRARIES ${NNPACK_LIB})
endif() endif()
# we need to disable seializer unless we import cereal # we need to disable seializer unless we import cereal and we gonna use caffe converter
add_definitions(-DCNN_NO_SERIALIZATION) add_definitions(-DCNN_NO_SERIALIZATION -DCNN_USE_CAFFE_CONVERTER)
# NOTE: In case that proto files already exist, # NOTE: In case that proto files already exist,
# this is not needed anymore. # 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}) list(APPEND REQUIRED_LIBRARIES ${PROTOBUF_LIBRARIES})
#### ####
...@@ -76,7 +103,6 @@ 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 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
message(STATUS "C++11 support has been enabled by default.")
# Unix # Unix
if(CMAKE_COMPILER_IS_GNUCXX OR MINGW OR if(CMAKE_COMPILER_IS_GNUCXX OR MINGW OR
...@@ -138,7 +164,9 @@ endif() ...@@ -138,7 +164,9 @@ endif()
# DNN-MODERN MODULE # 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_link_libraries(${the_module} ${REQUIRED_LIBRARIES})
ocv_target_include_directories(${the_module} ${TINYDNN_INCLUDE_DIRS}) 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)
Modern Deep Learning Module Modern Deep Learning Module
=========================== ===========================
The module is wrapper to [tiny-dnn](https://github.com/tiny-dnn/tiny-dnn) 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.
A header only, dependency-free deep learning framework in C++11
Installation Installation
------------ ------------
...@@ -12,19 +11,24 @@ Installation ...@@ -12,19 +11,24 @@ Installation
- System under Unix or Windows - System under Unix or Windows
- C++11 compiler - C++11 compiler
- tiny-dnn headers - tiny-dnn headers
- protoc compiler (part of the [protobuf](https://developers.google.com/protocol-buffers/docs/overview) library)
**How to install tiny-dnn?** **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 cd /opt
git clone https://github.com/tiny-dnn/tiny-dnn.git 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 cd /opt/opencv/build
cmake -DTINYDNN_ROOT=/opt/tiny-dnn .. cmake -DTINYDNN_ROOT=/opt/tiny-dnn ..
make -j4 make -j4
```
**Extra** **Extra**
...@@ -42,3 +46,5 @@ Installation ...@@ -42,3 +46,5 @@ Installation
Check project site for installation: [https://github.com/Maratyszcza/NNPACK](https://github.com/Maratyszcza/NNPACK) 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 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
...@@ -19,6 +19,7 @@ set(TINYDNN_INCLUDE_SEARCH_PATHS ...@@ -19,6 +19,7 @@ set(TINYDNN_INCLUDE_SEARCH_PATHS
$ENV{TINYDNN_ROOT} $ENV{TINYDNN_ROOT}
${TINYDNN_ROOT} ${TINYDNN_ROOT}
${TINYDNN_ROOT}/tiny_dnn ${TINYDNN_ROOT}/tiny_dnn
${TINY_DNN_CPP_ROOT}
) )
find_path(TINYDNN_INCLUDE_DIR find_path(TINYDNN_INCLUDE_DIR
......
...@@ -46,25 +46,33 @@ ...@@ -46,25 +46,33 @@
#define __OPENCV_DNN_M_HPP__ #define __OPENCV_DNN_M_HPP__
#include "opencv2/core.hpp" #include "opencv2/core.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/imgproc.hpp"
/** @defgroup dnn_modern Deep Learning Modern Module /** @defgroup dnn_modern Deep Learning Modern Module
@{ * 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.
Base class for tiny-dnn converter * tiny-dnn's converter only supports single input/single output network without branches.
@}
*/ */
namespace cv { namespace cv {
namespace dnn2 { namespace dnn2 {
//! @addtogroup dnn_modern
//! @{
/** @brief Base class for tiny-dnn converter.
*/
class CV_EXPORTS_W BaseConverter class CV_EXPORTS_W BaseConverter
{ {
public: public:
virtual ~BaseConverter() {}; virtual ~BaseConverter() {};
virtual void eval(const cv::InputArray image, std::vector<float_t>* 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<float>& results) = 0;
}; };
/** @brief Class implementing the CaffeConverter. /** @brief Class implementing the CaffeConverter.
...@@ -77,20 +85,22 @@ class CV_EXPORTS_W CaffeConverter : public BaseConverter { ...@@ -77,20 +85,22 @@ class CV_EXPORTS_W CaffeConverter : public BaseConverter {
public: public:
/** /**
@param model_file path to the prototxt file. @brief Creates a CaffeConverter object.
@param trained_file path to the caffemodel file.
@param mean_file path to binaryproto file. @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<CaffeConverter> create(const cv::String& model_file, CV_WRAP static Ptr<CaffeConverter> create(const String& model_file,
const cv::String& trained_file, const String& trained_file,
const cv::String& mean_file=cv::String()); const String& mean_file = String());
virtual void eval(const cv::InputArray image, std::vector<float_t>* results) = 0; CV_WRAP virtual void eval(InputArray image, CV_OUT std::vector<float>& results) = 0;
}; };
//! @}
} // namespace dnn2 } // namespace dnn2
} // namespace cv } // namespace cv
#endif #endif
/* End of file. */ /* End of file. */
#include <opencv2/dnn_modern.hpp> #include <opencv2/dnn_modern.hpp>
#include <opencv2/imgcodecs.hpp>
#include <iostream> #include <iostream>
#include <fstream> #include <fstream>
using namespace std; using namespace std;
using namespace cv; using namespace cv;
using namespace cv::dnn2; using namespace cv::dnn2;
...@@ -79,7 +81,7 @@ int main(int argc, char* argv[]) { ...@@ -79,7 +81,7 @@ int main(int argc, char* argv[]) {
// inference ! // inference !
vector<float_t> scores; vector<float_t> scores;
caffe_ptr->eval(img, &scores); caffe_ptr->eval(img, scores);
// retrieve n labels // retrieve n labels
const int n = 5; const int n = 5;
......
...@@ -43,9 +43,10 @@ ...@@ -43,9 +43,10 @@
*/ */
#include "precomp.hpp" #include "precomp.hpp"
#include <opencv2/imgproc.hpp>
#define CNN_USE_CAFFE_CONVERTER
#include <tiny_dnn/tiny_dnn.h> #include <tiny_dnn/tiny_dnn.h>
#include <tiny_dnn/io/caffe/caffe.pb.cc>
using namespace tiny_dnn; using namespace tiny_dnn;
using namespace tiny_dnn::activation; using namespace tiny_dnn::activation;
...@@ -59,9 +60,9 @@ namespace dnn2 { ...@@ -59,9 +60,9 @@ namespace dnn2 {
*/ */
class CaffeConverter_Impl : public CaffeConverter { class CaffeConverter_Impl : public CaffeConverter {
public: public:
explicit CaffeConverter_Impl(const cv::String& model_file, explicit CaffeConverter_Impl(const String& model_file,
const cv::String& trained_file, const String& trained_file,
const cv::String& mean_file) { const String& mean_file) {
net_ = create_net_from_caffe_prototxt(model_file); net_ = create_net_from_caffe_prototxt(model_file);
reload_weight_from_caffe_protobinary(trained_file, net_.get()); reload_weight_from_caffe_protobinary(trained_file, net_.get());
...@@ -73,31 +74,31 @@ class CaffeConverter_Impl : public CaffeConverter { ...@@ -73,31 +74,31 @@ class CaffeConverter_Impl : public CaffeConverter {
~CaffeConverter_Impl() {} ~CaffeConverter_Impl() {}
virtual void eval(const cv::InputArray image, std::vector<float>* results); virtual void eval(InputArray image, std::vector<float>& results);
private: 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); 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); const int dst_channels);
void preprocess(const cv::Mat& img, const cv::Mat& mean, void preprocess(const Mat& img, const Mat& mean,
const int num_channels, const cv::Size& geometry, const int num_channels, const Size& geometry,
vector<cv::Mat>* input_channels); vector<Mat>* input_channels);
cv::Mat mean_; Mat mean_;
std::shared_ptr<network<sequential>> net_; std::shared_ptr<network<sequential>> net_;
}; };
cv::Mat Mat
CaffeConverter_Impl::compute_mean(const string& mean_file, CaffeConverter_Impl::compute_mean(const string& mean_file,
const size_t width, const size_t width,
const size_t height) { const size_t height) {
caffe::BlobProto blob; caffe::BlobProto blob;
::detail::read_proto_from_binary(mean_file, &blob); ::detail::read_proto_from_binary(mean_file, &blob);
vector<cv::Mat> channels; vector<Mat> channels;
auto data = blob.mutable_data()->mutable_data(); auto data = blob.mutable_data()->mutable_data();
const size_t offset = blob.height() * blob.width(); const size_t offset = blob.height() * blob.width();
...@@ -106,69 +107,69 @@ CaffeConverter_Impl::compute_mean(const string& mean_file, ...@@ -106,69 +107,69 @@ CaffeConverter_Impl::compute_mean(const string& mean_file,
channels.emplace_back(blob.height(), blob.width(), CV_32FC1, data); channels.emplace_back(blob.height(), blob.width(), CV_32FC1, data);
} }
cv::Mat mean; Mat meanChannel;
cv::merge(channels, mean); 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, CaffeConverter_Impl::get_cvt_codes(const int src_channels,
const int dst_channels) { const int dst_channels) {
assert(src_channels != dst_channels); assert(src_channels != dst_channels);
if (dst_channels == 3) { 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) { } else if (dst_channels == 1) {
return src_channels == 3 ? cv::COLOR_BGR2GRAY : cv::COLOR_BGRA2GRAY; return src_channels == 3 ? COLOR_BGR2GRAY : COLOR_BGRA2GRAY;
} else { } else {
throw runtime_error("unsupported color code"); throw runtime_error("unsupported color code");
} }
} }
void CaffeConverter_Impl::preprocess(const cv::Mat& img, void CaffeConverter_Impl::preprocess(const Mat& img,
const cv::Mat& mean, const Mat& mean,
const int num_channels, const int num_channels,
const cv::Size& geometry, const Size& geometry,
vector<cv::Mat>* input_channels) { vector<Mat>* input_channels) {
cv::Mat sample; Mat sample;
// convert color // convert color
if (img.channels() != num_channels) { if (img.channels() != num_channels) {
cv::cvtColor(img, sample, cvtColor(img, sample,
get_cvt_codes(img.channels(), num_channels)); get_cvt_codes(img.channels(), num_channels));
} else { } else {
sample = img; sample = img;
} }
// resize // resize
cv::Mat sample_resized; Mat sample_resized;
cv::resize(sample, sample_resized, geometry); resize(sample, sample_resized, geometry);
cv::Mat sample_float; Mat sample_float;
sample_resized.convertTo(sample_float, sample_resized.convertTo(sample_float,
num_channels == 3 ? CV_32FC3 : CV_32FC1); num_channels == 3 ? CV_32FC3 : CV_32FC1);
// subtract mean // subtract mean
if (mean.size().width > 0) { if (mean.size().width > 0) {
cv::Mat sample_normalized; Mat sample_normalized;
cv::subtract(sample_float, mean, sample_normalized); subtract(sample_float, mean, sample_normalized);
cv::split(sample_normalized, *input_channels); split(sample_normalized, *input_channels);
} }
else { else {
cv::split(sample_float, *input_channels); split(sample_float, *input_channels);
} }
} }
void CaffeConverter_Impl::eval(const cv::InputArray image, void CaffeConverter_Impl::eval(InputArray image,
std::vector<float>* results) { std::vector<float>& results) {
const cv::Mat img = image.getMat(); const Mat img = image.getMat();
const size_t channels = (*net_)[0]->in_data_shape()[0].depth_; 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 width = (*net_)[0]->in_data_shape()[0].width_;
const size_t height = (*net_)[0]->in_data_shape()[0].height_; const size_t height = (*net_)[0]->in_data_shape()[0].height_;
vector<cv::Mat> input_channels; vector<Mat> input_channels;
vector<float> inputvec(width*height*channels); vector<float> inputvec(width*height*channels);
for (size_t i = 0; i < channels; i++) { for (size_t i = 0; i < channels; i++) {
...@@ -177,7 +178,7 @@ void CaffeConverter_Impl::eval(const cv::InputArray image, ...@@ -177,7 +178,7 @@ void CaffeConverter_Impl::eval(const cv::InputArray image,
} }
// subtract mean from input // 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<tiny_dnn::float_t> vec(inputvec.begin(), inputvec.end()); const vector<tiny_dnn::float_t> vec(inputvec.begin(), inputvec.end());
...@@ -185,17 +186,17 @@ void CaffeConverter_Impl::eval(const cv::InputArray image, ...@@ -185,17 +186,17 @@ void CaffeConverter_Impl::eval(const cv::InputArray image,
auto result = net_->predict(vec); auto result = net_->predict(vec);
// allocate output // allocate output
results->clear(); results.clear();
results->reserve(result.size()); results.reserve(result.size());
for (size_t i = 0; i < result.size(); i++) { for (size_t i = 0; i < result.size(); i++) {
results->push_back(result[i]); results.push_back(result[i]);
} }
} }
Ptr<CaffeConverter> CaffeConverter::create(const cv::String& model_file, Ptr<CaffeConverter> CaffeConverter::create(const String& model_file,
const cv::String& trained_file, const String& trained_file,
const cv::String& mean_file) { const String& mean_file) {
return makePtr<CaffeConverter_Impl>(model_file, trained_file, mean_file); return makePtr<CaffeConverter_Impl>(model_file, trained_file, mean_file);
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment