天天看點

PaddleX模型C++部署,python/c++互動(二)PaddleX模型C++部署後Python調用并讀取dll傳回資料前言一、預測結果資料類型C++預定義二、C++預測代碼四、python調用dll總結

PaddleX模型C++部署後Python調用并讀取dll傳回資料

提示:這裡可以添加系列文章的所有文章的目錄,目錄需要自己手動添加

例如:第一章 Python 機器學習入門之pandas的使用

提示:寫完文章後,目錄可以自動生成,如何生成可參考右邊的幫助文檔

文章目錄

  • PaddleX模型C++部署後Python調用并讀取dll傳回資料
  • 前言
  • 一、預測結果資料類型C++預定義
    • 1. 四種模型預測結果内容
    • 2. C++結果類型預定義
  • 二、C++預測代碼
    • 1.分類C++預測代碼
    • 2. 檢測C++預測代碼
    • 3. 語義分割C++預測代碼
    • 4. 執行個體分割C++預測代碼
  • 四、python調用dll
    • 1. python中定義各模型結果類
    • 2. 結果展示
  • 總結

前言

上篇文章講了PaddleX的c++部署方式,本篇講如何如何用python調用生成的dll做推理,python讀取dll傳回的預測結果。分類、檢測、語義分割、執行個體分割的dllpython調用本文都會涉及到。

一、預測結果資料類型C++預定義

1. 四種模型預測結果内容

由于檢測、 語義分割、執行個體分割的結果資料類型較複雜,是以除了int和float其他包含結構體或者數組或者清單的結果類型我都打包成了PyObject傳給python。

分類結果:包含一個category, category id和一個score, 分别為str, int和float

檢測結果:所有檢測目标的category,category ID, score以及bboxx的[x,y,h,w]

語義分割結果:label map和score map

執行個體分割結果:所有執行個體的category id、category、score、bbox、mask

2. C++結果類型預定義

在paddlex.h裡定義結構體

分類結果

typedef struct classi {
  char* cate;
  int cate_id;
  float score;
}Classi;

           

檢測結果都轉為python list, bbox的x, y, h, w拆分為四個list

typedef struct _DETECT {
  PyObject* cate;
  PyObject* cate_id;
  PyObject* score;
  PyObject* xmin;
  PyObject* ymin;
  PyObject* w;
  PyObject* h;
} DETECT;
           

語義分割結果出了label和score之外加入label_size和score_size

typedef struct _SemSeg {
  int label_size;
  int score_size;
  PyObject* label;
  PyObject* score;
}SemSeg;
           

執行個體分割也把bbox結果的x, y, h, w拆成了四個清單,還多加了一個boxes_num也就是執行個體的個數

typedef struct _InsSeg {
  PyObject* cate;
  PyObject* cate_id;
  PyObject* score;
  PyObject* xmin;
  PyObject* ymin;
  PyObject* w;
  PyObject* h;
  PyObject* InsSegMask;
  PyObject* mask_len;
  int boxes_num;
} InsSeg;

           

paddlex.h裡聲明外部函數

PaddleX模型C++部署,python/c++互動(二)PaddleX模型C++部署後Python調用并讀取dll傳回資料前言一、預測結果資料類型C++預定義二、C++預測代碼四、python調用dll總結

二、C++預測代碼

1.分類C++預測代碼

先聲明需要在.cpp裡引入Python.h庫

#include <Python.h>

Classi ClassiImage(int height, int width, uchar* frame_data, char* model_dir_p, int model_dir_len);

Classi ClassiImage(int height,
                   int width,
                   uchar* frame_data,
                   char* model_dir_p,
                   int model_dir_len) {
  int count = 0;
  cv::Mat image(height, width, CV_8UC3);
  uchar* pxvec = image.ptr<uchar>(0);
  for (int row = 0; row < height; row++) {
    pxvec = image.ptr<uchar>(row);
    for (int col = 0; col < width; col++) {
      for (int c = 0; c < 3; c++) {
        pxvec[col * 3 + c] = frame_data[count];
        count++;
      }
    }
  }


  int length = model_dir_len;
  char* aa = new char[length];
  for (int i = 0; i < length; i++) {
    aa[i] = model_dir_p[i * 2];
  }
   std::string model_dir = aa;

  PaddleX::ClsResult result;
  std::string key = "";
  std::string image_list;
  int gpu_id = 0;
  bool use_trt = 0;
  bool use_gpu = 1;
  PaddleX::Model model;
  model.Init(model_dir, use_gpu, use_trt, gpu_id, false);
  model.predict(image, &result);

  Classi ret;
  char CATE;
  strcpy(&CATE, result.category.c_str());
  // ret.cate = NULL;
  ret.cate = &CATE;
  ret.cate_id = result.category_id;
  ret.score = result.score;

  return ret;
}
           

傳入的參數 int height, int width, uchar* frame_data, char* model_dir_p, int model_dir_len分别為預測圖像的高、寬、圖像矩陣位址指針、模型路徑位址指針、模型路徑字元串長度。

