使用pybind11实现python调用c++实现的opencv代码

最近用c++的opencv实现了一个算法,但是要导入到python里面做训练处理,看了网上的教程感觉都不全面,自己摸索了一个晚上终于调通了,现在总结一下。

1. 安装pybind11

先pip安装一波:

pip3 install pybind11

首先创建个工程目录test_pybind,在自己的工程目录下面从pybind11上面下载源码:

git clone https://github.com/pybind/pybind11

然后使用cmake编译工程准备使用

cd pybind11
mkdir build
cd build
cmake ..
cmake --build . --config Release --target check

上面一波常规操作之后,画风及相对路径如下所示:
在这里插入图片描述

2.举个例子

我们在上述目录下面创建一个.cpp文件来先写下c++的代码测试一下,创建完的画风:
在这里插入图片描述

看下面例子:
最简单的我们要调用c++的接口实现两个数相加。具体c++代码这样实现:

#include <pybind11/pybind11.h>
namespace py = pybind11;
int add(int i, int j) {
    return i + j;
}
PYBIND11_MODULE(example, m)
{
    // optional module docstring
    m.doc() = "pybind11 example plugin";
    // expose add function, and add keyword arguments and default arguments
    m.def("add", &add, "A function which adds two numbers", py::arg("i")=1, py::arg("j")=2);
}

这上面所有的封装代码都需要在 PYBIND11_MODULE 函数里面,具体是什么意思呢?定义如下:

PYBIND11_MODULE( 模块名, 模块实例对象 ){
    m.doc() = "pybind11 example";   //可选,说明这个模块是做什么的
    //封装的具体操作。这些操作包括普通的函数的封装,类的访问等下面用不同例子来说明问题
    m.def( "给python调用方法名"&实际操作的函数, "函数功能说明" ). //其中函数功能说明为可选
}

完事后还要创建一个cmakelists文件:
在这里插入图片描述

cmakelists文件我直接把opencv也包含进去了,因为后面要用到:

cmake_minimum_required(VERSION 3.1) 
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
project(test_pybind)
find_package( OpenCV REQUIRED)
include_directories(${OpenCV_INCLUDE_DIRS})
add_subdirectory(pybind11)
SET(SOURCES
        ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp
        )
pybind11_add_module(test_pybind ${SOURCES})
target_link_libraries( test_pybind PRIVATE ${OpenCV_LIBS} )

这样完事后,直接常规操作:
在当前工程目录下面:
在这里插入图片描述
恩恩,回车之后非常完美通过。再make一下。
在这里插入图片描述
可以看到当前目录下面应该会有一个.so文件:
在这里插入图片描述
把他从build文件夹copy到上一层目录:
在这里插入图片描述
ok,现在工程目录变成这样了:
在这里插入图片描述
接下来就可以在这个目录里面创建.py文件进行调用了:
在这里插入图片描述

import test_pybind
print(test_pybind.add(3, 4))

运行一下python文件:
在这里插入图片描述
简直ojbk。

3.opencv的例子

经历上面opencv的也就八九不离十了,下面的代码来自别的博客:
一个是用来将cv::Mat转换成numpy的

#include"mat_warper.h"
#include <pybind11/numpy.h>

/*
Python->C++ Mat
*/


cv::Mat numpy_uint8_1c_to_cv_mat(py::array_t<unsigned char>& input) {

    if (input.ndim() != 2)
        throw std::runtime_error("1-channel image must be 2 dims ");
    py::buffer_info buf = input.request();
    cv::Mat mat(buf.shape[0], buf.shape[1], CV_8UC1, (unsigned char*)buf.ptr);
    return mat;
}


cv::Mat numpy_uint8_3c_to_cv_mat(py::array_t<unsigned char>& input) {
    if (input.ndim() != 3)
        throw std::runtime_error("3-channel image must be 3 dims ");
    py::buffer_info buf = input.request();
    cv::Mat mat(buf.shape[0], buf.shape[1], CV_8UC3, (unsigned char*)buf.ptr);
    return mat;
}


/*
C++ Mat ->numpy
*/
py::array_t<unsigned char> cv_mat_uint8_1c_to_numpy(cv::Mat& input) {
    py::array_t<unsigned char> dst = py::array_t<unsigned char>({ input.rows,input.cols }, input.data);
    return dst;
}

py::array_t<unsigned char> cv_mat_uint8_3c_to_numpy(cv::Mat& input) {
    py::array_t<unsigned char> dst = py::array_t<unsigned char>({ input.rows,input.cols,3}, input.data);
    return dst;
}

//py::array_t<std::complex<float>> cv_mat_float_3c_to_numpy(cv::Mat& input) {
//    py::array_t<std::complex<float>> dst = py::array_t<std::complex<float>>({ input.rows,input.cols,3}, input.data);
//    return dst;
//}



py::array_t<float> mat_to_py(cv::Mat& input)
{
    py::buffer_info buffer(
        input.data,
        sizeof(float),
        py::format_descriptor<float>::format(),
        2,
        {input.rows, input.cols},
        { sizeof(float)* input.cols, sizeof(float)}
    );
    return py::array_t<float>(buffer);
}


//PYBIND11_MODULE(cv_mat_warper, m) {
//
//  m.doc() = "OpenCV Mat -> Numpy.ndarray warper";
//  m.def("numpy_uint8_1c_to_cv_mat", &numpy_uint8_1c_to_cv_mat);
//  m.def("numpy_uint8_3c_to_cv_mat", &numpy_uint8_3c_to_cv_mat);
//}

头文件申明下:

#ifndef MAT_WARPER_H_

#include<opencv2/opencv.hpp>
#include<pybind11/pybind11.h>
#include<pybind11/numpy.h>

namespace py = pybind11;

cv::Mat numpy_uint8_1c_to_cv_mat(py::array_t<unsigned char>& input);

cv::Mat numpy_uint8_3c_to_cv_mat(py::array_t<unsigned char>& input);



py::array_t<unsigned char> cv_mat_uint8_1c_to_numpy(cv::Mat & input);

py::array_t<unsigned char> cv_mat_uint8_3c_to_numpy(cv::Mat & input);

//py::array_t<std::complex<float>> cv_mat_float_3c_to_numpy(cv::Mat& input);
py::array_t<float> mat_to_py(cv::Mat& input);

#endif // !MAT_WARPER_H_

改下cmakelist文件把这两个文件包含进去。

cmake_minimum_required(VERSION 3.1) 
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
project(test_pybind)
find_package( OpenCV REQUIRED)
include_directories(${OpenCV_INCLUDE_DIRS})
add_subdirectory(pybind11)
SET(SOURCES
        ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/mat_warper.h
        ${CMAKE_CURRENT_SOURCE_DIR}/mat_warper.cpp
        )
pybind11_add_module(test_pybind ${SOURCES})
target_link_libraries( test_pybind PRIVATE ${OpenCV_LIBS} )

main.cpp对接口进行调用:

#include<iostream>
#include<vector>
#include<opencv2/opencv.hpp>
#include<pybind11/pybind11.h>
#include<pybind11/numpy.h>
#include<pybind11/stl.h>
#include"mat_warper.h"

namespace py = pybind11;

py::array_t<unsigned char> test_rgb_to_gray(py::array_t<unsigned char>& input) {

    cv::Mat img_rgb = numpy_uint8_3c_to_cv_mat(input);
    cv::Mat dst;
    cv::cvtColor(img_rgb, dst, cv::COLOR_RGB2GRAY);
    return cv_mat_uint8_1c_to_numpy(dst);

}

py::array_t<unsigned char> test_gray_canny(py::array_t<unsigned char>& input) {
    cv::Mat src = numpy_uint8_1c_to_cv_mat(input);
    cv::Mat dst;
    cv::Canny(src, dst, 30, 60);
    return cv_mat_uint8_1c_to_numpy(dst);
}



