【PyBind11+anaconda+opencv+windows11+cmake+wsl+vscode】从入门到跑通。构建基于opencv c++的python package

如何安装请详见上文

【PyBind11+anaconda+opencv+windows11+cmake+wsl+vscode】从入门到跑通。构建基于opencv c++的python接口_子韵如初的博客-CSDN博客w不需要安装啥x11 、 xming直接mobxterm可以搞定点击右上角的X server打开可视化服务器然后 apt install x11-apps -yxclock 搞定从mobxterm可以看到display的端口X11显示可视化的原理是就是通过X协议,类似于http对linux于windows上进行通信。那么他们通信的ip地址就是这个DISPLAY=localhost:10.0比如一个XClinet要在屏幕 上输出一个圆,X应用程序只负责...https://blog.csdn.net/weixin_43953700/article/details/123760942计算机视觉-Paper&Code - 知乎V100 is all u needhttps://www.zhihu.com/column/c_1488286320929333249

 

第一步: 首先在C/C++ IDE中编写C/C++函数,然后采用pybind11封装为python可调用的包装函数, 之后采用C/C++编译器编译器生成.pyd文件


image.png

第二步:将生成的.pyd文件复制到python工程中,之后作为python module import导入使用


image.png
 

存在的问题

不同操作系统下直接调用生成的pyd可能会出错,不能跨平台调用

在上述过程中,pyd动态链接库的生成是在本地PC上,但是如果想在不同的操作系统、硬件平台上调用之前生成的pyd,显然是会出错的。比如在windows上编译生成了一个python扩展.pyd, 但是Ubuntu系统想调用这个python扩展就不行了。

解决方案

为了使得C/C++创建的python扩展可以跨平台使用,那么最简单的办法就是直接发布源码, 然后在该操作系统、硬件平台上编译生成python扩展。

因此本节内容利用python setuptools 方式实现

首先配置cmake编译不依赖外部工程链接

ex.cpp


#include
#include
#include
#include

namespace py = pybind11;

class Matrix
{
public:
    Matrix() {};
    Matrix(int rows, int cols) {
        this->m_rows = rows;
        this->m_cols = cols;
        m_data = new float[rows*cols];
    }
    ~Matrix() {};

private:
    int m_rows;
    int m_cols;
    float* m_data;

public:
    float* data() { return m_data; };
    int rows() { return m_rows; };
    int cols() { return m_cols; };

};




void save_2d_numpy_array(py::array_t a, std::string file_name) {

    std::ofstream out;
    out.open(file_name, std::ios::out);
    std::cout << a.ndim() << std::endl;
    for (int i = 0; i < a.ndim(); i++)
    {
        std::cout << a.shape()[i] << std::endl;
    }
    for (int i = 0; i < a.shape()[0]; i++)
    {
        for (int j = 0; j < a.shape()[1]; j++)
        {
            if (j == a.shape()[1]-1)
            {
                //访问读取,索引 numpy.ndarray 中的元素
                out << a.at(i, j)<< std::endl;
            }
            else {
                out << a.at(i, j) << " ";
            }
        }
    }

}


PYBIND11_MODULE(numpy_demo, m) {

    m.doc() = "Simple numpy demo";

    py::class_(m,"Matrix",py::buffer_protocol())
        .def_buffer([](Matrix& mm)->py::buffer_info {
        return py::buffer_info(
            mm.data(),          //Pointer to buffer, 数据指针
            sizeof(float),      //Size of one scalar, 每个元素大小(byte)
            py::format_descriptor::format(), //python struct-style foramt descriptor
            2,                      //Number of dims, 维度
            {mm.rows(), mm.cols()}, //strides (in bytes)
            {sizeof(float) * mm.cols(),sizeof(float)}
        );
    });

    m.def("save_2d_numpy_array", &save_2d_numpy_array);
}

setup.py使用pip安装install,生成numpy_demo.cpython-38-x86_64-linux-gnu.so

python setup.py build_ext --inplace

build_ext: 给python编译一个c、c++的拓展
-–inplace: 忽略build-lib,将编译后的扩展放到源目录中,与纯Python模块放在一起
from setuptools import setup
from setuptools import Extension

example_module = Extension(name='numpy_demo',  # 模块名称
                           sources=['ex.cpp'],    # 源码
                           include_dirs=[r'/root/anaconda3/envs/py3/lib/python3.8/site-packages/pybind11/include']
                           )

