【发布时间】:2020-07-10 01:39:48
【问题描述】:
我试图弄清楚如何在 C++ 中从 Python 接收 OpenCV 图像。我正在尝试将回调函数从 C++ 发送到我的 Python 模块,然后当我在 C++ 应用程序中调用特定的 Python 方法时,我可以访问所需的图像。
在我补充更多细节之前,我需要补充一点,这方面已经有几个问题,包括:
- how-to-convert-opencv-image-data-from-python-to-c
- pass-image-data-from-python-to-cvmat-in-c
- writing-python-bindings-for-c-code-that-use-opencv
- c-conversion-from-numpy-array-to-mat-opencv
但他们都没有关于Pybind11 的任何信息。事实上,他们都在使用PyObject(来自Python.h 标头)有和没有Boost.Python。所以我的第一个尝试是知道Pybind11 支持Numpy 数组是如何实现的,所以它可以让事情变得更容易。
同样在C++ 方面,OpenCV 有两个版本,3.x 和 4.x,我最近发现的 4.x 符合 C++11。在 Python 方面,我使用了 OpenCV 3.x,我正处于选择哪一个的十字路口
以及它对Pybind11 有什么影响。
到目前为止我所做的尝试:我做了一个快速的虚拟回调并尝试像这样传递一个简单的cv::Mat&:
#include <pybind11/embed.h>
#include <pybind11/numpy.h>
#include <pybind11/stl.h>
#include <pybind11/functional.h>
namespace py = pybind11;
...
void cpp_callback1(bool i, std::string id, cv::Mat img)
{
auto timenow = chrono::system_clock::to_time_t(chrono::system_clock::now());
cout <<"arg1: " << i << " arg2: " << id<<" arg3: " << typeid(img).name() <<" " << ctime(&timenow)<<endl;
}
并像这样使用它:
py::list callback_lst;
callback_lst.attr("append")(py::cpp_function(cpp_callback1));
py::dict core_kwargs = py::dict("callback_list"_a = callback_lst,
"debug_show_feed"_a = true);
py::object core_obj = core_cls(**core_kwargs);
core_obj.attr("start")();
但它失败,python 部分出现异常:
29/03/2020 21:56:47 : exception occured ("(): incompatible function arguments. The following argument types are supported:\n 1. (arg0: bool, arg1: str, arg2: cv::Mat) -> None\n\nInvoked with: True, '5', array([[[195, 217, 237],\n [195, 217, 237],\n [196, 218, 238],\n ...,\n [211, 241, 255],\n [211, 241, 255],\n [211, 241, 255]],\n\n [[195, 217, 237],\n [195, 217, 237],\n [195, 217, 237],\n ...,\n [211, 241, 255],\n [211, 241, 255],\n [211, 241, 255]],\n\n [[195, 217, 237],\n [195, 217, 237],\n [195, 217, 237],\n ...,\n [211, 241, 255],\n [211, 241, 255],\n [211, 241, 255]],\n\n ...,\n\n [[120, 129, 140],\n [110, 120, 130],\n [113, 122, 133],\n ...,\n [196, 209, 245],\n [195, 207, 244],\n [195, 207, 244]],\n\n [[120, 133, 142],\n [109, 121, 130],\n [114, 120, 131],\n ...,\n [195, 208, 242],\n [195, 208, 242],\n [195, 208, 242]],\n\n [[121, 134, 143],\n [106, 119, 128],\n [109, 114, 126],\n ...,\n [194, 207, 241],\n [195, 208, 242],\n [195, 208, 242]]], dtype=uint8)",)
Traceback (most recent call last):
File "C:\Users\Master\Anaconda3\Lib\site-packages\F\utils.py", line 257, in start
self._main_loop()
File "C:\Users\Master\Anaconda3\Lib\site-packages\F\utils.py", line 301, in _main_loop
self._execute_callbacks(is_valid, name, frame)
File "C:\Users\Master\Anaconda3\Lib\site-packages\F\utils.py", line 142, in _execute_callbacks
callback(*args)
TypeError: (): incompatible function arguments. The following argument types are supported:
1. (arg0: bool, arg1: str, arg2: cv::Mat) -> None
Invoked with: True, '5', array([[[195, 217, 237],
[195, 217, 237],
[196, 218, 238],
...,
[211, 241, 255],
[211, 241, 255],
[211, 241, 255]],
[[195, 217, 237],
[195, 217, 237],
[195, 217, 237],
...,
使用py::object 或py::array_t<uint8_t> 而不是cv::Mat 不会导致任何错误,但我似乎无法找到将它们正确转换回cv::Mat 的方法!
我尝试按照 cmets 中的说明将 numpy 数组转换为 cv::Mat,但输出是垃圾:
void cpp_callback1(bool i, std::string id, py::array_t<uint8_t>& img)
{
auto im = img.unchecked<3>();
auto rows = img.shape(0);
auto cols = img.shape(1);
auto type = CV_8UC3;
//py::buffer_info buf = img.request();
cv::Mat img2(rows, cols, type, img.ptr());
cv::imshow("test", img2);
}
结果:
在我看来,这个方向的步伐或某些东西搞砸了,图像显示是这样的。我在这里做错了什么?不过我不能使用 img.strides() !当使用 py::print 打印它时,它会显示 960 或类似的东西。所以我完全不知道如何解释!
【问题讨论】:
-
让回调采用 numpy 数组而不是
Mat。然后在回调中,为 numpy 数组的数据缓冲区创建一个Mat标头。 (请记住,这共享缓冲区,因此如果您需要Mat的生命周期比回调的范围更长,则必须进行深层复制) -
@DanMašek,非常感谢,但您的意思是
py::array_t<double>吗? -
是的,看起来像。尽管您可能必须更改模板参数以匹配 numpy 数组的实际数据类型——如果它是图像,则可能是
uint8_t。 -
我很乐意为您提供一个完整的代码示例,但我首先必须设置 PyBind 并了解一些细节。不介意再熟悉一点,所以这不是问题,但在这个特殊的时刻,我今晚还有一些工作要做……也许今晚晚些时候。
-
@DanMašek,非常感谢。它真的非常感激。我会解决的,希望能解决。如果我遇到问题,我会更新问题。
标签: python c++ opencv pybind11