# 动态shape(MTCNN人脸检测器) ## 模型简介 动态shape示例程序采用了MTCNN人脸检测器模型中的PNet模型(https://arxiv.org/abs/1604.02878),论文主页:https://kpzhang93.github.io/MTCNN_face_detection_alignment/index.html。MTCNN中的PNet是一个典型的动态shape模型,PNet网络结构如下图所示,PNet是一个全卷积网络,所以可以输入不同大小的图像。 ![image-20221214101121257](../Images/DynamicShape_01.png) MTCNN检测器在检测之前会对输入图像构建一个图像金字塔,这个图像金字塔中包含了不同尺度的图像,然后将不同尺度的图像依次输入到PNet提取人脸候选区域,经过PNet提取出来的人脸候选区域再经过RNet和ONet进一步筛选和微调得到最后的检测结果。 ![image-20221214104253947](../Images/DynamicShape_02.png) 本示例对原始的PNet模型做了一些调整,训练的时候将输入图像大小修改为24x24,同时将第1个卷积和第3个卷积的步长修改为2。 ## 模型转换 论文中作者提供的是Caffe训练好的模型,可以通过caffe-onnx这个工具(https://github.com/htshinichi/caffe-onnx)转换为onnx模型。 ## 设置最大输入shape 由于PNet是一个动态shape模型,所以在推理之前需要设置最大输入shape,Resource/Configuration.xml文件中的DetectorMTCNN节点的MaxHeight和MaxWidth表示最大输入图像的高和宽,可以根据实际情况来定,本示例设置为512和512,程序中通过如下代码设置最大输入shape: ``` // 设置最大输入大小 migraphx::onnx_options onnx_options; onnx_options.map_input_dims["input"]={1,3,maxHeight,maxWidth}; ``` ## 预处理 在将数据输入到模型之前,需要对图像做如下预处理操作: 1. 减去均值127.5并乘以系数0.0078125 2. 转换数据排布为NCHW 本示例代码采用了OpenCV的cv::dnn::blobFromImage()函数实现了预处理操作: ``` cv::Mat inputBlob = blobFromImage(inputImage, 0.0078125, cv::Size(), cv::Scalar(127.5, 127.5, 127.5), false); ``` ## 推理 完成预处理后,就可以执行推理了: ``` ... // 处理图像金字塔的每一级图像 for (int k = 0; k < scales.size(); ++k) { // 输入图像 float scale = scales[k]; cv::Mat inputImage; cv::Size inputSize=cv::Size(ceil(srcImage.cols*scale), ceil(srcImage.rows*scale)); resize(srcImage, inputImage,inputSize, 0, 0, cv::INTER_LINEAR); // 每次输入shape发生变化后,需要对模型进行reshape std::vector inputShapeOfReshape={1,3,inputSize.height,inputSize.width}; std::unordered_map> inputShapeMap; inputShapeMap[inputName] = inputShapeOfReshape; net.reshape(inputShapeMap); // 预处理并转换为NCHW cv::Mat inputBlob = blobFromImage(inputImage, 0.0078125, cv::Size(), cv::Scalar(127.5, 127.5, 127.5), false); // 输入数据 migraphx::parameter_map inputData; inputData[inputName]= migraphx::argument{migraphx::shape(inputShape.type(),inputShapeOfReshape), (float*)inputBlob.data}; // 推理 std::vector results = net.eval(inputData); ... } ... ``` 1. for循环表示依次处理图像金字塔的每一级图像,由于每一级图像大小都不一样,所以推理之前需要对模型进行reshape操作,每一级图像经过预处理操作之后就可以输入到模型中执行推理了。 2. results表示推理返回的结果,这里表示conv4-2_Y和prob1_Y节点,results中的顺序与onnx中输出节点保持一致,可以通过netron (https://netron.app/) 查看,可以看出results中依次表示conv4-2_Y,prob1_Y节点, ![image-20221214201946030](../Images/DynamicShape_04.png) 返回的结果需要经过进一步的后处理操作才能得到最后的人脸候选区域。 ## 运行示例 根据samples工程中的README.md构建成功C++ samples后,在build目录下输入如下命令运行该示例: ``` # 设置动态shape模式 export MIGRAPHX_DYNAMIC_SHAPE=1 # 运行示例 ./MIGraphX_Samples 3 ``` 会在当前目录生成检测结果图像Result.jpg ![image-20221214112810405](../Images/DynamicShape_03.png)