前言
C++作为一种编译型语言,在其设计之初就偏重于性能、效率和灵活性,偏向应用于系统编程、嵌入式、资源受限的软件和系统。Python作为一种解释型语言,内置了如str, tuple, list, dict等常用数据结构,支持自动垃圾回收,拥有简洁的语法、丰富的内置库和第三方库,被越来越广泛地使用在各种场景中。但Python在高便捷性的同时无可避免的缺乏高性能。
在部分应用场景中,我们需要在Python的灵活性上架构应用,底层算法希望借助C++的高性能, 那么我们可以考虑将C++开发的模块做成Python的bindings供Python调用。
而pybind11则是C++和Python两种语言之间的一座桥梁。Pybind11是一个轻量级只包含头文件的库,用于C++和Python之间的接口转换,可以为C++代码创建Python bindings。Pybind11通过C++编译时的自省来推断类型信息,来最大程度地减少传统拓展 Python 模块时繁杂的样板代码, 已实现STL数据结构、智能指针、类、函数重载、实例方法等到Python的映射转换,其中函数可以接收和返回自定义数据类型的值、指针或引用。
步骤
以基于OpenCV和CUDA实现的GPU版本导向滤波算法为例,演示Pybind11的应用。
代码地址:https://github.com/TracelessLe/pybind11_guidedfilter_cuda
-
创建工程目录
pybind11_guidedfilter_cuda
和C++代码
其中核心代码guidedfilter.cpp
和guidedfilter.h
来自Github用户acstacey的项目“GLFCV - Light field disparity estimation using a guided filter cost volume”。部分代码一瞥:
在guidedfilter.cpp
前面加入下述代码#include <pybind11/pybind11.h> #include "ndarray_converter.h" namespace py = pybind11;
在末尾加上下述代码:
PYBIND11_MODULE(gfcuda, m) { NDArrayConverter::init_numpy(); m.def("guidedFilter", &guidedFilter, "cv::cuda::guidedFilter", py::arg("guide"), py::arg("src"), py::arg("radius"), py::arg("eps"), py::arg("dDepth")); }
目的是通过pybind11针对此cpp生成一个名为gfcuda的python binding,通过Python下的
gfcuda.guidedFilter()
的方法指向的C++函数"cv::cuda::guidedFilter"
。 -
拷贝Pybind11工程到工程目录
pybind11_guidedfilter_cuda
下
Pybind11 CSDN资源链接:https://download.csdn.net/download/TracelessLe/13204469
Pybind11 GitHub链接:https://github.com/pybind/pybind11
将下载下来的包进行解压,解压后的pybind11文件夹拷贝到工程目录pybind11_guidedfilter_cuda
下。 -
拷贝
ndarray_converter.cpp
和ndarray_converter.h
【此项目需要】
由于涉及到numpy数组转换,拷贝相应.cpp和.h文件。
其中ndarray_converter.cpp
和ndarray_converter.h
来自Github用户mfedoseeva的项目“roboy-activity-recognition”。 -
编写CMakeLists.txt
CMakeLists.txt是C++项目的编译链接设置文件,在本项目中,需要用到的外部依赖有OpenCV和CUDA,所以需要根据你自己的路径去设置。另外需要注意添加pybind11_add_module(gfcuda ${SOURCES})
等用于生成binding的语句。总体设置如下:#Change this if you need to target a specific CMake version cmake_minimum_required(VERSION 2.8) # Enable C++11 set(CMAKE_CXX_STANDARD 11) # Set up project project(guided_filter_cuda) add_subdirectory(pybind11) SET(SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/guidedfilter.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ndarray_converter.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ndarray_converter.h ) pybind11_add_module(gfcuda ${SOURCES}) # Detect and add OpenCV set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}" ${CMAKE_MODULE_PATH}) find_package(OpenCV REQUIRED HINTS /usr/local/share/opencv4) find_package(CUDA 10.2 REQUIRED) # Define sources and executable set(EXECUTABLE_NAME "gfcuda") # Link against OpenCV include_directories(${OpenCV_INCLUDE_DIR}) target_link_libraries(${EXECUTABLE_NAME} PRIVATE ${OpenCV_LIBS}) # Link against CUDA include_directories(${CUDA_INCLUDE_DIRS}) target_link_libraries(${EXECUTABLE_NAME} PRIVATE ${CUDA_LIBRARIES})
特别注意设置OpenCV和CUDA库的路径:
target_link_libraries(${EXECUTABLE_NAME} PRIVATE ${OpenCV_LIBS}) target_link_libraries(${EXECUTABLE_NAME} PRIVATE ${CUDA_LIBRARIES})
-
编译
cmake . make
编译完成后会生成一个600K左右的.so文件如“
gfcuda.cpython-37m-x86_64-linux-gnu.so
”,将这个.so文件拷贝到你的工程目录即可通过import gfcuda
使用。
-
在Python中使用
import cv2 import gfcuda img = cv2.imread('cat.png') guide = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) final_img = gfcuda.guidedFilter(guide=guide, src=img, radius=5, eps=50*50, dDepth=-1) cv2.imwrite('out.png', final_img)
cat.png
out.png示例代码
test_gfcuda.py
已经提供在工程目录下。
特别说明
① 感谢Github用户acstacey,项目“GLFCV - Light field disparity estimation using a guided filter cost volume”提供代码guidedfilter.cpp
和guidedfilter.h
;
②感谢Github用户mfedoseeva,项目“roboy-activity-recognition”提供代码ndarray_converter.cpp
和ndarray_converter.h
。
③关于更多Pybind11对C++和Python的数据类型映射关系可以参考文末【参考资料】一栏。
参考资料
[1] pybind11 docs “pybind11— Seamless operability between C++11 and Python”
[2] GitHub - acstacey / GLFCV - Light field disparity estimation using a guided filter cost volume
[3] GitHub - mfedoseeva / roboy-activity-recognition
[4] GitHub - atilimcetin / Guided filter for OpenCV
[5] 知乎 - python调用c++利器–pybind11
[6] 知乎 - pybind11: C++ 工程如何提供 Python 接口
[7] 知乎 - 191123 使用 Pybind11 和 OpenCV 创建 Python 库
[8] GitHub - How OpenCV-Python Bindings Works?
[9] Docs » 1. OpenCV简介 » 1.7. OpenCV Python绑定
[10] Python Bindings: Calling C or C++ From Python
[11] GitHub - TracelessLe / pybind11_guidedfilter_cuda
[12] 知乎 - pybind11的最佳实践
[13] cnblogs - 基于pybind11实现Python调用c++编写的CV算法–下 (Linux+Cmake)