python编译安装没有c扩展_pybind11—python C/C++扩展编译

本文介绍了如何利用Python的setuptools和pybind11库,实现C/C++代码编译为Python扩展,以便跨平台使用。通过创建setup.py脚本,设置源码、依赖和编译选项,可以在目标平台编译生成适用于该平台的Python模块。
摘要由CSDN通过智能技术生成

前言

在之前的pybind11系列实践中,开发流程大致是这样的:

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

819e3e8fbe5e

image.png

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

819e3e8fbe5e

image.png

存在的问题

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

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

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

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

开发环境

windows 10 64bit

Anaconda3, with python 3.7

pybind11

C/C++ python扩展的实现

Project1

创建一个简单的工程, 之后创建一个package,取名demo1:

819e3e8fbe5e

image.png

创建如下文件:

__init__.py 创建包默认生成的

example.cpp C++代码

setup.py 用于编译C++代码,生成C/C++ python扩展

test.py 测试

在Visual Studio中测试通过之后,将C/C++源码文件添加的python工程目录中,然后创建一个setup.py文件,编写如下代码:

在Extension中设置C/C++源码文件、第三方库的依赖文件,由于本工程采用pybind11进行C++ 与python接口的封装,因此需要包含pybind11库的头文件,pybind11库是header-only,因此无需包含lib。

setup.py

from setuptools import setup

from setuptools import Extension

example_module = Extension(name='numpy_demo', # 模块名称

sources=['example.cpp'], # 源码

include_dirs=[r'D:\Anaconda3_2\include', # 依赖的第三方库的头文件

r'D:\pybind11-master\include']

)

setup(ext_modules=[example_module])

example.cpp

#include

#include

#include

#include

namespace py = pybind11;

/*

https://blog.csdn.net/u013701860/article/details/86313781

https://blog.csdn.net/u011021773/article/details/83188012

*/

py::array_t calcMul(py::array_t& input1, py::array_t& input2) {

// read inputs arrays buffer_info

py::buffer_info buf1 = input1.request();

py::buffer_info buf2 = input2.request();

if (buf1.size != buf2.size)

{

throw std::runtime_error("Input shapes must match");

}

// allocate the output buffer

py::array_t result = py::array_t(buf1.size);

}

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) << " ";

}

}

}

}

//

//py::array_t rgb_to_gray(py::array_t& a) {

//

// py::array_t dst = py::array_t(a.shape()[0] * a.shape()[1]);

// //指针访问numpy矩阵

// unsigned char* p = (unsigned char*)dst.ptr();

//

// for (int i = 0; i < a.shape()[0]; i++)

// {

// for (int j = 0; j < a.shape()[1]; j++)

// {

// auto var = a.data(i, j);

// auto R = var[0];

// auto G = var[1];

// auto B = var[2];

//

// //RGB to gray

// auto gray = (R * 30 + G * 59 + B * 11 + 50) / 100;

//

// std::cout << static_cast(R) << " " << static_cast(G) << " " << static_cast(B)<< std::endl;

//

// //p[i*a.shape()[1] + j] = static_cast(gray);

//

// }

// }

//}

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);

//m.def("rgb_to_gray", &rgb_to_gray);

}

在pycharm底下,打开终端:

819e3e8fbe5e

image.png

将路径切换到setup.py所在目录,然后执行命令:

819e3e8fbe5e

image.png

生成python扩展库:

819e3e8fbe5e

image.png

819e3e8fbe5e

image.png

最后,会发现工程中增加了一些新的目录和文件,其中xxxx.pyd就是生成的python扩展库。

819e3e8fbe5e

image.png

python扩展库测试

test.py

import demo1.numpy_demo as numpy_demo

import numpy as np

help(numpy_demo)

mat1 = numpy_demo.save_2d_numpy_array(np.zeros(shape=[10,10], dtype=np.float32), r'./data.dat')

print(mat1)

819e3e8fbe5e

image.png

819e3e8fbe5e

image.png

819e3e8fbe5e

image.png

Project2 opencv工程

第一个工程比较简单,没有包含和依赖第三方库,一般C/C++工程中,往往包含和依赖许多第三方库,本工程以opencv库为例,实现C/C++ python扩展模块的编译。

819e3e8fbe5e

image.png

编写setup.py

from setuptools import Extension

from setuptools import setup

__version__ = '0.0.1'

# 扩展模块

ext_module = Extension(

# 模块名称

name='cv_demo1',

# 源码

sources=[r'mat_warper.cpp', r'main.cpp'],

# 包含头文件

include_dirs=[r'D:\Anaconda3_2\include',

r'D:\opencv-4.1.0\opencv\build\include',

r'D:\pybind11-master\include'

],

# 库目录

library_dirs=[r'D:\opencv-4.1.0\opencv\build\x64\vc15\lib'],

# 链接库文件

libraries=[r'opencv_world410'],

language='c++'

)

setup(

name='cv_demo1',

version=__version__,

author_email='xxxx@qq.com',

description='A simaple demo',

ext_modules=[ext_module],

install_requires=['numpy']

)

编译python扩展库:

在终端执行命令

819e3e8fbe5e

image.png

819e3e8fbe5e

image.png

python代码测试:

test.py

import demo2.cv_demo1 as cv_demo

import numpy as np

import cv2

import matplotlib.pyplot as plt

help(cv_demo)

image = cv2.imread(r'F:\lena\lena_gray.jpg', cv2.IMREAD_GRAYSCALE)

# canny

img_canny = cv_demo.test_gray_canny(image)

plt.figure('canny')

plt.imshow(img_canny, cmap=plt.gray())

# pyramid

imgs_pyramid = cv_demo.test_pyramid_image(image)

plt.figure('pyramid')

for i in range(1, len(imgs_pyramid)):

plt.subplot(2, 2, i)

plt.imshow(imgs_pyramid[i])

# rgb to gray

plt.figure('rgb->gray')

img_gray = cv_demo.test_rgb_to_gray(cv2.imread(r'F:\lena\lena_rgb.jpg'))

plt.imshow(img_gray)

plt.show()

python测试结果:

canny边缘检测

819e3e8fbe5e

image.png

Gaussian图像金字塔

819e3e8fbe5e

image.png

图像RGB转Gray

819e3e8fbe5e

image.png

C++源码:

main.cpp

#include

#include

#include

#include

#include

#include

#include"mat_warper.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);

}

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

}

/*

@return Python list

*/

py::list test_pyramid_image(py::array_t& input) {

cv::Mat src = numpy_uint8_1c_to_cv_mat(input);

std::vector<:mat> dst;

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

py::list out;

for (int i = 0; i < dst.size(); i++)

{

out.append<:array_t char>>(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_gray", &test_rgb_to_gray);

m.def("test_gray_canny", &test_gray_canny);

m.def("test_pyramid_image", &test_pyramid_image);

}

mat_warper.h

#ifndef MAT_WARPER_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 // !MAT_WARPER_H_

mat_warper.cpp

#include"mat_warper.h"

#include

/*

Python->C++ Mat

*/

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;

}

//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_1c_to_cv_mat", &numpy_uint8_1c_to_cv_mat);

//

//

//}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值