总体思路(在OpenCV Python绑定中使用)是创建一个ndarray
与Mat
对象共享其数据缓冲区的numpy ,并将其传递给Python函数。
注意:在这一点上,我将示例限制为仅连续矩阵。
我们可以利用pybind11::array
该类。
我们需要确定适合dtype
numpy数组使用的数组。这是一个简单的一对一映射,我们可以使用switch
:
py::dtype determine_np_dtype(int depth)
{ switch (depth) { case CV_8U: return py::dtype::of
确定numpy数组的形状。为了使它的行为类似于OpenCV,让我们将1-channel映射Mat
到2D numpy数组,将multi-channel映射Mat
到3D numpy数组。
std::vector<std::size_t> determine_shape(cv::Mat& m)
{ if (m.channels() == 1) { return { static_cast
return {
static_cast<size_t>(m.rows)
, static_cast<size_t>(m.cols)
, static_cast<size_t>(m.channels())
};
}
提供将共享缓冲区的生存期延长到numpy数组的生存期的方法。我们可以pybind11::capsule
围绕源创建一个浅表副本Mat
-由于对象的实现方式,这可以在所需的时间内有效地增加其引用计数。
py::capsule make_capsule(cv::Mat& m)
{ return py::capsule(new cv::Mat(m) , { delete reinterpret_cast<:mat>(v); } ); }
现在,我们可以执行转换了。
py::array mat_to_nparray(cv::Mat& m)
{
if (!m.isContinuous()) {
throw std::invalid_argument("Only continuous Mats supported.");
}
return py::array(determine_np_dtype(m.depth())
, determine_shape(m)
, m.data
, make_capsule(m));
}
def foo(arr):
print(arr.shape)
捕获在pybind对象中fun
。然后使用aMat
作为源从C ++调用此函数,我们将执行以下操作:
cv::Mat img; // Initialize this somehow
auto result = fun(mat_to_nparray(img));
#include <pybind11/pybind11.h>
#include <pybind11/embed.h>
#include <pybind11/numpy.h>
#include <pybind11/stl.h>
#include <opencv2/opencv.hpp>
#include <iostream>
namespace py = pybind11;
// The 4 functions from above go here...
int main()
{
// Start the interpreter and keep it alive
py::scoped_interpreter guard{};
try {
auto locals = py::dict{};
py::exec(R"(
import numpy as np
def test_cpp_to_py(arr):
return (arr[0,0,0], 2.0, 30)
)");
auto test_cpp_to_py = py::globals()["test_cpp_to_py"];
for (int i = 0; i < 10; i++) {
int64 t0 = cv::getTickCount();
cv::Mat img(cv::Mat::zeros(1024, 1024, CV_8UC3) + cv::Scalar(1, 1, 1));
int64 t1 = cv::getTickCount();
auto result = test_cpp_to_py(mat_to_nparray(img));
int64 t2 = cv::getTickCount();
double delta0 = (t1 - t0) / cv::getTickFrequency() * 1000;
double delta1 = (t2 - t1) / cv::getTickFrequency() * 1000;
std::cout << "* " << delta0 << " ms | " << delta1 << " ms" << std::endl;
}
} catch (py::error_already_set& e) {
std::cerr << e.what() << "\n";
}
return 0;
}
* 4.56413 ms | 0.225657 ms
* 3.95923 ms | 0.0736127 ms
* 3.80335 ms | 0.0438603 ms
* 3.99262 ms | 0.0577587 ms
* 3.82262 ms | 0.0572 ms
* 3.72373 ms | 0.0394603 ms
* 3.74014 ms | 0.0405079 ms
* 3.80621 ms | 0.054546 ms
* 3.72177 ms | 0.0386222 ms
* 3.70683 ms | 0.0373651 ms