本篇对基于Caffe ResNet-50网络实现图片分类的代码进行详细说明
代码结果
样例代码结构如下所示。
transferPic.py
转换图片格式及分辨率,主要方法位于process函数中。
main.cpp
上述代码主要用于初始化资源和启动程序,主要过程被封装于sampleProcess类中。
sample_process.cpp
首先看sampleProcess类的头文件
需要引入utils.h和acl/acl.h,一个为封装的常用方法,一个为AscendCL; 该类除了构造函数和析构函数,还有两个公有方法和一个私有方法,以及三个私有成员变量。
构造函数
初始化私有成员变量。
InitResource
申请运行管理资源。
Process
析构函数
用于释放资源
DestroyResource
释放运行管理资源,与InitResource顺序相反。
model_process.cpp
首先看头文件,主要包括与模型相关的ACL接口调用,具体实现在进阶班中说明。
ModelProcess构造函数,用于私有成员变量的初始化; CreateModelDesc创建模型描述信息; LoadModel加载模型; Execute运行模型; OutputModelResult获取模型输出; ~ModelProcess析构函数,用于卸载模型、销毁模型描述信息、销毁输入、销毁输出(释放内存)。
结束语
本篇对图片分类的代码进行了说明,介绍了代码运行逻辑,但未进一步介绍ACL相关的接口,该部分将在进阶班中展开。
本篇对基于Caffe ResNet-50网络实现图片分类的代码进行详细说明
代码结果
样例代码结构如下所示。
transferPic.py
转换图片格式及分辨率,主要方法位于
process函数中。def process(input_path): try: input_image = Image.open(input_path) # 读入图片 input_image = input_image.resize((256, 256)) # 缩放到256*256 # hwc img = np.array(input_image) # 转为ndarray格式 height = img.shape[0] # 获取图片高度 width = img.shape[1] # 获取图片宽度 h_off = int((height-224)/2) # 计算高度偏移量,用于中心裁剪出224*224大小图片 w_off = int((width-224)/2) # 计算宽度偏移量,用于中心裁剪出224*224大小图片 crop_img = img[h_off:height-h_off, w_off:width-w_off, :] # 裁剪图像 # rgb to bgr img = crop_img[:, :, ::-1] # 通道转换,RGB转为BGR shape = img.shape # 获取HWC img = img.astype("float16") # 转为FP16 img[:, :, 0] -= 104 # B通道标准化 img[:, :, 1] -= 117 # G通道标准化 img[:, :, 2] -= 123 # R通道标准化 img = img.reshape([1] + list(shape)) # NHWC result = img.transpose([0, 3, 1, 2]) # NCHW output_name = input_path.split('.')[0] + ".bin" # 保存 result.tofile(output_name) except Exception as except_err: print(except_err) return 1 else: return 0main.cpp
#include <iostream> # 引入头文件 #include "sample_process.h" #include "utils.h" using namespace std; bool g_isDevice = false; int main() { SampleProcess sampleProcess; Result ret = sampleProcess.InitResource(); # 初始化资源 if (ret != SUCCESS) { ERROR_LOG("sample init resource failed"); return FAILED; } ret = sampleProcess.Process(); # 处理 if (ret != SUCCESS) { ERROR_LOG("sample process failed"); return FAILED; } INFO_LOG("execute sample success"); return SUCCESS; }上述代码主要用于初始化资源和启动程序,主要过程被封装于
sampleProcess类中。sample_process.cpp
首先看
sampleProcess类的头文件#pragma once #include "utils.h" #include "acl/acl.h" class SampleProcess { public: /** * @brief Constructor */ SampleProcess(); /** * @brief Destructor */ virtual ~SampleProcess(); /** * @brief init reousce * @return result */ Result InitResource(); /** * @brief sample process * @return result */ Result Process(); private: void DestroyResource(); int32_t deviceId_; aclrtContext context_; aclrtStream stream_; };需要引入
utils.h和acl/acl.h,一个为封装的常用方法,一个为AscendCL; 该类除了构造函数和析构函数,还有两个公有方法和一个私有方法,以及三个私有成员变量。构造函数
SampleProcess::SampleProcess() :deviceId_(0), context_(nullptr), stream_(nullptr) { }初始化私有成员变量。
InitResource
申请运行管理资源。
Result SampleProcess::InitResource() { // ACL init const char *aclConfigPath = "../src/acl.json"; # 配置文件 aclError ret = aclInit(aclConfigPath); # 初始化 if (ret != ACL_SUCCESS) { ERROR_LOG("acl init failed, errorCode = %d", static_cast<int32_t>(ret)); return FAILED; } INFO_LOG("acl init success"); // set device ret = aclrtSetDevice(deviceId_); # 指定设备 if (ret != ACL_SUCCESS) { ERROR_LOG("acl set device %d failed, errorCode = %d", deviceId_, static_cast<int32_t>(ret)); return FAILED; } INFO_LOG("set device %d success", deviceId_); // create context (set current) ret = aclrtCreateContext(&context_, deviceId_); # 创建context if (ret != ACL_SUCCESS) { ERROR_LOG("acl create context failed, deviceId = %d, errorCode = %d", deviceId_, static_cast<int32_t>(ret)); return FAILED; } INFO_LOG("create context success"); // create stream ret = aclrtCreateStream(&stream_); # 创建stream if (ret != ACL_SUCCESS) { ERROR_LOG("acl create stream failed, deviceId = %d, errorCode = %d", deviceId_, static_cast<int32_t>(ret)); return FAILED; } INFO_LOG("create stream success"); // get run mode // runMode is ACL_HOST which represents app is running in host // runMode is ACL_DEVICE which represents app is running in device aclrtRunMode runMode; ret = aclrtGetRunMode(&runMode); # 获取软件栈的运行模式 if (ret != ACL_SUCCESS) { ERROR_LOG("acl get run mode failed, errorCode = %d", static_cast<int32_t>(ret)); return FAILED; } g_isDevice = (runMode == ACL_DEVICE); INFO_LOG("get run mode success"); return SUCCESS; }Process
Result SampleProcess::Process() { // model init ModelProcess modelProcess; # 封装模型相关的处理过程 const char* omModelPath = "../model/resnet50.om"; # 模型目录 Result ret = modelProcess.LoadModel(omModelPath); # 加载模型 if (ret != SUCCESS) { ERROR_LOG("execute LoadModel failed"); return FAILED; } ret = modelProcess.CreateModelDesc(); # 获取模型描述信息 if (ret != SUCCESS) { ERROR_LOG("execute CreateModelDesc failed"); return FAILED; } string testFile[] = { # 测试图片 "../data/dog1_1024_683.bin", "../data/dog2_1024_683.bin" }; size_t devBufferSize; # 用于存放输入大小 void *picDevBuffer = nullptr; ret = modelProcess.GetInputSizeByIndex(0, devBufferSize); # 获取指定输入的大小 if (ret != SUCCESS) { ERROR_LOG("execute GetInputSizeByIndex failed"); return FAILED; } aclError aclRet = aclrtMalloc(&picDevBuffer, devBufferSize, ACL_MEM_MALLOC_NORMAL_ONLY); # 申请内存 if (aclRet != ACL_SUCCESS) { ERROR_LOG("malloc device buffer failed. size is %zu, errorCode is %d", devBufferSize, static_cast<int32_t>(aclRet)); return FAILED; } ret = modelProcess.CreateInput(picDevBuffer, devBufferSize); # 创建输入 if (ret != SUCCESS) { ERROR_LOG("execute CreateInput failed"); aclrtFree(picDevBuffer); return FAILED; } ret = modelProcess.CreateOutput(); # 创建输出 if (ret != SUCCESS) { aclrtFree(picDevBuffer); ERROR_LOG("execute CreateOutput failed"); return FAILED; } for (size_t index = 0; index < sizeof(testFile) / sizeof(testFile[0]); ++index) { INFO_LOG("start to process file:%s", testFile[index].c_str()); // copy image data to device buffer ret = Utils::MemcpyFileToDeviceBuffer(testFile[index], picDevBuffer, devBufferSize); # 移入device buffer if (ret != SUCCESS) { aclrtFree(picDevBuffer); ERROR_LOG("memcpy device buffer failed, index is %zu", index); return FAILED; } ret = modelProcess.Execute(); # 执行处理 if (ret != SUCCESS) { ERROR_LOG("execute inference failed"); aclrtFree(picDevBuffer); return FAILED; } // Print the top 5 confidence values with indexes. // Use function [DumpModelOutputResult] if you want to dump results to file in the current directory. modelProcess.OutputModelResult(); # 获得输出 } // release model input output modelProcess.DestroyInput(); # 释放输入 modelProcess.DestroyOutput(); # 释放输出 aclrtFree(picDevBuffer); # 释放内存 return SUCCESS; }析构函数
用于释放资源
SampleProcess::~SampleProcess() { DestroyResource(); }DestroyResource
释放运行管理资源,与
InitResource顺序相反。void SampleProcess::DestroyResource() { aclError ret; if (stream_ != nullptr) { ret = aclrtDestroyStream(stream_); if (ret != ACL_SUCCESS) { ERROR_LOG("destroy stream failed, errorCode = %d", static_cast<int32_t>(ret)); } stream_ = nullptr; } INFO_LOG("end to destroy stream"); if (context_ != nullptr) { ret = aclrtDestroyContext(context_); if (ret != ACL_SUCCESS) { ERROR_LOG("destroy context failed, errorCode = %d", static_cast<int32_t>(ret)); } context_ = nullptr; } INFO_LOG("end to destroy context"); ret = aclrtResetDevice(deviceId_); if (ret != ACL_SUCCESS) { ERROR_LOG("reset device %d failed, errorCode = %d", deviceId_, static_cast<int32_t>(ret)); } INFO_LOG("end to reset device %d", deviceId_); ret = aclFinalize(); if (ret != ACL_SUCCESS) { ERROR_LOG("finalize acl failed, errorCode = %d", static_cast<int32_t>(ret)); } INFO_LOG("end to finalize acl"); }model_process.cpp
首先看头文件,主要包括与模型相关的ACL接口调用,具体实现在进阶班中说明。
#pragma once #include <iostream> #include "utils.h" #include "acl/acl.h" class ModelProcess { public: /** * @brief Constructor */ ModelProcess(); /** * @brief Destructor */ virtual ~ModelProcess(); /** * @brief load model * @param [in] modelPath: model path * @return result */ Result LoadModel(const char *modelPath); /** * @brief unload model */ void UnloadModel(); /** * @brief create model desc * @return result */ Result CreateModelDesc(); /** * @brief destroy desc */ void DestroyModelDesc(); /** * @get input size by index * @param [in] index: input index * @param [out] inputSize: input size of index * @return result */ Result GetInputSizeByIndex(const size_t index, size_t &inputSize); /** * @brief create model input * @param [in] inputDataBuffer: input buffer * @param [in] bufferSize: input buffer size * @return result */ Result CreateInput(void *inputDataBuffer, size_t bufferSize); /** * @brief destroy input resource */ void DestroyInput(); /** * @brief create output buffer * @return result */ Result CreateOutput(); /** * @brief destroy output resource */ void DestroyOutput(); /** * @brief model execute * @return result */ Result Execute(); /** * @brief dump model output result to file */ void DumpModelOutputResult(); /** * @brief print model output result */ void OutputModelResult(); private: uint32_t modelId_; size_t modelWorkSize_; // model work memory buffer size size_t modelWeightSize_; // model weight memory buffer size void *modelWorkPtr_; // model work memory buffer void *modelWeightPtr_; // model weight memory buffer bool loadFlag_; // model load flag aclmdlDesc *modelDesc_; aclmdlDataset *input_; aclmdlDataset *output_; };ModelProcess构造函数,用于私有成员变量的初始化;CreateModelDesc创建模型描述信息;LoadModel加载模型;Execute运行模型;OutputModelResult获取模型输出;~ModelProcess析构函数,用于卸载模型、销毁模型描述信息、销毁输入、销毁输出(释放内存)。结束语
本篇对图片分类的代码进行了说明,介绍了代码运行逻辑,但未进一步介绍ACL相关的接口,该部分将在进阶班中展开。