Ubuntu下C++调python

文章目录

  • 前言
  • 一、Cython的失败过程
  • 二、python官方方法


前言

参考这篇文章如何实现 C/C++ 与 Python 的通信?分别测试了python官网方法和Cython方法。目前只测通了python官网方法,且只是一个小例子,自己的项目还没测试。如果有结果将继续更新。

下面将开始记录我的踩坑过程。


一、Cython的失败过程

先说明,这个方法不是完全失败,编译连接和python初始化还是成功了。就是运行时异常中断。换个代码可能会成功吧。感兴趣的可以尝试。

由于我的python项目有些复杂(39 directories, 165 files),如下

Yolov5_DeepSort_Pytorch-master
├── deep_sort
│   ├── configs
│   │   ├── deep_sort.yaml
│   │   ├── yolov3_tiny.yaml
│   │   └── yolov3.yaml
│   ├── deep_sort
│   │   ├── deep
│   │   │   ├── checkpoint
│   │   │   │   ├── ckpt.t7
│   │   │   │   └── original_ckpt.t7
│   │   │   ├── evaluate.py
│   │   │   ├── feature_extractor.py
│   │   │   ├── __init__.py
│   │   │   ├── model.py
│   │   │   ├── original_model.py
│   │   │   ├── __pycache__
│   │   │   │   ├── feature_extractor.cpython-38.pyc
│   │   │   │   ├── __init__.cpython-38.pyc
│   │   │   │   └── model.cpython-38.pyc
│   │   │   ├── test.py
│   │   │   ├── train.jpg
│   │   │   └── train.py
│   │   ├── deep_sort.py
│   │   ├── __init__.py
│   │   ├── __pycache__
│   │   │   ├── deep_sort.cpython-38.pyc
│   │   │   └── __init__.cpython-38.pyc
│   │   ├── README.md
│   │   └── sort
│   │       ├── detection.py
│   │       ├── __init__.py
│   │       ├── iou_matching.py
│   │       ├── kalman_filter.py
│   │       ├── linear_assignment.py
│   │       ├── nn_matching.py
│   │       ├── preprocessing.py
│   │       ├── __pycache__
│   │       │   ├── detection.cpython-38.pyc
│   │       │   ├── __init__.cpython-38.pyc
│   │       │   ├── iou_matching.cpython-38.pyc
│   │       │   ├── kalman_filter.cpython-38.pyc
│   │       │   ├── linear_assignment.cpython-38.pyc
│   │       │   ├── nn_matching.cpython-38.pyc
│   │       │   ├── preprocessing.cpython-38.pyc
│   │       │   ├── track.cpython-38.pyc
│   │       │   └── tracker.cpython-38.pyc
│   │       ├── tracker.py
│   │       └── track.py
│   ├── demo
│   │   ├── 1.jpg
│   │   ├── 2.jpg
│   │   └── demo.gif
│   ├── detector
│   │   ├── __init__.py
│   │   └── YOLOv3
│   │       ├── cfg
│   │       │   ├── coco.data
│   │       │   ├── coco.names
│   │       │   ├── darknet19_448.cfg
│   │       │   ├── tiny-yolo.cfg
│   │       │   ├── tiny-yolo-voc.cfg
│   │       │   ├── voc.data
│   │       │   ├── voc_gaotie.data
│   │       │   ├── voc.names
│   │       │   ├── yolo.cfg
│   │       │   ├── yolo_v3.cfg
│   │       │   ├── yolov3-tiny.cfg
│   │       │   └── yolo-voc.cfg
│   │       ├── cfg.py
│   │       ├── darknet.py
│   │       ├── demo
│   │       │   ├── 004545.jpg
│   │       │   └── results
│   │       │       └── 004545.jpg
│   │       ├── detector.py
│   │       ├── detect.py
│   │       ├── __init__.py
│   │       ├── nms
│   │       │   ├── build.sh
│   │       │   ├── ext
│   │       │   │   ├── build.py
│   │       │   │   ├── cpu
│   │       │   │   │   ├── nms_cpu.cpp
│   │       │   │   │   └── vision.h
│   │       │   │   ├── cuda
│   │       │   │   │   ├── nms.cu
│   │       │   │   │   └── vision.h
│   │       │   │   ├── __init__.py
│   │       │   │   ├── nms.h
│   │       │   │   └── vision.cpp
│   │       │   ├── __init__.py
│   │       │   ├── nms.py
│   │       │   └── python_nms.py
│   │       ├── README.md
│   │       ├── region_layer.py
│   │       ├── weight
│   │       ├── yolo_layer.py
│   │       └── yolo_utils.py
│   ├── __init__.py
│   ├── LICENSE
│   ├── ped_det_server.py
│   ├── __pycache__
│   │   └── __init__.cpython-38.pyc
│   ├── README.md
│   ├── scripts
│   │   ├── yolov3_deepsort.sh
│   │   └── yolov3_tiny_deepsort.sh
│   ├── utils
│   │   ├── asserts.py
│   │   ├── draw.py
│   │   ├── evaluation.py
│   │   ├── __init__.py
│   │   ├── io.py
│   │   ├── json_logger.py
│   │   ├── log.py
│   │   ├── parser.py
│   │   ├── __pycache__
│   │   │   ├── __init__.cpython-38.pyc
│   │   │   ├── parser.cpython-38.pyc
│   │   │   ├── pyxhook.cpython-38.pyc
│   │   │   └── register.cpython-38.pyc
│   │   ├── pyxhook.py
│   │   ├── register.py
│   │   └── tools.py
│   ├── webserver
│   │   ├── config
│   │   │   └── config.py
│   │   ├── images
│   │   │   ├── arc.png
│   │   │   ├── request.png
│   │   │   └── Thumbs.db
│   │   ├── __init__.py
│   │   ├── readme.md
│   │   ├── rtsp_threaded_tracker.py
│   │   ├── rtsp_webserver.py
│   │   ├── server_cfg.py
│   │   └── templates
│   │       └── index.html
│   ├── yolov3_deepsort_eval.py
│   └── yolov3_deepsort.py
├── inference
│   └── output
├── media
│   ├── Crazy.mp4
│   ├── demo2.avi
│   ├── demo.avi
│   └── test.flv
├── README.md
├── requirements.txt
├── setup.py
├── track.py
├── track.pyx
└── yolov5
    ├── detect.py
    ├── Dockerfile
    ├── hubconf.py
    ├── __init__.py
    ├── LICENSE
    ├── models
    │   ├── common.py
    │   ├── experimental.py
    │   ├── export.py
    │   ├── hub
    │   │   ├── yolov3-spp.yaml
    │   │   ├── yolov5-fpn.yaml
    │   │   └── yolov5-panet.yaml
    │   ├── __init__.py
    │   ├── __pycache__
    │   │   ├── common.cpython-38.pyc
    │   │   ├── experimental.cpython-38.pyc
    │   │   ├── __init__.cpython-38.pyc
    │   │   └── yolo.cpython-38.pyc
    │   ├── yolo.py
    │   ├── yolov5l.yaml
    │   ├── yolov5m.yaml
    │   ├── yolov5s.yaml
    │   └── yolov5x.yaml
    ├── __pycache__
    │   └── __init__.cpython-38.pyc
    ├── README.md
    ├── requirements.txt
    ├── test.py
    ├── train.py
    ├── tutorial.ipynb
    ├── utils
    │   ├── activations.py
    │   ├── datasets.py
    │   ├── evolve.sh
    │   ├── general.py
    │   ├── google_utils.py
    │   ├── __init__.py
    │   ├── __pycache__
    │   │   ├── datasets.cpython-38.pyc
    │   │   ├── general.cpython-38.pyc
    │   │   ├── google_utils.cpython-38.pyc
    │   │   ├── __init__.cpython-38.pyc
    │   │   └── torch_utils.cpython-38.pyc
    │   └── torch_utils.py
    └── weights
        ├── download_weights.sh
        ├── yolov5l.pt
        ├── yolov5m.pt
        ├── yolov5s.pt
        └── yolov5x.pt

