// Created by ausk @ 2019.11.23 // Based on // https://github.com/ausk/keras-unet-deploy/tree/master/cpp/libunet/cvbind.h #pragma once #include #include #include #include #include #include // Convert cv::Point, cv::Rest, cv::Mat namespace pybind11 { namespace detail { //! cv::Point <=> tuple(x,y) template <> struct type_caster { PYBIND11_TYPE_CASTER(cv::Point, _("tuple_xy")); // Convert from Python to C++. // Convert the Python tuple object to C++ cv::Point type, and return false // if the conversion fails. // The second argument indicates whether implicit conversions should be // applied. bool load(handle obj, bool) { // Ensure that the passed parameter is of tuple type if (!pybind11::isinstance(obj)) { std::logic_error("Point(x,y) should be a tuple!"); return false; } // Extract the tuple object from the handle and ensure its length is 2. pybind11::tuple pt = reinterpret_borrow(obj); if (pt.size() != 2) { std::logic_error("Point(x,y) tuple should be size of 2"); return false; } // Convert a tuple of length 2 to cv::Point. value = cv::Point(pt[0].cast(), pt[1].cast()); return true; } // Convert from C++ to Python. Convert C++ cv::Mat object to tuple, // parameter 2 and parameter 3 are ignored static handle cast(const cv::Point& pt, return_value_policy, handle) { return pybind11::make_tuple(pt.x, pt.y).release(); } }; // cv::Rect <=> tuple(x,y,w,h) template <> struct type_caster { PYBIND11_TYPE_CASTER(cv::Rect, _("tuple_xywh")); bool load(handle obj, bool) { if (!pybind11::isinstance(obj)) { std::logic_error("Rect should be a tuple!"); return false; } pybind11::tuple rect = reinterpret_borrow(obj); if (rect.size() != 4) { std::logic_error("Rect (x,y,w,h) tuple should be size of 4"); return false; } value = cv::Rect(rect[0].cast(), rect[1].cast(), rect[2].cast(), rect[3].cast()); return true; } static handle cast(const cv::Rect& rect, return_value_policy, handle) { return pybind11::make_tuple(rect.x, rect.y, rect.width, rect.height) .release(); } }; // Convert between cv::Mat and numpy.ndarray. // // Python supports a general buffer protocol for data exchange between plugins. // Let the type expose a buffer view, this allows direct access to the original // internal data, often used in matrix types. // // Pybind11 provides the pybind11::buffer_info type to map the Python buffer // protocol (buffer protocol). // // struct buffer_info { // void* ptr; /* Pointer to buffer */ // ssize_t itemsize; /* Size of one scalar */ // std::string format; /* Python struct-style format descriptor // */ ssize_t ndim; /* Number of dimensions */ // std::vector shape; /* Buffer dimensions */ // std::vector strides; /* Strides (in bytes) for each index */ //}; template <> struct type_caster { public: PYBIND11_TYPE_CASTER(cv::Mat, _("numpy.ndarray")); //! 1. cast numpy.ndarray to cv::Mat bool load(handle obj, bool) { array b = reinterpret_borrow(obj); buffer_info info = b.request(); int nh = 1; int nw = 1; int nc = 1; int ndims = info.ndim; if (ndims == 2) { nh = info.shape[0]; nw = info.shape[1]; } else if (ndims == 3) { nh = info.shape[0]; nw = info.shape[1]; nc = info.shape[2]; } else { throw std::logic_error("Only support 2d, 2d matrix"); return false; } int dtype; if (info.format == format_descriptor::format()) { dtype = CV_8UC(nc); } else if (info.format == format_descriptor::format()) { dtype = CV_32SC(nc); } else if (info.format == format_descriptor::format()) { dtype = CV_32FC(nc); } else if (info.format == format_descriptor::format()) { dtype = CV_64FC(nc); } else { throw std::logic_error( "Unsupported type, only support uchar, int32, float, double"); return false; } value = cv::Mat(nh, nw, dtype, info.ptr); return true; } //! Cast cv::Mat to numpy.ndarray static handle cast(const cv::Mat& mat, return_value_policy, handle /*defval*/) { std::string format = format_descriptor::format(); size_t elemsize = sizeof(unsigned char); int nw = mat.cols; int nh = mat.rows; int nc = mat.channels(); int depth = mat.depth(); int type = mat.type(); int dim = (depth == type) ? 2 : 3; if (depth == CV_8U) { format = format_descriptor::format(); elemsize = sizeof(unsigned char); } else if (depth == CV_32S) { format = format_descriptor::format(); elemsize = sizeof(int); } else if (depth == CV_32F) { format = format_descriptor::format(); elemsize = sizeof(float); } else if (depth == CV_64F) { format = format_descriptor::format(); elemsize = sizeof(double); } else { throw std::logic_error( "Unsupport type, only support uchar, int32, float, double"); } std::vector bufferdim; std::vector strides; if (dim == 2) { bufferdim = {(size_t)nh, (size_t)nw}; strides = {elemsize * (size_t)nw, elemsize}; } else if (dim == 3) { bufferdim = {(size_t)nh, (size_t)nw, (size_t)nc}; strides = {(size_t)elemsize * nw * nc, (size_t)elemsize * nc, (size_t)elemsize}; } return array(buffer_info( mat.data, elemsize, format, dim, bufferdim, strides)) .release(); } }; } // namespace detail } // namespace pybind11