CV深度学习模型Android端落地第二步:使用Face类进行实时人脸检测并将人脸框出
这个系列的博客主要介绍如何在Android设备上移植你训练的cv神经网络模型。
主要过程如下: 1、使用Android Camera2 APIs获得摄像头实时预览的画面。 2、如果是对人脸图像进行处理,使用Android Camera2自带的Face类来对人脸检测,并完成在预览画面上画框将人脸框出、添加文字显示神经网络处理结果的功能。 3、使用Tensorflow Lite 将自己的训练得到的模型移植到Android上。
以上三个步骤会分为三个博客,同时也会提供示例代码。步骤二可以根据你的实际需求跳过或修改。这是这个系列的第二篇博客。
主要业务逻辑代码都在Camera2BasicFragment.java中修改。不特殊说明,改动都是在Camera2BasicFragment.java中进行。
使用Face类检测Camera2捕捉画面中的人脸
准备工作
- 首先,要获得一些相机人脸检测的参数,在setUpCameraOutputs方法中获得相机有关人脸检测的一些属性,代码加在
mCameraId = cameraId;
和return;
之间。主要是要获得mFaceDetectMode。
1 |
|
- 然后要在createCameraPreviewSession方法中添加如下代码,createCameraPreviewSession方法用于创建一个新的相机预览,加上这句代码之后,我们指定下一个相机预览的人脸检测模式,这样才能直接从CaptureResult中直接得到人脸。
1 |
|
一行代码获得人脸位置
完成上述准备工作后,我们就可以在 CameraCaptureSession.CaptureCallback 方法中获得检测得到的人脸了。
使用Face类进行人脸检测依赖于CaptureResult,我们在CameraCaptureSession.CaptureCallback中写相关的代码,紧接着上一篇博客的内容。 Face、Rect 这些类型属于系统类型,如果没有引入造成报错,在AS里面,光标放到报错的Face等代码上,按住alt+Enter,选择第一个解决方法,会帮你引入这些类。
face[]中就是我们想要检测的人脸。face[0]就是最大人脸,调用face[0].getBounds()获取人脸矩形框左上角、右下角的坐标,传入drawRectangle方法,在预览图像上根据人脸画出矩形框。
1 |
|
将人脸在预览图像上框出来
由于成像画面和预览画面存在缩放以及旋转变化,所以需要调整,具体思路是这样的:
这个demo里面成像画面不是Canvas而是TextureView,TextureView加上FrameLayout部分才能构成Canvas,Canvas比TextureView大一些,网上很多博客使用了Canvas进行数据转换,会出现框不到人脸的错误,这一点要特别注意。
简单总结一下:
1、由于成像画面和预览画面存在的缩放问题,第一步是要把传入的基于成像画面的人脸框坐标缩放转换成预览画面中的人脸框坐标。
2、完成缩放之后根据相机画面和预览画面的旋转角度写特定的代码把矩形框左上角和右下角的坐标转换过来。
使用如下代码可以知道当前摄像头相机画面和预览画面的旋转角度,华为Mate 9的后置摄像头是顺时针90°,前置摄像头是顺时针270°。不同设备不一样,根据相机的旋转角度实际情况自行调整,这部分代码可以参考我的写法自行修改。
1 |
|
获取成像尺寸数据
在写方法之前要先保存成像画面的尺寸,在private void setUpCameraOutputs(int width, int height) {}方法里面保存变量值,setUpCameraOutputs方法的作用就是设置一些相机初始值,曝光模式、聚焦模式等。 1
2
3
4
5
6
7
8
9
10
11
12
13private static Size cPixelSize; // 在外面一个位置 声明保存成像尺寸的变量
// 然后在setUpCameraOutputs方法里面保存cPixelSize变量的值
private void setUpCameraOutputs(int width, int height) {
Activity activity = getActivity();
CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE);
try {
for (String cameraId : manager.getCameraIdList()) {
CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
cPixelSize = characteristics.get(CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE); // 获取成像尺寸 同上用于后续的人脸检测框调整
// .....
}
}
添加surfaceview用于画框
- 编辑Application/res/layout的xml文件,添加一个如下所示的SurfaceView。在页面上新建一个SurfaceView并置顶用于显示矩形框
1 |
|
- 在Camera2BasicFragment.java中修改onViewCreated方法,添加surfaceview_show_rectangle并将其初始化:
1 |
|
drawRectangle方法
以下就是drawRectangle方法,这里只使用了前置摄像头,所以只进行了270°变换的矫正。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41/**
* 在SurfaceView上画人脸框图
* */
public int[] drawRectangle(int left, int top, int right, int bottom){
//定义画笔
Paint mpaint = new Paint();
mpaint.setColor(Color.BLUE);
// mpaint.setAntiAlias(true);//去锯齿
mpaint.setStyle(Paint.Style.STROKE);//空心
// 设置paint的外框宽度
mpaint.setStrokeWidth(5f);
Canvas canvas = new Canvas();
canvas = surfaceHolder.lockCanvas();
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); //清楚掉上一次的画框。
// 由于成像画面和预览画面存在缩放旋转关系 所以需要调整
//成像画面与方框绘制画布长宽比比例(同画面角度情况下的长宽比例(此处前后摄像头成像画面相对预览画面倒置(±90°),计算比例时长宽互换))
float scaleWidth = canvas.getHeight()*1.0f/cPixelSize.getWidth();
float scaleHeight = canvas.getWidth()*1.0f/cPixelSize.getHeight();
//坐标缩放
int l = (int) (left*scaleWidth);
int t = (int) (top*scaleHeight);
int r = (int) (right*scaleWidth);
int b = (int) (bottom*scaleHeight);
// left、top、bottom、right变为bottom、right、top、left,并且由于坐标原点由左上角变为右下角,X,Y方向都要进行坐标换算
// 逆时针旋转了270°
left = canvas.getWidth()-b-180; // -180
top = mTextureView.getHeight()-r-180; // -180
right = canvas.getWidth()-t-90; // -90
bottom = mTextureView.getHeight()-l+20; // +20
// 开始画框
Rect draw_r = new Rect(left, top, right, bottom);
canvas.drawRect(draw_r, mpaint);
surfaceHolder.unlockCanvasAndPost(canvas);
int[] rec_coordinate = {left, top, right, bottom};
return rec_coordinate;
}
添加文字显示功能
一般我们框出人脸处理了图片之后,可能需要我们将结果以文字形式显示在旁边,这时候我们就要写一个显示文字的方法来完成这个工作了。
准备工作
- 编辑Application/res/layout的xml文件,添加一个如下所示的SurfaceView。在页面上新建一个SurfaceView并置顶用于文字显示
1 |
|
- 在Camera2BasicFragment.java中修改onViewCreated方法,添加surfaceview_show_result并将其初始化:
1 |
|
drawResult方法
1 |
|
下载Demo与使用
git clone或下载压缩包解压之后,在AS Build成功之后,将自己的代码放到Camera2BasicFragment.java的do_something方法中,do_something方法输入的是一个Bitmap和人脸框图坐标,处理Bitmap得到结果,会将结果显示在人脸框图坐标旁边。
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!