Commit 89618e74 authored by liucong's avatar liucong
Browse files

精简代码

parent ce8766c6
......@@ -2,7 +2,7 @@
cmake_minimum_required(VERSION 3.5)
# 设置项目名
project(MIGraphX_Samples)
project(RetinaFace)
# 设置编译器
set(CMAKE_CXX_COMPILER g++)
......@@ -12,7 +12,6 @@ set(CMAKE_BUILD_TYPE release)
# 添加头文件路径
set(INCLUDE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/Src/
${CMAKE_CURRENT_SOURCE_DIR}/Src/Utility/
${CMAKE_CURRENT_SOURCE_DIR}/Src/RetinaFace/
$ENV{DTKROOT}/include/
${CMAKE_CURRENT_SOURCE_DIR}/depend/include/)
include_directories(${INCLUDE_PATH})
......@@ -37,10 +36,9 @@ link_libraries(${LIBRARY})
# 添加源文件
set(SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/Src/main.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Src/Sample.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Src/RetinaFace/DetectorRetinaFace.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Src/RetinaFace.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Src/Utility/CommonUtility.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Src/Utility/Filesystem.cpp)
# 添加可执行目标
add_executable(MIGraphX_Samples ${SOURCE_FILES})
add_executable(RetinaFace ${SOURCE_FILES})
# RetinaFace人脸检测器
RetinaFace是目前被广泛使用的一种人脸检测器模型,本示例主要说明了如何在MIGraphX中使用RetinaFace人脸检测器。
## 模型简介
RetinaFace是一个经典的人脸检测模型(https://arxiv.org/abs/1905.00641),采用了SSD架构。
<img src="./Image/RetinaFace_01.png" style="zoom:60%;" align=center>
## 检测器参数设置
samples工程中的Resource/Configuration.xml文件的DetectorRetinaFace节点表示RetinaFace检测器的参数,这些参数是根据Pytorch_Retinaface工程中的data/config.py文件中的cfg_mnet来设置的,下面我们看一下是如何通过cfg_mnet来设置的。
- **设置anchor大小**
cfg_mnet的min_sizes表示每一个priorbox层的anchor大小,我们可以看到该模型一共有3个priorbox层,第一层anchor大小为16和32,第二层anchor大小为64和128,第三层anchor大小为256和512,注意:**Configuration.xml中priorbox层的顺序要与onnx文件中的输出节点顺序保持一致**,通过netron (https://netron.app/) 可以看到首先输出的是467和470节点,这两个节点对应的是特征图最大的检测层,所以对应的anchor大小为16和32,最后输出的是469和472节点,这两个节点对应的是特征图最小的检测层,所以对应的anchor大小为256和512,
<img src="./Image/RetinaFace_03.png" style="zoom:70%;" align=center>
所以Configuration.xml配置文件中的参数设置如下:
```
<!--priorbox层的个数-->
<PriorBoxLayerNumber>3</PriorBoxLayerNumber>
<!--每个priorbox层的minisize-->
<MinSize11>16</MinSize11>
<MinSize12>32</MinSize12>
<MinSize21>64</MinSize21>
<MinSize22>128</MinSize22>
<MinSize31>256</MinSize31>
<MinSize32>512</MinSize32>
```
- **设置Flip和Clip**
cfg_mnet中的clip为False,所以Configuration.xml中对应的参数设置为0即可,由于只有一个宽高比为1的anchor,所以Flip设置为0。
```
<Flip1>0</Flip1>
<Flip2>0</Flip2>
<Flip3>0</Flip3>
<Clip1>0</Clip1>
<Clip2>0</Clip2>
<Clip3>0</Clip3>
```
- **设置anchor的宽高比**
由于RetinaFace只包含宽高比为1的anchor,所以这里不需要设置宽高比。
- **设置每个priorbox层的步长**
cfg_mnet中的steps表示每个priorbox层的步长,所以三个priorbox的步长依次为8,16,32,对应的Configuration.xml的设置如下:
```
<!--每个priorbox层的step-->
<PriorBoxStepWidth1>8</PriorBoxStepWidth1><!--第一个priorbox层的step的width-->
<PriorBoxStepWidth2>16</PriorBoxStepWidth2>
<PriorBoxStepWidth3>32</PriorBoxStepWidth3>
<PriorBoxStepHeight1>8</PriorBoxStepHeight1><!--第一个priorbox层的step的height-->
<PriorBoxStepHeight2>16</PriorBoxStepHeight2>
<PriorBoxStepHeight3>32</PriorBoxStepHeight3>
```
- **设置DetectionOutput层的参数**
由于本示例模型是一个人脸检测模型,所以只有两类目标(背景和人脸),所以ClassNumber为2,DetectionOutput层的其他参数可以根据实际情况做微调,本示例中采用如下设置:
```
<TopK>400</TopK>
<KeepTopK>200</KeepTopK>
<NMSThreshold>0.3</NMSThreshold>
<ConfidenceThreshold>0.9</ConfidenceThreshold>
```
## 预处理
在将数据输入到模型之前,需要对图像做如下预处理操作:
- 减去均值,RetinaFace训练的时候对图像做了减均值的操作(train.py文件中的第38行),注意均值的顺序是BGR顺序。
- 转换数据排布为NCHW
本示例代码采用了OpenCV的cv::dnn::blobFromImage()函数实现了预处理操作:
```
ErrorCode DetectorRetinaFace::Detect(const cv::Mat &srcImage,std::vector<ResultOfDetection> &resultsOfDetection)
{
...
// 预处理并转换为NCHW
cv::Mat inputBlob;
blobFromImage(srcImage, // 输入数据
inputBlob, // 输出数据
scale, // 1
inputSize, // SSD输入大小,本示例为640x480
meanValue,// (104,117,123)
swapRB, // false
false);
...
}
```
## 推理
模型转换成功并且设置好检测器参数之后就可以执行推理了。
```
ErrorCode DetectorRetinaFace::Detect(const cv::Mat &srcImage,std::vector<ResultOfDetection> &resultsOfDetection)
{
...
// 输入数据
std::unordered_map<std::string, migraphx::shape> inputData;
inputData[inputName]= migraphx::argument{inputShape, (float*)inputBlob.data};
// 推理
std::vector<migraphx::argument> inferenceResults=net.eval(inputData);
vector<vector<float>> regressions;
vector<vector<float>> classifications;
for(int i=0;i<ssdParameter.numberOfPriorBoxLayer;++i) // 执行Permute操作
{
int numberOfPriorBox=ssdParameter.detectInputChn[i]/(4*(ssdParameter.priorBoxHeight[i] * ssdParameter.priorBoxWidth[i]));
// BboxHead
std::vector<float> regression;
migraphx::argument result0 = inferenceResults[2*i];
result0.visit([&](auto output) { regression.assign(output.begin(), output.end()); });
regression=PermuteLayer(regression,ssdParameter.priorBoxWidth[i],ssdParameter.priorBoxHeight[i],numberOfPriorBox*4);
regressions.push_back(regression);
// ClassHead
std::vector<float> classification;
migraphx::argument result1 = inferenceResults[2*i+1];
result1.visit([&](auto output) { classification.assign(output.begin(), output.end()); });
classification=PermuteLayer(classification,ssdParameter.priorBoxWidth[i],ssdParameter.priorBoxHeight[i],numberOfPriorBox*ssdParameter.classNum);
classifications.push_back(classification);
}
// 对推理结果进行处理,得到最后SSD检测的结果
GetResult(classifications,regressions,resultsOfDetection);
// 转换到原图坐标
for(int i=0;i<resultsOfDetection.size();++i)
{
float ratioOfWidth=(1.0*srcImage.cols)/inputSize.width;
float ratioOfHeight=(1.0*srcImage.rows)/inputSize.height;
resultsOfDetection[i].boundingBox.x*=ratioOfWidth;
resultsOfDetection[i].boundingBox.width*=ratioOfWidth;
resultsOfDetection[i].boundingBox.y*=ratioOfHeight;
resultsOfDetection[i].boundingBox.height*=ratioOfHeight;
}
// 按照置信度排序
sort(resultsOfDetection.begin(), resultsOfDetection.end(),CompareConfidence);
return SUCCESS;
}
```
- net.eval(inputData)返回推理结果,顺序与onnx输出保持一致,可以通过netron查看输出节点顺序,其中inferenceResults[2 * i]表示每个检测层的BboxHead的输出,inferenceResults [2 * i + 1]表示每个检测层的ClassHead的输出。
- 经过PermuteLayer处理之后的所有检测层数据通过GetResult()得到最后的输出结果,注意这里的输出结果还不是最后的检测结果,最后需要转换到原图坐标才能够得到最终的检测结果。
# RetinaFace人脸检测器
RetinaFace是目前被广泛使用的一种人脸检测器模型,本示例主要说明了如何在MIGraphX中使用RetinaFace人脸检测器。
## 模型简介
RetinaFace是一个经典的人脸检测模型(https://arxiv.org/abs/1905.00641),采用了SSD架构。
<img src="./Image/RetinaFace_01.png" style="zoom:60%;" align=center>
本示例采用了如下的开源实现:https://github.com/biubug6/Pytorch_Retinaface,作者提供了restnet50 和mobilenet0.25两个预训练模型,本示例使用了mobilenet0.25预训练模型,将mobilenet0.25预训练模型下载下来后,保存到Pytorch_Retinaface工程的weights目录。
## 模型转换
通过下面的步骤可以将mobilenet0.25预训练转换成onnx文件:
- 修改data/config.py:将cfg_mnet中的'pretrain': True,修改为'pretrain': False,
- 执行如下命令就可以将weights目录下的mobilenet0.25_Final.pth模型转换为onnx文件了
```
# 进入Pytorch_Retinaface工程根目录
cd <path_to_Pytorch_Retinaface>
# 转换模型
python convert_to_onnx.py
```
注意:如果需要修改模型的输入大小,可以修改args.long_side参数,默认为640x640。
模型转换成功后,会在当前目录生成FaceDetector.onnx文件,利用该模型就可以使用MIGraphX进行推理了,本示例在samples工程中的Python/RetinaFace目录中提供了已经修改好的代码,在该目录下执行python convert_to_onnx.py可以直接生成onnx文件,注意运行前需要安装依赖,你可以在非DCU环境下执行模型转换,如果需要在DCU环境下安装依赖,依赖安装方法参考下文中运行示例一节中的说明。
## 推理
Pytorch_Retinaface工程提供了原始Pytorch版本的推理测试代码detect.py,我们只需要将其中使用Pytorch推理的部分转换为MIGraphX推理就可以了,samples工程中的Python/RetinaFace/detect.py文件为已经转换好的推理代码,下面我们看一下是如何转换的:
- 将加载模型部分修改为migraphx的方式加载
```
# 加载模型
model = migraphx.parse_onnx("./FaceDetector.onnx")
```
- 模型加载成功后,需要通过model.compile进行编译,可以通过device_id设置使用哪一块设备
```
model.compile(t=migraphx.get_target("gpu"),device_id=0)
```
- 编译成功后,就可以输入图像进行推理了,由于本示例使用的onnx模型的输入大小是640x640,所以对于输入图像需要先resize到640x640
```
# resize到onnx模型输入大小
image_path = "./curve/test.jpg"
img_raw = cv2.imread(image_path, cv2.IMREAD_COLOR)
img_raw = cv2.resize(img_raw, (640,640))
```
- 预处理部分跟作者的代码保持一致即可,这部分不需要修改
- 下面是最关键的一步,将pytorch推理net(img)转换为MIGraphX推理migraphx_run(model,args.cpu,img),其中migraphx_run实现如下:
```
def migraphx_run(model,cpu,data_tensor):
# 将输入的tensor数据转换为numpy
if cpu:
data_numpy=data_tensor.cpu().numpy()
device = torch.device("cpu")
else:
data_numpy=data_tensor.detach().cpu().numpy()
device = torch.device("cuda")
img_data = np.zeros(data_numpy.shape).astype("float32")
for i in range(data_numpy.shape[0]):
img_data[i, :, :, :] = data_numpy[i, :, :, :]
# 执行推理
result = model.run({model.get_parameter_names()[0]: img_data})
# 将结果转换为tensor
result0=torch.from_numpy(np.array(result[0], copy=False)).to(device)
result1=torch.from_numpy(np.array(result[1], copy=False)).to(device)
result2=torch.from_numpy(np.array(result[2], copy=False)).to(device)
return (result0,result1,result2)
```
首先需要将tensor数据转换为numpy,转换好的数据保存在img_data中,然后通过{model.get_parameter_names()[0]: img_data}创建MIGraphX的输入数据,并使用model.run执行推理,result为推理返回的结果,然后通过torch.from_numpy的方式转换为tensor类型并返回,为了保持与Pytorch推理结果的格式一致,转换的时候需要注意输出结果的顺序,MIGraphX的输出结果顺序与onnx中保持一致,可以通过netron (https://netron.app/) 查看:
<img src="./Image/RetinaFace_02.png" style="zoom:80%;" align=center>
所以第一个输出结果对应pytorch结果中的loc,第二个对应conf, 第三个对应landms,所以返回的结果是(result0,result1,result2)。
- 推理执行成功后,需要执行后处理才能得到最终的检测结果,由于我们模型推理输出的格式与原始的Pytorch模型输出是一致的,所以后处理可以直接使用原来的,不需要修改。
......@@ -12,7 +12,11 @@ RetinaFace模型 有几个主要特点:
- 引入 SSH 算法的 Context Modeling;
- 多任务训练,提供额外的监督信息。
## 构建安装
## python版本推理
下面介绍如何运行python代码示例,具体推理代码解析,在Doc/Tutorial_Python.md中有详细说明。
### 构建安装
在光源可拉取推理的docker镜像,RetinaFace工程推荐的镜像如下:
......@@ -20,6 +24,31 @@ RetinaFace模型 有几个主要特点:
docker pull image.sourcefind.cn:5000/dcu/admin/base/custom:ort1.14.0_migraphx3.0.0-dtk22.10.1
```
### 推理示例
RetinaFace模型的推理示例程序是RetinaFace_infer_migraphx.py,进入python文件夹使用如下命令运行该推理示例:
```
# 进入python示例目录
cd ./Python
# 安装依赖
pip install -r requirements.txt
# 运行程序
python RetinaFace_infer_migraphx.py
```
程序运行结束会在当前目录生成RetinaFace检测结果图像。
<img src="./Resource/Images/Result_2.jpg" alt="Result_2" style="zoom:67%;" />
## C++版本推理
下面介绍如何运行C++代码示例,具体推理代码解析,在Doc/Tutorial_Cpp.md目录中有详细说明。
参考Python版本推理中的构建安装,在光源中拉取推理的docker镜像。
### 安装Opencv依赖
```python
......@@ -63,38 +92,24 @@ export LD_LIBRARY_PATH=<path_to_migraphx_samples>/depend/lib/:$LD_LIBRARY_PATH
source ~/.bashrc
```
## 推理
### C++版本推理
成功编译RetinaFace工程后,在build目录下输入如下命令运行该示例:
```
./MIGraphX_Samples 0
```
程序运行结束会在build目录生成RetinaFace人脸检测结果图像。
<img src="./Resource/Images/Result_1.jpg" alt="Result" style="zoom:67%;" />
### python版本推理
### 推理示例
RetinaFace模型的推理示例程序是RetinaFace_infer_migraphx.py,进入python文件夹使用如下命令运行该推理示例:
成功编译RetinaFace工程后,执行如下命令运行该示例:
```
# 进入python示例目录
cd ./Python
# 进入migraphx samples工程根目录
cd <path_to_migraphx_samples>
# 安装依赖
pip install -r requirements.txt
# 进入build目录
cd ./build/
# 运行程序
python RetinaFace_infer_migraphx.py
# 执行示例程序
./RetinaFace
```
程序运行结束会在当前目录生成RetinaFace检测结果图像。
程序运行结束会在build目录生成RetinaFace人脸检测结果图像。
<img src="./Resource/Images/Result_2.jpg" alt="Result_2" style="zoom:67%;" />
<img src="./Resource/Images/Result_1.jpg" alt="Result" style="zoom:67%;" />
## 历史版本
......
......@@ -3,7 +3,7 @@
<!--RetinaFace检测器-->
<DetectorRetinaFace>
<ModelPath>"../Resource/Models/Detector/RetinaFace/mobilenet0.25_Final.onnx"</ModelPath>
<ModelPath>"../Resource/Models/mobilenet0.25_Final.onnx"</ModelPath>
<Scale>1.0</Scale><!--缩放尺度-->
<MeanValue1>104</MeanValue1><!--均值,顺序为bgr-->
<MeanValue2>117</MeanValue2>
......
#include <DetectorRetinaFace.h>
#include <RetinaFace.h>
#include <migraphx/onnx.hpp>
#include <migraphx/gpu/target.hpp>
#include <migraphx/gpu/hip.hpp>
#include <migraphx/generate.hpp>
#include <migraphx/quantization.hpp>
#include <opencv2/dnn.hpp>
#include <CommonUtility.h>
#include <Filesystem.h>
#include <SimpleLog.h>
using namespace cv::dnn;
namespace migraphxSamples
{
......@@ -27,7 +24,7 @@ namespace migraphxSamples
#define SSD_ALIGN_16 16
#define SSD_ALIGN16(number) ((number + SSD_ALIGN_16-1) / SSD_ALIGN_16*SSD_ALIGN_16)
DetectorRetinaFace::DetectorRetinaFace():logFile(NULL)
DetectorRetinaFace::DetectorRetinaFace()
{
}
......@@ -41,18 +38,23 @@ DetectorRetinaFace::~DetectorRetinaFace()
ErrorCode DetectorRetinaFace::Initialize(InitializationParameterOfDetector initializationParameterOfDetector)
{
// 初始化(获取日志文件,加载配置文件等)
ErrorCode errorCode=DoCommonInitialization(initializationParameterOfDetector);
if(errorCode!=SUCCESS)
// 读取配置文件
std::string configFilePath=initializationParameterOfDetector.configFilePath;
if(Exists(configFilePath)==false)
{
LOG_ERROR(logFile,"fail to DoCommonInitialization\n");
return errorCode;
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(logFile,"succeed to DoCommonInitialization\n");
LOG_INFO(stdout, "succeed to open configuration file\n");
// 获取配置文件参数
FileNode netNode = configurationFile["DetectorRetinaFace"];
string modelPath=initializationParameter.parentPath+(string)netNode["ModelPath"];
cv::FileNode netNode = configurationFile["DetectorRetinaFace"];
string modelPath=(string)netNode["ModelPath"];
scale=(float)netNode["Scale"];
meanValue.val[0]=(float)netNode["MeanValue1"];
meanValue.val[1]=(float)netNode["MeanValue2"];
......@@ -65,17 +67,21 @@ ErrorCode DetectorRetinaFace::Initialize(InitializationParameterOfDetector initi
// 加载模型
if(Exists(modelPath)==false)
{
LOG_ERROR(logFile,"%s not exist!\n",modelPath.c_str());
LOG_ERROR(stdout,"%s not exist!\n",modelPath.c_str());
return MODEL_NOT_EXIST;
}
net = migraphx::parse_onnx(modelPath);
LOG_INFO(logFile,"succeed to load model: %s\n",GetFileName(modelPath).c_str());
LOG_INFO(stdout,"succeed to load model: %s\n",GetFileName(modelPath).c_str());
// 获取模型输入属性
std::pair<std::string, shape> inputAttribute=*(net.get_parameter_shapes().begin());
inputName=inputAttribute.first;
inputShape=inputAttribute.second;
inputSize=Size(inputShape.lens()[3],inputShape.lens()[2]);
std::unordered_map<std::string, migraphx::shape> inputMap=net.get_parameter_shapes();
inputName=inputMap.begin()->first;
inputShape=inputMap.begin()->second;
int N=inputShape.lens()[0];
int C=inputShape.lens()[1];
int H=inputShape.lens()[2];
int W=inputShape.lens()[3];
inputSize=cv::Size(W,H);
// 设置模型为GPU模式
migraphx::target gpuTarget = migraphx::gpu::target{};
......@@ -84,28 +90,28 @@ ErrorCode DetectorRetinaFace::Initialize(InitializationParameterOfDetector initi
if(useInt8)
{
// 创建量化校准数据,建议使用测试集中的多张典型图像
cv::Mat srcImage=imread("../Resource/Images/FaceDetect_2.jpg",1);
cv::Mat srcImage=cv::imread("../Resource/Images/FaceDetect_2.jpg",1);
std::vector<cv::Mat> srcImages;
for(int i=0;i<inputShape.lens()[0];++i)
{
srcImages.push_back(srcImage);
}
cv::Mat inputBlob;
blobFromImages(srcImages,
cv::dnn::blobFromImages(srcImages,
inputBlob,
scale,
inputSize,
meanValue,
swapRB,
false);
migraphx::parameter_map inputData;
std::unordered_map<std::string, migraphx::argument> inputData;
inputData[inputName]= migraphx::argument{inputShape, (float*)inputBlob.data};
std::vector<migraphx::parameter_map> calibrationData = {inputData};
std::vector<std::unordered_map<std::string, migraphx::argument>> calibrationData = {inputData};
// INT8量化
migraphx::quantize_int8(net, gpuTarget, calibrationData);
}
if(useFP16)
else if(useFP16)
{
migraphx::quantize_fp16(net);
}
......@@ -113,64 +119,30 @@ ErrorCode DetectorRetinaFace::Initialize(InitializationParameterOfDetector initi
// 编译模型
migraphx::compile_options options;
options.device_id=0; // 设置GPU设备,默认为0号设备
options.offload_copy=true; // 设置offload_copy
options.offload_copy=true;
net.compile(gpuTarget,options);
LOG_INFO(logFile,"succeed to compile model: %s\n",GetFileName(modelPath).c_str());
LOG_INFO(stdout,"succeed to compile model: %s\n",GetFileName(modelPath).c_str());
// Run once by itself
migraphx::parameter_map inputData;
inputData[inputName]=migraphx::generate_argument(inputShape);
// warm up
std::unordered_map<std::string, migraphx::argument> inputData;
inputData[inputName]=migraphx::argument{inputShape};
net.eval(inputData);
// log
LOG_INFO(logFile,"InputSize:%dx%d\n",inputSize.width,inputSize.height);
LOG_INFO(logFile,"InputName:%s\n",inputName.c_str());
LOG_INFO(logFile,"Scale:%.6f\n",scale);
LOG_INFO(logFile,"Mean:%.2f,%.2f,%.2f\n",meanValue.val[0],meanValue.val[1],meanValue.val[2]);
LOG_INFO(logFile,"SwapRB:%d\n",(int)swapRB);
LOG_INFO(logFile,"Crop:%d\n",(int)crop);
LOG_INFO(logFile,"UseInt8:%d\n",(int)useInt8);
LOG_INFO(logFile,"UseFP16:%d\n",(int)useFP16);
LOG_INFO(stdout,"InputSize:%dx%d\n",inputSize.width,inputSize.height);
LOG_INFO(stdout,"InputName:%s\n",inputName.c_str());
LOG_INFO(stdout,"Scale:%.6f\n",scale);
LOG_INFO(stdout,"Mean:%.2f,%.2f,%.2f\n",meanValue.val[0],meanValue.val[1],meanValue.val[2]);
LOG_INFO(stdout,"SwapRB:%d\n",(int)swapRB);
LOG_INFO(stdout,"Crop:%d\n",(int)crop);
LOG_INFO(stdout,"UseInt8:%d\n",(int)useInt8);
LOG_INFO(stdout,"UseFP16:%d\n",(int)useFP16);
// 读取SSD 参数
GetSSDParameter();
return SUCCESS;
}
ErrorCode DetectorRetinaFace::DoCommonInitialization(InitializationParameterOfDetector initializationParameterOfDetector)
{
initializationParameter=initializationParameterOfDetector;
// 获取日志文件
logFile=LogManager::GetInstance()->GetLogFile(initializationParameter.logName);
// 加载配置文件
std::string configFilePath=initializationParameter.configFilePath;
if(!Exists(configFilePath))
{
LOG_ERROR(logFile, "no configuration file!\n");
return CONFIG_FILE_NOT_EXIST;
}
if(!configurationFile.open(configFilePath, FileStorage::READ))
{
LOG_ERROR(logFile, "fail to open configuration file\n");
return FAIL_TO_OPEN_CONFIG_FILE;
}
LOG_INFO(logFile, "succeed to open configuration file\n");
// 修改父路径
std::string &parentPath = initializationParameter.parentPath;
if (!parentPath.empty())
{
if(!IsPathSeparator(parentPath[parentPath.size() - 1]))
{
parentPath+=PATH_SEPARATOR;
}
}
return SUCCESS;
}
ErrorCode DetectorRetinaFace::Detect(const cv::Mat &srcImage,std::vector<ResultOfDetection> &resultsOfDetection)
......@@ -178,13 +150,13 @@ ErrorCode DetectorRetinaFace::Detect(const cv::Mat &srcImage,std::vector<ResultO
if(srcImage.empty()||srcImage.type()!=CV_8UC3)
{
LOG_ERROR(logFile, "image error!\n");
LOG_ERROR(stdout, "image error!\n");
return IMAGE_ERROR;
}
// 预处理并转换为NCHW
// 数据预处理并转换为NCHW格式
cv::Mat inputBlob;
blobFromImage(srcImage,
cv::dnn::blobFromImage(srcImage,
inputBlob,
scale,
inputSize,
......@@ -192,14 +164,14 @@ ErrorCode DetectorRetinaFace::Detect(const cv::Mat &srcImage,std::vector<ResultO
swapRB,
false);
// 输入数据
migraphx::parameter_map inputData;
// 创建输入数据
std::unordered_map<std::string, migraphx::argument> inputData;
inputData[inputName]= migraphx::argument{inputShape, (float*)inputBlob.data};
// 推理
std::vector<migraphx::argument> inferenceResults=net.eval(inputData);
vector<vector<float>> regressions;
vector<vector<float>> classifications;
std::vector<std::vector<float>> regressions;
std::vector<std::vector<float>> classifications;
for(int i=0;i<ssdParameter.numberOfPriorBoxLayer;++i) // 执行Permute操作
{
int numberOfPriorBox=ssdParameter.detectInputChn[i]/(4*(ssdParameter.priorBoxHeight[i] * ssdParameter.priorBoxWidth[i]));
......@@ -243,7 +215,7 @@ ErrorCode DetectorRetinaFace::Detect(const cv::Mat &srcImage,std::vector<ResultO
void DetectorRetinaFace::GetSSDParameter()
{
FileNode rootNode = configurationFile["DetectorRetinaFace"];
cv::FileNode rootNode = configurationFile["DetectorRetinaFace"];
ssdParameter.numberOfPriorBoxLayer=(int)rootNode["PriorBoxLayerNumber"];
ssdParameter.srcImageHeight = inputSize.height;
......@@ -262,7 +234,7 @@ void DetectorRetinaFace::GetSSDParameter()
while(true)
{
sprintf(nodeName, "MinSize%d%d", (i + 1),++j);
FileNode miniSizeNode = rootNode[nodeName];
cv::FileNode miniSizeNode = rootNode[nodeName];
if(miniSizeNode.empty())
{
break;
......@@ -280,7 +252,7 @@ void DetectorRetinaFace::GetSSDParameter()
while(true)
{
sprintf(nodeName, "MaxSize%d%d", (i + 1),++j);
FileNode maxSizeNode = rootNode[nodeName];
cv::FileNode maxSizeNode = rootNode[nodeName];
if(maxSizeNode.empty())
{
break;
......@@ -325,7 +297,7 @@ void DetectorRetinaFace::GetSSDParameter()
while(true)
{
sprintf(nodeName, "AspectRatio%d%d", (i + 1),++j);
FileNode aspectRatioNode = rootNode[nodeName];
cv::FileNode aspectRatioNode = rootNode[nodeName];
if(aspectRatioNode.empty())
{
break;
......
#ifndef __DETECTOR_RETINAFACE_H__
#define __DETECTOR_RETINAFACE_H__
#include <string>
#include <migraphx/program.hpp>
#include <opencv2/opencv.hpp>
#include <CommonDefinition.h>
#include <SSDDefinition.h>
using namespace std;
using namespace cv;
using namespace migraphx;
namespace migraphxSamples
{
......@@ -26,7 +21,6 @@ public:
ErrorCode Detect(const cv::Mat &srcImage,std::vector<ResultOfDetection> &resultsOfDetection);
private:
ErrorCode DoCommonInitialization(InitializationParameterOfDetector initializationParameterOfDetector);
void GetSSDParameter();
void GetResult(const std::vector<std::vector<float>> &classification,const std::vector<std::vector<float>> &regression,std::vector<ResultOfDetection> &resultsOfDetection);
......@@ -44,12 +38,10 @@ private:
private:
cv::FileStorage configurationFile;
InitializationParameterOfDetector initializationParameter;
FILE *logFile;
migraphx::program net;
cv::Size inputSize;
string inputName;
std::string inputName;
migraphx::shape inputShape;
float scale;
......
#include <Sample.h>
#include <opencv2/dnn.hpp>
#include <SimpleLog.h>
#include <Filesystem.h>
#include <DetectorRetinaFace.h>
#include <fstream>
using namespace std;
using namespace cv;
using namespace cv::dnn;
using namespace migraphx;
using namespace migraphxSamples;
void Sample_DetectorRetinaFace()
{
// 创建RetinaFace检测器
DetectorRetinaFace detector;
InitializationParameterOfDetector initParamOfDetectorRetinaFace;
initParamOfDetectorRetinaFace.parentPath = "";
initParamOfDetectorRetinaFace.configFilePath = CONFIG_FILE;
initParamOfDetectorRetinaFace.logName = "";
ErrorCode errorCode=detector.Initialize(initParamOfDetectorRetinaFace);
if(errorCode!=SUCCESS)
{
LOG_ERROR(stdout, "fail to initialize detector!\n");
exit(-1);
}
LOG_INFO(stdout, "succeed to initialize detector\n");
// 读取测试图片
Mat srcImage=imread("../Resource/Images/FaceDetect.jpg",1);
// 推理
std::vector<ResultOfDetection> predictions;
double time1 = getTickCount();
detector.Detect(srcImage,predictions);
double time2 = getTickCount();
double elapsedTime = (time2 - time1)*1000 / getTickFrequency();
LOG_INFO(stdout, "inference time:%f ms\n", elapsedTime);
// 获取推理结果
LOG_INFO(stdout,"========== Detection Results ==========\n");
for(int i=0;i<predictions.size();++i)
{
ResultOfDetection result=predictions[i];
cv::rectangle(srcImage,result.boundingBox,Scalar(0,255,255),2);
LOG_INFO(stdout,"box:%d %d %d %d,label:%d,confidence:%f\n",predictions[i].boundingBox.x,
predictions[i].boundingBox.y,predictions[i].boundingBox.width,predictions[i].boundingBox.height,predictions[i].classID,predictions[i].confidence);
}
imwrite("Result.jpg",srcImage);
LOG_INFO(stdout,"Detection results have been saved to ./Result.jpg\n");
}
\ No newline at end of file
// 示例程序
#ifndef __SAMPLE_H__
#define __SAMPLE_H__
// RetinaFace sample
void Sample_DetectorRetinaFace();
#endif
\ No newline at end of file
// 常用数据类型和宏定义
// 常用定义
#ifndef __COMMON_DEFINITION_H__
#define __COMMON_DEFINITION_H__
#include <string>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
namespace migraphxSamples
{
......@@ -21,20 +17,7 @@ namespace migraphxSamples
#define CONFIG_FILE "../Resource/Configuration.xml"
typedef struct __Time
{
string year;
string month;
string day;
string hour;
string minute;
string second;
string millisecond; // ms
string microsecond; // us
string weekDay;
}_Time;
typedef enum _ErrorCode
typedef enum _ErrorCode
{
SUCCESS=0, // 0
MODEL_NOT_EXIST, // 模型不存在
......@@ -44,7 +27,7 @@ typedef enum _ErrorCode
IMAGE_ERROR, // 图像错误
}ErrorCode;
typedef struct _ResultOfPrediction
typedef struct _ResultOfPrediction
{
float confidence;
int label;
......@@ -52,27 +35,24 @@ typedef struct _ResultOfPrediction
}ResultOfPrediction;
typedef struct _ResultOfDetection
typedef struct _ResultOfDetection
{
Rect boundingBox;
cv::Rect boundingBox;
float confidence;
int classID;
string className;
std::string className;
bool exist;
_ResultOfDetection():confidence(0.0f),classID(0),exist(true){}
}ResultOfDetection;
typedef struct _InitializationParameterOfDetector
typedef struct _InitializationParameterOfDetector
{
std::string parentPath;
std::string configFilePath;
cv::Size inputSize;
std::string logName;
}InitializationParameterOfDetector;
}
#endif
......
#include <CommonUtility.h>
#include <assert.h>
#include <ctype.h>
#include <time.h>
#include <stdlib.h>
#include <algorithm>
#include <sstream>
#include <vector>
#ifdef _WIN32
#include <io.h>
#include <direct.h>
#include <Windows.h>
#else
#include <unistd.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/time.h>
#endif
#include <SimpleLog.h>
namespace migraphxSamples
{
_Time GetCurrentTime3()
{
_Time currentTime;
#if (defined WIN32 || defined _WIN32)
SYSTEMTIME systemTime;
GetLocalTime(&systemTime);
char temp[8] = { 0 };
sprintf(temp, "%04d", systemTime.wYear);
currentTime.year=string(temp);
sprintf(temp, "%02d", systemTime.wMonth);
currentTime.month=string(temp);
sprintf(temp, "%02d", systemTime.wDay);
currentTime.day=string(temp);
sprintf(temp, "%02d", systemTime.wHour);
currentTime.hour=string(temp);
sprintf(temp, "%02d", systemTime.wMinute);
currentTime.minute=string(temp);
sprintf(temp, "%02d", systemTime.wSecond);
currentTime.second=string(temp);
sprintf(temp, "%03d", systemTime.wMilliseconds);
currentTime.millisecond=string(temp);
sprintf(temp, "%d", systemTime.wDayOfWeek);
currentTime.weekDay=string(temp);
#else
struct timeval tv;
struct tm *p;
gettimeofday(&tv, NULL);
p = localtime(&tv.tv_sec);
char temp[8]={0};
sprintf(temp,"%04d",1900+p->tm_year);
currentTime.year=string(temp);
sprintf(temp,"%02d",1+p->tm_mon);
currentTime.month=string(temp);
sprintf(temp,"%02d",p->tm_mday);
currentTime.day=string(temp);
sprintf(temp,"%02d",p->tm_hour);
currentTime.hour=string(temp);
sprintf(temp,"%02d",p->tm_min);
currentTime.minute=string(temp);
sprintf(temp,"%02d",p->tm_sec);
currentTime.second=string(temp);
sprintf(temp,"%03d",tv.tv_usec/1000);
currentTime.millisecond = string(temp);
sprintf(temp, "%03d", tv.tv_usec % 1000);
currentTime.microsecond = string(temp);
sprintf(temp, "%d", p->tm_wday);
currentTime.weekDay = string(temp);
#endif
return currentTime;
}
std::vector<std::string> SplitString(std::string str, std::string separator)
{
std::string::size_type pos;
std::vector<std::string> result;
str+=separator;//扩展字符串以方便操作
int size=str.size();
for(int i=0; i<size; i++)
{
pos=str.find(separator,i);
if(pos<size)
{
std::string s=str.substr(i,pos-i);
result.push_back(s);
i=pos+separator.size()-1;
}
}
return result;
}
bool CompareConfidence(const ResultOfDetection &L,const ResultOfDetection &R)
{
return L.confidence > R.confidence;
......@@ -109,7 +13,7 @@ bool CompareArea(const ResultOfDetection &L,const ResultOfDetection &R)
return L.boundingBox.area() > R.boundingBox.area();
}
void NMS(vector<ResultOfDetection> &detections, float IOUThreshold)
void NMS(std::vector<ResultOfDetection> &detections, float IOUThreshold)
{
// sort
std::sort(detections.begin(), detections.end(), CompareConfidence);
......
......@@ -3,23 +3,16 @@
#ifndef __COMMON_UTILITY_H__
#define __COMMON_UTILITY_H__
#include <mutex>
#include <string>
#include <vector>
#include <CommonDefinition.h>
using namespace std;
namespace migraphxSamples
{
// 分割字符串
std::vector<std::string> SplitString(std::string str,std::string separator);
// 排序规则: 按照置信度或者按照面积排序
bool CompareConfidence(const ResultOfDetection &L,const ResultOfDetection &R);
bool CompareArea(const ResultOfDetection &L,const ResultOfDetection &R);
// 非极大抑制
void NMS(std::vector<ResultOfDetection> &detections, float IOUThreshold);
}
......
......@@ -11,12 +11,7 @@
#include <unistd.h>
#include <dirent.h>
#endif
#include <CommonUtility.h>
#include <opencv2/opencv.hpp>
#include <SimpleLog.h>
using namespace cv;
// 路径分隔符(Linux:‘/’,Windows:’\\’)
#ifdef _WIN32
#define PATH_SEPARATOR '\\'
......@@ -24,9 +19,31 @@ using namespace cv;
#define PATH_SEPARATOR '/'
#endif
using namespace std;
namespace migraphxSamples
{
static std::vector<std::string> SplitString(std::string str, std::string separator)
{
std::string::size_type pos;
std::vector<std::string> result;
str+=separator;//扩展字符串以方便操作
int size=str.size();
for(int i=0; i<size; i++)
{
pos=str.find(separator,i);
if(pos<size)
{
std::string s=str.substr(i,pos-i);
result.push_back(s);
i=pos+separator.size()-1;
}
}
return result;
}
#if defined _WIN32 || defined WINCE
const char dir_separators[] = "/\\";
......@@ -293,7 +310,7 @@ namespace migraphxSamples
}
else
{
LOG_INFO(stdout, "could not open directory: %s", directory.c_str());
printf("could not open directory: %s", directory.c_str());
}
}
......@@ -390,7 +407,7 @@ namespace migraphxSamples
#endif
if (!result)
{
LOG_INFO(stdout, "can't remove directory: %s\n", path.c_str());
printf("can't remove directory: %s\n", path.c_str());
}
}
else
......@@ -402,7 +419,7 @@ namespace migraphxSamples
#endif
if (!result)
{
LOG_INFO(stdout, "can't remove file: %s\n", path.c_str());
printf("can't remove file: %s\n", path.c_str());
}
}
}
......@@ -438,7 +455,7 @@ namespace migraphxSamples
{
RemoveAll(path);
++numberOfFiles;
LOG_INFO(stdout, "%s deleted! number of deleted files:%d\n", path.c_str(), numberOfFiles);
printf("%s deleted! number of deleted files:%d\n", path.c_str(), numberOfFiles);
}
}
......@@ -452,7 +469,7 @@ namespace migraphxSamples
}
else
{
LOG_INFO(stdout, "could not open directory: %s", directory.c_str());
printf("could not open directory: %s", directory.c_str());
}
// ����RemoveAllɾ��Ŀ¼
......@@ -592,17 +609,17 @@ namespace migraphxSamples
if(!srcFile.is_open())
{
LOG_ERROR(stdout,"can not open %s\n",srcPath.c_str());
printf("can not open %s\n",srcPath.c_str());
return false;
}
if(!dstFile.is_open())
{
LOG_ERROR(stdout, "can not open %s\n", dstPath.c_str());
printf("can not open %s\n", dstPath.c_str());
return false;
}
if(srcPath==dstPath)
{
LOG_ERROR(stdout, "src can not be same with dst\n");
printf("src can not be same with dst\n");
return false;
}
char buffer[2048];
......@@ -622,7 +639,7 @@ namespace migraphxSamples
{
if(srcPath==dstPath)
{
LOG_ERROR(stdout, "src can not be same with dst\n");
printf("src can not be same with dst\n");
return false;
}
......@@ -662,9 +679,9 @@ namespace migraphxSamples
// process
double process = (1.0*(i + 1) / fileNameList.size()) * 100;
LOG_INFO(stdout, "%s done! %f% \n", GetFileName(fileNameList[i]).c_str(), process);
printf("%s done! %f% \n", GetFileName(fileNameList[i]).c_str(), process);
}
LOG_INFO(stdout, "all done!(the number of files:%d)\n", fileNameList.size());
printf("all done!(the number of files:%d)\n", fileNameList.size());
return true;
......
......@@ -3,10 +3,8 @@
#ifndef __FILE_SYSTEM_H__
#define __FILE_SYSTEM_H__
#include <vector>
#include <string>
using namespace std;
#include <vector>
namespace migraphxSamples
{
......@@ -21,7 +19,7 @@ bool IsDirectory(const std::string &path);
bool IsPathSeparator(char c);
// 路径拼接
string JoinPath(const std::string &base, const std::string &path);
std::string JoinPath(const std::string &base, const std::string &path);
// 创建多级目录,注意:创建多级目录的时候,目标目录是不能有文件存在的
bool CreateDirectories(const std::string &directoryPath);
......@@ -49,14 +47,13 @@ void Remove(const std::string &directory, const std::string &extension="");
/** 获取路径的文件名和扩展名
*
* 示例:path为D:/1/1.txt,则GetFileName()为1.txt,GetFileName_NoExtension()为1,GetExtension()为.txt,GetParentPath()为D:/1/
*/
string GetFileName(const std::string &path); // 1.txt
string GetFileName_NoExtension(const std::string &path); // 1
string GetExtension(const std::string &path);// .txt
string GetParentPath(const std::string &path);// D:/1/
std::string GetFileName(const std::string &path);
std::string GetFileName_NoExtension(const std::string &path);
std::string GetExtension(const std::string &path);
std::string GetParentPath(const std::string &path);
// 拷贝文件:CopyFile("D:/1.txt","D:/2.txt");将1.txt拷贝为2.txt
// 拷贝文件
bool CopyFile(const std::string srcPath,const std::string dstPath);
/** 拷贝目录
......
......@@ -3,11 +3,8 @@
#ifndef __SSD_DEFINITION_H__
#define __SSD_DEFINITION_H__
#include <string>
#include <vector>
using namespace std;
namespace migraphxSamples
{
......
......@@ -19,7 +19,7 @@ using namespace std;
/** 简易日志
*
* 轻量级日志系统,不依赖于其他第三方库,只需要包含一个头文件就可以使用。提供了4种日志级别,包括INFO,DEBUG,WARN和ERROR。
* 不依赖于其他第三方库,只需要包含一个头文件就可以使用。提供了4种日志级别,包括INFO,DEBUG,WARN和ERROR。
*
* 示例1:
// 初始化日志,在./Log/目录下创建两个日志文件log1.log和log2.log(注意:目录./Log/需要存在,否则日志创建失败)
......
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