效果图

演示手机为红米10X pro,可以实时跑人脸检测+关键点识别二个模型.

主要优化

上次看见有人讨论人脸检测与关键点识别,用的是opencv相关,于是想看下深度神经网络相关部分的进展,先选定了推理框架ncnn,支持window/android等多种平台,然后在github参照多个ncnn+人脸检测/关键点识别的项目,大部分都是ncnn前期处理图像大小与改成rgb三平面格式,然后经过ncnn处理后再经opencv画矩形与多点.

在本机PC平台下,先用相关的人脸检测demo测试了下,Release下ncnn前期图像处理时间就需要ncnn(vulkan版本)推理时间的一半,有点奇怪,明明分辨率才那么点,不知是否更有效CPU前期图像处理方式,我能想到就是改为GPU处理,于是就有了本次优化,主要是把ncnn前期图像处理与opencv后期画矩形与多点全改成vulkan的computeshader处理,整个过程理想情况下全在GPU下处理,只有中间CPU-GPU传输数据占用大头,顺便去掉相关opencv的所有调用.

ncnn前期图像处理

首先ncnn前期图像处理主要就是三步,一是缩放,二是把数据交叉格式变成平面格式,三是数据的归一化,其相关过程改为如下vulkan的computeshader.

  1. #version 450
  2. layout (local_size_x = 16, local_size_y = 16) in;
  3. layout (binding = 0) uniform sampler2D inSampler;
  4. layout (binding = 1) buffer outBuffer{
  5. float dataOut[];
  6. };
  7. layout (std140, binding = 2) uniform UBO {
  8. int outWidth;
  9. int outHeight;
  10. float meanX;
  11. float meanY;
  12. float meanZ;
  13. float meanW;
  14. float scaleX;
  15. float scaleY;
  16. float scaleZ;
  17. float scaleW;
  18. } ubo;
  19. void main(){
  20. ivec2 uv = ivec2(gl_GlobalInvocationID.xy);
  21. if(uv.x >= ubo.outWidth || uv.y >= ubo.outHeight){
  22. return;
  23. }
  24. vec2 suv = (vec2(uv)+vec2(0.5f))/vec2(ubo.outWidth,ubo.outHeight);
  25. vec4 inColor = textureLod(inSampler,suv,0)*255.0f;
  26. int size = ubo.outWidth*ubo.outHeight;
  27. int index = uv.y*ubo.outWidth+uv.x;
  28. vec4 mean = vec4(ubo.meanX,ubo.meanY,ubo.meanZ,ubo.meanW);
  29. vec4 scale = vec4(ubo.scaleX,ubo.scaleY,ubo.scaleZ,ubo.scaleW);
  30. inColor = inColor*scale-mean;
  31. #if NCNN_BGR
  32. dataOut[index] = inColor.b;
  33. dataOut[index+size] = inColor.g;
  34. dataOut[index+2*size] = inColor.r;
  35. #endif
  36. #if NCNN_RGB
  37. dataOut[index] = inColor.r;
  38. dataOut[index+size] = inColor.g;
  39. dataOut[index+2*size] = inColor.b;
  40. #endif
  41. }

