最近项目中有一个比较奇葩的需求,我相信很多人都没有听过这个需求,可能这个需求分开做过,但是合在一起的需求肯定没有做过。那么这个需求是什么呢?
需求:同一个入口进入,先判断是不是人脸,如果是人脸的话,就把人脸的照片截取下来传到后台进行比对。如果不是人脸,要求对二维码进行扫描,当时刚接到这个需求的时候,也是一脸懵逼,还可以这样操作?
这个需求困扰了我好几天,人脸检测倒还可以,通过第三方的SDK可以判断是不是人脸,加上二维码检测就不会写了,两个是共用一个相机,如果两个分开的话,这个需求就很简单了。
人脸检测
人脸检测我用的是虹软的SDK,这个SDK是免费的(如果不需要活体检测,活体检测是收费的),Android端的是有3个版本,分别是1.2、2.0、2.1三个版本,当时这三个版本我都用过,最早是用的2.1版本的,然后不行,2.0也用了,也不行,没法和二维码扫描兼容,最后用的是1.2版本的。1.2版本的,虹软是没有例子的,不过大部分的方法是有的,这个可以根据自己的需求来定制,这个还是非常好的。
一.使用虹软SDK
1.准备工作
①需要先去虹软官网https://ai.arcsoft.com.cn,进行开发者的注册,个人开发者注册是需要上传个人身份证。注册成功以后就可以使用虹软的服务了。
②进入下载界面,点击下载,填写所需要的信息。
③填写完毕后,就会得到很多key,这些key根据项目的需要,看看是否能够用到,点击下载ArcFace v1.2,下载相应的SDK。
④下载完成以后,需要导入依赖包,虹软人脸SDK的包是so包,我们可以在下载的压缩包中把这些文件找到并导入。因为我这里只需要人脸检测功能,不需要识别之类,我只需要导入检测的so包就可以
准备动作到这里差不多就已经完成了,下面就要开始我们的代码的编写了。
代码编写
1.初始化引擎
public static String FREESDKAPPID = "DB37dp1wuNFxpjdZioe64J5hYJaw8PX44ovK11JxkzVi";
public static String FDSDKKEY = "EkMaQskfHUqAduqp3a6UdPenb8TgKoupppjXaaHrjrqg";
public static String FTSDKKEY = "EkMaQskfHUqAduqp3a6UdPenb8TgKoupppjXaaHrjrqg";
ftEngine = new AFT_FSDKEngine();
int ftInitErrorCode = ftEngine.AFT_FSDK_InitialFaceEngine(APPID,
FT_SDKKEY, AFT_FSDKEngine.AFT_OPF_0_HIGHER_EXT,
16, 5).getCode();
if (ftInitErrorCode != 0) {
Log.d("FT初始化失败,errorcode:" , ftInitErrorCode+"");
}
2.使用相机预览图像,判断获取到的图像是不是人脸,这里我们需要调用AFT_FSDKEngine中的AFT_FSDK_FaceFeatureDetect()方法来获取人脸的位置
List<AFT_FSDKFace> ftFaceList = new ArrayList<>();
//视频FT检测人脸
int ftCode = ftEngine.AFT_FSDK_FaceFeatureDetect(data, width, height,
AFT_FSDKEngine.CP_PAF_NV21, ftFaceList).getCode();
if (ftCode != AFT_FSDKError.MOK) {
Log.d("FaceTrackService","AFT_FSDK_FaceFeatureDetect: errorcode "+ftCode);
}
return ftFaceList;
3.扫描到人脸以后,画出人脸的位置
if (svRect == null) {
return;
}
if (svRect != null) {
Canvas canvas = svRect.getHolder().lockCanvas();
canvas.drawColor(0, PorterDuff.Mode.CLEAR);
Paint paint = new Paint();
paint.setColor(Color.RED);
paint.setStyle(Paint.Style.FILL);
paint.setStrokeWidth(5);
paint.setTextSize(80);
if (fsdkFaces.size() > 0) {
for (AFT_FSDKFace aft_fsdkFace : fsdkFaces) {
Rect rect = new Rect(aft_fsdkFace.getRect());
if (rect != null) {
//画人脸框
Rect adjustedRect = DrawUtils.adjustRect(rect, faceTrackService.getWidth(),
faceTrackService.getHeight(),
canvas.getWidth(), canvas.getHeight(), cameraOri, cameraId);
DrawUtils.drawFaceRect(canvas, adjustedRect, Color.YELLOW, 5);
}
}
}
svRect.getHolder().unlockCanvasAndPost(canvas);
}
4.将识别到的人脸转成图片展示出来,我这里就不写我那种方式了,比较麻烦。这里我就弄一种相对简单的方法,如果检测到人脸以后,直接把检测到的图片展示出来。
ByteArrayOutputStream baos;
byte[] rawImage;
Bitmap bitmap;
//处理data
Camera.Size previewSize = camera.getParameters().getPreviewSize();//获取尺寸,格式转换的时候要用到
BitmapFactory.Options newOpts = new BitmapFactory.Options();
newOpts.inJustDecodeBounds = true;
YuvImage yuvimage = new YuvImage(
data,
ImageFormat.NV21,
previewSize.width,
previewSize.height,
null);
baos = new ByteArrayOutputStream();
yuvimage.compressToJpeg(new Rect(0, 0, previewSize.width, previewSize.height), 100, baos);// 80--JPG图片的质量[0-100],100最高
rawImage = baos.toByteArray();
//将rawImage转换成bitmap
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.RGB_565;
bitmap = BitmapFactory.decodeByteArray(rawImage, 0, rawImage.length, options);
return bitmap;
如果直接通过BitmapFactory.decodeByteArray这个方法的话,是无法转换的。我们这里要对获取到的data进行预处理。
到这里人脸检测基本就已经完成了,我们的人脸识别是后台来完成的,不需要我们来识别,我们只需要把带有人脸的照片传到后台就可以。
上面我说过这个的需求,如果能够检测到人脸,就通过后台来识别人脸,如果没有检测到人脸,我们就需要通过扫描二维码的方式来进行。
二维码扫描
我们这个二维码扫描的方式比较简单,只需要把预览到的data转成bitmap,然后通过zxing来判断图片中是否存在二维码,存在二维码的话,直接扫描二维码就可以。
预览到的图像转成bitmap,上面那个方法就是,这个方法我就不贴出来了
识别图像中的二维码
1.导入zxing
implementation 'com.google.zxing:core:3.2.1'
2.检测图片中是否存在二维码
Hashtable<DecodeHintType, String> hints = new Hashtable<DecodeHintType, String>();
hints.put(DecodeHintType.CHARACTER_SET, "utf-8"); // 设置二维码内容的编码
Bitmap scanBitmap = Bitmap.createBitmap(mBitmap);
int px[] = new int[scanBitmap.getWidth() * scanBitmap.getHeight()];
scanBitmap.getPixels(px, 0, scanBitmap.getWidth(), 0, 0,
scanBitmap.getWidth(), scanBitmap.getHeight());
RGBLuminanceSource source = new RGBLuminanceSource(scanBitmap.getWidth(), scanBitmap.getHeight(), px);
BinaryBitmap tempBitmap = new BinaryBitmap(new HybridBinarizer(source));
QRCodeReader reader = new QRCodeReader();
try {
return reader.decode(tempBitmap, hints);
} catch (NotFoundException e) {
e.printStackTrace();
} catch (ChecksumException e) {
e.printStackTrace();
} catch (FormatException e) {
e.printStackTrace();
}
这个方法中reader.decode(tempBitmap,hints)返回Result,而result中的getText()方法就是获取到的二维码的内容。
识别二维码这一块比较简单,就是识别图片中的二维码,基本的就是这些了。
源码我就不贴出来了,涉及到公司的东西,后面我会单独把代码提出来写一个demo,如果有需要的话,可以在下面进行留言。