"vscode:/vscode.git/clone" did not exist on "a15ebc463ed388b62b9c20828765fe5a5d739fbd"
Tutorial_Cpp.md 6.88 KB
Newer Older
liucong's avatar
liucong committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
# 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()得到最后的输出结果,注意这里的输出结果还不是最后的检测结果,最后需要转换到原图坐标才能够得到最终的检测结果。