关键点模型的识别需要在面部识别的RECT区域上进行识别,相关代码修改为.

  1. #version 450
  2. layout (local_size_x = 16, local_size_y = 16) in;
  3. layout (binding = 0) uniform sampler2D inSampler;
  4. layout (binding = 1) buffer outBuffer{
  5. float dataOut[];
  6. };
  7. layout (std140, binding = 2) uniform UBO {
  8. int outWidth;
  9. int outHeight;
  10. float meanX;
  11. float meanY;
  12. float meanZ;
  13. float meanW;
  14. float scaleX;
  15. float scaleY;
  16. float scaleZ;
  17. float scaleW;
  18. float x1;
  19. float x2;
  20. float y1;
  21. float y2;
  22. } ubo;
  23. void main(){
  24. ivec2 uv = ivec2(gl_GlobalInvocationID.xy);
  25. if(uv.x >= ubo.outWidth || uv.y >= ubo.outHeight){
  26. return;
  27. }
  28. vec2 isize = vec2(ubo.x2-ubo.x1,ubo.y2-ubo.y1);
  29. vec2 suv = (vec2(uv)+vec2(0.5f))/vec2(ubo.outWidth,ubo.outHeight);
  30. vec2 isuv = suv*isize+vec2(ubo.x1,ubo.y1);
  31. vec4 inColor = textureLod(inSampler,isuv,0)*255.0f;
  32. int size = ubo.outWidth*ubo.outHeight;
  33. int index = uv.y*ubo.outWidth+uv.x;
  34. vec4 mean = vec4(ubo.meanX,ubo.meanY,ubo.meanZ,ubo.meanW);
  35. vec4 scale = vec4(ubo.scaleX,ubo.scaleY,ubo.scaleZ,ubo.scaleW);
  36. inColor = inColor*scale-mean;
  37. #if NCNN_BGR
  38. dataOut[index] = inColor.b;
  39. dataOut[index+size] = inColor.g;
  40. dataOut[index+2*size] = inColor.r;
  41. #endif
  42. #if NCNN_RGB
  43. dataOut[index] = inColor.r;
  44. dataOut[index+size] = inColor.g;
  45. dataOut[index+2*size] = inColor.b;
  46. #endif
  47. }

opencv矩形与多点绘制

画矩形与多点,我在移植GPUImage里相关滤镜时考虑过这个,当时想的是把渲染管线这一套集成就容易了,但是渲染管线本身,以及和计算管线的通用交互设计又是很多东东.

暂时决定先简单点来,画矩形,这种写法算力肯定有点浪费.

  1. #version 450
  2. layout (local_size_x = 16, local_size_y = 16) in;// gl_WorkGroupSize
  3. layout (binding = 0, rgba8) uniform readonly image2D inTex;
  4. layout (binding = 1, rgba8) uniform image2D outTex;
  5. layout (binding = 2) uniform UBO {
  6. int radius;
  7. float x1;
  8. float x2;
  9. float y1;
  10. float y2;
  11. float colorR;
  12. float colorG;
  13. float colorB;
  14. float colorA;
  15. } ubo;
  16. void main(){
  17. ivec2 uv = ivec2(gl_GlobalInvocationID.xy);
  18. ivec2 size = imageSize(inTex);
  19. if(uv.x >= size.x || uv.y >= size.y){
  20. return;
  21. }
  22. int xmin = int(ubo.x1 * size.x);
  23. int xmax = int(ubo.x2 * size.x);
  24. int ymin = int(ubo.y1 * size.y);
  25. int ymax = int(ubo.y2 * size.y);
  26. ivec4 xx = ivec4(uv.x, xmax, uv.y, ymax);
  27. ivec4 yy = ivec4(xmin, uv.x, ymin, uv.y);
  28. ivec4 xy = abs(xx - yy);
  29. float sum = step(xy.x, ubo.radius) + step(xy.y, ubo.radius) + step(xy.z, ubo.radius) + step(xy.w, ubo.radius);
  30. vec2 lr = vec2(xy.x + xy.y, xy.z + xy.w);
  31. vec2 rl = vec2(xmax - xmin, ymax - ymin);
  32. vec4 color = imageLoad(inTex,uv);
  33. if (sum > 0 && length(lr - rl) < ubo.radius) {
  34. vec3 drawColor = vec3(ubo.colorR,ubo.colorG,ubo.colorB);
  35. color.rgb = color.rgb*(1.0f - ubo.colorA) + drawColor*ubo.colorA;
  36. }
  37. imageStore(outTex,uv,color);
  38. }