39 directories, 165 files

(项目目录结构这么复杂,滑鼠标都费劲。emmm)
我首先考虑的就是用Cython先生成.so文件。为什么是.so呢,一是可以部署是加密打包,二是.so中就包含了复杂的链接关系(我最开始是这么理解的)。

首先就是要用Cython生成.so文件了。

要先在根目录下创建setup.py文件,然后进入对应的虚拟环境执行python setup.py。参考使用Cython将py编译成.so文件

import sys, os, shutil, time
from distutils.core import setup
from Cython.Build import cythonize

start_time = time.time()
curr_dir = os.path.abspath('.')
parent_path = sys.argv[1] if len(sys.argv) > 1 else ""
setup_file = __file__.replace('/', '\\')
build_dir = "build"
build_tmp_dir = build_dir + "/temp"

s = "# cython: language_level=3"


def get_py(base_path=os.path.abspath('.'), parent_path='', name = '', excepts=(), copyOther=False, delC = False):
    """
    获取py文件的路径
    :param base_path: 根路径
    :param parent_path: 父路径
    :param excepts: 排除文件
    :return: py文件的迭代器
    """
    full_path = os.path.join(base_path, parent_path, name)
    for filename in os.listdir(full_path):
        full_filename = os.path.join(full_path, filename)
        if os.path.isdir(full_filename) and filename != build_dir and not filename.startswith('.'):
            for f in get_py(base_path, os.path.join(parent_path, name), filename, excepts, copyOther, delC):
                yield f
        elif os.path.isfile(full_filename):
            ext = os.path.splitext(filename)[1]
            if ext == ".c":
                if delC and os.stat(full_filename).st_mtime > start_time:
                    os.remove(full_filename)
            elif full_filename not in excepts and os.path.splitext(filename)[1] not in ('.pyc', '.pyx'):
                if os.path.splitext(filename)[1] in ('.py', '.pyx') and not filename.startswith('__'):
                    path = os.path.join(parent_path, name, filename)
                    yield path
        else:
            pass


