Commit 9c945072 authored by yangql's avatar yangql
Browse files

Initial commit

parents
Pipeline #383 canceled with stages
#! /bin/sh
############### Ubuntu ###############
# 参考:https://docs.opencv.org/3.4.11/d7/d9f/tutorial_linux_install.html
# apt-get install build-essential -y
# apt-get install cmake git libgtk2.0-dev pkg-config libavcodec-dev libavformat-dev libswscale-dev -y
# apt-get install python-dev python-numpy libtbb2 libtbb-dev libjpeg-dev libpng-dev libtiff-dev libjasper-dev libdc1394-22-dev -y # 处理图像所需的包,可选
############### CentOS ###############
yum install gcc gcc-c++ gtk2-devel gimp-devel gimp-devel-tools gimp-help-browser zlib-devel libtiff-devel libjpeg-devel libpng-devel gstreamer-devel libavc1394-devel libraw1394-devel libdc1394-devel jasper-devel jasper-utils swig python libtool nasm -y
\ No newline at end of file
############################ 在线安装依赖 ###############################
#cd ./3rdParty
#pip install rbuild-master.tar.gz
############################ 离线安装依赖 ###############################
# 安装依赖
cd ./3rdParty/rbuild_depend
pip install click-6.6-py2.py3-none-any.whl
pip install six-1.15.0-py2.py3-none-any.whl
pip install subprocess32-3.5.4.tar.gz
pip install cget-0.1.9.tar.gz
# 安装rbuild
cd ../
pip install rbuild-master.tar.gz
# 设置cmake的最低版本
cmake_minimum_required(VERSION 3.5)
# 设置项目名
project(ResNet50)
# 设置编译器
set(CMAKE_CXX_COMPILER g++)
set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} -std=c++17) # 2.2版本以上需要c++17
set(CMAKE_BUILD_TYPE release)
# 添加头文件路径
set(INCLUDE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/Src/
${CMAKE_CURRENT_SOURCE_DIR}/Src/Utility/
$ENV{DTKROOT}/include/
${CMAKE_CURRENT_SOURCE_DIR}/depend/include/)
include_directories(${INCLUDE_PATH})
# 添加依赖库路径
set(LIBRARY_PATH ${CMAKE_CURRENT_SOURCE_DIR}/depend/lib64/
$ENV{DTKROOT}/lib/)
link_directories(${LIBRARY_PATH})
# 添加依赖库
set(LIBRARY opencv_core
opencv_imgproc
opencv_imgcodecs
opencv_dnn
onnxruntime
)
link_libraries(${LIBRARY})
# 添加源文件
set(SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/Src/main.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Src/Classifier.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Src/Utility/CommonUtility.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Src/Utility/Filesystem.cpp)
# 添加可执行目标
add_executable(ResNet50 ${SOURCE_FILES})
# 分类器
本示例通过ResNet50模型说明如何使用OnnxRunTime C++ API进行分类模型的推理,包括如何预处理、推理并获取推理结果。
## 模型简介
本示例使用了经典的ResNet50模型,onnx文件在Resource/Models/文件夹下,模型结构可以通过netron (https://netron.app/) 查看,该模型的输入shape为[1,3,224,224] ,数据排布为NCHW。
## 预处理
在将数据输入到模型之前,需要对图像做如下预处理操作:
- 图像格式转换,BGR转换为RGB
- 调整图像大小,并在中心窗口位置裁剪出224x224大小的图像
- normalize操作,对图像减均值再除方差
- 转换数据排布为NCHW
本示例代码主要采用了OpenCV实现了预处理操作:
```c++
ErrorCode Classifier::Classify(const std::vector<cv::Mat> &srcImages,std::vector<std::vector<ResultOfPrediction>> &predictions)
{
...
// 预处理
std::vector<cv::Mat> image;
for(int i =0;i<srcImages.size();++i)
{
//BGR转换为RGB
cv::Mat imgRGB;
cv::cvtColor(srcImages[i], imgRGB, cv::COLOR_BGR2RGB);
// 调整大小,保持长宽比
cv::Mat shrink;
float ratio = (float)256 / min(imgRGB.cols, imgRGB.rows);
if(imgRGB.rows > imgRGB.cols)
{
cv::resize(imgRGB, shrink, cv::Size(256, int(ratio * imgRGB.rows)), 0, 0);
}
else
{
cv::resize(imgRGB, shrink, cv::Size(int(ratio * imgRGB.cols), 256), 0, 0);
}
// 裁剪中心窗口
int start_x = shrink.cols/2 - 224/2;
int start_y = shrink.rows/2 - 224/2;
cv::Rect rect(start_x, start_y, 224, 224);
cv::Mat images = shrink(rect);
image.push_back(images);
}
// normalize并转换为NCHW
cv::Mat inputBlob;
Image2BlobParams image2BlobParams;
image2BlobParams.scalefactor=cv::Scalar(1/58.395, 1/57.12, 1/57.375);
image2BlobParams.mean=cv::Scalar(123.675, 116.28, 103.53);
image2BlobParams.swapRB=false;
blobFromImagesWithParams(image,inputBlob,image2BlobParams);
...
}
```
blobFromImagesWithParams()函数支持多个输入图像,首先对输入图像各通道减对应的均值,然后乘以各通道对应的缩放系数,最后转换为NCHW,最终将转换好的数据保存到inputBlob中,然后就可以输入到模型中执行推理了。
## 推理
完成预处理后,就可以执行推理了:
```c++
ErrorCode Classifier::Classify(const std::vector<cv::Mat> &srcImages,std::vector<std::vector<ResultOfPrediction>> &predictions)
{
...
// 预处理
// 创建输入数据
std::array<int64_t, 4> inputShape{1, 3, 224, 224};
auto memoryInfo = Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault);
std::array<float, 3 * 224 * 224> input_image{};
float* input_test = (float*)inputBlob.data;
Ort::Value inputTensor = Ort::Value::CreateTensor<float>(memoryInfo, input_test, input_image.size(), inputShape.data(), inputShape.size());
std::vector<Ort::Value> intput_tensors;
intput_tensors.push_back(std::move(inputTensor));
// 推理
auto output_tensors = session->Run(Ort::RunOptions{nullptr}, input_node_names.data(), intput_tensors.data(), 1, output_node_names.data(), 1);
// 获取输出节点的属性
const float* pdata = output_tensors[0].GetTensorMutableData<float>(); // 获取第一个输出节点的数据
...
}
```
- input_node_names为一个字符串数组,包含输入节点的名称。intput_tensors表示一个OrtValue类型的指针数组,包含输入节点的值。"1"为输入节点的数量。output_node_names为一个字符串数组,包含输出节点的名称。最后一个1为输出节点的数量。
- session->Run(...)返回模型的推理结果,由于这里只有一个输出节点,所以返回数据中只有一个数据,output_tensors[0]表示第一个输出节点,这里对应resnetv24_dense0_fwd节点,获取输出数据。
# 分类器
本示例通过ResNet50模型说明如何使用OnnxRunTime Python API进行分类模型的推理,包括预处理、推理并获取推理结果。
## 模型简介
本示例使用了经典的ResNet50模型,onnx文件在Resource/Models/文件夹下,模型结构可以通过netron (https://netron.app/) 查看,该模型的输入shape为[1,3,224,224] ,数据排布为NCHW。
## 预处理
在将数据输入到模型之前,需要对图像做如下预处理操作:
- 图像格式转换,将BGR转换为RGB
- 调整图像大小,使短边为 256,长边等比例缩放
- 裁剪图像,在中心窗口位置裁剪出224x224大小的图像
- normalize操作,对图像减均值再除方差
- 调整输入数据的尺寸为(1, 3, 224, 224)
本示例代码采用了OpenCV实现了预处理操作:
```
def Preprocessing(pathOfImage):
image = cv2.imread(pathOfImage, cv2.IMREAD_COLOR)
# 转换为RGB格式
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
# 调整大小,保持长宽比
ratio = float(256) / min(image.shape[0], image.shape[1])
if image.shape[0] > image.shape[1]:
new_size = [int(round(ratio * image.shape[0])), 256]
else:
new_size = [256, int(round(ratio * image.shape[1]))]
image = np.array(cv2.resize(image, (new_size[1],new_size[0]))) # w h 格式
# 裁剪中心窗口
h, w, c = image.shape
start_x = w//2 - 224//2
start_y = h//2 - 224//2
image = image[start_y:start_y+224, start_x:start_x+224, :]
# transpose
image = image.transpose(2, 0, 1)
# 将输入数据转换为float32
img_data = image.astype('float32')
# normalize
mean_vec = np.array([123.675, 116.28, 103.53])
stddev_vec = np.array([58.395, 57.12, 57.375])
norm_img_data = np.zeros(img_data.shape).astype('float32')
for i in range(img_data.shape[0]):
norm_img_data[i,:,:] = (img_data[i,:,:] - mean_vec[i]) / stddev_vec[i]
# 调整输入数据的尺寸
norm_img_data = norm_img_data.reshape(1, 3, 224, 224).astype('float32')
return norm_img_data
```
## 推理
完成预处理后,就可以执行推理了:
```
def ort_seg_dcu(model_path,image):
#创建sess_options
sess_options = ort.SessionOptions()
#设置图优化
sess_options.graph_optimization_level = ort.GraphOptimizationLevel.ORT_ENABLE_BASIC
#是否开启profiling
sess_options.enable_profiling = False
dcu_session = ort.InferenceSession(model_path,sess_options,providers=['ROCMExecutionProvider'],)
input_name=dcu_session.get_inputs()[0].name
results = dcu_session.run(None, input_feed={input_name:image })
scores=np.array(results[0])
# print("ort result.shape:",scores.shape)
return scores
```
- Preprocessing()函数返回输入数据(numpy类型),然后通过构造一个字典输入模型执行推理,如果模型有多个输入,则在字典中需要添加多个输入数据。
- model.run()返回模型的推理结果,返回结果是一个list类型,results[0]表示第一个输出节点的输出。由于示例模型只有一个输出节点,所以results[0]对应resnetv24_dense0_fwd节点。最后,通过np.array(result)获取分类结果。
# -*- coding: utf-8 -*-
"""
分类器示例
"""
import cv2
import numpy as np
import onnxruntime as ort
def Preprocessing(pathOfImage):
# 读取图像
image = cv2.imread(pathOfImage, cv2.IMREAD_COLOR)
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
# 调整大小,使短边为256,保持长宽比
ratio = float(256) / min(image.shape[0], image.shape[1])
if image.shape[0] > image.shape[1]:
new_size = [int(round(ratio * image.shape[0])), 256]
else:
new_size = [256, int(round(ratio * image.shape[1]))]
image = np.array(cv2.resize(image, (new_size[1],new_size[0])))
# 裁剪中心窗口为224*224
h, w, c = image.shape
start_x = w//2 - 224//2
start_y = h//2 - 224//2
image = image[start_y:start_y+224, start_x:start_x+224, :]
# transpose
image = image.transpose(2, 0, 1)
# 将输入数据转换为float32
img_data = image.astype('float32')
# normalize
mean_vec = np.array([123.675, 116.28, 103.53])
stddev_vec = np.array([58.395, 57.12, 57.375])
norm_img_data = np.zeros(img_data.shape).astype('float32')
for i in range(img_data.shape[0]):
norm_img_data[i,:,:] = (img_data[i,:,:] - mean_vec[i]) / stddev_vec[i]
# 调整尺寸
norm_img_data = norm_img_data.reshape(1, 3, 224, 224).astype('float32')
return norm_img_data
def postprocess(scores):
'''
Postprocessing with mxnet gluon
The function takes scores generated by the network and returns the class IDs in decreasing order
of probability
'''
with open('../Resource/synset.txt', 'r') as f:
labels = [l.rstrip() for l in f]
preds = np.squeeze(scores)
a = np.argsort(preds)[::-1]
print('class=%s ; probability=%f' %(labels[a[0]],preds[a[0]]))
def ort_seg_dcu(model_path,image):
#创建sess_options
sess_options = ort.SessionOptions()
#设置图优化
sess_options.graph_optimization_level = ort.GraphOptimizationLevel.ORT_ENABLE_BASIC
#是否开启profiling
sess_options.enable_profiling = False
dcu_session = ort.InferenceSession(model_path,sess_options,providers=['ROCMExecutionProvider'],)
input_name=dcu_session.get_inputs()[0].name
results = dcu_session.run(None, input_feed={input_name:image })
scores=np.array(results[0])
print("ort result.shape:",scores.shape)
return scores
if __name__ == '__main__':
pathOfImage ="../Resource/Images/ImageNet_01.jpg"
image = Preprocessing(pathOfImage)
model_path = "../Resource/Models/resnet50-v2-7.onnx"
# 推理
result = ort_seg_dcu(model_path,image)
# 解析分类结果
postprocess(result)
opencv-python
numpy
\ No newline at end of file
# ResNet50
## 模型介绍
使用ONNXRunTime推理框架对ResNet50模型进行推理。
## 模型结构
ResNet50模型包含了49个卷积层、一个全连接层。
## Python版本推理
采用ONNXRunTime框架使用DCU进行推理,模型文件下载链接:https://github.com/onnx/models/blob/main/vision/classification/resnet/model/resnet50-v2-7.onnx ,并将resnet50-v2-7.onnx模型文件保存在Resource/文件夹下。下面介绍如何运行python代码示例,Python示例的详细说明见Doc目录下的Tutorial_Python.md。
### 下载镜像
在光源中下载ONNXRunTime推理框架镜像:
```python
docker pull image.sourcefind.cn:5000/dcu/admin/base/custom:ort1.14.0_migraphx3.0.0-dtk22.10.1
```
### 设置Python环境变量
```
export PYTHONPATH=/opt/dtk/lib:$PYTHONPATH
```
### 安装依赖
```python
# 进入resnet50 migraphx工程根目录
cd <path_to_resnet50_ort>
# 进入示例程序目录
cd Python/
# 安装依赖
pip install -r requirements.txt
```
### 运行示例
```python
python Classifier.py
```
## C++版本推理
下面介绍如何运行C++代码示例,C++示例的详细说明见Doc目录下的Tutorial_Cpp.md。
### 下载镜像
在光源中下载ONNXRunTime推理框架镜像:
```
docker pull image.sourcefind.cn:5000/dcu/admin/base/custom:ort1.14.0_migraphx3.0.0-dtk22.10.1
```
### 安装Opencv依赖
```python
cd <path_to_resnet50_ort>
sh ./3rdParty/InstallOpenCVDependences.sh
```
### 安装OpenCV并构建工程
```
source /opt/dtk/env.sh
rbuild build -d depend
```
### 设置环境变量
将依赖库依赖加入环境变量LD_LIBRARY_PATH,在~/.bashrc中添加如下语句:
```
export LD_LIBRARY_PATH=<path_to_resnet50_migraphx>/depend/lib64/:$LD_LIBRARY_PATH
```
然后执行:
```
source ~/.bashrc
```
### 运行示例
```python
# 进入resnet50 migraphx工程根目录
cd <path_to_resnet50_ort>
# 进入build目录
cd build/
# 执行示例程序
./ResNet50
```
## 性能和准确率数据
测试数据使用的是ImageNet-1k,使用的加速卡是DCU Z100。
| Engine | Model Path| Model Format | accuracy (%) | Speed(qps) |
| :------: | :------: | :------: | :------: |:------: |
| ONNXRuntime | resnet50-v2-7.onnx | onnx | 74.83 | 149.83 |
## 源码仓库及问题反馈
https://developer.hpccube.com/codes/modelzoo/resnet50_ort
## 参考
https://github.com/onnx/models/tree/main/vision/classification/resnet
<?xml version="1.0" encoding="GB2312"?>
<opencv_storage>
<!--分类器-->
<Classifier>
<ModelPath>"../Resource/Models/resnet50-v2-7.onnx"</ModelPath>
<UseInt8>0</UseInt8><!--是否使用int8,不支持-->
<UseFP16>0</UseFP16><!--是否使用FP16-->
</Classifier>
</opencv_storage>
This diff is collapsed.
#include <Classifier.h>
#include <Filesystem.h>
#include <SimpleLog.h>
#include <algorithm>
#include <CommonUtility.h>
namespace ortSamples
{
Classifier::Classifier()
{
}
Classifier::~Classifier()
{
configurationFile.release();
}
ErrorCode Classifier::Initialize(InitializationParameterOfClassifier initializationParameterOfClassifier)
{
// 读取配置文件
std::string configFilePath=initializationParameterOfClassifier.configFilePath;
if(Exists(configFilePath)==false)
{
LOG_ERROR(stdout, "no configuration file!\n");
return CONFIG_FILE_NOT_EXIST;
}
if(!configurationFile.open(configFilePath, cv::FileStorage::READ))
{
LOG_ERROR(stdout, "fail to open configuration file\n");
return FAIL_TO_OPEN_CONFIG_FILE;
}
LOG_INFO(stdout, "succeed to open configuration file\n");
// 获取配置文件参数
cv::FileNode netNode = configurationFile["Classifier"];
std::string modelPath=(std::string)netNode["ModelPath"];
// 初始化session
//设置DCU
OrtROCMProviderOptions rocm_options;
rocm_options.device_id = 0;
sessionOptions.AppendExecutionProvider_ROCM(rocm_options);
sessionOptions.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_BASIC);
// sessionOptions.EnableProfiling("profile_prefix");
session = new Ort::Session(env, modelPath.c_str(), sessionOptions);
return SUCCESS;
}
ErrorCode Classifier::Classify(const std::vector<cv::Mat> &srcImages,std::vector<std::vector<ResultOfPrediction>> &predictions)
{
if(srcImages.size()==0||srcImages[0].empty()||srcImages[0].depth()!=CV_8U)
{
LOG_ERROR(stdout, "image error!\n");
return IMAGE_ERROR;
}
// 数据预处理
std::vector<cv::Mat> image;
for(int i =0;i<srcImages.size();++i)
{
//BGR转换为RGB
cv::Mat imgRGB;
cv::cvtColor(srcImages[i], imgRGB, cv::COLOR_BGR2RGB);
// 调整大小,使短边为256,保持长宽比
cv::Mat shrink;
float ratio = (float)256 / min(imgRGB.cols, imgRGB.rows);
if(imgRGB.rows > imgRGB.cols)
{
cv::resize(imgRGB, shrink, cv::Size(256, int(ratio * imgRGB.rows)), 0, 0);
}
else
{
cv::resize(imgRGB, shrink, cv::Size(int(ratio * imgRGB.cols), 256), 0, 0);
}
// 裁剪中心窗口为224*224
int start_x = shrink.cols/2 - 224/2;
int start_y = shrink.rows/2 - 224/2;
cv::Rect rect(start_x, start_y, 224, 224);
cv::Mat images = shrink(rect);
image.push_back(images);
}
// normalize并转换为NCHW
cv::Mat inputBlob;
Image2BlobParams image2BlobParams;
image2BlobParams.scalefactor=cv::Scalar(1/58.395, 1/57.12, 1/57.375);
image2BlobParams.mean=cv::Scalar(123.675, 116.28, 103.53);
image2BlobParams.swapRB=false;
blobFromImagesWithParams(image,inputBlob,image2BlobParams);
// 设置onnx的输入和输出名
std::vector<const char*> input_node_names = {"data"};
std::vector<const char*> output_node_names = {"resnetv24_dense0_fwd"};
// 初始化输入数据
std::array<int64_t, 4> inputShape{1, 3, 224, 224};
auto memoryInfo = Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault);
std::array<float, 3 * 224 * 224> input_image{};
float* input_test = (float*)inputBlob.data;
Ort::Value inputTensor = Ort::Value::CreateTensor<float>(memoryInfo, input_test, input_image.size(), inputShape.data(), inputShape.size());
std::vector<Ort::Value> intput_tensors;
intput_tensors.push_back(std::move(inputTensor));
// 进行推理
auto output_tensors = session->Run(Ort::RunOptions{nullptr}, input_node_names.data(), intput_tensors.data(), 1, output_node_names.data(), 1);
// 解析输出结果
const float* pdata = output_tensors[0].GetTensorMutableData<float>();
int numberOfClasses = 1000 ;
for(int i=0;i<srcImages.size();++i)
{
int startIndex=numberOfClasses*i;
std::vector<float> logit;
for(int j=0;j<numberOfClasses;++j)
{
logit.push_back(pdata[startIndex+j]);
}
std::vector<ResultOfPrediction> resultOfPredictions;
for(int j=0;j<numberOfClasses;++j)
{
ResultOfPrediction prediction;
prediction.label=j;
prediction.confidence=logit[j];
resultOfPredictions.push_back(prediction);
}
predictions.push_back(resultOfPredictions);
}
return SUCCESS;
}
}
#ifndef __CLASSIFIER_H__
#define __CLASSIFIER_H__
#include <onnxruntime/core/session/onnxruntime_cxx_api.h>
#include <CommonDefinition.h>
namespace ortSamples
{
class Classifier
{
public:
Classifier();
~Classifier();
ErrorCode Initialize(InitializationParameterOfClassifier initializationParameterOfClassifier);
ErrorCode Classify(const std::vector<cv::Mat> &srcImages,std::vector<std::vector<ResultOfPrediction>> &predictions);
private:
cv::FileStorage configurationFile;
Ort::Session *session;
cv::Size inputSize;
std::string inputName;
Ort::Env env = Ort::Env(ORT_LOGGING_LEVEL_ERROR, "ONNXRuntime");
Ort::SessionOptions sessionOptions = Ort::SessionOptions();
bool useInt8;
bool useFP16;
};
}
#endif
// 常用定义
#ifndef __COMMON_DEFINITION_H__
#define __COMMON_DEFINITION_H__
#include <opencv2/opencv.hpp>
namespace ortSamples
{
// 路径分隔符(Linux:‘/’,Windows:’\\’)
#ifdef _WIN32
#define PATH_SEPARATOR '\\'
#else
#define PATH_SEPARATOR '/'
#endif
#define CONFIG_FILE "../Resource/Configuration.xml"
typedef enum _ErrorCode
{
SUCCESS=0, // 0
MODEL_NOT_EXIST, // 模型不存在
CONFIG_FILE_NOT_EXIST, // 配置文件不存在
FAIL_TO_LOAD_MODEL, // 加载模型失败
FAIL_TO_OPEN_CONFIG_FILE, // 加载配置文件失败
IMAGE_ERROR, // 图像错误
}ErrorCode;
typedef struct _ResultOfPrediction
{
float confidence;
int label;
_ResultOfPrediction():confidence(0.0f),label(0){}
}ResultOfPrediction;
typedef struct _InitializationParameterOfClassifier
{
std::string parentPath;
std::string configFilePath;
}InitializationParameterOfClassifier;
}
#endif
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment