前言
目前网上常规解决这块问题的方案总结了一下有以下五种,当然有更多的暂时还未了解过~
1、OpenCV (API level 8 +)
- 识别效果一般,侧脸无法识别.
- 对识别的距离有限制(2~3米).
- 如果需要做静态图片识别的话,需要对 Java library层进行修改.
- 项目里有我编好的动态链接库,拿来就能用,不需要再装官方 OpencvManger.apk 了.
- 文档:https://link.zhihu.com/?target=http%3A//www.opencv.org/platforms/android/
2、Camera内部的 API (API level 14+)
- 效果很好.
- 几乎所有的手机都支持(小米系统相机的人脸检测就是用这种方法做的).
- 可以识别侧脸.
- 如果需要做静态图片识别的话,成本很高.
- 文档:https://link.zhihu.com/?target=https%3A//developer.android.google.com/reference/android/hardware/Camera.html
3、android.media.FaceDetector 静态检测 (API level 1 +)
- 底层代码:android/external/neven/
- 只能接受Bitmap 格式的数据.
- Bitmap 编码格式必须为Bitmap.Config.RGB_565. - Bitmap 的宽度一定要是整数. - 只能识别双眼距离大于20 像素的人脸像,这个限制应该可以在 FrameWork 中做修改.
- 文档:https://link.zhihu.com/?target=https%3A//developer.android.google.com/reference/android/media/FaceDetector.html
4、Google Play Service 的 Vision API (API 9,在 API 17 增加了一些功能)
- 非常强大,效果基本能和 Camera API 持平.
- 静态识别支持比较低清晰度的图片.
- 可以识别是否睁眼.
- 可以得到眼睛,鼻子嘴巴等的位置.
- 有关于情绪的返回值.
- 可以识别头部姿势.
- 手机必须安装了 Google 服务框架才能使用.
- 文档:https://link.zhihu.com/?target=https%3A//developers.google.com/vision/face-detection-concepts
5、Face++ Android SDK
- 试用需要发邮件申请.
- 文档很挫.
- 没有示例 demo.
- 效果应该没问题.
- 官网:https://www.faceplusplus.com.cn/
正文
1、效果图
本文重点介绍FaceDetector基于静态人脸的检测实现,先上效果图~
2、检测说明
由于FaceDetector是只接收bitmap的静态图片的人脸检测方案,因此我们在做检测的时候需要将每一帧预览帧拿去做检验。
但是你以为这样就完了,太天真了,你每一帧是一个完整的手机屏幕里面的人脸可能检测的时候根本就没有出现在圆圈内。因此我们需要用前景遮挡图+背景预览帧画面组合合成一张待检测图片进行送检。
3、FaceDetector检测核心代码
FaceDetector face_detector = new FaceDetector(newBP.getWidth(), newBP.getHeight(), faceNum);
FaceDetector.Face[] faces = new FaceDetector.Face[faceNum];
4、注意事项
FaceDetector检测的bitmap要求为RGB_565格式(The bitmap must be in 565 format)
实例
1、相机实时预览
使用相机实时预览这边使用的是SurfaceView
<?xml version="1.0" encoding="utf-8"?>
<layout>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<SurfaceView
android:id="@+id/surfaceView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
</layout>
2、定义圆形透明遮挡层
2.1 使用Canvas首先绘制整屏幕的白色遮挡层
canvas.drawARGB(255, 255, 255, 255);
2.2 使用Canvas在白色遮挡层上抠出一个透明的原型显示预览画面
mPaint = new Paint();
mPaint.setStyle(Paint.Style.FILL);
mPaint.setAlpha(0);
// android.graphics.PorterDuff.Mode.CLEAR 显示挖空canvas为透明
mPaint.setXfermode(new PorterDuffXfermode(android.graphics.PorterDuff.Mode.CLEAR));
canvas.drawCircle(宽度, 高度, 大小, mPaint);
3、开始进行相机预览
使用SurfaceHolder对象增加回调监听,在监听中预览和处理响应识别业务
/**
* 开始预览
*/
private void startPreview() {
SurfaceHolder holder = binding.surfaceView.getHolder();
holder.addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
mCamera = getCustomCamera();
mCamera.setPreviewCallback((data, camera) -> {
// 是否已经检测成功,如果是则不继续检测
if (isIdentify) {
checkFaces(data, camera, surfaceHolder, this);
}
});
}
@Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {
if (mCamera != null) {
Camera.Parameters parameters = mCamera.getParameters();
// 选择合适的图片尺寸,必须是手机支持的尺寸
List<Camera.Size> sizeList = parameters.getSupportedPictureSizes();
// 如果sizeList只有一个我们也没有必要做什么了,因为就他一个别无选择
if (sizeList.size() > 1) {
for (int j = 0; j < sizeList.size(); j++) {
Camera.Size size = sizeList.get(j);
previewWidth = size.width;
previewHeight = size.height;
}
}
//设置照片的大小
parameters.setPictureSize(previewWidth, previewHeight);
mCamera.setParameters(parameters);
try {
mCamera.setPreviewDisplay(holder);
} catch (IOException e) {
e.printStackTrace();
}
CameraUtils.takePicture((data, camera) -> {
CameraUtils.startPreview();
Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
if (bitmap != null) {
bitmap = ImageUtils.getRotatedBitmap(bitmap, 180);
}
CameraUtils.startPreview();
});
//调用相机预览功能
mCamera.startPreview();
}
}
@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
if (null != mCamera) {
holder.removeCallback(this);
mCamera.setPreviewCallback(null);
//停止预览
mCamera.stopPreview();
mCamera.lock();
//释放相机资源
mCamera.release();
mCamera = null;
}
}
});
}
4、人脸识别检测
4.1 预览图片
通过回调方法我们可以实时获取相机预览的图片字节数据,但是需要注意的是这个图片的格式是Yuv格式的,如果要在Android中正常的显示和操作则需要进行常规的转换,这里Yuv转bitmap的方法如下:
public Bitmap convertYUVtoRGB(byte[] yuvData, int width, int height) {
if (yuvType == null) {
yuvType = new Type.Builder(rs, Element.U8(rs)).setX(yuvData.length);
in = Allocation.createTyped(rs, yuvType.create(), Allocation.USAGE_SCRIPT);
rgbaType = new Type.Builder(rs, Element.RGBA_8888(rs)).setX(width).setY(height);
out = Allocation.createTyped(rs, rgbaType.create(), Allocation.USAGE_SCRIPT);
}
in.copyFrom(yuvData);
yuvToRgbIntrinsic.setInput(in);
yuvToRgbIntrinsic.forEach(out);
Bitmap bmpout = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
out.copyTo(bmpout);
return bmpout;
}
4.2 构建检测图片
其中核心的如前面说的我们需要将整个画面截屏出来进行识别,但是SurfaceView由于他的缓存机制是无法通过常规的getDrawableCache获取其截图的,就算你获取了也是空白图片,所以这里需要获取非SurfaceView部分和相机预览部分进行人为的叠拼。
/**
* 合并两张bitmap图片
*
* @param firstBitmap
* @param secondBitmap
* @return
*/
private Bitmap mergeBitmap(Bitmap firstBitmap, Bitmap secondBitmap) {
Bitmap bitmap = Bitmap.createBitmap(firstBitmap.getWidth(), firstBitmap.getHeight(), Bitmap.Config.RGB_565);
Canvas canvas = new Canvas(bitmap);
canvas.drawBitmap(firstBitmap, new Matrix(), null);
canvas.drawBitmap(secondBitmap, new Matrix(), null);
return bitmap;
}
4.3 进行人脸检测
进行检测时直接调用上述的系统API即可完成操作,需要注意的是根据测试,当设置的检测数量大于人脸数量时,有时会直接返回最大数量而非实际数量。
Bitmap bitmap565 = bp.copy(Bitmap.Config.RGB_565, true);
Bitmap newBP = mergeBitmap(bitmap565, getViewBitmap());
FaceDetector face_detector = new FaceDetector(newBP.getWidth(), newBP.getHeight(), 1);
FaceDetector.Face[] faces = new FaceDetector.Face[1];
int face_count = face_detector.findFaces(newBP, faces);
标签:人脸识别,FaceDetector,预览,mCamera,bitmap,new,Android,Bitmap
From: https://www.cnblogs.com/java-six/p/17407082.html