因为项目原因,需要使用人脸检测(face detection)功能。身为一名前JAVA业务程序员和现前端程序员,这样的功能还是陌生的领域。那能不能通过搜索和学习能力,加上已有的编程思想,快速实现功能呢?这就是这篇文章的重点,凭借自己的技术与积累,走向自己不熟悉的领域。

需求

使用PC加摄像头,通过人脸检测,完成自动拍照功能。在PC的屏幕上,显示摄像头的实时画面,要是画面中检测出人脸,则触发拍照。

抽其关键点:摄像头,实时画面,人脸检测,和拍照

自己拥有的技能

项目并没有要求开发应用的类型,那么我肯定是先从自身拥有的技能出发,看是否能寻求解决方案。身为前端,深知前端技术潜力无限,肯定有自己还不知道的技术。果不其然,找到了Tracking.js

Tracking.js是一个很有意思的库,体积小巧,在浏览器上,直接通过JS提供一些基本的Computer Vison相关的功能,如人脸检测(face detection),颜色识别(color detection),特征识别(feature detection)。关键是,出了对静态图片能够进行识别和处理之外,还能够直接对视频(video)和摄像头(webcam)视屏流进行处理,这就基本给出了人脸识别的方案。

那如何拍照呢?我找到了WebcamJS。能够在浏览器上读取摄像头视频流,调用函数截取某个时刻视频流帧为图片,即视为拍照。

解决方案一

两个库一结合,就有了初步的解决方案,实现了功能,简单快速。

人脸检测能力不足

在Tracking.js的官网上可以看到,demo的视频尺寸都是很小的,320 * 240,才能有30FPS的处理速度。尺寸如果大了,则识别速度慢,卡顿明显。项目中视频显示大小基本为1080P分辨率屏幕的三分之一,直接处理这个大小的视频,速度肯定不够。就算我不直接对大尺寸视频流进行处理,转而对一个隐藏的小尺寸视频流处理,将人脸位置映射到大视频流中,速度上没问题,但是可检测的人脸大小就有限了,即人必须离摄像头比较近才能被检测到。

视频流的清晰度不够

使用的是罗技的1080P的网络摄像头,但是在浏览器上能够看到的,好像并没有以1080P的分辨率进行展示。尝试了一些参数的修改,但是结果都不尽人意。

人脸识别造成性能问题

实时的视频流人脸识别,是很耗CPU的,下图中右边那个蓝色的陡坡就是在我关闭了Tracking.js的demo页面后CPU下降的曲线。

这种情况下,想添加一些传统的网页动效,简直卡出翔来。(关于这点,使用web worker进行人脸检测的工作,应该能有所帮助,但是自己并没有往这方面走)

继续寻找方案

为了寻找上面的问题的解决办法,我了解到了Tracking.js和WebcamJS的运行原理。比如在浏览器上获取摄像头视频流,是通过getUserMedia,是基于WebRTC的支持。

WebcamJS就是通过getuserMedia()方法,获得摄像头的视频流信息,作为<video>标签的src属性,从来能够在网页上进行显示实时画面。通过CanvasdrawImage()方法,将video标签传入,即可绘制那个时刻视频帧的图像。(也是通过这次机会我才了解到drawImage()原来还可以接受HTMLVideoElement作为参数的)

之前就知道,一般做计算机视觉的,都会用opencv库,这是个C++的库。同时又查到了,有人在node上做了opencv的扩展,并且看到了这个Github项目。于是为了检测效果,自己做了尝试。

解决方案二

通过上面的学习,我已经能够在浏览器段获取摄像头的帧图像,并且知道opencv能做人脸检测。那么这次的方案思路就是:将视频流的帧图像,通过websocket发到后台服务器上,在服务器里使用opencv进行人脸分析,将人脸的坐标发送到前端。

这里后台我并没有使用node-opencv,而是使用QT直接做websocket服务器和调用opencv库(仗着自己曾也学过C++,就大胆的直接奔着C++去了)。

但是结果也不理想,原因如下。

发送图像给后台耗时大

将图片发送给后台,首先想到的是使用Canvas的toDataURL()方法,将图片转成base64字符串,发给后台。但实测该方法很慢!640 * 480大小的图片,通过toDataURL(),大致需要50ms时间。

然后考虑使用getImageData(0, 0, 640, 480)方法,获取图片像素信息,然后转成字符串发到后台。经测,该方法比toDataURL()确实快不少,大致在5ms左右。但是将它转成JSON字符串,则很慢很慢。

最后查到,toDataURL('image/jpeg')会加快速度,因为这里不需要计算Alpha通道。实测,小于10ms。

之前有担心的网络传输耗时问题,倒是得到了证实,这个担心是没有必要的。因为是本地传输,通过websocket传输一张图片(小尺寸或者大尺寸)base64字符串大小的内容,耗时都很小,算下来FPS能够上50。

CPU消耗大

Approach CPU %
QT Opencv Face Detection 30%
Tracking.js Face Detection 50%
Websocket + Opencv 90%

不确定自己在实现上是否哪里出了问题,导致这么高的CPU使用率。但不管怎么,还是放弃了这个方案。

解决方案三

竟然都使用上了QT,就大步向前走好了,毕竟这样的图像处理程序,还是做桌面应用是做合适的。况且QT体系中的QML语言,能够使用JS,学起来有点像在学一个新的前端MVVM框架,好感度和信心瞬间提升不少。

使用QML做界面,使用CameraVideoOutput组件进行摄像头视频的实时显示,这里能指定显示分辨率和FPS,很方便;配合使用QVideoFilterRunnableQAbstractVideoFilter类对帧进行处理,异步返回给主界面人脸检测的结果;opencv和另外一个能做人脸识别的C++库Dlib结合使用,能够完成640 * 480尺寸的30FPS处理。

再优化!给每一帧图片进行人脸识别,速度和识别能力都可以提高,就是通过Dlib中提供的Video Object Tracking来完成。一旦对某一帧能够检测到人脸之后,对之后的帧执行该人脸区域图像的tracking。这样做的效果能够获得更高的FPS,同时tracking还能完成更远距离脸部的捕获。

到这里,方案才觉得差不多了。

总结

面对项目中自己的未知领域,如果不缺钱,那么直接买商业解决方案。Visage Tech提供的HTML5的人脸识别解决方案(好像用了WebAssembly),简直变态:快速!准确!稳定!核心科技就是和我们这些小打小闹的不一样。

如果不购买商用解决方案,那么应该尽量找到能够帮助自己的朋友,指条正确的方向能够节省很多调查摸索的时间。比如,如果需求要求程序在拍照时还能控制外接的灯泡,完成闪光灯的效果。那么如何使用软件完成对外部硬件的控制呢?这样的功能对于我这个非计算机专业的而言,真是蒙圈了。最后还是经历的大半天的摸索,才找到GPIO的解决办法。

提高编程的素养,扩大自己的兴趣面,热爱技术,善于google,逻辑思路清晰,那么在面对不熟悉的领域,新的技术,也能够找到解决方案。并且这个过程能让自己获得不少知识,face detection, object tracking, tracking.js, webcamJS, getUserMedia(), toDataURL()的性能,opencv,dlib,QML,GPIO,树莓派,我还差点去现学了Python。这些东西没有必要都是深究,但是知道他们的存在,会给扩展自己的思路。

本文固定链接: http://www.js-code.com/cpp/cpp_59254.html