画多点也是有渲染管线就很容易实现,在这还好,固定多点,简单来说,针对多个UV,在图上对应UV标记,然后和原图混合.

  1. #version 450
  2. layout (local_size_x = 240, local_size_y = 1) in;
  3. layout (binding = 0) buffer inBuffer{
  4. vec2 points[];
  5. };
  6. layout (binding = 1, rgba8) uniform image2D outTex;
  7. layout (binding = 2) uniform UBO {
  8. int showCount;
  9. int radius;
  10. float colorR;
  11. float colorG;
  12. float colorB;
  13. float colorA;
  14. } ubo;
  15. void main(){
  16. int index = int(gl_GlobalInvocationID.x);
  17. ivec2 size = imageSize(outTex);
  18. if(index >= ubo.showCount){
  19. return;
  20. }
  21. ivec2 uv = ivec2(points[index] * size);
  22. vec4 drawColor = vec4(ubo.colorR,ubo.colorG,ubo.colorB,ubo.colorA);
  23. int radius = max(1,ubo.radius);
  24. for(int i = 0; i< radius; ++i){
  25. for(int j= 0; j< radius; ++j){
  26. int x = uv.x - 1 + j;
  27. int y = uv.y - 1 + i;
  28. // REPLICATE border
  29. x = max(0,min(x,size.x-1));
  30. y = max(0,min(y,size.y-1));
  31. imageStore(outTex, ivec2(x,y), drawColor);
  32. }
  33. }
  34. }

有大佬有更好的想法欢迎指点.

编译与运行

如上glsl逻辑封装与组合逻辑主要代码在aoce_ncnn,

win端测试demo主要在ncnntest,其目录下CMakeLists.txt提供选项NCNN_VULKAN_WINDOW,决定是用vulkan绘制还是opencv绘制.android端demo主要封装逻辑在aocencnntest.

大家可以自己下载相关ncnn编译,调试,测试其中的细节,也可以直接使用我配置好的目录aoce_thirdparty,把下载的thirdparty文件夹下文件放入aoce目录下thirdparty文件夹下,位置正确CMake会自动查找链接相关dll.

在android下,需要先用swig自动把aoce提供的接口转化成java,详细请看android build,现在需要把手机横着检测才有比较好的效果,这个后期应该会调整.

最后是比较遗憾的地方,原计划是把vulkan前期处理完的buffer直接和ncnn进行显存交互对接,不像现在用的VK_MEMORY_PROPERTY_HOST_COHERENT_BIT类型的buffer做中转,其中测试一些写法,暂时都没成功,有做过类似的大佬欢迎指点.

参照:

FaceDetect-FaceLandmark

pfld-ncnn

pfld-ncnn

PFLD-pytorch

Face-Detector-1MB-with-landmark

Ultra-Light-Fast-Generic-Face-Detector-1MB

QT+ncnn实现人脸检测及关键点

人脸检测之Ultra-Light-Fast-Generic-Face-Detector-1MB

人脸检测--MTCNN从头到尾的详解

NCNN优化实时面部关键点检测的更多相关文章

  1. OpenCV实战:人脸关键点检测(FaceMark)

    Summary:利用OpenCV中的LBF算法进行人脸关键点检测(Facial Landmark Detection) Author:    Amusi Date:       2018-03-20 ...

  2. OpenCV Facial Landmark Detection 人脸关键点检测

    Opencv-Facial-Landmark-Detection 利用OpenCV中的LBF算法进行人脸关键点检测(Facial Landmark Detection) Note: OpenCV3.4 ...

  3. dlib人脸关键点检测的模型分析与压缩

    本文系原创,转载请注明出处~ 小喵的博客:https://www.miaoerduo.com 博客原文(排版更精美):https://www.miaoerduo.com/c/dlib人脸关键点检测的模 ...

  4. 用keras实现人脸关键点检测(2)

    上一个代码只能实现小数据的读取与训练,在大数据训练的情况下.会造内存紧张,于是我根据keras的官方文档,对上一个代码进行了改进. 用keras实现人脸关键点检测 数据集:https://pan.ba ...

  5. Android 性能优化之内存泄漏检测以及内存优化(中)

    https://blog.csdn.net/self_study/article/details/66969064 上篇博客我们写到了 Java/Android 内存的分配以及相关 GC 的详细分析, ...

  6. PCL—低层次视觉—关键点检测(iss&Trajkovic)

    关键点检测往往需要和特征提取联合在一起,关键点检测的一个重要性质就是旋转不变性,也就是说,物体旋转后还能够检测出对应的关键点.不过说实话我觉的这个要求对机器人视觉来说是比较鸡肋的.因为机器人采集到的三 ...

  7. PCL—关键点检测(iss&Trajkovic)低层次点云处理

    博客转载自:http://www.cnblogs.com/ironstark/p/5069311.html 关键点检测往往需要和特征提取联合在一起,关键点检测的一个重要性质就是旋转不变性,也就是说,物 ...

  8. 转载:点云上实时三维目标检测的欧拉区域方案 ----Complex-YOLO

    感觉是机器翻译,好多地方不通顺,凑合看看 原文名称:Complex-YOLO: An Euler-Region-Proposal for  Real-time 3D Object Detection ...

  9. [OpenCV]基于特征匹配的实时平面目标检测算法

    一直想基于传统图像匹配方式做一个融合Demo,也算是对上个阶段学习的一个总结. 由此,便采购了一个摄像头,在此基础上做了实时检测平面目标的特征匹配算法. 代码如下: # coding: utf-8 ' ...