setup(ext_modules=[example_module])

# build_ext:build C/C++ extensions (compile/link to build directory),给python编译一个c、c++的拓展
# –inplace:ignore build-lib and put compiled extensions into the source directory alongside your pure Python modules,忽略build-lib,将编译后的扩展放到源目录中,与纯Python模块放在一起
# python setup.py build_ext --inplace

test.py测试

import numpy as np
import numpy_demo 
from numpy_demo import Matrix

res = numpy_demo.save_2d_numpy_array(np.zeros(shape=[4,7], dtype=np.float32), './data.dat')
print(Matrix.__dict__)
print(Matrix)

如果需要引用opencv怎么做

上面c++的拓展,只是使用了pybind11

因此继续编辑c++代码

main.cpp

#include
#include
#include
#include
#include
#include
#include"wrapper.h"

namespace py = pybind11;

py::array_t test_rgb_to_gray(py::array_t& 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);

}

/*
@return Python list
*/
py::list test_pyramid_image(py::array_t& input) {
    cv::Mat src = numpy_uint8_1c_to_cv_mat(input);
    std::vector dst;

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

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

PYBIND11_MODULE(cv_demo1, m) {
    
    m.doc() = "Simple opencv demo";

    m.def("test_rgb_to_gray1", &test_rgb_to_gray);
    m.def("test_gray_canny1", &test_gray_canny);
    m.def("test_pyramid_image1", &test_pyramid_image);

}

wrapper.h

#ifndef WRAPPER_H_

#include
#include
#include

namespace py = pybind11;

cv::Mat numpy_uint8_1c_to_cv_mat(py::array_t& input);

cv::Mat numpy_uint8_3c_to_cv_mat(py::array_t& input);

py::array_t cv_mat_uint8_1c_to_numpy(cv::Mat & input);

py::array_t cv_mat_uint8_3c_to_numpy(cv::Mat & input);

#endif // !WRAPPER_H_

wrapper.​​​​​cpp

#include"wrapper.h"
#include 


cv::Mat numpy_uint8_1c_to_cv_mat(py::array_t& 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& 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 cv_mat_uint8_1c_to_numpy(cv::Mat& input) {

    py::array_t dst = py::array_t({ input.rows,input.cols }, input.data);
    return dst;
}

py::array_t cv_mat_uint8_3c_to_numpy(cv::Mat& input) {

    py::array_t dst = py::array_t({ input.rows,input.cols,3}, input.data);
    return dst;
}

setup.py

其中需要注意的是

  • include_dirs:指定pybind11,和opencv include的路径
  • library_dirs:指定使用的动态库,静态库的路径
  • libraries:指定使用的库名称,原文章是将opencv编译成了opencv_world库的
from setuptools import Extension
from setuptools import setup


__version__ = '0.0.1'

# 扩展模块
ext_module = Extension(
    # 模块名称
    name='cv_demo1',
    # 源码
    sources=[r'wrapper.cpp', r'main.cpp'],
    # 包含头文件
    include_dirs=[r'/usr/local/include',
                  r'/root/anaconda3/envs/py3/lib/python3.8/site-packages/pybind11/include' ],
    # 库目录
    library_dirs=[r'/usr/local/lib'],
    # 链接库文件
    libraries=[r'opencv_core', r'opencv_imgproc'],
    language='c++'
)

setup(
    name='cv_demo1',
    description='A simaple demo',
    ext_modules=[ext_module],
    install_requires=['numpy']
)

python setup.py build_ext --inplace编辑成so文件,编写test.py查看结果

test.py

import cv_demo1 as cv_demo
import numpy as np
import cv2
import matplotlib.pyplot as plt


# help(cv_demo)

image = cv2.imread('./8.jpg', cv2.IMREAD_GRAYSCALE)

# rgb to gray
plt.figure('rgb->gray')
img_gray = cv_demo.test_rgb_to_gray1(cv2.imread('./8.jpg'))
plt.imshow(img_gray)
plt.show()

【PyBind11+anaconda+opencv+windows11+cmake+wsl+vscode】从入门到跑通。构建基于opencv c++的python package_第1张图片

 

总体代码结构如下

【PyBind11+anaconda+opencv+windows11+cmake+wsl+vscode】从入门到跑通。构建基于opencv c++的python package_第2张图片

你可能感兴趣的:(python,计算机视觉,c++,visualstudio,visual,studio,code)