Android端基於JavaCV實現人臉檢測功能

冬季穿短褲 2021-09-18 13:46:58 阅读数:843

android javacv 功能

JavaCV-FaceDetect

Android端基於JavaCV實現人臉檢測功能

實現功能

項目引入框架

效果展示

FaceDetectCameraView運行效果

FaceDetectCameraView.gif

FaceDetectRequestDialog運行效果

FaceDetectRequestDialog.gif

引入

將JitPack存儲庫添加到您的項目中(項目根目錄下build.gradle文件)

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:0.0.5'
}
複制代碼

使用事例

  • 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-styleable name="FaceDetectCameraView">
<!--攝像頭類型,default:back-->
<attr name="fdv_cameraFacing" format="enum">
<enum name="back" value="0" />
<enum name="front" value="1" />
</attr>
<!--是否僅檢測最大人臉,default:true-->
<attr name="fdv_keepMaxFace" format="boolean" />
<!--預覽畫面是否鏡像,default:false-->
<attr name="fdv_previewMirror" format="boolean" />
<!--是否限制檢測區域,default:true-->
<attr name="fdv_detectAreaLimited" format="boolean" />
<!--級聯分類器,default:R.raw.haarcascade_frontalface_alt-->
<attr name="fdv_classifierFileRaw" format="reference" />
<!--是否繪制人臉框,default:true-->
<attr name="fdv_drawFaceRect" format="boolean" />
<!--繪制人臉框的顏色,default:Color.GREEN-->
<attr name="fdv_faceRectStrokeColor" format="color" />
<!--繪制人臉框的寬度-->
<attr name="fdv_faceRectStrokeWidth" format="dimension" />
</declare-styleable>
複制代碼

代碼示例