随机推荐

  1. Python 数值中的下划线是怎么回事?

    花下猫语:Python 中下划线的用法令人叹为观止,相信你已在各种文章或教程中见识过了.在 2016 年的 3.6 版本之后,Python 还引入了一种新的语法,使得下划线也可以出现在数值中.这篇翻译 ...

  2. anyRTC 重磅推出在线实时 K 歌解决方案

    在线音乐领域一直是各大资本巨头投资的热点,从抢占版权到现在的"云上之争", 主流平台的战火从版权资源转向创新领域扩延.而如今,在线K歌正在成为抢占"云音乐"市场 ...

  3. rabbitmq消息处理-转载

    目录 1. 消息如何保障百分之百的投递成功? 1.1 方案一:消息落库,对消息状态进行打标 1.2 方案二:消息的延迟投递,做二次确认,回调检查 2. 幂等性 2.1 幂等性是什么? 2.2 消息端幂 ...

  4. 自学linux——6.安全外壳协议(ssh服务)

    ssh服务 ssh(secure shell)安全外壳协议:远程连接协议,远程文件传输协议 1.协议使用端口号默认:22 若要修改,则修改ssh服务的配置文件/etc/ssh/ssh_config a ...

  5. 阿里钉钉Android实习面试也太太太太难了吧,对算法的要求堪比字节

    本人研究生在读,在2月26日找了师兄内推阿里钉钉团队,28号接到了约1面的电话.幸好我提前准备了一个多月的样子,刷面试题.刷LeetCode(面了之后才觉得自己刷少了),对于我这样一个实习生来说题目还 ...

  6. Linux连接工具final配置

    Linux连接工具 putty .CRT.XShell 在terminal里面敲不太方便,所以需要一款连接工具 这是一款美观医用的网络服务管理软件 安装final shell Windows版下载地址 ...

  7. Docker部署ELK之部署elasticsearch7.6.0(1)

    1. 拉取elasticsearch7.6.0镜像: sudo docker pull elasticsearch:7.6.0 2. 输入命令,构建容器: sudo docker run --name ...

  8. 【动画消消乐|CSS】086.炫酷水波浪Loading过渡动画

    前言 Hello!小伙伴! 非常感谢您阅读海轰的文章,倘若文中有错误的地方,欢迎您指出-   自我介绍 ଘ(੭ˊᵕˋ)੭ 昵称:海轰 标签:程序猿|C++选手|学生 简介:因C语言结识编程,随后转入计 ...

  9. SpringCloud-技术专区-从源码层面让你认识Feign工作流程和运作机制

    Feign工作流程源码解析 什么是feign:一款基于注解和动态代理的声明式restful http客户端. 原理 Feign发送请求实现原理 微服务启动类上标记@EnableFeignClients ...

  10. MATLAB批量存储图像和显示算法处理的图像不留空白

    一 前言 最近收到审稿人的修改意见,其中一条为<RC: There were only five images evaluated in the experiment, and I recomm ...