【飛槳開發者說】:餘志良,菲特(天津)智能科技有限公司項目經理,百度黃埔學院二期學員
今年3月、4月我分別發佈了兩篇文章:《如何用PaddleDetection做一個完整的目標檢測項目》以及《飛槳與PyQt的碰撞,圖形化目標檢測So Easy》,爲大家講解了從模型訓練到模型部署的全過程,其中模型部署基於Paddle預測庫的Python接口。本篇將教大家通過PaddleDetection編譯C++預測庫,並將其封裝成dll,實現PaddleDetection在Windows環境下的模型部署。
本文內容主要包括:
如何進行C++預測的編譯(生成.sln解決方案)
如何將C++預測代碼封裝成一個dll
如何使用Python調用生成好的dll
如何使用C#調用生成好的dll
如何進行C++預測代碼的編譯
(生成.sln解決方案)
使用工具Cmake vs2019社區版 Git(提前下載好git,不然在後期編譯過程中會不成功)
預測代碼來源:
https://github.com/PaddlePaddle/PaddleDetection/tree/master(注意選擇master分支)
依賴庫:
OpenCV:選擇3.4.6版本
https://sourceforge.net/projects/opencvlibrary/files/3.4.6/opencv-3.4.6-vc14_vc15.exe/download
Paddle預測庫:選擇win10下的cuda10版本。
https://www.paddlepaddle.org.cn/documentation/docs/zh/advanced_guide/inference_deployment/inference/windows_cpp_inference.html
一、首先將上述需要依賴的OpenCV和預測庫,PaddleDetection下載好,並保存在某個文件夾中。如下圖是我存放的一個文件夾。
二、將OpenCV添加到環境變量裏面,如下圖所示。
三、利用Cmake軟件進行編譯。源碼路徑爲cpp文件所在目錄,因爲裏面有CMakeLists.txt文件,同時在該目錄下創建了新的文件夾/out用於生成編譯後的文件。
四、點擊Configure,選擇vs2019 X64選項後,點擊Generate。
五、根據報錯進行修改,主要修改cuda_lib、OpenCV、paddle_dir路徑。
六、再次點擊Generate。
七、點擊Open Project,同時我們在out文件夾下發現了生成了解決方案。
八、針對“main”項目進行“重新生成”。同時一定要將模式配置成爲Release。
九、出現報錯。
十、修改報錯---根據報錯,應該是編譯過程中,cudnn.lib尋找不正確。通過修改路徑實現。方式爲點擊屬性--連接器--輸入--附加依賴項。
十一、改正上述錯誤後的正確的形式。
十二、重新生成,如下圖是重新生成後的結果。
十三、運行該生成的main.exe文件。方式爲打開out/release文件,會發現有一個main.exe文件。利用命令行打開後,運行即可。運行過程中需要添加模型的路徑以及預測圖像的路徑。
注意:此時此刻進行預測的模型是按照《如何使用PaddleDetection實現完整的項目》中export.py文件形式導出“__model__”和“__params__”的形式,但是在後續paddle的升級過程中,export.py文件不僅僅會導出“__model__”和“__params__”,還會導出一個infer_cfg.yml的文件。在這個過程中,是使用PaddleDetection-release0.3版本進行導出的,因爲之前安裝的paddle版本是1.7的,而目前的master分支是必須使用paddle2.0版本的。因此小夥伴們需要注意這個問題。
如下圖是所示的模型保存後的結果形式,依舊使用水果檢測的模型進行預測。
其中yml文件內容
十四、預測結果如下圖所示,我們看到輸出的結果中有檢測框的座標、置信度、類別信息。
如下是在out文件夾生成的命名爲“output.jpeg”的檢測後圖像
十五、對代碼進行一下小修改。之前我們需要在命令行中輸入圖像和模型的路徑,在代碼中添加圖像和模型的路徑,查看預測情況。修改src/main.cpp這個文件如下:
十六、修改上述兩處代碼後,設置“設爲啓動項目”,並點擊“本地windows調試器”
,直接查看輸出的結果。
至此,完成了第一步以及第一步的所有測試。
如何將C++預測代碼
封裝成一個dll
一、 我們需要修改CMakeLists.txt文件,修改倒數第十三行,將
add_executable(main src/main.cc src/preprocess_op.cc src/object_detector.cc) 變成
ADD_library(main SHARED src/main.cc src/preprocess_op.cc src/object_detector.cc)
二、按照上文繼續重新cmake一次。
三、再次經歷之前的cudnn的路徑問題,同時需要在屬性—常規—配置類型中修改成.dll文件。
四、修改完上述內容後,點擊“重新生成”,會發現out/release中出現了main.dll文件。
五、看到dll後,我們彷彿看到了曙光,但是還是需要進行修改,因爲這樣我們才能被調用,我簡化了main.cc文件中的內容,並且配置了dll的接口,代碼如下。其中有兩個dll的接口,其中“add”是爲了測試。
#include <glog/logging.h>
#include <iostream>
#include <string>
#include <vector>
#include "include/object_detector.h"
extern "C" __declspec(dllexport) void Loadmodel();
extern "C" __declspec(dllexport) int add(int a, int b);
void PredictImage(const std::string& image_path,
PaddleDetection::ObjectDetector* det);
int add(int a, int b) {
return a + b;
}
void Loadmodel() {
std::string model_dir = "D:\\0524\\test\\model";
std::string image_path = "D:\\0524\\test\\orange_71.jpg";
std::string video_path = "";
std::string run_mode = "fluid";
bool use_gpu = true;
// Load model and create a object detector
PaddleDetection::ObjectDetector det(model_dir, use_gpu, run_mode);
PredictImage(image_path, &det);
}
void PredictImage(const std::string& image_path,
PaddleDetection::ObjectDetector* det) {
// Open input image as an opencv cv::Mat object
cv::Mat im = cv::imread(image_path, 1);
// Store all detected result
std::vector<PaddleDetection::ObjectResult> result;
det->Predict(im, &result);
for (const auto& item : result) {
printf("class=%d confidence=%.2f rect=[%d %d %d %d]\n",
item.class_id,
item.confidence,
item.rect[0],
item.rect[1],
item.rect[2],
item.rect[3]);
}
// Visualization result
auto labels = det->GetLabelList();
auto colormap = PaddleDetection::GenerateColorMap(labels.size());
cv::Mat vis_img = PaddleDetection::VisualizeResult(
im, result, labels, colormap);
std::vector<int> compression_params;
compression_params.push_back(CV_IMWRITE_JPEG_QUALITY);
compression_params.push_back(95);
cv::imwrite("output.jpeg", vis_img, compression_params);
printf("Visualized output saved as output.jpeg\n");
}
六、繼續點擊“重新生成”,重新生成的dll就是下述步驟中我們即將調用的dll。
如何使用Python調用
生成好的dll
在上一章節我們說了如何生成dll,這一章節,我們需要進行測試,在這裏我們使用Python進行測試,利用Python調用dll。在生成的dll的文件中創建一個叫mian.py的Python文件。Python部分代碼如下:
from ctypes import *
dll=CDLL("main.dll")
print(dll.add(1,2))
print(dll.Loadmodel())
運行Python代碼,可以看到最終輸出結果如下圖:
至此說明利用Python調用dll成功了。
如何使用C#調用生成好的dll
在上一個章節中我們說了如何使用Python調用dll,接着,我們嘗試使用C#調用一個dll,此方式爲工業上經常使用的一種方式。
一、首先創建一個C#的窗體應用程序。
二、在改窗體應用程序中設置一個button事件。
三、設置dll接口代碼以及設置button,相關代碼如下
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
int a = 3;
int b = 2;
public Form1()
{
InitializeComponent();
}
[DllImport("main.dll", EntryPoint = "Loadmodel", CharSet = CharSet.Ansi)]
public static extern void Loadmodel();
[DllImport("main.dll", EntryPoint = "add", CharSet = CharSet.Ansi)]
public static extern int add(int a, int b);
private void button1_Click(object sender, EventArgs e)
{
int c = add(a, b);
Loadmodel();
}
private void Form1_Load(object sender, EventArgs e)
{
}
}
}
四、將C++ release路徑下生成的文件全部複製到C#項目運行目錄下,在C++的release文件中,有一些文件只有dll,沒有對應的lib文件,這個時候,我們需要搜索到這些dll對應的lib文件,全部放在C#的運行目錄下。(PS#這些對應的文件都在我們下載的paddle預測庫中可以找到)
五、點擊“啓動按鈕”進行測試;
六、我們在C#的運行目錄下發現了生成了一張output.jpeg圖片,證明我們調用成功
至此,該系列文章基本上完成了從訓練到部署的所有流程,十分感謝在寫作過程中飛槳同學的幫助,非常感謝高松鶴、梁鈺同學的大力幫助。後續會根據飛槳針對C++預測的更新,繼續更新完善該文章。
更多資源
更多PaddleDetection的應用方法,歡迎訪問項目地址:
GitHub:
https://github.com/PaddlePaddle/PaddleDetection
Gitee:
https://gitee.com/paddlepaddle/PaddleDetection
如在使用過程中有問題,可加入飛槳官方QQ羣進行交流:703252161。
如果您想詳細瞭解更多飛槳的相關內容,請參閱以下文檔。
官網地址:
https://www.paddlepaddle.org.cn
飛槳開源框架項目地址:
GitHub:
https://github.com/PaddlePaddle/Paddle
Gitee:
https://gitee.com/paddlepaddle/Paddle
END