Android端基于JavaCV实现人脸检测功能
目前仅支持摄像机预览角度为0°
- 人脸检测功能:FaceDetectCameraView
- 人脸检测后自动网络请求功能:FaceDetectRequestDialog
FaceDetectCameraView运行效果
FaceDetectRequestDialog运行效果
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}在您引入项目的build.gradle中添加
android {
...
defaultConfig {
...
ndk {
// 设置支持的SO库架构,仅支持armeabi-v7a、arm64-v8a,若想减小APK体积,可只引用对应的SO库架构
abiFilters 'armeabi-v7a', 'arm64-v8a'
}
}
//解决JavaCV中文件重复问题
packagingOptions {
pickFirst 'META-INF/native-image/android-arm/jnijavacpp/jni-config.json'
pickFirst 'META-INF/native-image/android-arm64/jnijavacpp/jni-config.json'
pickFirst 'META-INF/native-image/android-arm/jnijavacpp/reflect-config.json'
pickFirst 'META-INF/native-image/android-arm64/jnijavacpp/reflect-config.json'
}
}
dependencies {
implementation 'com.github.shenbengit:JavaCV-FaceDetect:Tag'
}- FaceDetectCameraView
布局示例
<com.shencoder.javacv_facedetect.FaceDetectCameraView
android:id="@+id/fdv"android:layout_width="match_parent"android:layout_height="500dp"app:fdv_cameraFacing="back"app:fdv_classifierFileRaw="@raw/haarcascade_frontalface_alt"app:fdv_detectAreaLimited="true"app:fdv_drawFaceRect="true"app:fdv_faceRectStrokeColor="@color/design_default_color_error"app:fdv_faceRectStrokeWidth="3dp"app:fdv_keepMaxFace="true"app:fdv_previewMirror="true" />布局说明
<declare-styleablename="FaceDetectCameraView">
<!--摄像头类型,default:back-->
<attrname="fdv_cameraFacing"format="enum">
<enumname="back"value="0" />
<enumname="front"value="1" />
</attr>
<!--是否仅检测最大人脸,default:true-->
<attrname="fdv_keepMaxFace"format="boolean" />
<!--预览画面是否镜像,default:false-->
<attrname="fdv_previewMirror"format="boolean" />
<!--是否限制检测区域,default:true-->
<attrname="fdv_detectAreaLimited"format="boolean" />
<!--级联分类器,default:R.raw.haarcascade_frontalface_alt-->
<attrname="fdv_classifierFileRaw"format="reference" />
<!--是否绘制人脸框,default:true-->
<attrname="fdv_drawFaceRect"format="boolean" />
<!--绘制人脸框的颜色,default:Color.GREEN-->
<attrname="fdv_faceRectStrokeColor"format="color" />
<!--绘制人脸框的宽度-->
<attrname="fdv_faceRectStrokeWidth"format="dimension" />
</declare-styleable>代码示例
FaceDetectCameraViewfdv = findViewById(R.id.fdv);
//设置摄像头相关回调fdv.setOnCameraListener(newOnCameraListener() {
@OverridepublicvoidonCameraOpened() {
}
@OverridepublicvoidonCameraClosed() {
}
@OverridepublicvoidonCameraError(@NonNullCameraExceptionexception) {
}
});
//设置人脸检测相关回调接口fdv.setOnFaceDetectListener(newOnFaceDetectListener() {
/** * 摄像头的预览帧画面里检测到人就会调用 * 子线程调用 * * @param data nv21 * @param width camera frame width * @param height camera frame height * @param faceRectList 人脸位置数据 */@WorkerThread@OverridepublicvoidsomebodyFrame(byte[] data, intwidth, intheight, List<Rect> faceRectList) {
}
/** * 检测到有人会调用一次,和{@link OnFaceDetectListener#somebody()}一起调用 * 子线程调用 * * @param data nv21 * @param width camera frame width * @param height camera frame height * @param faceRectList 人脸位置数据 */@WorkerThread@OverridepublicvoidsomebodyFirstFrame(byte[] data, intwidth, intheight, List<Rect> faceRectList) {
Bitmapbitmap = Nv21Util.nv21ToBitmap(data, width, height);
if (!faceRectList.isEmpty()) {
Rectrect = faceRectList.get(0);
//剪裁人脸BitmapcropBitmap = Nv21Util.cropNv21ToBitmap(data, width, height, rect);
}
}
/** * 首次检测到有人时调用一次 */@MainThread@Overridepublicvoidsomebody() {
}
/** * 首次检测到无人时调用一次 */@MainThread@Overridepublicvoidnobody() {
}
});
//设置相机预览分辨率fdv.setPreviewStreamSize(source -> Collections.singletonList(newSize(1280, 720)));
//设置摄像头fdv.setCameraFacing(Facing.BACK);
//设置是否只保留最大人脸fdv.setKeepMaxFace(true);
//是否镜像预览fdv.setPreviewMirror(false);
//设置是否检测区域限制,注意:目前限制的区域是人脸是否完整在预览View显示的画面里fdv.setDetectAreaLimited(true);
//是否绘制人脸框fdv.setDrawFaceRect(true);
//设置人脸框颜色fdv.setFaceRectStrokeColor(Color.GREEN);
//设置人脸框的宽度fdv.setFaceRectStrokeWidth(2f);
//重试操作fdv.needRetry();
//延迟重试操作fdv.needRetryDelay(1000L);
//设置目标检测的级联分类器fdv.loadClassifierCascade(R.raw.haarcascade_eye, newLoadClassifierCallback() {
@OverridepublicvoidonSuccess() {
}
@OverridepublicvoidonError(Exceptione) {
}
});
fdv.setLifecycleOwner(this);
//fdv.open();//fdv.close();//fdv.destroy();- FaceDetectRequestDialog
代码示例
FaceDetectRequestDialogrequestDialog = FaceDetectRequestDialog.builder(this,
newRequestDialogLayoutCallback() {
/** * 设置{@link FaceDetectRequestDialog#setContentView(int)} * * @return */@OverridepublicintgetLayoutId() {
returnR.layout.dialog_face_detect_request;
}
/** * 设置{@link RequestDialogLayoutCallback#getLayoutId()} 中 {@link FaceDetectCameraView}的id * * @return */@OverridepublicintgetFaceDetectCameraViewId() {
returnR.id.detectCameraView;
}
/** * init view * (e.g. {@code dialog.findViewById(id)}). * * @param dialog FaceDetectRequestDialog */@OverridepublicvoidinitView(FaceDetectRequestDialogdialog) {
// Button btnClose = dialog.findViewById(R.id.btnClose);
}
/** * Called when the dialog is starting. * * @param dialog FaceDetectRequestDialog */@OverridepublicvoidonStart(FaceDetectRequestDialogdialog) {
}
/** * Called when the dialog is starting. * * @param dialog FaceDetectRequestDialog */@OverridepublicvoidonStop(FaceDetectRequestDialogdialog) {
}
/** * @param dialog FaceDetectRequestDialog * @see FaceDetectRequestDialog#destroy() */@OverridepublicvoidonDestroy(FaceDetectRequestDialogdialog) {
}
},
newRequestCallback() {
/** * 生成用于网络请求的{@link OkHttpClient} * 自动调用{@link OkHttpClient.Builder#build()} * * @param builder * @return */@Override@NonNullpublicOkHttpClient.BuildergenerateOkhttpClient(OkHttpClient.Builderbuilder) {
returnbuilder;
}
/** * 生成网络请求的{@link Request} * 自行根据人脸照片数据进行二次封装 * <p> * 自动调用{@link Request.Builder#build()} * * @param builder * @return */@NonNull@OverridepublicRequest.BuildergenerateRequest(Request.Builderbuilder, byte[] data, intwidth, intheight, List<Rect> faceRectList) {
Bitmapbitmap = Nv21Util.nv21ToBitmap(data, width, height);
// Bitmap bitmap = Nv21Util.cropNv21ToBitmap(data, width, height, faceRectList.get(0));if (bitmap != null) {
Stringbase64 = BitmapUtil.bitmapToBase64(bitmap, 100);
RequestFaceBeanbean = newRequestFaceBean("cmd", base64);
RequestBodybody = RequestBody.create(MediaType.parse("application/json"), GsonUtil.toJson(bean));
builder.url("http://xxx:8080")
.post(body);
}
returnbuilder;
}
/** * 网络请求开始 * * @param dialog FaceDetectRequestDialog */@OverridepublicvoidonRequestStart(FaceDetectRequestDialogdialog) {
}
/** * 网络请求失败 * * @param e error * @param dialog FaceDetectRequestDialog */@OverridepublicvoidonRequestFailure(Exceptione, FaceDetectRequestDialogdialog) {
Toast.makeText(MainActivity.this, "人脸识别Error:" + e.getMessage(), Toast.LENGTH_SHORT).show();
dialog.needRetryDelay(2000L);
}
/** * 网络请求成功 * * @param bodyStr 网络请求返回的String字符串 * @param dialog FaceDetectRequestDialog */@OverridepublicvoidonRequestSuccess(StringbodyStr, FaceDetectRequestDialogdialog) {
ResultBeanresultBean = GsonUtil.jsonToBean(bodyStr, ResultBean.class);
if (resultBean.getResCode() == 1) {
Toast.makeText(MainActivity.this, "人脸识别成功:" + resultBean.getData().getUserName(), Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(MainActivity.this, "人脸识别失败", Toast.LENGTH_SHORT).show();
dialog.needRetryDelay(2000L);
}
}
})
.setPreviewSizeSelector(source -> Collections.singletonList(newSize(1280, 720)))
.setShowLoadingDialog(true)
.setCameraListener(exception -> Toast.makeText(MainActivity.this, "摄像头开启异常:" + exception.getMessage(), Toast.LENGTH_SHORT).show())
.setAnybodyCallback(newAnybodyCallback() {
@Overridepublicvoidsomebody() {
System.out.println("有人--->");
}
@Overridepublicvoidnobody() {
System.out.println("无人--->");
}
}).build();
//重试操作requestDialog.needRetry();
//延迟重试操作requestDialog.needRetryDelay(1000L);
//设置目标检测的级联分类器requestDialog.loadClassifierCascade(R.raw.haarcascade_eye, newLoadClassifierCallback() {
@OverridepublicvoidonSuccess() {
}
@OverridepublicvoidonError(Exceptione) {
}
});
requestDialog.show();
requestDialog.dismiss();//requestDialog.cancel();requestDialog.destroy();- Nv21Util
nv21转bitmap工具类
//nv21转BitmapNv21Util.nv21ToBitmap(@NonNullbyte[] nv21, int width, int height);
//nv21剪裁转Bitmap,注:会进行二次转换,剪裁出的图片要比提供位置矩阵的略大Nv21Util.cropNv21ToBitmap(@NonNullbyte[] nv21, intwidth, intheight, Rectrect);- BitmapUtil
//Bitmap转base64BitmapUtil.bitmapToBase64(@NullableBitmapbitmap, int quality);
