相关文章推荐
个性的饼干  ·  实用的 Python 之 ...·  1 月前    · 
霸气的花卷  ·  python list 错位相减 ...·  1 周前    · 
憨厚的大脸猫  ·  python ...·  1 周前    · 
小猫猫  ·  python tornado ...·  2 天前    · 
会开车的钱包  ·  用sql ...·  1 年前    · 
爽快的大熊猫  ·  nginx配置端口转发 ...·  1 年前    · 
苦闷的鞭炮  ·  Mariadb Galera ...·  1 年前    · 

【码记录】在Ubuntu16.04使用pybind11+cmake实现python调用C++运行ncnn模型

“之前在Windows系统下成功实现过python调用C++运行ncnn模型,但在Windows下编译C++生成动态链接库可以借助VS2019进行,而在Ubuntu系统下编译的工具没有这么“大容量集成化并且安装好就能用” ,在Ubuntu系统下编译C++可以使用g++或者cmake,在ncnn的QQ群里询问过意见后,选择使用cmake进行编译。”

总体流程如下:

1、编译安装ncnn

2、编译pybind11

3、编写ncnn调用运行程序(C++)

4、使用cmake编译上述程序为.so的动态库

5、使用python调用.so动态库

1、CmakeLists.txt编写简述

CmakeLists.txt是使用cmake编译时参考的一个类似清单的文件,其中定义了生成文件是什么、编译需要链接哪些库、需要从哪里找头文件以及编译时输出哪些信息等等。

我对cmake的了解只有一点点,于是将使用到的关键方法进行一下记录。

1)定义项目名称

project(demo)

2)定义变量

set(ncnn_DIR "/home/ljy/ncnn725/build/install/lib/cmake/ncnn")

变量在CmakeLists.txt里定义后,也可以在执行编译命令时手动传入。

值得注意的是,cmake里变量名称的定义并不是自由的,首先,cmake中存在很多预先定义的变量,其次,对于涉及了某些包名的变量,比如OpenCV_LIBS,其实是代表着OpenCV这个包的库文件名这一个属性,如果重新定义这个变量将会覆盖原有的值,而ncnn_DIR这个变量则为后续查找ncnn这个包提供了路径,具体的包属性以及变量名称之间的关系目前我还没有找到官方文档中的解释说明。

3)查找包

find_package(ncnn)

查找包其实是找一个.cmake文件,比如上面这句话寻找的是ncnnConfig.cmake这个文件,包含ncnn这个包的一些配置信息。

4)定义包含目录

include_directories(
    ${CMAKE_CURRENT_SOURCE_DIR}
    ${CMAKE_CURRENT_SOURCE_DIR}/include
)

包含目录将决定源文件以及头文件中include去什么地方找文件,如果找不到就会报错。

5)定义链接目录

link_directories(
    /usr/local/lib
)

链接目录决定当前编译的文件去什么路径寻找需要链接的库。

6)定义编译输出文件

add_executable(module main.cpp func.cpp)
add_library(module STATIC main.cpp func.cpp)
add_library(module SHARED main.cpp func.cpp)

定义输出文件的类型(分别是可执行文件、静态库、动态库),以及包含了哪些源文件,在Ubuntu下三种类型输出的文件分别为:

module

module.a

module.so

7)定义链接

target_link_libraries(module
    ${OpenCV_LIBS}
)

定义编译后输出的文件将链接哪些库。

参考:

CMakeLists.txt 语法介绍与实例演练_阿飞的博客-CSDN博客_cmakelists.txt 教程

CMake官方文档: Documentation | CMake

2、使用pybind的CmakeLists.txt编写方法

由于pybind11并没有生成库,所以不通过链接的方式链接pybind11和自定义的module,而是将pybind11添加为自定义module的子模块(将编译完成的pybind11放入自定义module的目录下),再进行编译,此时在自定义module的CmakeLists.txt中将使用pybind11的方法来定义输出的库。

# define pybind11 as a submodule
add_subdirectory(pybind11)
# define the output library facequality
pybind11_add_module(module main.cpp func.cpp)
# link required libraries
target_link_libraries(module PRIVATE
    ${OpenCV_LIBS}
    ${ncnn_LIBS}
    ${PYTHON_LIBRARIES}
)

在链接库这一步,需要加上选项PRIVATE,否则编译会报错,大概原因是重复定义了module这个模块,具体原理没有深究。

在此附上本次实现过程中编写的完整CmakeLists.txt文件。

cmake_minimum_required(VERSION 3.5.1)
set( CMAKE_CXX_FLAGS "-std=c++11" )
set(CMAKE_BUILD_TYPE "Release")
project(demo)
# define variables
set(PYTHON_EXECUTABLE "/usr/bin/python3.5")
set(PYTHON_INCLUDE_DIR "/usr/include/python3.5")
set(PYTHON_LIBRARIES "/usr/lib/x86_64-linux-gnu/libpython3.5m.so")
set(ncnn_LIBS ncnn;SPIRV;glslang;OGLCompiler;OSDependent)
set(Vulkan_LIBRARY "/home/z/vulkansdk1.2.182.0/x86_64/lib/libvulkan.so")
set(Vulkan_INCLUDE_DIR "/home/z/vulkansdk1.2.182.0/x86_64/include")
set(ncnn_DIR "/home/z/ncnn/build/install/lib/cmake/ncnn")
# define where to find libraries
link_directories(
    /usr/local/lib
    /home/z/ncnn/build/install/lib
find_package(OpenCV REQUIRED)
find_package(ncnn REQUIRED)
find_package(PythonLibs REQUIRED)
#define where to find include files
include_directories(
    ${CMAKE_CURRENT_SOURCE_DIR}
    ${CMAKE_CURRENT_SOURCE_DIR}/include
    /usr/local/include/opencv4
    /usr/local/include
    /home/z/ncnn725/build/install/include/ncnn
    /home/z/vulkansdk1.2.182.0/x86_64/include
    ${PYTHON_INCLUDE_DIR}
# add_executable(quality main.cpp face_quality.cpp)
# define pybind11 as a submodule
add_subdirectory(pybind11)
# define the output library facequality
pybind11_add_module(qualityvalid face_quality.cpp mat_warper.cpp)
# link required libraries
target_link_libraries(qualityvalid PRIVATE
    ${OpenCV_LIBS}
    ${ncnn_LIBS}
    ${PYTHON_LIBRARIES}
)

3、编译以及调用流程

需要环境:cmake、vulkan、protobuf、ncnn、pybind11、python

1)在c++代码中定义pybind11模块

PYBIND11_MODULE(module, m) {
    //an example function
    m.def("function1_py", &function1_cpp);
    //function with python parameter and default value
    m.def("function2_py", &function2_cpp, "some description", py::arg("param1")=1, py::arg("param2")=2);
    //class define
    py::class_<CppClass>(m, "PythonClass")
        .def(py::init([](int param) { return new CppClass(param); })) //construct function
        .def("class_func", &CppClass::class_func) // member function
        .def_readonly("variable1", &CppClass::variable1);// member const