C++調用python(二)



一、基本使用方法
二、調用簡單語句
三、調用函數
四、調用類
五、調用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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM