当前位置:首页 > 行业发展 > 正文

工程详细记录:超准确人脸检测(带关键点)YOLO5Face C++

作者丨DefTruth1.YOLO5Face简介Github:ArXiv2021:C++实现:YOLO5Face是深圳神目科技LinkSpriteTechnologies开源的一个新SOTA的人脸检测器(带关键点),基于YOLOv5,并且对YOLOv5的骨干网络进行的改造,使得新的模型更加适合用于人...

作者丨DefTruth1.YOLO5Face简介Github:ArXiv2021:C++实现:YOLO5Face是深圳神目科技LinkSpriteTechnologies开源的一个新SOTA的人脸检测......

作者丨DefTruth

1.YOLO5Face简介

Github:

ArXiv2021:

C++实现:

YOLO5Face是深圳神目科技LinkSpriteTechnologies开源的一个新SOTA的人脸检测器(带关键点),基于YOLOv5,并且对YOLOv5的骨干网络进行的改造,使得新的模型更加适合用于人脸检测的任务。并且在YOLOv5网络中加了一个预测5个关键点regressionhead,采用Wingloss进行作为损失函数。从论文中放出的实验结果看YOLO5Face的平均精度(mAP)和速度方面的性能都非常优秀。在模型精度和速度方面,论文中给出了和当前SOTA算法的详细比较,包括比较新的SCRFD(CVPR2021)、RetinaFace(CVPR2020)等等。

另外由于YOLO5Face采用Stem块结构取代YOLOv5的Focus层,作者认为这样增加了网络的泛化能力,并降低了计算的复杂性。对于替换Focus层带来精度的提升,论文也给出了一些消融实验的对比,还是提了一些点。另外就是,去掉Focus的骚操作后,C++工程的难度也降低了一些,起码在用NCNN的时候,不用再额外捏个YoloV5FocusLayer自定义层进去了。

需要了解YOLO5Face相关的算法细节的同学可以看看原论文,或者阅读:

深圳神目科技《YOLO5Face》:人脸检测在WiderFace实现SOTA

本文主要记录一下YOLO5FaceC++工程相关的问题,并且简单介绍下如何使用++工具箱来跑直接YOLO5Face人脸检测(带关键点)(),这些案例包含了ONNXRuntimeC++、MNN、TNN和NCNN版本。

2.C++版本源码

YOLO5FaceC++版本的源码包含ONNXRuntime、MNN、TNN和NCNN四个版本,源码可以在(t)工具箱中找到。本文主要介绍如何基于工具箱,直接使用YOLO5Face来跑人脸检测。需要说明的是,本文是基于MacOS下编译的()来实现的,对于使用MacOS的用户,可以直接下载本项目包含的动态库和其他依赖库进行使用。而非MacOS用户,则需要从中下载源码进行编译。++工具箱目前包含80+流行的开源模型,就不多介绍了,只是平时顺手捏的,整合了自己学习过程中接触到的一些模型,感兴趣的同学可以去看看。

()

()

mnn_()

mnn_()

tnn_()

tnn_()

ncnn_()

ncnn_()

ONNXRuntimeC++、MNN、TNN和NCNN版本的推理实现均已测试通过,欢迎白嫖~本文章的案例代码和工具箱仓库地址为:

代码描述++测试用例代码,包含ONNXRuntime、NCNN、MNN、TNN版本++toolkitofawesomeAImodels.(一个开箱即用的C++AI模型工具箱,emmm,平时学一些新算法的时候顺手捏的,目前包含80+流行的开源模型。不知不觉已经将近800⭐️star啦,欢迎大家来点star⭐️、提issue呀~)

如果觉得有用,不妨给个Star⭐️支持一下吧~

3.模型文件3.1ONNX模型文件

可以从我提供的链接下载BaiduDrive()code:8gin,也可以从本仓库下载。

YOLO5Face()

3.2MNN模型文件

MNN模型文件下载地址,BaiduDrive()code:9v63,也可以从本仓库下载。

3.3TNN模型文件

TNN模型文件下载地址,BaiduDrive()code:6o6k,也可以从本仓库下载。

3.4NCNN模型文件

NCNN模型文件下载地址,BaiduDrive()code:sc7f,也可以从本仓库下载。

4.接口文档

在中,YOLO5Face的实现类为:

classLITE_EXPORTSlite::cv::face::detect::YOLO5Face;classLITE_EXPORTSlite::mnn::cv::face::detect::YOLO5Face;classLITE_EXPORTSlite::tnn::cv::face::detect::YOLO5Face;classLITE_EXPORTSlite::ncnn::cv::face::detect::YOLO5Face;

该类型目前包含1公共接口detect用于进行目标检测。

public:/***@parammatcv::MatBGRformat*@paramdetected_boxes_kpsvectorofBoxfWithLandmarkstocatchdetectedboxesandlandmarks.*@paramscore_,onlykeeptheresultwhich=score_threshold.*@paramiou_,iouthresholdforNMS.*@paramtopkdefault400,maximumoutputboxesafterNMS.*/voiddetect(constcv::Matmat,std::vectortypes::BoxfWithLandmarksdetected_boxes_kps,floatscore_threshold=0.25f,floatiou_threshold=0.45f,unsignedinttopk=400);

detect接口的输入参数说明:

mat:cv::Mat类型,BGR格式。

detected_boxes_kps:BoxfWithLandmarks向量,包含被检测到的框box(Boxf),box中包含x1,y1,x2,y2,label,score等成员;以及landmarks(landmarks)人脸关键点(5个),其中包含了points,代表关键点,是一个cv::point2f向量(vector);

score_threshold:分类得分(质量得分)阈值,默认0.25,小于该阈值的框将被丢弃。

iou_threshold:NMS中的iou阈值,默认0.45。

topk:默认400,只保留前k个检测到的结果。

5.使用案例

这里测试使用的是(yolov5n-face)nano版本的模型,你可以尝试使用其他版本的模型。

5.1ONNXRuntime版本
include"lite/"staticvoidtest_mnn(){if}
5.3TNN版本
ifdefENABLE_TNNstd::stringproto_path="../hub/tnn/cv/";//yolov5n-facestd::stringmodel_path="../hub/tnn/cv/";std::stringtest_img_path="../resources/9.jpg";std::stringsave_img_path="../logs/9.jpg";auto*yolov5face=newlite::tnn::cv::face::detect::YOLO5Face(proto_path,model_path);std::vectorlite::types::BoxfWithLandmarksdetected_boxes;cv::Matimg_bgr=cv::imread(test_img_path);yolov5face-detect(img_bgr,detected_boxes);lite::utils::draw_boxes_with_landmarks_inplace(img_bgr,detected_boxes);cv::imwrite(save_img_path,img_bgr);std::cout"TNNVersionDone!DetectedFaceNum:"detected_()std::l;deleteyolov5face;include"lite/"staticvoidtest_ncnn(){if}

输出结果为:

虽然是nano版本的模型,但结果看起来还是非常准确的啊!还自带了5个人脸关键点,可以用来做人脸对齐,也是比较方便~

6.编译运行

在MacOS下可以直接编译运行本项目,无需下载其他依赖库。其他系统则需要从中下载源码先编译动态库。

gitclone--depth=1

设置