def pack_pyd():
    # 获取py列表
    module_list = list(get_py(base_path=curr_dir, parent_path=parent_path, excepts=(setup_file,)))
    try:
        setup(
            ext_modules=cythonize(module_list, compiler_directives={'language_level': "3"}),
            script_args=["build_ext", "-b", build_dir, "-t", build_tmp_dir],
        )
    except Exception as ex:
        print("error! ", str(ex))
    else:
        module_list = list(get_py(base_path=curr_dir, parent_path=parent_path, excepts=(setup_file,), copyOther=True))

    module_list = list(get_py(base_path=curr_dir, parent_path=parent_path, excepts=(setup_file,), delC=True))
    if os.path.exists(build_tmp_dir):
        shutil.rmtree(build_tmp_dir)

    print("complate! time:", time.time() - start_time, 's')


def delete_c(path='.', excepts=(setup_file,)):
    '''
    删除编译过程中生成的.c文件
    :param path:
    :param excepts:
    :return:
    '''
    dirs = os.listdir(path)
    for dir in dirs:
        new_dir = os.path.join(path, dir)
        if os.path.isfile(new_dir):
            ext = os.path.splitext(new_dir)[1]
            if ext == '.c':
                os.remove(new_dir)
        elif os.path.isdir(new_dir):
            delete_c(new_dir)


if __name__ == '__main__':
    try:
        pack_pyd()
    except Exception as e:
        print(str(e))
    finally:
        delete_c()

然后来到C/C++工程,编写CMakeList文件。其中我的C文件就叫test.c,我的工程名是c_python

cmake_minimum_required(VERSION 3.0.0)
project(c_python_test VERSION 0.1.0)

if(CMAKE_COMPILER_IS_GNUCC)
    message("COMPILER IS GNUCC")
        ADD_DEFINITIONS ( -std=c++11 )
endif(CMAKE_COMPILER_IS_GNUCC)

