一、基本使用方法
二、調用簡單語句
三、調用函數
四、調用類
五、調用SSD目標檢測算法
六、遇到的錯誤
三、調用函數
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/heygears/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/heygears/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/xm/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