cmake_minimum_required()project()set(CMAKE_CXX_STANDARD11)addyourexecutableset(EXECUTABLE_OUTPUT_PATH${CMAKE_SOURCE_DIR}/examples/build)add_executable(lite_yolo5faceexamples/test_lite_)target_link_libraries(lite_,_NCNN=ON,

buildingtestinginformation:

[50%]BuildingCXXobjectCMakeFiles/lite_/examples/test_lite_[100%]LinkingCXXexecutablelite_yolo5face[100%]Builttargetlite_yolo5faceTestingStartLITEORT_DEBUGLogId:../hub/onnx/cv/===============Input-Dims==============input_node_dims:1input_node_dims:3input_node_dims:640input_node_dims:640===============Output-Dims==============Output:0Name:outputDim:0:1Output:0Name:outputDim:1:25200Output:0Name:outputDim:2:16========================================generate_bboxes_kpsnum:2824DefaultVersionDone!DetectedFaceNum:326LITEMNN_DEBUGLogId:../hub/mnn/cv/===============Input-Dims==============**Tensorshape**:1,3,640,640,DimensionType:(CAFFE/PyTorch/ONNX)NCHW===============Output-Dims==============getSessionOutputAlldone!Output:output:**Tensorshape**:1,25200,16,========================================generate_bboxes_kpsnum:71MNNVersionDone!DetectedFaceNum:5LITENCNN_DEBUGLogId:../hub/ncnn/cv/_bboxes_kpsnum:34NCNNVersionDone!DetectedFaceNum:2LITETNN_DEBUGLogId:../hub/tnn/cv/===============Input-Dims==============input:[13640640]InputDataFormat:NCHW===============Output-Dims==============output:[12520016]========================================generate_bboxes_kpsnum:98TNNVersionDone!DetectedFaceNum:7TestingSuccessful!

其中一个测试结果为:

7.模型转换过程记录

ok,到这里,nano版本模型的效果大家都看到了,还是很不错的,640x640的inputsize下很多小人脸都检测出来了。C++版本的推理结果对齐也基本没有问题。那么这小节就主要记录一下,各种类型(ONNX/MNN/TNN/NCNN)的模型文件转换问题。毕竟这可以说是比较重要的一步了,因此也想和大家简单分享下。个人知识面有限,以下表述有不足之处,欢迎各位大佬指出哈~

7.1Detect模块推理源码分析(pytorch)
defforward(self,x):forprofilingz=[]convbs,_,ny,nx=x[i].shapex[i]=x[i].view(bs,3,16,-1).permute(0,1,3,2).contiguous()[i].shape[2:4]!=x[i].shape[2:4]:[i]=self._make_grid(nx,ny).to(x[i].device)这是YOLO5Face原来的代码[i],_grid[i]=self._make_grid_new(nx,ny,i)xybox_wh=(y[:,:,:,:,2:4]*2)**2*_grid[i]box_conf=((box_xy,((box_wh,y[:,:,:,:,4:5]),4)),4)landm1=y[:,:,:,:,5:7]*_grid[i]+[i].to(x[i].device)*[i]x2y2landm3=y[:,:,:,:,9:11]*_grid[i]+[i].to(x[i].device)*[i]x4y4landm5=y[:,:,:,:,13:15]*_grid[i]+[i].to(x[i].device)*[i]landm=((landm1,((landm2,((landm3,((landm4,landm5),4)),4)),4)),4)(bs,-1,16)(z,1)returnx原来的函数def_make_grid_new(self,nx=20,ny=20,i=0):d=[i].deviceif'1.10.0'intorch.__version__:新函数

为什么要这样做呢?我们先来看看anchor_grid和anchor的初始代码。

=[(1)]*(nl,na,2)_buffer('anchor_grid',().view(,1,-1,1,1,2))[i].shape[2:4]!=x[i].shape[2:4]:[i]=self._make_grid(nx,ny).to(x[i].device)这是YOLO5Face原来的代码[i],_grid[i]=self._make_grid_new(nx,ny,i)MNN模型转换python3.//YOLO5Face/-[i]=x[i].view(bs,,,ny,nx).permute(0,1,3,4,2).contiguous()原来的处理x[i]=x[i].view(bs,3,16,-1).permute(0,1,3,2).contiguous()注释掉坐标反算的逻辑(bs,?,16)原来的返回(model,img,f,verbose=False,opset_version=12,output_names=output_names,'output':{0:'batch'}forncnn

正常导出即可,然后转换成NCNN文件,并用ncnnoptimze过一遍,很顺利,没有再出现算子不支持的问题。

~PYTHONPATH=./_size640640--batch_size1--simplify~ncnn_~,shape_inferenceskippedInputlayerinputwithoutshapeinfo,estimate_memory_footprintskipped

其实,这样做还是有好处的,因为不需要把anchors和anchor_grid导出来,那么模型文件的size就变小了,比如按照原来方式导出的文件占了9.5Mb内存,修改后,不导出anchors和anchor_grid的模型文件只有6.5Mb。最后,关于YOLO5Face的C++前后处理以及NMS的实现,建议大家可以去看看我仓库的源码,就不在这里啰嗦了~

最新文章