# RetinaFace人脸检测器 RetinaFace是目前被广泛使用的一种人脸检测器模型,本示例主要说明了如何在MIGraphX中使用RetinaFace人脸检测器。 ## 模型简介 RetinaFace是一个经典的人脸检测模型(https://arxiv.org/abs/1905.00641),采用了SSD架构。 ## 检测器参数设置 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, 所以Configuration.xml配置文件中的参数设置如下: ``` 3 16 32 64 128 256 512 ``` - **设置Flip和Clip** cfg_mnet中的clip为False,所以Configuration.xml中对应的参数设置为0即可,由于只有一个宽高比为1的anchor,所以Flip设置为0。 ``` 0 0 0 0 0 0 ``` - **设置anchor的宽高比** 由于RetinaFace只包含宽高比为1的anchor,所以这里不需要设置宽高比。 - **设置每个priorbox层的步长** cfg_mnet中的steps表示每个priorbox层的步长,所以三个priorbox的步长依次为8,16,32,对应的Configuration.xml的设置如下: ``` 8 16 32 8 16 32 ``` - **设置DetectionOutput层的参数** 由于本示例模型是一个人脸检测模型,所以只有两类目标(背景和人脸),所以ClassNumber为2,DetectionOutput层的其他参数可以根据实际情况做微调,本示例中采用如下设置: ``` 400 200 0.3 0.9 ``` ## 预处理 在将数据输入到模型之前,需要对图像做如下预处理操作: - 减去均值,RetinaFace训练的时候对图像做了减均值的操作(train.py文件中的第38行),注意均值的顺序是BGR顺序。 - 转换数据排布为NCHW 本示例代码采用了OpenCV的cv::dnn::blobFromImage()函数实现了预处理操作: ``` ErrorCode DetectorRetinaFace::Detect(const cv::Mat &srcImage,std::vector &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 &resultsOfDetection) { ... // 输入数据 std::unordered_map inputData; inputData[inputName]= migraphx::argument{inputShape, (float*)inputBlob.data}; // 推理 std::vector inferenceResults=net.eval(inputData); vector> regressions; vector> classifications; for(int i=0;i 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 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 outputNames = {"467","470","468"}; std::vector results = net.eval(inputData, outputNames); ... ```