本样例以Atlas 推理系列产品为例,通过mxVision图像分类案例,介绍如何使用mxVision流程编排方式开发推理应用。案例使用ResNet-50模型对图片进行分类并最后输出分类结果。
ResNet-50模型的基本介绍如下:
软件依赖名称 |
推荐版本 |
获取链接 |
---|---|---|
系统依赖 |
- |
|
CANN开发套件包 |
7.0.0 |
CANN获取链接。 |
npu-driver驱动包 |
23.0.0 |
请参见版本配套表,根据实际硬件设备型号选择配套的驱动、固件。 请参见各硬件产品中驱动和固件安装升级指南获取对应的指导。 |
npu-firmware固件包 |
23.0.0 |
请访问获取链接,获取样例代码压缩包。
unzip pipelineSample.zip cd pipelineSample
样例代码目录结构参考如下。样例代码目录结构参考如下,其中om模型仅供示例,用户可通过模型转换功能(ATC)转换为om模型进行推理。
pipelineSample ├── data │ ├── dog1_1024_683.jpg // 测试图片 ├── models //存放模型目录 │ ├── resnet50_tensorflow_1.7.om //om模型文件 │ ├── resnet50_aipp_tf.cfg //模型配置文件 │ ├── resnet50_clsidx_to_labels.names //模型输出类别名称文件 ├── pipeline // 存放pipeline文件 │ ├── Sample.pipeline // pipeline文件 ├── src │ ├── CMakeLists.txt // CMakeLists文件 │ ├── main.cpp // 主函数,图片分类功能的实现文件 │ ├── run.sh // 编译脚本
用户需使用自行获取的图片进行测试(请将获取的图片名称更名为与样例代码的图片名字一致,如dog1_1024_683.jpg),以下图片为展示用途。
编排pipeline文件是使用mxVision开发应用最核心的任务,图像分类应用可拆解为一系列的业务流程,通过编辑pipeline文件,调用mxVision插件库完成推理业务,本文的pipeline文件内容以图2中所示的业务流程进行样例配置编排。
样例如下所示。
{ "classification": { // 修改"classification" 为当前业务推理流程的名称 "stream_config": { "deviceId": "0" // "deviceId" 表示要使用的芯片的ID号 }, "appsrc0": { // "appsrc0" 表示输入元件名称 "props": { // "props"指元件属性 "blocksize": "409600" // 每个buffer读取的大小 }, "factory": "appsrc", // "factory" 定义该元件类型 "next": "mxpi_imagedecoder0" // "next" 填写连接的下游元件--图像解码元件 }, "mxpi_imagedecoder0": { // 图像解码元件名称,0表示编号,如果一个流程中要使用多个图像解码元件,可以依次按照0、1、2...命名 "props": { "handleMethod": "opencv" // 解码方法为opencv }, "factory": "mxpi_imagedecoder",// 使用图像解码插件 "next": "mxpi_imageresize0" // "next" 填写连接的下游元件--图像缩放元件 }, "mxpi_imageresize0": { // 图像缩放元件名称 "props": { "handleMethod": "opencv", // 解码方法为opencv "resizeHeight": "280", // 指定缩放后的高 "resizeWidth": "280", // 指定缩放后的宽 "resizeType": "Resizer_Stretch"// 缩放方式为拉伸缩放 }, "factory": "mxpi_imageresize", // 使用图像缩放插件 "next": "mxpi_opencvcentercrop0" // "next" 填写连接的下游元件--图像中心裁剪元件 }, "mxpi_opencvcentercrop0": { // 图像中心裁剪元件名称 "props": { "dataSource": "mxpi_imageresize0", // "dataSource"填写连接的上游元件--图像缩放元件 "cropHeight": "224", // 裁剪出的图片高 "cropWidth": "224" // 裁剪出的图片宽 }, "factory": "mxpi_opencvcentercrop", // 使用图像中心裁剪元件插件 "next": "mxpi_tensorinfer0" // "next"填写连接的下游元件--模型推理元件 }, "mxpi_tensorinfer0": { // 模型推理元件名称 "props": { // "props"指元件属性,可以加载指定目录中的文件 "dataSource": "mxpi_opencvcentercrop0", // "dataSource"填写连接的上游元件--图像中心裁剪元件 "modelPath": "../models/resnet50_tensorflow_1.7.om", // "modelPath" 属性定义了推理业务使用的模型,用户需要根据获取的模型修改文件名 "waitingTime": "2000", // 多batch模型可容忍的等待组BATCH时间 "outputDeviceId": "-1" // 内存拷贝到指定位置,设为-1则拷贝至Host侧 }, "factory": "mxpi_tensorinfer", // 使用模型推理插件 "next": "mxpi_classpostprocessor0" // "next"填写连接的下游元件--模型后处理元件 }, "mxpi_classpostprocessor0": { // 模型后处理元件名称 "props": { // "props"指元件属性,可以加载指定目录中的文件 "dataSource": "mxpi_tensorinfer0", // "dataSource"填写连接的上游元件--模型推理元件 "postProcessConfigPath": "../models/resnet50_aipp_tf.cfg",// "postProcessConfigPath" 指定模型后处理配置文件 "labelPath": "../models/resnet50_clsidx_to_labels.names", // "labelPath" 指定模型输出的类别名称文件 "postProcessLibPath": "libresnet50postprocess.so" // "postProcessLibPath" 指定模型后处理依赖的动态库 }, "factory": "mxpi_classpostprocessor", // 使用模型后处理插件 "next": "mxpi_dataserialize0" // "next" 填写连接的下游元件--序列化元件 }, "mxpi_dataserialize0": { // 序列化元件名称 "props": { "outputDataKeys": "mxpi_classpostprocessor0" // "outputDataKeys" 指定需要输出的数据的索引 }, "factory": "mxpi_dataserialize", // 使用序列化插件 "next": "appsink0" // "next" 填写连接的下游元件--输出元件 }, "appsink0": { // 输出元件名称 "props": { "blocksize": "4096000" // 每个buffer读取的大小 }, "factory": "appsink" // 使用输出插件 } } }
pipeline文件中的注释仅用于辅助理解,在编写pipeline文件时,请删除其中的注释文字,否则会导致文件解析失败。
在这段pipeline中,有以下关键概念:
“pipelineSample/src”目录中的“main.cpp”文件为应用程序源码。
int main(int argc, char* argv[])
{
// 1. 判断是否使用Atlas 推理系列产品
if (!MxBase::DeviceManager::IsAscend310P()) {
LogError << "Current demo only support on Ascend310P, please check!";
return APP_ERR_INVALID_DEVICE;
}
// 2.解析pipeline文件
std::string pipelineConfigPath = "../pipeline/Sample.pipeline"; // 修改pipeline文件路径
std::string pipelineConfig = ReadPipeline(pipelineConfigPath);
if (pipelineConfig == "") {
LogError << "Read pipeline failed.";
return APP_ERR_COMM_INIT_FAIL;
}
// 3.初始化stream manager
MxStream::MxStreamManager mxStreamManager;
APP_ERROR ret = mxStreamManager.InitManager();
if (ret != APP_ERR_OK) {
LogError << GetError(ret) << "Failed to init Stream manager.";
return ret;
}
// 4.创建stream
ret = mxStreamManager.CreateMultipleStreams(pipelineConfig);
if (ret != APP_ERR_OK) {
LogError << GetError(ret) << "Failed to create Stream.";
mxStreamManager.DestroyAllStreams();
return ret;
}
// 5.读取待推理图片
MxStream::MxstDataInput dataBuffer;
ret = ReadFile("../data/dog1_1024_683.jpg", dataBuffer); // 修改输入图片路径
if (ret != APP_ERR_OK) {
LogError << GetError(ret) << "Failed to read image file.";
mxStreamManager.DestroyAllStreams();
return ret;
}
std::string streamName = "classification"; // 修改业务推理流程的名称
int inPluginId = 0;
// 6.发送待推理图片至stream
ret = mxStreamManager.SendData(streamName, inPluginId, dataBuffer);
if (ret != APP_ERR_OK) {
LogError << GetError(ret) << "Failed to send data to stream.";
delete dataBuffer.dataPtr;
dataBuffer.dataPtr = nullptr;
mxStreamManager.DestroyAllStreams();
return ret;
}
// 7.获取推理结果
MxStream::MxstDataOutput* output = mxStreamManager.GetResult(streamName, inPluginId);
if (output == nullptr) {
LogError << "Failed to get pipeline output.";
delete dataBuffer.dataPtr;
dataBuffer.dataPtr = nullptr;
mxStreamManager.DestroyAllStreams();
return ret;
}
std::string result = std::string((char *)output->dataPtr, output->dataSize);
std::cout << "Results:" << result << std::endl;
// 8.销毁stream,并释放资源
mxStreamManager.DestroyAllStreams();
delete dataBuffer.dataPtr;
dataBuffer.dataPtr = nullptr;
delete output;
return 0;
}
source /usr/local/Ascend/ascend-toolkit/set_env.sh source /home/mxVision-{version}/set_env.sh
./run.sh
Results: { "MxpiClass": [{ "classId": 163, "className": "beagle", "confidence": 0.86181640599999998 }] }