基于上篇文章部署三,将详细介绍Python 和 C# 调用Dll传参 实现PaddleClas的预测部署
项目地址:Windows: PaddleClas 预测部署(四)
上篇文章主要介绍了:如何生成 exe可执行文件进行预测,如何生成dll动态链接库Python/C#调用预测(图片路径是再生成dll前规定好的,并不能实现从外界传参调用)
接下来主要介绍传参进来实现更便捷的dll调用
准备:
往下进行前,需要已经完成文章部署三的cmake编译工作
注意生成dll前,需要把deploy/cpp_infer/CMakeLists.txt文件内的
add_executable(${DEMO_NAME} ${SRCS})
改为
ADD_library(${DEMO_NAME} SHARED ${SRCS})
否则会生成exe文件
![](https://i-blog.csdnimg.cn/blog_migrate/41aa116a9458b4cabfafa7f168bd7e8d.png)
能够按照文章部署三1.4章节成功编译,并打开:
![](https://i-blog.csdnimg.cn/blog_migrate/3b73d84bd8d024328f738cb540524390.png)
‘
一,Python调用DLL 进行预测(对DLL 传输字符串)
1.1更改main.cpp
传输前,需要在编译生成的C++项目中更改main.cpp,(如果忘记如何更改请查看文章部署三):
主要是将预测分为了 加载模型 和 图片预测 两个函数用于分别调用,这样也可以缩短我们的预测时间
main.cpp代码具体如下:
#include "opencv2/core.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/imgproc.hpp"
#include <chrono>
#include <iomanip>
#include <iostream>
#include <opencv2/core/utils/filesystem.hpp>
#include <ostream>
#include <vector>
#include <cstring>
#include <fstream>
#include <numeric>
#include <include/cls.h>
#include <include/cls_config.h>
using namespace std;
using namespace cv;
using namespace PaddleClas;
static PaddleClas::Classifier *clas; //定义全局对象
extern "C" __declspec(dllexport) int LoadModel(char* cfig); //表示python可以调用该dll
extern "C" __declspec(dllexport) int Infer(char* jpg); //表示python可以调用该dll
int LoadModel(char* cfig) { //加载模型
ClsConfig config(cfig);
config.PrintConfigInfo();
clas = new PaddleClas::Classifier(config.cls_model_path, config.cls_params_path,
config.use_gpu, config.gpu_id, config.gpu_mem,
config.cpu_math_library_num_threads, config.use_mkldnn,
config.use_tensorrt, config.use_fp16,
config.resize_short_size, config.crop_size);
return 0;
}
int Infer(char* jpg) { //预测图片
std::string path(jpg);
std::vector<std::string> img_files_list;
if (cv::utils::fs::isDirectory(path)) {
std::vector<cv::String> filenames;
cv::glob(path, filenames);
for (auto f : filenames) {
img_files_list.push_back(f);
}
}
else {
img_files_list.push_back(path);
}
std::cout << "img_file_list length: " << img_files_list.size() << std::endl;
double elapsed_time = 0.0;
int warmup_iter = img_files_list.size() > 5 ? 5 : 0;
for (int idx = 0; idx < img_files_list.size(); ++idx) {
std::string img_path = img_files_list[idx];
cv::Mat srcimg = cv::imread(img_path, cv::IMREAD_COLOR);
cv::cvtColor(srcimg, srcimg, cv::COLOR_BGR2RGB);
double run_time = clas->Run(srcimg);
if (idx >= warmup_iter) {
elapsed_time += run_time;
std::cout << "Current image path: " << img_path << std::endl;
std::cout << "Current time cost: " << run_time << " s, "
<< "average time cost in all: "
<< elapsed_time / (idx + 1 - warmup_iter) << " s." << std::endl;
}
else {
std::cout << "Current time cost: " << run_time << " s." << std::endl;
}
}
return 0;
}
![](https://i-blog.csdnimg.cn/blog_migrate/82f508690db520240ddb86de19349e26.png)
完成后重新生成 DLL文件即可
1.2 更改Python
C++部分更改完成后,开始配置python用于调用DLL
Python传输DLL字符串,需要使用ctypes对字符串进行转换,因为python里面没有string类型 ,代码主要为下:
from ctypes import *
import ctypes
dll=CDLL("G:\\projects\\PaddleClas-release-2.0\\deploy\\cpp_infer\\out\\Release\\clas_system.dll")
config=bytes("G:/projects/PaddleClas-release-2.0/deploy/cpp_infer/tools/config.txt","utf-8")
jpg=bytes('E:/PaddleClas-release-2.0/demo/cat_9.jpg',"utf-8")
LoadModel = dll.LoadModel(config) #加载模型
infer = dll.Infer(jpg) #预测图片
1.3导入关联DLL
如果出现以下错误,提示找不到DLL文件:(如果没有出现请忽略)
![](https://i-blog.csdnimg.cn/blog_migrate/317527f9641279fc7dc180fc2d7f92cd.png)
原因在于生成的DLL再调用其他DLL文件,但是该文件DLL没有找到,可以将预测库内paddle_inference_install_dir\paddle\lib\
paddle_fluid.dll 文件复制到 生成DLL的Release目录内:
![](https://i-blog.csdnimg.cn/blog_migrate/498763a0b613a8f28d47b25a553785c5.png)
1.3 更改config.txt配置
调用DLL会为其传 config.txt 和 预测图片 的路径 ,在传输前需要对 config.txt的内容进行更改:
deploy\cpp_infer\tools\config.txt内容如下:
# model load config
use_gpu 1 #使用gpu
gpu_id 0
gpu_mem 2000 #4000 #gpu内存,根据自己gpu内存来
cpu_math_library_num_threads 10
use_mkldnn 1
use_tensorrt 0
use_fp16 0
# cls config
cls_model_path G:\projects\PaddleClas-release-2.0\output\ResNet50_vd\best_model\mp\inference.pdmodel #inference 模型文件路径
cls_params_path G:\projects\PaddleClas-release-2.0\output\ResNet50_vd\best_model\mp\inference.pdiparams #inference 权重文件路径
resize_short_size 256
crop_size 224
inference文件再部署一四章节中有说明和导出
![](https://i-blog.csdnimg.cn/blog_migrate/91940585071b59abdad1546ad8f0f7b5.png)
1.4 配置准备就绪后运行python
![](https://i-blog.csdnimg.cn/blog_migrate/18cc472bff994e6b36a3b1499b0e7508.png)
成功预测(此次使用的模型是再本机训练的,轮数不高只做示例,若提高效果可以尝试使用预训练模型训练后的模型,具体内容请查看PaddleClasGitHub)
调用DLL,还有一点,config和jpg路径千万不要写错,否则会出Windows Error -529697949错误
调用DLL,还有一点,config和jpg路径千万不要写错,否则会出Windows Error -529697949错误
调用DLL,还有一点,config和jpg路径千万不要写错,否则会出Windows Error -529697949错误,重要的事情说三篇
二、C#调用dll
C#调用DLL前,需要创建一个控制台应用,如果有问题可以参考部署三2.6章节
2.1 C#导入DLL
控制台应用创建完成后,需要将我们之前的DLL以及关联的DLL都导入到C#内(关联DLL也包括预测库内的paddle_fluid.dll)
关联DLL从目录PaddleClas-release-2.0\deploy\cpp_infer\out\Release
复制到C#项目 ConsoleApp1\bin\Debug\netcoreapp3.1
目录内
![](https://i-blog.csdnimg.cn/blog_migrate/f0ba5b9936f925d080c69b76c4cd727e.png)
2.2 打开C#项目,录入代码如下:
using System;
using System.Runtime.InteropServices;
namespace ConsoleApp1
{
class Program
{
[DllImport("clas_system.dll", EntryPoint = "LoadModel", CharSet = CharSet.Ansi)]
public static extern void LoadModel(string config);//,string jpg
[DllImport("clas_system.dll", EntryPoint = "Infer", CharSet = CharSet.Ansi)]
public static extern void Infer(string jpg);//,string jpg
static void Main()
{
string config = "G:/projects/PaddleClas-release-2.0/deploy/cpp_infer/tools/config.txt";
string jpg = "E:/PaddleClas-release-2.0/demo/cat_9.jpg";
LoadModel(config);
Infer(jpg);
Console.Write("Press any key to continue . . . ");
Console.ReadKey(true);
}
}
}
![](https://i-blog.csdnimg.cn/blog_migrate/2bd83b4a4588f07086648d2ff7d6f9ce.png)
2.3 运行:
可以F5运行程序,或者在目录ConsoleApp1\bin\Debug\netcoreapp3.1
内运行程序 ConsoleApp1.exe即可
![](https://i-blog.csdnimg.cn/blog_migrate/b780ff3a309a92ef012326baf58fe1bb.png)
预测程序没有设置返回值,预测结果即显示在控制台内,在此基础上我们还可以添加更多便于预测的方法,比如在控制台内输入图片路径,或者一批图片等等,再或者创建窗体(需要修改c++返回值),更多有趣的功能可以自己尝试~
还有一点,一定要确定 文件目录 是存在的,要不报错会很迷糊