diff --git a/ext/opencv/dnn.cpp b/ext/opencv/dnn.cpp new file mode 100644 index 0000000..84d5734 --- /dev/null +++ b/ext/opencv/dnn.cpp @@ -0,0 +1,74 @@ +#include "opencv2/dnn.hpp" + +#include "opencv.hpp" +#include "mat.hpp" +#include "size.hpp" +#include "scalar.hpp" +#include "dnn_net.hpp" +#include "error.hpp" + +// https://docs.opencv.org/trunk/d6/d0f/group__dnn.html#ga29d0ea5e52b1d1a6c2681e3f7d68473a +// https://github.com/opencv/opencv/blob/master/modules/dnn/src/caffe/caffe_importer.cpp + +namespace rubyopencv { + namespace Dnn { + VALUE rb_module = Qnil; + + // Mat blobFromImage(const Mat& image, double scalefactor=1.0, const Size& size = Size(), const Scalar& mean = Scalar(), bool swapRB=true) + VALUE rb_blob_from_image(int argc, VALUE *argv, VALUE self) { + VALUE image, options; + rb_scan_args(argc, argv, "11", &image, &options); + + cv::Mat *b; + cv::Mat *m = Mat::obj2mat(image); + + try { + cv::Mat r; + + if (NIL_P(options)) { + r = cv::dnn::blobFromImage(*m); + } else { + Check_Type(options, T_HASH); + + double scale_factor = NUM2DBL_DEFAULT(HASH_LOOKUP(options, "scale_factor"), 1.0); + cv::Size *s = Size::obj2size(HASH_LOOKUP(options, "size")); + cv::Scalar *sc = Scalar::obj2scalar(HASH_LOOKUP(options, "mean"));; + + r = cv::dnn::blobFromImage(*m, scale_factor, *s, *sc); + } + + b = new cv::Mat(r); + } catch(cv::Exception& e) { + delete b; + Error::raise(e); + } + + 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::experimental_dnn_v1::Net *net; + + try { + net = new cv::dnn::experimental_dnn_v1::Net(cv::dnn::readNetFromCaffe(StringValueCStr(prototxt), StringValueCStr(caffe_model))); + } catch(cv::Exception& e) { + delete net; + Error::raise(e); + } + + return Dnn::Net::net2obj(net); + } + + void init() { + VALUE opencv = rb_define_module("Cv"); + + 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); + + Dnn::Net::init(rb_module); + } + } +} diff --git a/ext/opencv/dnn.hpp b/ext/opencv/dnn.hpp new file mode 100644 index 0000000..d01e476 --- /dev/null +++ b/ext/opencv/dnn.hpp @@ -0,0 +1,13 @@ +#ifndef RUBY_OPENCV_DNN_H +#define RUBY_OPENCV_DNN_H + +/* + * Document-class: Cv::Dnn + */ +namespace rubyopencv { + namespace Dnn { + void init(); + } +} + +#endif // RUBY_OPENCV_DNN_H diff --git a/ext/opencv/dnn_net.cpp b/ext/opencv/dnn_net.cpp new file mode 100644 index 0000000..0bbeba5 --- /dev/null +++ b/ext/opencv/dnn_net.cpp @@ -0,0 +1,133 @@ +#include "opencv2/dnn.hpp" + +#include "opencv.hpp" +#include "mat.hpp" +#include "error.hpp" + +namespace rubyopencv { + namespace Dnn { + namespace Net { + VALUE rb_klass = Qnil; + + void free_net(void* ptr) { + delete (cv::dnn::experimental_dnn_v1::Net*)ptr; + } + + size_t memsize_net(const void* ptr) { + return sizeof(cv::dnn::experimental_dnn_v1::Net); + } + + rb_data_type_t opencv_net_type = { + "Dnn::Net", { 0, free_net, memsize_net, }, 0, 0, 0 + }; + + VALUE net2obj(cv::dnn::experimental_dnn_v1::Net* ptr) { + return TypedData_Wrap_Struct(rb_klass, &opencv_net_type, ptr); + } + + cv::dnn::experimental_dnn_v1::Net* obj2net(VALUE obj) { + cv::dnn::experimental_dnn_v1::Net* ptr = NULL; + TypedData_Get_Struct(obj, cv::dnn::experimental_dnn_v1::Net, &opencv_net_type, ptr); + return ptr; + } + + VALUE rb_allocate(VALUE klass) { + cv::dnn::experimental_dnn_v1::Net* ptr = new cv::dnn::experimental_dnn_v1::Net(); + return TypedData_Wrap_Struct(klass, &opencv_net_type, ptr); + } + + VALUE rb_initialize(VALUE self) { + return self; + } + + // void setInput(const Mat &blob, const String& name = "") + VALUE rb_set_input(int argc, VALUE *argv, VALUE self) { + VALUE blob, name; + rb_scan_args(argc, argv, "11", &blob, &name); + + cv::dnn::experimental_dnn_v1::Net* selfptr = obj2net(self); + + cv::Mat *m = Mat::obj2mat(blob); + + try { + if (NIL_P(name)) { + selfptr->setInput(*m); + } else { + selfptr->setInput(*m, StringValueCStr(name)); + } + } catch(cv::Exception& e) { + delete m; + Error::raise(e); + } + + return Qnil; + } + + // Mat forward(const String& outputName = String()) + VALUE rb_forward(int argc, VALUE *argv, VALUE self) { + VALUE output_name; + rb_scan_args(argc, argv, "01", &output_name); + + cv::dnn::experimental_dnn_v1::Net* selfptr = obj2net(self); + + cv::Mat* m = NULL; + // cv::Mat m; + + try { + cv::Mat r; + + if (NIL_P(output_name)) { + r = selfptr->forward(); + } else { + r = selfptr->forward(StringValueCStr(output_name)); + } + + m = new cv::Mat(r.reshape(1, 1)); + // m = r; + } catch(cv::Exception& e) { + delete m; + Error::raise(e); + } + + // int indxCls[4] = { 0, 0, 401, 1 }; + // int cls = m->at(indxCls); + + return Mat::mat2obj(m); + + // const long size = m->size[2]; + // return(ULL2NUM(m.size[2])); + // VALUE detected_objects = rb_ary_new_capa(size); + // for (long i = 0; i < size; i++) { + // int indxCls[4] = { 0, 0, i, 1 }; + // int cls = m->at(indxCls); + // rb_ary_store(detected_objects, i, INT2NUM(cls)); + // } + // + // return detected_objects; + + // cv::Point classIdPoint; + // double confidence; + // cv::minMaxLoc(m.reshape(1, 1), 0, &confidence, 0, &classIdPoint); + // int classId = classIdPoint.x; + // return(INT2NUM(classId)); + } + + // bool empty() const + VALUE rb_empty(VALUE self) { + cv::dnn::experimental_dnn_v1::Net* selfptr = obj2net(self); + return selfptr->empty() ? Qtrue : Qfalse; + } + + 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_method(rb_klass, "set_input", RUBY_METHOD_FUNC(rb_set_input), -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); + } + } + } +} diff --git a/ext/opencv/dnn_net.hpp b/ext/opencv/dnn_net.hpp new file mode 100644 index 0000000..5636a83 --- /dev/null +++ b/ext/opencv/dnn_net.hpp @@ -0,0 +1,16 @@ +#ifndef RUBY_OPENCV_DNN_NET_H +#define RUBY_OPENCV_DNN_NET_H + +/* + * Document-class: Cv::Dnn::Net + */ +namespace rubyopencv { + namespace Dnn { + namespace Net { + void init(VALUE rb_module); + VALUE net2obj(cv::dnn::experimental_dnn_v1::Net* ptr); + } + } +} + +#endif // RUBY_OPENCV_DNN_NET_H diff --git a/ext/opencv/extconf.rb b/ext/opencv/extconf.rb index a67c5de..cf01355 100755 --- a/ext/opencv/extconf.rb +++ b/ext/opencv/extconf.rb @@ -21,10 +21,10 @@ def cv_version_suffix(incdir) major + minor + subminor end -incdir, libdir = dir_config("opencv", "/usr/local/include", "/usr/local/lib") +incdir, _ = dir_config("opencv", "/usr/local/include", "/usr/local/lib") -opencv_headers = ["opencv2/core.hpp", "opencv2/highgui.hpp", "opencv2/imgcodecs.hpp", "opencv2/imgproc.hpp", "opencv2/objdetect.hpp", "opencv2/videoio.hpp"] -opencv_libraries = ["opencv_core", "opencv_highgui", "opencv_imgcodecs", "opencv_imgproc", "opencv_objdetect", "opencv_videoio"] +opencv_headers = ["opencv2/core.hpp", "opencv2/highgui.hpp", "opencv2/imgcodecs.hpp", "opencv2/imgproc.hpp", "opencv2/objdetect.hpp", "opencv2/videoio.hpp", "opencv2/dnn.hpp"] +opencv_libraries = ["opencv_core", "opencv_highgui", "opencv_imgcodecs", "opencv_imgproc", "opencv_objdetect", "opencv_videoio", "opencv_dnn"] puts ">> Check the required libraries..." if $mswin or $mingw diff --git a/ext/opencv/opencv.cpp b/ext/opencv/opencv.cpp index e406b86..5113a49 100644 --- a/ext/opencv/opencv.cpp +++ b/ext/opencv/opencv.cpp @@ -12,6 +12,7 @@ #include "scalar.hpp" #include "cascadeclassifier.hpp" +#include "dnn.hpp" #include "videocapture.hpp" #include "error.hpp" @@ -92,6 +93,7 @@ namespace rubyopencv { Size::init(); Scalar::init(); CascadeClassifier::init(); + Dnn::init(); VideoCapture::init(); Window::init(); Trackbar::init();