From 801d1e86944d929f88bc3d20804f98de8027217a Mon Sep 17 00:00:00 2001 From: Francois Deschenes Date: Thu, 26 Jul 2018 15:28:00 -0700 Subject: [PATCH] Added Dnn::Layer, more Ruby-ish code, etc. --- ext/opencv/dnn.cpp | 120 ++++++++++++++++++------------------ ext/opencv/dnn_layer.cpp | 63 +++++++++++++++++++ ext/opencv/dnn_layer.hpp | 16 +++++ ext/opencv/dnn_net.cpp | 70 ++++++++++++++------- ext/opencv/mat.cpp | 13 ++-- ext/opencv/mat.hpp | 6 +- ext/opencv/opencv.cpp | 4 +- ext/opencv/opencv.hpp | 4 ++ ext/opencv/opencv_const.cpp | 12 +++- 9 files changed, 218 insertions(+), 90 deletions(-) create mode 100644 ext/opencv/dnn_layer.cpp create mode 100644 ext/opencv/dnn_layer.hpp diff --git a/ext/opencv/dnn.cpp b/ext/opencv/dnn.cpp index 8dbd978..c31a163 100644 --- a/ext/opencv/dnn.cpp +++ b/ext/opencv/dnn.cpp @@ -5,6 +5,7 @@ #include "size.hpp" #include "scalar.hpp" #include "dnn_net.hpp" +#include "dnn_layer.hpp" #include "error.hpp" // https://docs.opencv.org/trunk/d6/d0f/group__dnn.html#ga29d0ea5e52b1d1a6c2681e3f7d68473a @@ -59,61 +60,61 @@ namespace rubyopencv { return Mat::mat2obj(b); } - // Net readNetFromCaffe(const String &prototxt, const String &caffeModel = String()); - VALUE rb_read_net_from_caffe(VALUE self, VALUE prototxt, VALUE caffe_model) { - cv::dnn::Net *net = NULL; - - try { - net = new cv::dnn::Net(cv::dnn::readNetFromCaffe(StringValueCStr(prototxt), StringValueCStr(caffe_model))); - } catch(cv::Exception& e) { - delete net; - Error::raise(e); - } - - return Dnn::Net::net2obj(net); - } - - // Net readNetFromTorch(const String &model, bool isBinary) - VALUE rb_read_net_from_tensorflow(VALUE self, VALUE model) { - cv::dnn::Net *net = NULL; - - try { - net = new cv::dnn::Net(cv::dnn::readNetFromTensorflow(StringValueCStr(model))); - } catch(cv::Exception& e) { - delete net; - Error::raise(e); - } - - return Dnn::Net::net2obj(net); - } - - // Net readNetFromTorch(const String &model, bool isBinary) - VALUE rb_read_net_from_torch(VALUE self, VALUE model) { - cv::dnn::Net *net = NULL; - - try { - net = new cv::dnn::Net(cv::dnn::readNetFromTorch(StringValueCStr(model))); - } catch(cv::Exception& e) { - delete net; - Error::raise(e); - } - - return Dnn::Net::net2obj(net); - } - - // Net readNetFromDarknet(const String &cfgFile, const String &darknetModel /*= String()*/) - VALUE rb_read_net_from_darknet(VALUE self, VALUE cfg_file, VALUE darknet_model) { - cv::dnn::Net *net = NULL; - - try { - net = new cv::dnn::Net(cv::dnn::readNetFromDarknet(StringValueCStr(cfg_file), StringValueCStr(darknet_model))); - } catch(cv::Exception& e) { - delete net; - Error::raise(e); - } - - return Dnn::Net::net2obj(net); - } + // // Net readNetFromCaffe(const String &prototxt, const String &caffeModel = String()); + // VALUE rb_read_net_from_caffe(VALUE self, VALUE prototxt, VALUE caffe_model) { + // cv::dnn::Net *net = NULL; + // + // try { + // net = new cv::dnn::Net(cv::dnn::readNetFromCaffe(StringValueCStr(prototxt), StringValueCStr(caffe_model))); + // } catch(cv::Exception& e) { + // delete net; + // Error::raise(e); + // } + // + // return Dnn::Net::net2obj(net); + // } + // + // // Net readNetFromTorch(const String &model, bool isBinary) + // VALUE rb_read_net_from_tensorflow(VALUE self, VALUE model) { + // cv::dnn::Net *net = NULL; + // + // try { + // net = new cv::dnn::Net(cv::dnn::readNetFromTensorflow(StringValueCStr(model))); + // } catch(cv::Exception& e) { + // delete net; + // Error::raise(e); + // } + // + // return Dnn::Net::net2obj(net); + // } + // + // // Net readNetFromTorch(const String &model, bool isBinary) + // VALUE rb_read_net_from_torch(VALUE self, VALUE model) { + // cv::dnn::Net *net = NULL; + // + // try { + // net = new cv::dnn::Net(cv::dnn::readNetFromTorch(StringValueCStr(model))); + // } catch(cv::Exception& e) { + // delete net; + // Error::raise(e); + // } + // + // return Dnn::Net::net2obj(net); + // } + // + // // Net readNetFromDarknet(const String &cfgFile, const String &darknetModel /*= String()*/) + // VALUE rb_read_net_from_darknet(VALUE self, VALUE cfg_file, VALUE darknet_model) { + // cv::dnn::Net *net = NULL; + // + // try { + // net = new cv::dnn::Net(cv::dnn::readNetFromDarknet(StringValueCStr(cfg_file), StringValueCStr(darknet_model))); + // } catch(cv::Exception& e) { + // delete net; + // Error::raise(e); + // } + // + // return Dnn::Net::net2obj(net); + // } void init() { VALUE opencv = rb_define_module("Cv"); @@ -121,12 +122,13 @@ namespace rubyopencv { rb_module = rb_define_module_under(opencv, "Dnn"); rb_define_singleton_method(rb_module, "blob_from_image", RUBY_METHOD_FUNC(rb_blob_from_image), -1); - rb_define_singleton_method(rb_module, "read_net_from_caffe", RUBY_METHOD_FUNC(rb_read_net_from_caffe), 2); - rb_define_singleton_method(rb_module, "read_net_from_tensorflow", RUBY_METHOD_FUNC(rb_read_net_from_tensorflow), 1); - rb_define_singleton_method(rb_module, "read_net_from_torch", RUBY_METHOD_FUNC(rb_read_net_from_torch), 1); - rb_define_singleton_method(rb_module, "read_net_from_darknet", RUBY_METHOD_FUNC(rb_read_net_from_darknet), 2); + // rb_define_singleton_method(rb_module, "read_net_from_caffe", RUBY_METHOD_FUNC(rb_read_net_from_caffe), 2); + // rb_define_singleton_method(rb_module, "read_net_from_tensorflow", RUBY_METHOD_FUNC(rb_read_net_from_tensorflow), 1); + // rb_define_singleton_method(rb_module, "read_net_from_torch", RUBY_METHOD_FUNC(rb_read_net_from_torch), 1); + // rb_define_singleton_method(rb_module, "read_net_from_darknet", RUBY_METHOD_FUNC(rb_read_net_from_darknet), 2); Dnn::Net::init(rb_module); + Dnn::Layer::init(rb_module); } } } diff --git a/ext/opencv/dnn_layer.cpp b/ext/opencv/dnn_layer.cpp new file mode 100644 index 0000000..8a58b81 --- /dev/null +++ b/ext/opencv/dnn_layer.cpp @@ -0,0 +1,63 @@ +#include "opencv2/dnn.hpp" + +#include "opencv.hpp" +#include "error.hpp" + +namespace rubyopencv { + namespace Dnn { + namespace Layer { + VALUE rb_klass = Qnil; + + void free_layer(void* ptr) { + delete (cv::dnn::Layer*)ptr; + } + + size_t memsize_layer(const void* ptr) { + return sizeof(cv::dnn::Layer); + } + + rb_data_type_t opencv_layer_type = { + "Dnn::Layer", { 0, free_layer, memsize_layer, }, 0, 0, 0 + }; + + VALUE layer2obj(cv::dnn::Layer* ptr) { + return TypedData_Wrap_Struct(rb_klass, &opencv_layer_type, ptr); + } + + cv::dnn::Layer* obj2layer(VALUE obj) { + cv::dnn::Layer* ptr = NULL; + TypedData_Get_Struct(obj, cv::dnn::Layer, &opencv_layer_type, ptr); + return ptr; + } + + VALUE rb_allocate(VALUE klass) { + cv::dnn::Layer* ptr = NULL; + return TypedData_Wrap_Struct(klass, &opencv_layer_type, ptr); + } + + VALUE rb_initialize(VALUE self) { + return self; + } + + VALUE rb_name(VALUE self) { + cv::dnn::Layer* selfptr = obj2layer(self); + return rb_str_new_cstr(selfptr->name.c_str()); + } + + VALUE rb_type(VALUE self) { + cv::dnn::Layer* selfptr = obj2layer(self); + return rb_str_new_cstr(selfptr->type.c_str()); + } + + void init(VALUE rb_module) { + rb_klass = rb_define_class_under(rb_module, "Layer", rb_cData); + rb_define_alloc_func(rb_klass, rb_allocate); + + rb_define_private_method(rb_klass, "initialize", RUBY_METHOD_FUNC(rb_initialize), 0); + + rb_define_method(rb_klass, "name", RUBY_METHOD_FUNC(rb_name), 0); + rb_define_method(rb_klass, "type", RUBY_METHOD_FUNC(rb_type), 0); + } + } + } +} diff --git a/ext/opencv/dnn_layer.hpp b/ext/opencv/dnn_layer.hpp new file mode 100644 index 0000000..e2b4295 --- /dev/null +++ b/ext/opencv/dnn_layer.hpp @@ -0,0 +1,16 @@ +#ifndef RUBY_OPENCV_DNN_LAYER_H +#define RUBY_OPENCV_DNN_LAYER_H + +/* + * Document-class: Cv::Dnn::Layer + */ +namespace rubyopencv { + namespace Dnn { + namespace Layer { + void init(VALUE rb_module); + VALUE layer2obj(cv::dnn::Layer* ptr); + } + } +} + +#endif // RUBY_OPENCV_DNN_LAYER_H diff --git a/ext/opencv/dnn_net.cpp b/ext/opencv/dnn_net.cpp index 745c49f..47bb9ae 100644 --- a/ext/opencv/dnn_net.cpp +++ b/ext/opencv/dnn_net.cpp @@ -3,6 +3,7 @@ #include "opencv.hpp" #include "mat.hpp" #include "error.hpp" +#include "dnn_layer.hpp" namespace rubyopencv { namespace Dnn { @@ -36,7 +37,23 @@ namespace rubyopencv { return TypedData_Wrap_Struct(klass, &opencv_net_type, ptr); } - VALUE rb_initialize(VALUE self) { + VALUE rb_initialize(int argc, VALUE *argv, VALUE self) { + VALUE model, config, framework; + rb_scan_args(argc, argv, "03", &model, &config, &framework); + + if (!NIL_P(model)) { + cv::dnn::Net* dataptr = NULL; + + try { + cv::dnn::Net net = cv::dnn::readNet(StringValueCStr(model), CSTR_DEFAULT(config, ""), CSTR_DEFAULT(framework, "")); + cv::dnn::Net* dataptr = new cv::dnn::Net(net); + RTYPEDDATA_DATA(self) = dataptr; + } catch(cv::Exception& e) { + delete dataptr; + Error::raise(e); + } + } + return self; } @@ -69,25 +86,16 @@ namespace rubyopencv { rb_scan_args(argc, argv, "01", &output_name); cv::dnn::Net* selfptr = obj2net(self); - cv::Mat* m = NULL; try { - cv::Mat r; - - if (NIL_P(output_name)) { - r = selfptr->forward(); - } else { - r = selfptr->forward(StringValueCStr(output_name)); - } - - m = new cv::Mat(r); + m = new cv::Mat(selfptr->forward(CSTR_DEFAULT(output_name, ""))); } catch(cv::Exception& e) { delete m; Error::raise(e); } - return Mat::mat2obj(m); + return Mat::rb_clone(Mat::mat2obj(m)); } // bool empty() const @@ -96,34 +104,54 @@ namespace rubyopencv { return selfptr->empty() ? Qtrue : Qfalse; } - VALUE rb_get_layer_names(VALUE self) { + VALUE rb_get_layers(VALUE self) { cv::dnn::Net* selfptr = obj2net(self); std::vector v = selfptr->getLayerNames(); const long size = v.size(); - VALUE layer_names = rb_ary_new_capa(size); + VALUE layers = rb_ary_new_capa(size); for (long i = 0; i < size; i++) { - VALUE layer_name = rb_str_new_cstr(v[i].c_str()); - rb_ary_store(layer_names, i, layer_name); + VALUE layer = Dnn::Layer::layer2obj(selfptr->getLayer(v[i])); + rb_ary_store(layers, i, layer); } - return layer_names; + return layers; + } + + VALUE rb_enable_fusion(VALUE self, VALUE fusion) { + cv::dnn::Net* selfptr = obj2net(self); + selfptr->enableFusion(RTEST(fusion) ? true : false); + return self; + } + + VALUE rb_set_preferable_backend(VALUE self, VALUE backend_id) { + cv::dnn::Net* selfptr = obj2net(self); + selfptr->setPreferableBackend(NUM2INT(backend_id)); + return self; + } + + VALUE rb_set_preferable_target(VALUE self, VALUE target_id) { + cv::dnn::Net* selfptr = obj2net(self); + selfptr->setPreferableTarget(NUM2INT(target_id)); + return self; } void init(VALUE rb_module) { rb_klass = rb_define_class_under(rb_module, "Net", rb_cData); rb_define_alloc_func(rb_klass, rb_allocate); - rb_define_private_method(rb_klass, "initialize", RUBY_METHOD_FUNC(rb_initialize), 0); + rb_define_private_method(rb_klass, "initialize", RUBY_METHOD_FUNC(rb_initialize), -1); rb_define_method(rb_klass, "input=", RUBY_METHOD_FUNC(rb_set_input), -1); - rb_define_alias(rb_klass, "set_input", "input="); + rb_define_alias(rb_klass, "input", "input="); + rb_define_method(rb_klass, "fusion=", RUBY_METHOD_FUNC(rb_enable_fusion), 1); + rb_define_method(rb_klass, "preferable_backend=", RUBY_METHOD_FUNC(rb_set_preferable_backend), 1); + rb_define_method(rb_klass, "preferable_target=", RUBY_METHOD_FUNC(rb_set_preferable_target), 1); rb_define_method(rb_klass, "forward", RUBY_METHOD_FUNC(rb_forward), -1); rb_define_method(rb_klass, "empty?", RUBY_METHOD_FUNC(rb_empty), 0); - - rb_define_method(rb_klass, "get_layer_names", RUBY_METHOD_FUNC(rb_get_layer_names), 0); + rb_define_method(rb_klass, "layers", RUBY_METHOD_FUNC(rb_get_layers), 0); } } } diff --git a/ext/opencv/mat.cpp b/ext/opencv/mat.cpp index 7c5b229..fdad6ce 100644 --- a/ext/opencv/mat.cpp +++ b/ext/opencv/mat.cpp @@ -2,6 +2,7 @@ #include #include "opencv2/highgui.hpp" +#include "opencv.hpp" #include "mat.hpp" #include "mat_imgproc.hpp" #include "mat_drawing.hpp" @@ -246,8 +247,10 @@ namespace rubyopencv { * @return [Mat] Loaded image * @opencv_func cv::imread */ - VALUE rb_imread(VALUE self, VALUE filename, VALUE flags) { - return rb_imread_internal(self, filename, flags, rb_klass); + VALUE rb_imread(int argc, VALUE *argv, VALUE self) { + VALUE filename, flags; + rb_scan_args(argc, argv, "11", &filename, &flags); + return rb_imread_internal(self, filename, NUM2INT_DEFAULT(flags, cv::IMREAD_UNCHANGED), rb_klass); } VALUE rb_imread_as(VALUE self, VALUE filename, VALUE flags, VALUE klass) { @@ -366,8 +369,10 @@ namespace rubyopencv { * @return [CvMat] Loaded matrix * @opencv_func cv::imdecode */ - VALUE rb_imdecode(VALUE self, VALUE buf, VALUE flags) { - return rb_imdecode_internal(self, buf, flags, rb_klass); + VALUE rb_imdecode(int argc, VALUE *argv, VALUE self) { + VALUE buf, flags; + rb_scan_args(argc, argv, "11", &buf, &flags); + return rb_imdecode_internal(self, buf, NUM2INT_DEFAULT(flags, cv::IMREAD_UNCHANGED), rb_klass); } VALUE rb_imdecode_as(VALUE self, VALUE buf, VALUE flags, VALUE klass) { diff --git a/ext/opencv/mat.hpp b/ext/opencv/mat.hpp index 2fbd036..d623b88 100644 --- a/ext/opencv/mat.hpp +++ b/ext/opencv/mat.hpp @@ -9,8 +9,8 @@ namespace rubyopencv { namespace Mat { void init(); - VALUE rb_imread(VALUE self, VALUE filename, VALUE flags); - VALUE rb_imdecode(VALUE self, VALUE buf, VALUE flags); + VALUE rb_imread(int argc, VALUE *argv, VALUE self); + VALUE rb_imdecode(int argc, VALUE *argv, VALUE self); VALUE rb_imwrite_internal(VALUE filename, VALUE img, VALUE params); VALUE rb_clone(VALUE self); VALUE rb_add_weighted(int argc, VALUE *argv, VALUE self); @@ -21,7 +21,7 @@ namespace rubyopencv { cv::Mat* obj2mat(VALUE obj); VALUE mat2obj(cv::Mat* ptr); VALUE mat2obj(cv::Mat* ptr, VALUE klass); - + class RubyMatAllocator: public cv::MatAllocator { public: RubyMatAllocator() {} diff --git a/ext/opencv/opencv.cpp b/ext/opencv/opencv.cpp index 5113a49..cf7e189 100644 --- a/ext/opencv/opencv.cpp +++ b/ext/opencv/opencv.cpp @@ -101,9 +101,9 @@ namespace rubyopencv { rb_define_module_function(rb_module, "build_information", RUBY_METHOD_FUNC(rb_build_information), 0); - rb_define_singleton_method(rb_module, "imread", RUBY_METHOD_FUNC(Mat::rb_imread), 2); // in ext/opencv/mat.cpp + rb_define_singleton_method(rb_module, "imread", RUBY_METHOD_FUNC(Mat::rb_imread), -1); // in ext/opencv/mat.cpp rb_define_singleton_method(rb_module, "imwrite", RUBY_METHOD_FUNC(rb_imwrite), -1); - rb_define_singleton_method(rb_module, "imdecode", RUBY_METHOD_FUNC(Mat::rb_imdecode), 2); // in ext/opencv/mat.cpp + rb_define_singleton_method(rb_module, "imdecode", RUBY_METHOD_FUNC(Mat::rb_imdecode), -1); // in ext/opencv/mat.cpp rb_define_singleton_method(rb_module, "wait_key", RUBY_METHOD_FUNC(Window::rb_wait_key), -1); // in ext/opencv/window.cpp rb_define_singleton_method(rb_module, "add_weighted", RUBY_METHOD_FUNC(Mat::rb_add_weighted), -1); // in ext/opencv/mat.cpp diff --git a/ext/opencv/opencv.hpp b/ext/opencv/opencv.hpp index 4660aa9..533a035 100644 --- a/ext/opencv/opencv.hpp +++ b/ext/opencv/opencv.hpp @@ -15,6 +15,10 @@ namespace rubyopencv { return NIL_P(value) ? (default_value) : NUM2DBL(value); } + inline std::string CSTR_DEFAULT(VALUE value, std::string default_value) { + return NIL_P(value) ? (default_value) : StringValueCStr(value); + } + inline double RTEST_DEFAULT(VALUE value, bool default_value) { return NIL_P(value) ? (default_value) : (RTEST(value) ? true : false); } diff --git a/ext/opencv/opencv_const.cpp b/ext/opencv/opencv_const.cpp index cc771c1..59dfc9a 100644 --- a/ext/opencv/opencv_const.cpp +++ b/ext/opencv/opencv_const.cpp @@ -1,6 +1,7 @@ // -*- mode: c++; coding: utf-8 -*- #include "ruby.h" #include "opencv2/core.hpp" +#include "opencv2/dnn.hpp" #include "opencv2/imgproc.hpp" #include "opencv2/imgcodecs.hpp" #include "opencv2/highgui.hpp" @@ -318,7 +319,7 @@ namespace rubyopencv { rb_define_const(rb_module, "BORDER_REFLECT101", INT2FIX(cv::BORDER_REFLECT101)); rb_define_const(rb_module, "BORDER_DEFAULT", INT2FIX(cv::BORDER_DEFAULT)); rb_define_const(rb_module, "BORDER_ISOLATED", INT2FIX(cv::BORDER_ISOLATED)); - + rb_define_const(rb_module, "EVENT_MOUSEMOVE", INT2FIX(cv::EVENT_MOUSEMOVE)); rb_define_const(rb_module, "EVENT_LBUTTONDOWN", INT2FIX(cv::EVENT_LBUTTONDOWN)); rb_define_const(rb_module, "EVENT_RBUTTONDOWN", INT2FIX(cv::EVENT_RBUTTONDOWN)); @@ -387,6 +388,15 @@ namespace rubyopencv { rb_define_const(rb_module, "THRESH_OTSU", INT2FIX(cv::THRESH_OTSU)); rb_define_const(rb_module, "THRESH_TRIANGLE", INT2FIX(cv::THRESH_TRIANGLE)); + rb_define_const(rb_module, "DNN_BACKEND_DEFAULT", INT2FIX(cv::dnn::DNN_BACKEND_DEFAULT)); + rb_define_const(rb_module, "DNN_BACKEND_INFERENCE_ENGINE", INT2FIX(cv::dnn::DNN_BACKEND_INFERENCE_ENGINE)); + rb_define_const(rb_module, "DNN_BACKEND_OPENCV", INT2FIX(cv::dnn::DNN_BACKEND_OPENCV)); + rb_define_const(rb_module, "DNN_BACKEND_HALIDE", INT2FIX(cv::dnn::DNN_BACKEND_HALIDE)); + + rb_define_const(rb_module, "DNN_TARGET_CPU", INT2FIX(cv::dnn::DNN_TARGET_CPU)); + rb_define_const(rb_module, "DNN_TARGET_OPENCL", INT2FIX(cv::dnn::DNN_TARGET_OPENCL)); + rb_define_const(rb_module, "DNN_TARGET_OPENCL_FP16", INT2FIX(cv::dnn::DNN_TARGET_OPENCL_FP16)); + rb_define_const(rb_module, "ADAPTIVE_THRESH_MEAN_C", INT2FIX(cv::ADAPTIVE_THRESH_MEAN_C)); rb_define_const(rb_module, "ADAPTIVE_THRESH_GAUSSIAN_C", INT2FIX(cv::ADAPTIVE_THRESH_GAUSSIAN_C)); }