基于算丰SOPHON BM1880 模塊+ ON Semiconductor AR0130&AR0230 USB双目人脸识别方案

随着社会的发展进步及人工智能的逐步普及,人们已经慢慢习惯AI给人类带来的便利,比如:上班考勤、小区门禁,办公大楼门禁,高铁站检票闸机都可以让人们不用配带钥匙或识别卡而刷脸快速通过,比特大陆-算丰 推出算力芯片BM1880+安森美AR0130&AR0230(双目)人脸识别方案,BM1880可支持最高1TOPS的算力,可支持本地3万张脸谱存储,AR0230 Senso支持r内部HDR合成输出,可很好的解决背光、强光的过曝光、过暗现象,AR0130在近红外的效果表现良好,此方案可支持应用场景:
1.小区门禁。
2.人脸识别考勤系统。
3.关口闸机。

支持AI功能:
1.人体属性与姿势分析。
2.人体检测、识别与表情分析。
3.物体体测与识别。
4.车牌识别。
5.声纹识别。

一.概述
如何搭建一个最基本的人脸检测以及识别的场景,此讲解不止关于人脸检测相关的部分,而是围绕这个核心,包含概括了整个前端的uvc,甚至socket通信等各个方面都有所涉及。

最终的产品实际效果以及主体框图如下所示:

如上图所示,该展示需要用到:

    一块1880edb开发板(官网购买)

   1linux pc(推荐ubuntu)

   一个支持uvc标准的摄像头,该实例使用的是logitech C922(此处非广告)

   网线/电源等各个配件

 

该展示从流程上来看,主要分为四大部分:

1.uvc camera将实时拍摄到的数据通过usb线,传给1880主板,数据可以是h264/yuv/mjpg等各种格式,取决于不同的camera型号。

2.1880接收到uvc camera的数据后,通过Opencv的api,调用ffmpeg,并内部对接了底层的bitmain的各个驱动模块,比如硬件Jpeg解码,硬件视频解码等。

3.通过上层高度封装的API(bmiva),依次调用人脸检测,人脸识别,特征值入库以及比对的相关函数,来获得人脸坐标以及特征值等各个参数。

4.1880将合成后的图片(包含人脸框以及特征值查找到的人名),通过socket的方式传给pc(1880作为socket client,PC作为socket server),并利用PC的opencv进行显示。

接下来会详细介绍这4个部分~

 

二. camera和1880之间的数据传输

1.背景介绍 

       -> 什么是uvc camera,以及uvc camera的基本用法

简单来说,uvc camera可以理解为免驱摄像头,而对于上层应用来说,配合简单的v4l2协议以及Ioctl的方式,就可以很方便的获取/设置摄像头的各个属性。

示范代码如下所示:

 

通常uvc camera会被识别为/dev/video0。 所以代码通过调用open函数,打开摄像头,然后通过ioctl,来获得摄像头的各个属性,实际应用中可以通过相关网站查找或者直接看底层代码来查阅所有支持的属性。

同样的,如果想设置摄像头的各个属性,也是类似的做法:

如下代码截图所示,该例子设置摄像头的格式为320×240MJPG

 


2.实际代码

 

如图所示,可以看到通过Opencv来打开摄像头相比之前的做法,封装度更高。只需要调用VideoCapture类的open函数即可。opencv则帮忙隐藏了实现细节。

当然,此处有个问题是,为什么open的对应idx是200,此处略过不做展开,只要简单的记住,video0是对应的从200开始,依次类推,video1->201...

3.查询/修改摄像头属性

对于我们的人脸检测的应用来说,跟帧率强相关的一个指标则是摄像头的输入速度,通俗的理解,摄像头给BM1880开发板的速度越快,那自然帧率就有机会越高,假如摄像头以很慢的速度给BM1880开发板,那相当于前端就被卡住了,自然整体速度也快不起来。

所以,通常我会习惯于在开始的时候,先获得摄像头的基本帧率参数,从而对大概帧率有一个简单的估算。

代码如下:

 

通常摄像头的帧率会跟几个参数强相关,宽/高/格式。比如1080P的就要比720P的FPS要慢,YUYV就比MJPG的要慢,类似这些规律。大家也可以自己尝试。

