流程编排开发方式
样例介绍
本样例以Atlas 推理系列产品为例,通过Vision SDK图像分类案例,介绍如何使用Vision SDK流程编排方式开发推理应用。案例使用ResNet-50模型对图片进行分类并最后输出分类结果。
ResNet-50模型的基本介绍如下:
- 输入数据:RGB格式、224*224分辨率的输入图片。
- 输出数据:图片的类别标签及其对应置信度(置信度是指图片所属某个类别可能性)。
准备工作
- 请先完成Vision SDK安装部署后,再进行快速入门样例。
表1 环境要求软件依赖 软件依赖名称
推荐版本
获取链接
操作系统
请参见支持的硬件和操作系统
-
系统依赖
-
CANN开发套件包
8.0.RC3
npu-driver驱动包
Ascend HDK 24.1.RC3
npu-firmware固件包
Ascend HDK 24.1.RC3
- 获取样例代码。本样例支持Atlas 推理系列产品上运行。
- 以普通用户登录已安装Vision SDK的开发环境并将样例代码压缩包上传。
- 解压样例代码压缩包,进入解压后的目录,命令参考如下。
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),以下图片为展示用途。
图1 典型样本图片
编排pipeline文件
编排pipeline文件是使用Vision SDK开发应用最核心的任务,图像分类应用可拆解为一系列的业务流程,通过编辑pipeline文件,调用Vision SDK插件库完成推理业务,本文的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中,有以下关键概念:
- “next”属性值指明了元件之间的连接关系。
- 用户通过“appsrc0”元件向Stream发送数据,通过“appsink0”元件从Stream获取推理结果。
- “mxpi_classpostprocessor0”元件用于对模型推理的输出张量进行后处理,比如在上述样例中,模型后处理元件处理上游模型推理元件输出的一维张量,最终返回模型的识别结果(类别ID、名称、置信度)。
- “mxpi_dataserialize0”元件将推理结果组装成JSON字符串输出。
代码解析
“pipelineSample/src”目录中的“main.cpp”文件为应用程序源码。
在本样例中,关键步骤与代码参考如下,不可以直接拷贝编译运行,需要根据实际情况修改pipeline文件路径、输入图片路径、Stream名称,Stream名称需要与pipeline文件中的业务推理流程的名称匹配,如上述pipeline文件的业务推理流程的名称为“classification”。完整样例代码请参考样例文件。
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;
}
编译和运行应用
- 以普通用户登录,进入“pipelineSample/src”目录。
- 配置环境变量(以CANN的默认安装路径“/usr/local/Ascend/ascend-toolkit”和Vision SDK的安装路径/home/mxVision-{version}为例)。
source /usr/local/Ascend/ascend-toolkit/set_env.sh source /home/mxVision-{version}/set_env.sh
- 运行应用,执行编译脚本。
chmod +x run.sh ./run.sh
终端上屏显的结果如下,“classId”表示类别号、“className”表示类名称,“confidence”表示该分类的最大置信度:Results: { "MxpiClass": [{ "classId": 163, "className": "beagle", "confidence": 0.86181640599999998 }] }
类别标签和类别的对应关系与训练模型时使用的数据集有关,本样例的模型基于Imagenet数据集进行训练,您可以在互联网上查阅对应数据集的标签及类别的对应关系。
当前屏显信息中的类别标识与类别的对应关系如下:
- "160" ["Rhodesian ridgeback"]
- "161" ["Afghan hound, Afghan"]
- "162" ["basset, basset hound"]
- "163" ["beagle"]
- "164" ["bloodhound, sleuthhound"]
- "165" ["bluetick"]
- "166" ["black-and-tan coonhound"]
父主题: 快速入门