#SET(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -ggdb3")
SET(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall")

# 添加头文件路径
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
include_directories(/home/dreamdeck/anaconda3/envs/track/include/python3.8) # 虚拟环境python头文件

# 添加源文件
#FILE(GLOB_RECURSE SOURCE_CPP ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
#FILE(GLOB_RECURSE SOURCE_C ${CMAKE_CURRENT_SOURCE_DIR}/*.c)
#message(${CMAKE_CURRENT_SOURCE_DIR})

# 添加链接库
LINK_DIRECTORIES(/home/dreamdeck/anaconda3/envs/track/lib)  #虚拟环境中python库的文件夹
LINK_DIRECTORIES(/media/dreamdeck/d/MJJ/code/track/c_python/libs)
LINK_LIBRARIES(python3.8)
LINK_LIBRARIES(track)

# 添加要编译的可执行文件
add_executable(${PROJECT_NAME} test.c track.c)

# 隐式链接库文件
# target_link_libraries(${PROJECT_NAME} python3.8)
#target_link_libraries(${PROJECT_NAME} track.cpython-38-x86_64-linux-gnu.so)

# 开启调试
SET(CMAKE_CXX_FLAGS  "${CMAKE_CXX_FLAGS} -g")
message($(CMAKE_CXX_FLAGS))

test.c文件如下

#include 
#include "track.h"

int main(int argc, char *argv[]) {
    printf("hello world.");
    Py_Initialize();

    //判断初始化是否成功
    if(!Py_IsInitialized())
    {
        printf("Python init failed!\n");
        return -1;
    }
    PyInit_track();     // Python文件文件名(模块名),我的是track.py文件
	track_interface();  //函数接口名
    Py_Finalize();
    return 0;
}

上面的track.h文件哪来的?
回到python项目中,终端进入虚拟环境,改写主文件track.pyx,其中在接口函数track_interface开头加上几个字cdef public

...
cdef public track_interface():
...

接下来在终端中执行

cython track.pyx -3

这会生成对应的.h和.c文件,将之拷贝到C项目中。
但是我也不知道为什么我的执行过程中track.c文件会异常中断。

整个项目的编译链接 以及 python 初始化还是没有问题的。
因为着急,所以这部分先搁置了,等以后有时间再解决吧。

二、python官方方法

这里倒是成功了,不过是我自己写的测试程序成功了。移植到项目上预测还会有其他问题。先说怎么解决。后面再更新。

其实就是参考如何实现 C/C++ 与 Python 的通信?。值得注意的是,如果你按照链接文的代码去做,可能会报错。主要还是看你的python版本。python2.X的没问题,如果是python3就用本文的代码。

CMakeList文件要更新一下,但也不用改太多,就是把对应的不用的动态库,源文件删掉。

CMakeLists.txt

cmake_minimum_required(VERSION 3.0.0)
project(c_python_test VERSION 0.1.0)

if(CMAKE_COMPILER_IS_GNUCC)
    message("COMPILER IS GNUCC")
        ADD_DEFINITIONS ( -std=c++11 )
endif(CMAKE_COMPILER_IS_GNUCC)

#SET(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -ggdb3")
SET(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall")

# 添加头文件路径
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
include_directories(/home/dreamdeck/anaconda3/envs/track/include/python3.8) # 虚拟环境python头文件

# 添加链接库
LINK_DIRECTORIES(/home/dreamdeck/anaconda3/envs/track/lib)  #虚拟环境中python库的文件夹
LINK_LIBRARIES(python3.8)

# 添加要编译的可执行文件
add_executable(${PROJECT_NAME} test.c )

# 隐式链接库文件
# target_link_libraries(${PROJECT_NAME} python3.8)
#target_link_libraries(${PROJECT_NAME} track.cpython-38-x86_64-linux-gnu.so)

# 开启调试
SET(CMAKE_CXX_FLAGS  "${CMAKE_CXX_FLAGS} -g")
message($(CMAKE_CXX_FLAGS))

编写python文件:great_module.py

def great_function(a):
    print("hello python")
    return a + 1

编写.c文件:test.c

#include 
#include "track.h"

int great_function_from_python(int a) {
    int res;
    PyObject *pModule,*pFunc;
    PyObject *pArgs, *pValue;

    PyRun_SimpleString("import sys");
    PyRun_SimpleString("sys.path.append('./')");//若python文件在c++工程下
    
    /* import */
    PyRun_SimpleString("import sys");
    pModule = PyImport_ImportModule("great_module");
    if (!pModule) {
        printf("Can not open python file!\n");
        return -1;
    }

    /* great_module.great_function */
    pFunc = PyObject_GetAttrString(pModule, "great_function"); 
    
    /* build args */
    pArgs = PyTuple_New(1);
    PyTuple_SET_ITEM(pArgs,0, PyLong_FromLong(a));
      
    /* call */
    pValue = PyObject_CallObject(pFunc, pArgs);
    
    res = PyLong_AsLong(pValue);
    return res;
}

int main(int argc, char *argv[]) {
    Py_Initialize();
    printf("%d",great_function_from_python(2));
    Py_Finalize();
}

输出结果:

hello python
3

你可能感兴趣的:(python,C++,c++,cmake,python)