You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

233 lines
6.8 KiB

// Created by ausk<jincsu#126.com> @ 2019.11.23
// Based on
// https://github.com/ausk/keras-unet-deploy/tree/master/cpp/libunet/cvbind.h
#pragma once
#include <pybind11/numpy.h>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
// Convert cv::Point, cv::Rest, cv::Mat
namespace pybind11
{
namespace detail
{
//! cv::Point <=> tuple(x,y)
template <>
struct type_caster<cv::Point>
{
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<pybind11::tuple>(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<pybind11::tuple>(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<int>(), pt[1].cast<int>());
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<cv::Rect>
{
PYBIND11_TYPE_CASTER(cv::Rect, _("tuple_xywh"));
bool load(handle obj, bool)
{
if (!pybind11::isinstance<pybind11::tuple>(obj))
{
std::logic_error("Rect should be a tuple!");
return false;
}
pybind11::tuple rect = reinterpret_borrow<pybind11::tuple>(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<int>(),
rect[1].cast<int>(),
rect[2].cast<int>(),
rect[3].cast<int>());
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<ssize_t> shape; /* Buffer dimensions */
// std::vector<ssize_t> strides; /* Strides (in bytes) for each index */
//};
template <>
struct type_caster<cv::Mat>
{
public:
PYBIND11_TYPE_CASTER(cv::Mat, _("numpy.ndarray"));
//! 1. cast numpy.ndarray to cv::Mat
bool load(handle obj, bool)
{
array b = reinterpret_borrow<array>(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<unsigned char>::format())
{
dtype = CV_8UC(nc);
}
else if (info.format == format_descriptor<int>::format())
{
dtype = CV_32SC(nc);
}
else if (info.format == format_descriptor<float>::format())
{
dtype = CV_32FC(nc);
}
else if (info.format == format_descriptor<double>::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<unsigned char>::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<unsigned char>::format();
elemsize = sizeof(unsigned char);
}
else if (depth == CV_32S)
{
format = format_descriptor<int>::format();
elemsize = sizeof(int);
}
else if (depth == CV_32F)
{
format = format_descriptor<float>::format();
elemsize = sizeof(float);
}
else if (depth == CV_64F)
{
format = format_descriptor<double>::format();
elemsize = sizeof(double);
}
else
{
throw std::logic_error(
"Unsupport type, only support uchar, int32, float, double");
}
std::vector<size_t> bufferdim;
std::vector<size_t> 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