C++调用python(二)
三、调用函数
3.1 无参
-CMakeLists.txt
cmake_minimum_required(VERSION 3.9)
project(say_hello)
set(SDK_VERSION 0_0_1)
# >>> build type
set(CMAKE_BUILD_TYPE "Release") # 指定生成的版本
set(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g2 -ggdb")
set(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall")
# <<<
# >>> CXX11
set(CMAKE_CXX_STANDARD 11) # C++ 11 编译器
SET(CMAKE_CXX_STANDARD_REQUIRED TRUE)
# <<<
# >>> Python3
set(PYTHON_ROOT "/home/zjh/anaconda3/envs/learn")
message("python root: " ${PYTHON_ROOT})
include_directories(${PYTHON_ROOT}/include/)
link_directories(${PYTHON_ROOT}/lib/)
# <<<
# --- generate ---
add_executable(say_hello hello.cpp)
target_link_libraries(say_hello -lpython3.6m)
- hello.py
def say():
print("hello")
if __name__ == "__main__":
say()
- hello.cpp
#include <python3.6m/Python.h>
#include <iostream>
int main(int argc, char *argv[])
{
wchar_t *program = Py_DecodeLocale(argv[0], nullptr);
if ( program == nullptr ){
std::cout << "Fatal Error: cannot decode argv[0]!" << std::endl;
return -1;
}
Py_SetProgramName(program);
Py_SetPythonHome((wchar_t*)L"/home/zjh/anaconda3/envs/learn");
Py_Initialize();
if ( !Py_IsInitialized() ){
std::cout << "Python init failed!" << std::endl;
return 0;
}
PyRun_SimpleString("import sys");
PyRun_SimpleString("sys.path.append('/media/zjh/code/C++_call_python/test/test_hello')");
PyObject* pModule = PyImport_ImportModule("hello");
if ( pModule == nullptr ){
std::cout << "module not found!" << std::endl;
return 1;
}
PyObject* pFunc = PyObject_GetAttrString(pModule, "say");
if ( !pFunc || !PyCallable_Check(pFunc) ){
std::cout << "not found function add_num!" << std::endl;
return 2;
}
PyObject_CallObject(pFunc, nullptr);
if ( Py_FinalizeEx() < 0 ){
exit(120);
}
PyMem_RawFree(program);
return 0;
}
3.2 有参
-
CMakeLists.txt
在3.1的基础上修改开始的program 和最后generate
部分就行。 -
add.py
def add_num(a, b):
print("the result {} + {} is {}".format(a, b, a+b))
return a + b
if __name__ == "__main__":
add_num(1, 2)
- add.cpp
#include <python3.6m/Python.h>
#include <iostream>
int main(int argc, char *argv[])
{
wchar_t *program = Py_DecodeLocale(argv[0], nullptr);
if ( program == nullptr ){
std::cout << "Fatal Error: cannot decode argv[0]!" << std::endl;
return -1;
}
Py_SetProgramName(program);
Py_Initialize();
if ( !Py_IsInitialized() ){
std::cout << "Python init failed!" << std::endl;
return 0;
}
PyRun_SimpleString("import sys");
PyRun_SimpleString("sys.path.append('/home/work/C++_python/test/test_add')");
PyObject* pModule = PyImport_ImportModule("add");
if ( pModule == nullptr ){
std::cout << "module not found!" << std::endl;
return 1;
}
PyObject* pFunc = PyObject_GetAttrString(pModule, "add_num");
if ( !pFunc || !PyCallable_Check(pFunc) ){
std::cout << "not found function add_num!" << std::endl;
return 2;
}
PyObject* args = Py_BuildValue("(ii)", 28, 103);
PyObject* pRet = PyObject_CallObject(pFunc, args);
Py_DECREF(args);
int res = 0;
PyArg_Parse(pRet, "i", &res);
Py_DECREF(pRet);
std::cout << "the res is: " << res << std::endl;
if ( Py_FinalizeEx() < 0 ){
exit(120);
}
PyMem_RawFree(program);
return 0;
}
四、调用类
-
CMakeLists.txt
-
test_class.py
class Test(object):
def __init__(self):
self.i = 1
print("init!")
def modify(self):
self.i += 1
def do(self):
print(self.i)
if __name__ == "__main__":
test_class = Test()
test_class.do()
test_class.modify()
test_class.do()
- testClass.cpp
#include <python3.6m/Python.h>
#include <iostream>
int main(int argc, char *argv[])
{
wchar_t *program = Py_DecodeLocale(argv[0], nullptr);
if ( program == nullptr ){
std::cout << "Fatal Error: cannot decode argv[0]!" << std::endl;
return -1;
}
Py_SetProgramName(program);
Py_Initialize();
if ( !Py_IsInitialized() ){
std::cout << "Python init failed!" << std::endl;
return 0;
}
PyRun_SimpleString("import sys");
PyRun_SimpleString("sys.path.append('/home/work/C++_python/test/test_class')");
// 1. 导入模块
PyObject* pModule = PyImport_ImportModule("test_class");
if ( pModule == nullptr ){
std::cout << "module not found!" << std::endl;
return 1;
}
PyObject* pTestDict = PyModule_GetDict(pModule);
// 2. 导入模块中方法或类
PyObject* pTestClass = PyDict_GetItemString(pTestDict, "Test");
// 3. 创建实例
PyObject* pTestInstance = nullptr;
if ( PyCallable_Check(pTestClass) ){
pTestInstance = PyObject_CallObject(pTestClass, nullptr);
}
// 4. 调用类方法
PyObject_CallMethod(pTestInstance, "do", nullptr);
PyObject_CallMethod(pTestInstance, "modify", nullptr);
PyObject_CallMethod(pTestInstance, "do", nullptr);
if ( Py_FinalizeEx() < 0 ){
exit(120);
}
PyMem_RawFree(program);
return 0;
}
注:如果类函数有参数,可以参照4.2中方法
五、调用SSD目标检测算法
参考C/C++调用Python [OpenCV与Numpy] 和 Windows下C++调用Python版的Pytorch模型
这里以调用SSD模型为例
- CMakeLists.txt
由于需要用到Numpy,所以在CMakeLists.txt中先引入Numpy的头文件,可以参考一、基本使用方法
另外还需要用到OpenCV库,在CMakeLists.txt后面添加如下代码,target_link_libraries
也需要追加${OpenCV_LIBS}
库。
# >>> opencv
set(OpenCV_DIR "/usr/local")
message(STATUS ${OpenCV_DIR})
find_package(OpenCV REQUIRED)
if(OpenCV_FOUND)
include_directories(${OpenCV_DIR}/include/opencv4/opencv2)
include_directories( ${OpenCV_INCLUDE_DIRS})
link_directories(${OpenCV_DIR}/lib)
message(STATUS "Configure package with OpenCV!")
set(HAVE_OpenCV True)
else()
set(HAVE_OpenCV False)
message(STATUS "Configure package without OpenCV!")
endif()
# <<<
- inferencePb.py
# -*- coding : utf-8 -*-
# @File : inferencePb.py
# Desctiption: MobileNetV2SSDlite
import os
import cv2
import time
import numpy as np
import tensorflow as tf
#os.environ["CUDA_VISIBLE_DEVICES"]="-1"
###############################################################################
#- 定义识别函数
def arrayreset(array):
a = array[:, 0:len(array[0] - 2):3]
b = array[:, 1:len(array[0] - 2):3]
c = array[:, 2:len(array[0] - 2):3]
a = a[:, :, None]
b = b[:, :, None]
c = c[:, :, None]
m = np.concatenate((a, b, c), axis=2)
return m
def recognize(src_image):
"""
MobileNetV2-SSDLite
:param src_image: 输入视频流或图像
:param pb_file_path: the model file path
:return:
"""
with tf.Graph().as_default():
output_graph_def = tf.GraphDef()
#---------------------------------------
# 打开 .pb 模型
pb_file = "ssd300_pascal_07+12_epoch-86_loss-1.2568_val_loss-0.5428.pb"
with open(pb_file, "rb") as f:
output_graph_def.ParseFromString(f.read())
tensors = tf.import_graph_def(output_graph_def, name="")
print("tensors:",tensors)
with tf.Session() as sess:
# init = tf.global_variables_initializer()
# sess.run(init)
#---------------------------------------
# 打开图中所有的操作
op = sess.graph.get_operations()
for i,m in enumerate(op):
print('op{}:'.format(i),m.values())
#---------------------------------------
# 模型的的输入和输出名称
#--------------------------------------
# 遍历某目录下的图像
input_x = sess.graph.get_tensor_by_name("input_1:0")
#print("input_X:",input_x)
output_tensor = sess.graph.get_tensor_by_name("ssd_decoded_predictions/loop_over_batch/TensorArrayStack/TensorArrayGatherV3:0")
#print("Output:",output_tensor)
#--------------------------------------
# 计算时间, 持续加载同一张图像
# src_image = arrayreset(src_image)
src_image = cv2.imread(src_image)
org_img = src_image.copy()
img=cv2.resize(src_image,(300,300))
img=img.astype(np.float32)
y_pred = sess.run([output_tensor], feed_dict={input_x:np.reshape(img,(1,300,300,3))})
confidence_threshold = 0.8
y_pred_array = np.array(y_pred[0])
y_pred_thresh = [y_pred_array[k][y_pred_array[k,:,1] > confidence_threshold] for k in range(y_pred_array.shape[0])]
classes = ['background', 'tank']
image_size = (300, 300, 3)
for box in y_pred_thresh[0]:
xmin = box[2] * org_img.shape[1] / image_size[0]
ymin = box[3] * org_img.shape[0] / image_size[1]
xmax = box[4] * org_img.shape[1] / image_size[1]
ymax = box[5] * org_img.shape[0] / image_size[0]
label = '{}: {:.2f}'.format(classes[int(box[0])], box[1])
print("label", label)
def main():
src_image = "002394.jpg"
pb_file = "ssd300_pascal_07+12_epoch-86_loss-1.2568_val_loss-0.5428.pb"
recognize(src_image)
if __name__ == '__main__':
main()
- TargetDetection.cpp
/******************************************************************************
* @file : TargetDetection.cpp
* @Desctiption: c++ 调用 SSD inferencePb.py 模块
*
*****************************************************************************/
#include <Python.h>
#include <iostream>
#include <cstring>
#include <opencv2/opencv.hpp>
#include <numpy/arrayobject.h>
#include <time.h>
static void help()
{
std::cout << std::endl;
std::cout << "This sample demostrates MobileNet-V2-SSDLite detection with tensorflow server inference." << std::endl;
std::cout << "Call" << std::endl;
}
int main(int argc, char* argv[])
{
if (argc != 2)
{
help();
}
Py_Initialize(); // 初始化 Python 环境
if (!Py_IsInitialized())
{
std::cout << "init faild ..." << std::endl;
}
import_array(); // 初始化numpy
// 如果查找函数文件一直是 nullptr 则加上下面两行路径
PyRun_SimpleString("import sys");
PyRun_SimpleString("sys.path.append('/home/project_ssd/build/')");
PyObject* pModule = nullptr; //.py文件
pModule = PyImport_ImportModule("inferencePb"); //调用上述路径下的inferencePb.py文件
if (pModule == nullptr)
{
std::cout << "don't find the python file!" << std::endl;
return -1;
}
clock_t start, end;
for (int i=0;i<100;i++)
{
start = clock();
// 这里用视频流替换传入的图像参数
std::string image = argv[1];
cv::Mat img = cv::imread(image);
if (img.empty())
{
std::cout << "could not load image ..." << std::endl;
return -1;
}
int m, n;
n = img.cols *3;
m = img.rows;
unsigned char *data = (unsigned char*)malloc(sizeof(unsigned char) * m * n);
int p = 0;
for (int i = 0; i < m;i++)
{
for (int j = 0; j < n; j++)
{
data[p]= img.at<unsigned char>(i, j);
p++;
}
}
npy_intp Dims[2] = { m,n }; //图像的维度信息
PyObject* PyArray = nullptr;
PyArray = PyArray_SimpleNewFromData(2, Dims, NPY_UINT8, data); //建立函数的形参
PyObject* ArgArray =nullptr;
ArgArray = PyTuple_New(1); //新建长度为1的元组
PyTuple_SetItem(ArgArray, 0, PyArray); //设置元组ArgArray[0]为PyArray图像
PyObject* pFunc = nullptr; //py文件中的函数
pFunc = PyObject_GetAttrString(pModule,"recognize");
if (pFunc==nullptr)
{
std::cout << "can't find function recognize ... " << std::endl;
return -1;
}
PyObject* pReturnValue;
pReturnValue = PyObject_CallObject(pFunc, ArgArray);
// 从结果中得到结果,将结果画在图像上
if (pReturnValue)
{
int list_len = PyObject_Size(pReturnValue);
std::cout << list_len << std::endl;
std::cout << "++++++++++++++++++++++++" << std::endl; // 返回 list 长度,表示检测到目标的个数
PyArrayObject *pyResultArr = (PyArrayObject *) pReturnValue;
float *resDataArr = (float *) PyArray_DATA(pyResultArr);
//int dimNum = PyArray_NDIM(pyResultArr);//返回数组的维度数,此处恒为1
//std::cout << dimNum << std::endl;
npy_intp *pdim = PyArray_DIMS(pyResultArr);//返回数组各维度上的元素个数值
//std::cout << pdim << "+++++++++++++++" << pdim[1] << "+++++++++" << std::endl;
for (int i = 0; i < list_len; ++i)
{
for (int j = 0; j < pdim[1]; ++j)
{
std::cout << resDataArr[i*pdim[1] + j] << ", ";
}
std::cout << std::endl;
}
}
end = clock();
std::cout << "Time is" << (double) (end-start) / 1000000 << std::endl;
}
// 这张图是用来传入 下面 Python 的算法中的一张图像,需要提前处理,
// 功能实现之后,该功能替换为海康相机 SDK 的视频流
std::cout << "Finish ..." << std::endl;
Py_Finalize(); // 释放 python 环境
return 0;
}
六、遇到的错误
6.1 Error: ModuleNotFoundError: No module named 'encodings'
Could not find platform independent libraries <prefix>
Could not find platform dependent libraries <exec_prefix>
Consider setting $PYTHONHOME to <prefix>[:<exec_prefix>]
Fatal Python error: Py_Initialize: Unable to get the locale encoding
ModuleNotFoundError: No module named 'encodings'
Current thread 0x00007ff4011626c0 (most recent call first):
Aborted (core dumped)
解决办法
如果采用anaconda3的base
环境就可以正常运行。我采用的是虚拟环境,在初始化之前需加入Py_SetPythonHome
函数,如:
Py_Initialize();
变为:
Py_SetPythonHome((wchar_t*)L"/home/zjh/anaconda3/envs/learn");
Py_Initialize();
6.2 numpy Warning:
Warning "Using deprecated Numpy API, disable it with "
解决办法
在cpp文件最开始加入
#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
参考链接:
https://docs.python.org/2/extending/embedding.html
https://zhuanlan.zhihu.com/p/79896193
https://blog.csdn.net/ziweipolaris/article/details/83689597
https://blog.csdn.net/u011681952/article/details/92765549
https://blog.csdn.net/hnlylyb/article/details/89498651