py::list normal_feature_estimation(py::array_t<unsigned char>& input) {
    cv::Mat depth = numpy_uint8_1c_to_cv_mat(input);
    cv::Mat nor(depth.size(), CV_32FC3);

    depth.convertTo(depth, CV_32FC1);
//    depth = depth / 1000;
    for (int x = 1; x < depth.cols-1; x++) {
        for (int y = 1; y < depth.rows-1; y++) {
            cv::Vec3f t(x,y-1,depth.at<float>(y-1,x));
            cv::Vec3f l(x-1,y,depth.at<float>(y, x-1));
            cv::Vec3f c(x,y,depth.at<float>(y, x));

            cv::Vec3f d = (l-c).cross(t-c);
            cv::Vec3f n = normalize(d);
            nor.at<cv::Vec3f>(y, x) = n;
//            std::cout<<"normal: "<<n.val[0]<<" "<<n.val[1]<<" "<<n.val[2]<<std::endl;
        }
    }
    std::vector<cv::Mat> channels;
    cv::split(nor, channels);
    py::list res;
    for(int i=0;i<3;i++) {
        res.append<py::array_t<float>>(mat_to_py(channels[i]));
    }
    return res;
}
/*
@return Python list
*/
py::list test_pyramid_image(py::array_t<unsigned char>& input) {
    cv::Mat src = numpy_uint8_1c_to_cv_mat(input);
    std::vector<cv::Mat> dst;

    cv::buildPyramid(src, dst, 4);

    py::list out;
    for (int i = 0; i < dst.size(); i++)
    {
        out.append<py::array_t<unsigned char>>(cv_mat_uint8_1c_to_numpy(dst.at(i)));
    }

    return out;
}

PYBIND11_MODULE(test_pybind, m) {

    m.doc() = "Simple opencv demo";
    m.def("test_rgb_to_gray", &test_rgb_to_gray);
    m.def("test_gray_canny", &test_gray_canny);
    m.def("normal_feature_estimation", &normal_feature_estimation);
    m.def("test_pyramid_image", &test_pyramid_image);

}

常规操作进去build文件夹,cmake… 然后make。接下来就可以通过python文件调用相对应接口了。

  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
实现 Python 调用 C++ 函数并返回 cv::Mat,可以使用以下步骤: 1. 在 C++ 中编写一个函数,该函数接受输入参数并返回 cv::Mat 类型的值。例如: ```c++ #include <opencv2/opencv.hpp> cv::Mat myFunction(cv::Mat inputImage) { // 这里可以进行一些图像处理操作 cv::Mat outputImage = inputImage.clone(); // 这里只是简单地将输入图像复制到输出图像中 return outputImage; } ``` 2. 将 C++ 函数编译为动态链接库(DLL)或共享对象(SO)文件,以便能够从 Python调用该函数。 3. 在 Python使用 ctypes 模块加载 C++ 动态链接库或共享对象,并使用 ctypes 将输入参数传递给 C++ 函数,并将返回值转换为 Python 中的 numpy 数组或 PIL 图像等格式。例如: ```python import numpy as np import ctypes import cv2 # 加载 C++ 动态链接库或共享对象 my_lib = ctypes.cdll.LoadLibrary('./my_lib.so') # 定义 C++ 函数的输入和输出类型 my_lib.myFunction.argtypes = [ctypes.c_void_p] my_lib.myFunction.restype = ctypes.c_void_p # 加载输入图像并将其转换为 cv::Mat 类型 input_image = cv2.imread('input.jpg') input_mat = cv2.cvtColor(input_image, cv2.COLOR_BGR2RGB) input_mat_ptr = ctypes.c_void_p(input_mat.ctypes.data) # 调用 C++ 函数并将返回值转换为 numpy 数组 output_mat_ptr = my_lib.myFunction(input_mat_ptr) output_mat = np.ctypeslib.as_array(output_mat_ptr, shape=input_mat.shape) # 将 cv::Mat 转换为 PIL 图像 output_image = cv2.cvtColor(output_mat, cv2.COLOR_RGB2BGR) pil_image = Image.fromarray(output_image) pil_image.show() ``` 以上就是实现 Python 调用 C++ 函数并返回 cv::Mat 的一般方法。注意,在使用 ctypes 模块进行转换时,需要确保传递给 C++ 函数的指针和返回的指针类型正确,并且需要进行适当的内存管理以避免内存泄漏。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值