参考这篇文章如何实现 C/C++ 与 Python 的通信?分别测试了python官网方法和Cython方法。目前只测通了python官网方法,且只是一个小例子,自己的项目还没测试。如果有结果将继续更新。
下面将开始记录我的踩坑过程。
先说明,这个方法不是完全失败,编译连接和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 初始化还是没有问题的。
因为着急,所以这部分先搁置了,等以后有时间再解决吧。
这里倒是成功了,不过是我自己写的测试程序成功了。移植到项目上预测还会有其他问题。先说怎么解决。后面再更新。
其实就是参考如何实现 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