当然,也跟具体特定的摄像头有关。

 

4.实时获得每一帧的数据

对于基于Opencv的写法,到了这一步其实真的非常简单。只有一行代码而已:

capture.read(),  -> 即可以将数据保存到Mat frame对象里面。

 

 至此,第一阶段的工作已经完成,数据已经从camera到了bm1880开发板本地的Mat对象里面了。至于里面的具体细节,

比如数据是怎么通过opencv给到ffmpeg,并进行底层硬件解码的,都对开发者是透明的。从而大大降低了上层应用搭建的难度。

 

三.人脸检测识别相关操作

1.人脸识别函数调用流程.

整个过程会用到的函数如下图所示:

 

可以看到,其实主要函数的使用非常简洁,只需依次调用bmiva_init->bmiva_face_detector_create->bmiva_face_detector_detect即可完成人脸的检测.

实际代码如下所示(该例子以MTCNN为例):

最终返回的信息,存放在vectorresults里面

 

 至此,检测的部分告一段落。

补充:其中使用到的bmodel,是需要预先放在1880开发板的存储设备上的。

因为我们的bm1880开发板是推理芯片。所以只是使用已经训练好的模型。

当然,目前主流的模型都可以支持转成我们的bmodel. 至于转换的部分,可以参考社区论坛的sdk的部分。

至于如何将人脸坐标,展现出来,顺便提一下即可,如下代码所示:

 

 简单的for循环,将faceinfo里面的x/y坐标提取出来,然后利用opencv来绘画矩形框即可.

 

2.人脸识别.

接下来会稍微麻烦一些,是人脸入库,人脸识别,特征值提取以及比对的部分。

先照例看一下示意图:

首先是特征值加载,如下截图所示:

 

目前demo的做法是在flash里面存了一份特征值的数据,比如features.txt.然后将特征值通过ifstream读进来,并调用bmiva提供的NameLoad()函数,加载到BmivaFeature g_name_features里面。

然后是创建extractor

 

 此处跟bmiva_face_detector_create可以类比。对应的是bmiva_face_extractor_create(),

承接之前的人脸检测的结果, 会先将之前检测到的人脸信息进行align,按照指定的宽高

 

 然后,会将对齐过的人脸信息(align_images),进行核心的特征值提取操作,如下函数所示:

 

这个函数会将提取出来的特征值存在Vectorfeatures里面

然后会进行similarity的运算:

 

 这里的第一个参数,就是前面提到的,从存储设备里面加载出来的特征值数据库.至于这个函数内部的实现,大家可以简单的理解为,遍历整个数据库,然后做相似度运算,比如Cosine之类的算法.并把最终的结果保存在vector<std::pair<string,float>>result里面

 

再之后,就是一个简单的排序并选择相似度最高的结果,并将该结果对应的人名copy出来,进行最后的人名的显示.

 

 当然,这个操作的过程中,也有一个阀值(threshold)的概念,如果相似度最高的数值,都低于我们预设的阀值,那我们就认为此次没有检测到想要的结果.

这个阀值也是一个经验值,方便用户在不同的场景进行调节.

至此,整个检测/识别的全过程就已经完成.

接下来就是收尾的数据传输以及显示的部分了~

 

四.数据传输以及显示

因为我们的bm1880开发板本身没有显示输出接口,所以必须要利用其它设备来进行显示.开发演示阶段,我们建议利用ubuntu电脑来完成该功能.

为了达到送给ubunutu显示的目的.该例子使用的是简单的socket传输.即:将每帧合成好的数据,通过socket 传送给pc端.

 1.建立连接

 

如上函数所示,此处略过不提.

毕竟都是标准的玩法, 所以大家也可以在网络上很方便的查找到相关使用方法.

2.创建线程

因为socket相对会非常耗时,所以为了并行处理,通常会建议开新的线程来做这个事情.

 

然后我的demo利用的是一个简单的queueimagebuffer

 

从而很方便的进行push/pop操作.

 

3.数据传输:

 

核心还是利用socket的send函数,将数据发送出去.

至于PC端,则是调用相对应的recv函数即可:

  

最后一步,PC端将接受的数据,存在opencv的image里面,并调用imshow进行显示。




场景应用图

sceneryUrl

产品实体图

imgUrl