".github/vscode:/vscode.git/clone" did not exist on "4750cddf68aef4b073a5504a014691767f25f086"
Commit 8070f4bc authored by liucong's avatar liucong
Browse files

Merge remote-tracking branch 'resnet50_onnxruntime/master'

parents 19b523eb 4ee9fbce
#! /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
############################ 在线安装依赖 ###############################
#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)
# 配置OpenCV环境
find_package(OpenCV REQUIRED)
# 添加头文件路径
set(INCLUDE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/Src/
${CMAKE_CURRENT_SOURCE_DIR}/Src/Utility/
$ENV{DTKROOT}/include/
$ENV{DTKROOT}/onnxruntime/include/
${OpenCV_INCLUDE_DIRS}
${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
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})
target_link_libraries(ResNet50 ${OpenCV_LIBS} onnxruntime)
# 分类器
本示例通过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类型),然后通过构造一个字典输入模型执行推理,如果模型有多个输入,则在字典中需要添加多个输入数据。
- dcu_session.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,pathOfImage):
'''
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]]))
text = 'class=%s ' % (labels[a[0]])
saveimage(pathOfImage,text)
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
def saveimage(pathOfImage,text):
iimage = cv2.imread(pathOfImage, cv2.IMREAD_COLOR)
font = cv2.FONT_HERSHEY_SIMPLEX
font_scale = 0.5
font_color = (0, 0, 255)
font_thickness = 1
text_position = (5, 20)
cv2.putText(iimage, text, text_position, font, font_scale, font_color, font_thickness)
cv2.imwrite("./output_image.jpg", iimage)
cv2.destroyAllWindows()
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,pathOfImage)
opencv-python
numpy
\ No newline at end of file
# resnet50_ort_migraphx # ResNet50
## 论文
`Deep Residual Learning for Image Recognition`
- https://arxiv.org/abs/1512.03385
## 模型结构
ResNet50模型包含了49个卷积层、一个全连接层。
![ResNet50模型结构](./Doc/images/1.png)
## 算法原理
ResNet50使用了多个具有残差连接的残差块来解决梯度消失或梯度爆炸问题,并使得网络可以向更深层发展。
![算法原理](./Doc/images/2.png)
## 环境配置
### Docker(方法一)
拉取镜像:
```python
docker pull image.sourcefind.cn:5000/dcu/admin/base/migraphx:4.3.0-ubuntu20.04-dtk24.04.1-py3.10
```
创建并启动容器:
```
docker run --shm-size 16g --network=host --name=resnet50_onnxruntime -v /opt/hyhal:/opt/hyhal:ro --privileged --device=/dev/kfd --device=/dev/dri --group-add video --cap-add=SYS_PTRACE --security-opt seccomp=unconfined -v $PWD/resnet50_onnxruntime:/home/resnet50_onnxruntime -it <Your Image ID> /bin/bash
# 激活dtk
source /opt/dtk/env.sh
```
### Dockerfile(方法二)
```
cd ./docker
docker build --no-cache -t resnet50_onnxruntime:2.0 .
docker run --shm-size 16g --network=host --name=resnet50_onnxruntime -v /opt/hyhal:/opt/hyhal:ro --privileged --device=/dev/kfd --device=/dev/dri --group-add video --cap-add=SYS_PTRACE --security-opt seccomp=unconfined -v $PWD/resnet50_onnxruntime:/home/resnet50_onnxruntime -it <Your Image ID> /bin/bash
```
## 数据集
<!--
下载ImageNet数据集:[ImageNet (image-net.org)](https://image-net.org/) 数据集快速下载通道[ImageNet](http://113.200.138.88:18080/aidatasets/project-dependency/imagenet-2012)
```
data
|
train
|
n01440764
n01443537
...
val
|
n01440764
n01443537
...
```
-->
## 推理
### Python版本推理
采用ONNXRuntime框架使用DCU进行推理,下面介绍如何运行python代码示例,Python示例的详细说明见Doc目录下的Tutorial_Python.md。
#### 配置环境
```python
# 进入resnet50 onnxruntime工程根目录
cd <path_to_resnet50_onnxruntime>
# 安装依赖
pip install -r ./Python/requirements.txt
```
#### 运行示例
```python
# 进入resnet50 onnxruntime工程根目录
cd <path_to_resnet50_onnxruntime>
# 进入示例程序目录
cd Python/
# 运行示例
python Classifier.py
```
### C++版本推理
采用ONNXRuntime框架使用DCU进行推理,下面介绍如何运行C++代码示例,C++示例的详细说明见Doc目录下的Tutorial_Cpp.md。
#### 构建工程
```c++
cd <path_to_resnet50_onnxruntime>
rbuild build -d depend
```
#### 设置环境变量
将依赖库依赖加入环境变量LD_LIBRARY_PATH,在~/.bashrc中添加如下语句:
```c++
export LD_LIBRARY_PATH=<path_to_resnet50_onnxruntime>/depend/lib64/:$LD_LIBRARY_PATH
```
然后执行:
```
source ~/.bashrc
```
#### 运行示例
```c++
# 进入resnet50_onnxruntime工程根目录
cd <path_to_resnet50_onnxruntime>
# 进入build目录
cd build/
# 执行示例程序
./ResNet50
```
## result
### python版本
![result](./Doc/images/output_image.jpg)
### C++版本
![result](./Doc/images/output_image.jpg)
### 精度
## 应用场景
### 算法类别
`图像分类`
### 热点应用行业
`制造,政府,医疗,科研`
## 源码仓库及问题反馈
https://developer.hpccube.com/codes/modelzoo/resnet50_onnxruntime
## 参考资料
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>
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