下面這段代碼用于還原圖像矩陣,該代碼參考自

添加連結描述

//讀取圖像矩陣
  int count = 0;
  cv::Mat image(height, width, CV_8UC3);
  uchar* pxvec = image.ptr<uchar>(0);
  for (int row = 0; row < height; row++) {
    pxvec = image.ptr<uchar>(row);
    for (int col = 0; col < width; col++) {
      for (int c = 0; c < 3; c++) {
        pxvec[col * 3 + c] = frame_data[count];
        count++;
      }
    }
  }
           

下面這段代碼用于還原模型路徑字元串

int length = model_dir_len;
  char* aa = new char[length];
  for (int i = 0; i < length; i++) {
    aa[i] = model_dir_p[i * 2];
  }
   std::string model_dir = aa;
           

下面這段代碼調用模型預測

PaddleX::ClsResult result;
  std::string key = "";
  std::string image_list;
  int gpu_id = 0;
  bool use_trt = 0;
  bool use_gpu = 1;
  PaddleX::Model model;
  model.Init(model_dir, use_gpu, use_trt, gpu_id, false);
  model.predict(image, &result);
           

以上三段代碼分類、檢測、語義分割、執行個體分割都一樣

下面這段代碼是分類結果的傳回,這裡四種模型不一樣。

Classi ret;
  char CATE;
  strcpy(&CATE, result.category.c_str());
  // ret.cate = NULL;
  ret.cate = &CATE;
  ret.cate_id = result.category_id;
  ret.score = result.score;

  return ret;
           

category字元串通過指針位址賦給ret.cate

cate_id和score分貝時int和float類型直接指派即可,最終傳回ret。

2. 檢測C++預測代碼

圖像矩陣的還原,模型路徑的還原還有結果的預測代碼與分類一緻,下面這段傳回的是檢測結果,比分類複雜一些。我們把category, category id, score, bbox的x, y, h, w分别放在不同的清單中,最終傳回所有結果合起來的結構體ret

std::cout << "result mask data size: " << typeid(result.boxes[1].mask.data[0]).name() << std::endl;
  DETECT ret;
  ret.cate = PyList_New(0);
  for (int i = 0; i < result.boxes.size(); i++) {
    PyList_Append(ret.cate, Py_BuildValue("s", result.boxes[i].category));
  }

  ret.cate_id = PyList_New(0);
  for (int i = 0; i < result.boxes.size(); i++) {
    int cate_id = result.boxes[i].category_id;
    PyList_Append(ret.cate_id, Py_BuildValue("i",result.boxes[i].category_id));
  }

  ret.score = PyList_New(0);
  for (int i = 0; i < result.boxes.size(); i++) {
    //int a = round(result.boxes[i].score * 1000000);
    PyList_Append(ret.score, Py_BuildValue("f", result.boxes[i].score));
  }
  
  ret.xmin = PyList_New(0);
  for (int i = 0; i < result.boxes.size(); i++) {
    PyList_Append(ret.xmin, Py_BuildValue("f", result.boxes[i].coordinate[0]));
  }

  ret.ymin = PyList_New(0);
  for (int i = 0; i < result.boxes.size(); i++) {
    PyList_Append(ret.ymin, Py_BuildValue("f", result.boxes[i].coordinate[1]));
  }

  ret.w = PyList_New(0);
  for (int i = 0; i < result.boxes.size(); i++) {
    PyList_Append(ret.w, Py_BuildValue("f", result.boxes[i].coordinate[2]));
  }
   
  ret.h = PyList_New(0);
  for (int i = 0; i < result.boxes.size(); i++) {
    PyList_Append(ret.h, Py_BuildValue("f", result.boxes[i].coordinate[3]));
  }


  return ret;
           

3. 語義分割C++預測代碼

圖像矩陣的還原,模型路徑的還原還有結果的預測代碼與分類一緻,下面這段傳回的是語義分割結果。我們利用循環從預測結果result中讀取label_map和score_map并寫入清單中,同時也傳回label_size和score_size。

PaddleX::SegResult result;
  std::string model_dir = aa;
  std::string key = "";
  std::string image_list;
  int gpu_id = 0;
  bool use_trt = 0;
  bool use_gpu = 1;
  PaddleX::Model model;
  model.Init(model_dir, use_gpu, use_trt, gpu_id, false);
  model.predict(image, &result);

  SemSeg ret;
  ret.label_size = result.label_map.data.size();
  ret.score_size = result.score_map.data.size();

  ret.label = PyList_New(0);
  std::cout << "type of ret.label" << typeid(ret.label).name()
            << std::endl;
  for (int i = 0; i < result.label_map.data.size(); i++) {
    PyList_Append(ret.label, Py_BuildValue("i", result.label_map.data[i]));
  }
  ret.score = PyList_New(0);
  for (int i = 0; i < result.score_map.data.size(); i++) {

    int a = round(result.score_map.data[i] * 1000000);
    PyList_Append(ret.score, Py_BuildValue("i", a));
  }


  return ret;
           

4. 執行個體分割C++預測代碼

圖像矩陣的還原,模型路徑的還原還有結果的預測代碼與分類一緻,下面這段傳回的是執行個體分割的結果。從預測結果的boxes裡的每個bbox循環讀取category, category id, score, bbox的x, y, h, w以及InsSegMask, mask_len和boxes_num分别寫入不同的PyList中。

InsSeg ret;
  ret.cate = PyList_New(0);
  for (int i = 0; i < result.boxes.size(); i++) {
    PyList_Append(ret.cate, Py_BuildValue("s", result.boxes[i].category));
  }

  ret.cate_id = PyList_New(0);
  for (int i = 0; i < result.boxes.size(); i++) {
    int cate_id = result.boxes[i].category_id;
    PyList_Append(ret.cate_id, Py_BuildValue("i", result.boxes[i].category_id));
  }

  ret.score = PyList_New(0);
  for (int i = 0; i < result.boxes.size(); i++) {
    // int a = round(result.boxes[i].score * 1000000);
    PyList_Append(ret.score, Py_BuildValue("f", result.boxes[i].score));
  }

  ret.xmin = PyList_New(0);
  for (int i = 0; i < result.boxes.size(); i++) {
    PyList_Append(ret.xmin, Py_BuildValue("f", result.boxes[i].coordinate[0]));
  }

  ret.ymin = PyList_New(0);
  for (int i = 0; i < result.boxes.size(); i++) {
    PyList_Append(ret.ymin, Py_BuildValue("f", result.boxes[i].coordinate[1]));
  }

  ret.w = PyList_New(0);
  for (int i = 0; i < result.boxes.size(); i++) {
    PyList_Append(ret.w, Py_BuildValue("f", result.boxes[i].coordinate[2]));
  }

  ret.h = PyList_New(0);
  for (int i = 0; i < result.boxes.size(); i++) {
    PyList_Append(ret.h, Py_BuildValue("f", result.boxes[i].coordinate[3]));
  }

   ret.InsSegMask = PyList_New(0);
   for (int i = 0; i < result.boxes.size(); i++) {
    for (int j = 0; j < result.boxes[i].mask.data.size(); j++) {
        PyList_Append(ret.InsSegMask, Py_BuildValue("i",
        result.boxes[i].mask.data[j]));
      //std::cout << result.boxes[j].mask.data[j] << std::endl;
    }
  }

  ret.mask_len = PyList_New(0);
  for (int i = 0; i < result.boxes.size(); i++) {
    PyList_Append(ret.mask_len, Py_BuildValue("i", result.boxes[i].mask.data.size()));
  }

  ret.boxes_num = result.boxes.size();
  return ret;
           

四、python調用dll

1. python中定義各模型結果類

class InsSegResult(Structure):
    _fields_ = [
        ("cate", py_object),
        ("cate_id", py_object),
        ("score", py_object),
        ("xmin", py_object),
        ("ymin", py_object),
        ("w", py_object),
        ("h", py_object),
        ("InsSegMask", py_object),
        ("mask_len", py_object),
        ("boxes_num", c_int)
    ]

class ClassiResult(Structure):
    _fields_ = [
        ("cate", c_wchar_p),
        ("cate_id", c_int),
        ("score", c_float)
    ]

class SemSegResult(Structure):
    _fields_ = [
        ("label_size", c_int),
        ("score_size", c_int),
        ("label", py_object),
        ("score", py_object)
    ]

class DetResult(Structure):
    _fields_ = [
        ("cate", py_object),
        ("cate_id", py_object),
        ("score", py_object),
        ("xmin", py_object),
        ("ymin", py_object),
        ("w", py_object),
        ("h", py_object)
    ]
           

2. 結果展示

下圖是我用python調用dll推理出的語義分割模型結果,模型為UNet,python讀取dll傳回的結果成功

PaddleX模型C++部署,python/c++互動(二)PaddleX模型C++部署後Python調用并讀取dll傳回資料前言一、預測結果資料類型C++預定義二、C++預測代碼四、python調用dll總結

下面展示部分UNet畫出的結果

PaddleX模型C++部署,python/c++互動(二)PaddleX模型C++部署後Python調用并讀取dll傳回資料前言一、預測結果資料類型C++預定義二、C++預測代碼四、python調用dll總結
PaddleX模型C++部署,python/c++互動(二)PaddleX模型C++部署後Python調用并讀取dll傳回資料前言一、預測結果資料類型C++預定義二、C++預測代碼四、python調用dll總結

總結

提示:這裡對文章進行總結:

例如:以上就是今天要講的内容,本文僅僅簡單介紹了pandas的使用,而pandas提供了大量能使我們快速便捷地處理資料的函數和方法。