FaceDetectCameraView fdv = findViewById(R.id.fdv);
//設置攝像頭相關回調
fdv.setOnCameraListener(new OnCameraListener() {
@Override
public void onCameraOpened() {
}
@Override
public void onCameraClosed() {
}
@Override
public void onCameraError(@NonNull CameraException exception) {
}
});
//設置人臉檢測相關回調接口
fdv.setOnFaceDetectListener(new OnFaceDetectListener() {
/** * 攝像頭的預覽幀畫面裏檢測到人就會調用 * 子線程調用 * * @param data nv21 * @param width camera frame width * @param height camera frame height * @param faceRectList 人臉比特置數據 */
@WorkerThread
@Override
public void somebodyFrame(byte[] data, int width, int height, List<Rect> faceRectList) {
}
/** * 檢測到有人會調用一次,和{@link OnFaceDetectListener#somebody()}一起調用 * 子線程調用 * * @param data nv21 * @param width camera frame width * @param height camera frame height * @param faceRectList 人臉比特置數據 */
@WorkerThread
@Override
public void somebodyFirstFrame(byte[] data, int width, int height, List<Rect> faceRectList) {
if (!faceRectList.isEmpty()) {
Rect rect = faceRectList.get(0);
//剪裁人臉
Bitmap cropBitmap = Nv21ToBitmapUtil.cropNv21ToBitmap(data, width, height, rect);
Bitmap bitmap = Nv21ToBitmapUtil.nv21ToBitmap(data, width, height);
}
}
/** * 首次檢測到有人時調用一次 */
@MainThread
@Override
public void somebody() {
}
/** * 首次檢測到無人時調用一次 */
@MainThread
@Override
public void nobody() {
}
});
//設置相機預覽分辨率
fdv.setPreviewStreamSize(source -> Collections.singletonList(new Size(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.setLifecycleOwner(this);
//fdv.open();
//fdv.close();
//fdv.destroy();
複制代碼
  • FaceDetectRequestDialog

代碼示例

FaceDetectRequestDialog dialog = FaceDetectRequestDialog.builder(this,
new RequestDialogLayoutCallback() {
/** * 設置{@link FaceDetectRequestDialog#setContentView(int)} * * @return */
@Override
public int getLayoutId() {
return R.layout.dialog_face_detect_request;
}
/** * 設置{@link RequestDialogLayoutCallback#getLayoutId()} 中 {@link FaceDetectCameraView}的id * * @return */
@Override
public int getFaceDetectCameraViewId() {
return R.id.detectCameraView;
}
/** * init view * (e.g. {@code dialog.findViewById(id)}). * * @param dialog FaceDetectRequestDialog */
@Override
public void initView(FaceDetectRequestDialog dialog) {
// Button btnClose = dialog.findViewById(R.id.btnClose);
}
/** * Called when the dialog is starting. * * @param dialog FaceDetectRequestDialog */
@Override
public void onStart(FaceDetectRequestDialog dialog) {
}
/** * Called when the dialog is starting. * * @param dialog FaceDetectRequestDialog */
@Override
public void onStop(FaceDetectRequestDialog dialog) {
}
/** * @param dialog FaceDetectRequestDialog * @see FaceDetectRequestDialog#destroy() */
@Override
public void onDestroy(FaceDetectRequestDialog dialog) {
}
},
new RequestCallback() {
/** * 生成用於網絡請求的{@link OkHttpClient} * 自動調用{@link OkHttpClient.Builder#build()} * * @param builder * @return */
@Override
@NonNull
public OkHttpClient.Builder generateOkhttpClient(OkHttpClient.Builder builder) {
return builder;
}
/** * 生成網絡請求的{@link Request} * 自行根據人臉照片數據進行二次封裝 * <p> * 自動調用{@link Request.Builder#build()} * * @param builder * @return */
@NonNull
@Override
public Request.Builder generateRequest(Request.Builder builder, byte[] data, int width, int height, List<Rect> faceRectList) {
Bitmap bitmap = Nv21ToBitmapUtil.nv21ToBitmap(data, width, height);
// Bitmap bitmap = Nv21ToBitmapUtil.cropNv21ToBitmap(data, width, height, faceRectList.get(0));
if (bitmap != null) {
String base64 = Nv21ToBitmapUtil.bitmapToBase64(bitmap, 100);
RequestFaceBean bean = new RequestFaceBean("imagecompare", base64);
RequestBody body = RequestBody.create(MediaType.parse("application/json"), GsonUtil.toJson(bean));
builder.url("http://192.168.2.186:25110")
.post(body);
}
return builder;
}
@Override
public void onRequestStart() {
}
@Override
public void onRequestFailure(Exception e) {
Toast.makeText(MainActivity.this, "人臉識別Error:" + e.getMessage(), Toast.LENGTH_SHORT).show();
dialog.needRetryDelay(2000L);
}
@Override
public void onRequestSuccess(String bodyStr) {
ResultBean resultBean = 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(new Size(1280, 720)))
.setShowLoadingDialog(true)
.setCameraListener(exception -> Toast.makeText(MainActivity.this, "攝像頭開啟异常:" + exception.getMessage(), Toast.LENGTH_SHORT).show())
.setAnybodyCallback(new AnybodyCallback() {
@Override
public void somebody() {
System.out.println("有人--->");
}
@Override
public void nobody() {
System.out.println("無人--->");
}
}).build();
dialog.show();
dialog.dismiss();//dialog.cancel();
dialog.destroy();
複制代碼
  • Nv21ToBitmapUtil

nv21轉bitmap工具類

//Bitmap轉base64
Nv21ToBitmapUtil.bitmapToBase64(@Nullable Bitmap bitmap, int quality);
//nv21轉Bitmap
Nv21ToBitmapUtil.nv21ToBitmap(@NonNull byte[] nv21, int width, int height);
//nv21剪裁轉Bitmap,注:會進行二次轉換,剪裁出的圖片要比提供比特置矩陣的略大
Nv21ToBitmapUtil.cropNv21ToBitmap(@NonNull byte[] nv21, int width, int height, Rect rect);
複制代碼

Github傳送門

版权声明:本文为[冬季穿短褲]所创,转载请带上原文链接,感谢。 https://gsmany.com/2021/09/20210918134657614m.html