作者 张卫卫

sdk架构修改

正在显示 93 个修改的文件 包含 7191 行增加4005 行删除
... ... @@ -13,6 +13,7 @@
<option value="$PROJECT_DIR$/arcface" />
<option value="$PROJECT_DIR$/bdface" />
<option value="$PROJECT_DIR$/sdk" />
<option value="$PROJECT_DIR$/test" />
</set>
</option>
<option name="resolveModulePerSourceSet" value="false" />
... ...
... ... @@ -24,9 +24,6 @@ android {
dependencies {
api fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:28.0.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
implementation 'com.android.support:recyclerview-v7:28.0.0'
implementation 'com.github.bumptech.glide:glide:4.9.0'
implementation 'io.reactivex.rxjava2:rxjava:2.2.6'
... ...
package com.arcsoft.arcfacedemo;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumented test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getTargetContext();
assertEquals("com.arcsoft.arcfacedemo", appContext.getPackageName());
}
}
... ... @@ -2,61 +2,11 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.arcsoft.arcfacedemo">
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application android:allowBackup="false"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name=".activity.FaceAttrPreviewActivity"
android:launchMode="singleTop" />
<activity
android:name=".activity.ChooseFunctionActivity"
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".activity.SingleImageActivity"
android:configChanges="orientation|screenSize"
android:launchMode="singleTop" />
<activity
android:name=".activity.MultiImageActivity"
android:launchMode="singleTop" />
<activity
android:name=".activity.IrRegisterAndRecognizeActivity"
android:launchMode="singleTop" />
<activity
android:name=".activity.RegisterAndRecognizeActivity"
android:launchMode="singleTop" />
<activity
android:name=".activity.FaceManageActivity"
android:launchMode="singleTop" />
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths" />
</provider>
android:supportsRtl="true">
</application>
... ...
package com.arcsoft.arcfacedemo.activity;
import android.content.pm.PackageManager;
import android.support.annotation.NonNull;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.widget.Toast;
public abstract class BaseActivity extends AppCompatActivity {
/**
* 权限检查
*
* @param neededPermissions 需要的权限
* @return 是否全部被允许
*/
protected boolean checkPermissions(String[] neededPermissions) {
if (neededPermissions == null || neededPermissions.length == 0) {
return true;
}
boolean allGranted = true;
for (String neededPermission : neededPermissions) {
allGranted &= ContextCompat.checkSelfPermission(this, neededPermission) == PackageManager.PERMISSION_GRANTED;
}
return allGranted;
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
boolean isAllGranted = true;
for (int grantResult : grantResults) {
isAllGranted &= (grantResult == PackageManager.PERMISSION_GRANTED);
}
afterRequestPermission(requestCode, isAllGranted);
}
/**
* 请求权限的回调
*
* @param requestCode 请求码
* @param isAllGranted 是否全部被同意
*/
abstract void afterRequestPermission(int requestCode, boolean isAllGranted);
protected void showToast(String s) {
Toast.makeText(getApplicationContext(), s, Toast.LENGTH_SHORT).show();
}
protected void showLongToast(String s) {
Toast.makeText(getApplicationContext(), s, Toast.LENGTH_LONG).show();
}
}
package com.arcsoft.arcfacedemo.activity;
import android.Manifest;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.os.Bundle;
import android.support.v4.app.ActivityCompat;
import android.util.Log;
import android.view.View;
import com.arcsoft.arcfacedemo.R;
import com.arcsoft.arcfacedemo.common.Constants;
import com.arcsoft.arcfacedemo.fragment.ChooseDetectDegreeDialog;
import com.arcsoft.face.ActiveFileInfo;
import com.arcsoft.face.ErrorInfo;
import com.arcsoft.face.FaceEngine;
import com.arcsoft.face.VersionInfo;
import com.arcsoft.face.enums.RuntimeABI;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import io.reactivex.Observable;
import io.reactivex.ObservableEmitter;
import io.reactivex.ObservableOnSubscribe;
import io.reactivex.Observer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
public class ChooseFunctionActivity extends BaseActivity {
private static final String TAG = "ChooseFunctionActivity";
private static final int ACTION_REQUEST_PERMISSIONS = 0x001;
// 在线激活所需的权限
private static final String[] NEEDED_PERMISSIONS = new String[]{
Manifest.permission.READ_PHONE_STATE
};
boolean libraryExists = true;
// Demo 所需的动态库文件
private static final String[] LIBRARIES = new String[]{
// 人脸相关
"libarcsoft_face_engine.so",
"libarcsoft_face.so",
// 图像库相关
"libarcsoft_image_util.so",
};
// 修改配置项的对话框
ChooseDetectDegreeDialog chooseDetectDegreeDialog;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_choose_function);
libraryExists = checkSoFile(LIBRARIES);
ApplicationInfo applicationInfo = getApplicationInfo();
Log.i(TAG, "onCreate: " + applicationInfo.nativeLibraryDir);
if (!libraryExists) {
showToast(getString(R.string.library_not_found));
}else {
VersionInfo versionInfo = new VersionInfo();
int code = FaceEngine.getVersion(versionInfo);
Log.i(TAG, "onCreate: getVersion, code is: " + code + ", versionInfo is: " + versionInfo);
}
}
/**
* 检查能否找到动态链接库,如果找不到,请修改工程配置
*
* @param libraries 需要的动态链接库
* @return 动态库是否存在
*/
private boolean checkSoFile(String[] libraries) {
File dir = new File(getApplicationInfo().nativeLibraryDir);
File[] files = dir.listFiles();
if (files == null || files.length == 0) {
return false;
}
List<String> libraryNameList = new ArrayList<>();
for (File file : files) {
libraryNameList.add(file.getName());
}
boolean exists = true;
for (String library : libraries) {
exists &= libraryNameList.contains(library);
}
return exists;
}
/**
* 打开相机,显示年龄性别
*
* @param view
*/
public void jumpToPreviewActivity(View view) {
checkLibraryAndJump(FaceAttrPreviewActivity.class);
}
/**
* 处理单张图片,显示图片中所有人脸的信息,并且一一比对相似度
*
* @param view
*/
public void jumpToSingleImageActivity(View view) {
checkLibraryAndJump(SingleImageActivity.class);
}
/**
* 选择一张主照,显示主照中人脸的详细信息,然后选择图片和主照进行比对
*
* @param view
*/
public void jumpToMultiImageActivity(View view) {
checkLibraryAndJump(MultiImageActivity.class);
}
/**
* 打开相机,RGB活体检测,人脸注册,人脸识别
*
* @param view
*/
public void jumpToFaceRecognizeActivity(View view) {
checkLibraryAndJump(RegisterAndRecognizeActivity.class);
}
/**
* 打开相机,IR活体检测,人脸注册,人脸识别
*
* @param view
*/
public void jumpToIrFaceRecognizeActivity(View view) {
checkLibraryAndJump(IrRegisterAndRecognizeActivity.class);
}
/**
* 批量注册和删除功能
*
* @param view
*/
public void jumpToBatchRegisterActivity(View view) {
checkLibraryAndJump(FaceManageActivity.class);
}
/**
* 激活引擎
*
* @param view
*/
public void activeEngine(final View view) {
if (!libraryExists) {
showToast(getString(R.string.library_not_found));
return;
}
if (!checkPermissions(NEEDED_PERMISSIONS)) {
ActivityCompat.requestPermissions(this, NEEDED_PERMISSIONS, ACTION_REQUEST_PERMISSIONS);
return;
}
if (view != null) {
view.setClickable(false);
}
Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> emitter) {
RuntimeABI runtimeABI = FaceEngine.getRuntimeABI();
Log.i(TAG, "subscribe: getRuntimeABI() " + runtimeABI);
long start = System.currentTimeMillis();
int activeCode = FaceEngine.activeOnline(ChooseFunctionActivity.this, Constants.APP_ID, Constants.SDK_KEY);
Log.i(TAG, "subscribe cost: " + (System.currentTimeMillis() - start));
emitter.onNext(activeCode);
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Integer>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(Integer activeCode) {
if (activeCode == ErrorInfo.MOK) {
showToast(getString(R.string.active_success));
} else if (activeCode == ErrorInfo.MERR_ASF_ALREADY_ACTIVATED) {
showToast(getString(R.string.already_activated));
} else {
showToast(getString(R.string.active_failed, activeCode));
}
if (view != null) {
view.setClickable(true);
}
ActiveFileInfo activeFileInfo = new ActiveFileInfo();
int res = FaceEngine.getActiveFileInfo(ChooseFunctionActivity.this, activeFileInfo);
if (res == ErrorInfo.MOK) {
Log.i(TAG, activeFileInfo.toString());
}
}
@Override
public void onError(Throwable e) {
showToast(e.getMessage());
if (view != null) {
view.setClickable(true);
}
}
@Override
public void onComplete() {
}
});
}
@Override
void afterRequestPermission(int requestCode, boolean isAllGranted) {
if (requestCode == ACTION_REQUEST_PERMISSIONS) {
if (isAllGranted) {
activeEngine(null);
} else {
showToast(getString(R.string.permission_denied));
}
}
}
void checkLibraryAndJump(Class activityClass) {
if (!libraryExists) {
showToast(getString(R.string.library_not_found));
return;
}
startActivity(new Intent(this, activityClass));
}
public void chooseDetectDegree(View view) {
if (chooseDetectDegreeDialog == null) {
chooseDetectDegreeDialog = new ChooseDetectDegreeDialog();
}
if (chooseDetectDegreeDialog.isAdded()) {
chooseDetectDegreeDialog.dismiss();
}
chooseDetectDegreeDialog.show(getSupportFragmentManager(), ChooseDetectDegreeDialog.class.getSimpleName());
}
}
package com.arcsoft.arcfacedemo.activity;
import android.Manifest;
import android.content.pm.ActivityInfo;
import android.graphics.Point;
import android.hardware.Camera;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.ActivityCompat;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
import com.arcsoft.arcfacedemo.R;
import com.arcsoft.arcfacedemo.model.DrawInfo;
import com.arcsoft.arcfacedemo.util.ConfigUtil;
import com.arcsoft.arcfacedemo.util.DrawHelper;
import com.arcsoft.arcfacedemo.util.camera.CameraHelper;
import com.arcsoft.arcfacedemo.util.camera.CameraListener;
import com.arcsoft.arcfacedemo.util.face.RecognizeColor;
import com.arcsoft.arcfacedemo.widget.FaceRectView;
import com.arcsoft.face.AgeInfo;
import com.arcsoft.face.ErrorInfo;
import com.arcsoft.face.Face3DAngle;
import com.arcsoft.face.FaceEngine;
import com.arcsoft.face.FaceInfo;
import com.arcsoft.face.GenderInfo;
import com.arcsoft.face.LivenessInfo;
import com.arcsoft.face.enums.DetectMode;
import java.util.ArrayList;
import java.util.List;
public class FaceAttrPreviewActivity extends BaseActivity implements ViewTreeObserver.OnGlobalLayoutListener {
private static final String TAG = "FaceAttrPreviewActivity";
private CameraHelper cameraHelper;
private DrawHelper drawHelper;
private Camera.Size previewSize;
private Integer rgbCameraId = Camera.CameraInfo.CAMERA_FACING_BACK;
private FaceEngine faceEngine;
private int afCode = -1;
private int processMask = FaceEngine.ASF_AGE | FaceEngine.ASF_FACE3DANGLE | FaceEngine.ASF_GENDER | FaceEngine.ASF_LIVENESS;
/**
* 相机预览显示的控件,可为SurfaceView或TextureView
*/
private View previewView;
private FaceRectView faceRectView;
private static final int ACTION_REQUEST_PERMISSIONS = 0x001;
/**
* 所需的所有权限信息
*/
private static final String[] NEEDED_PERMISSIONS = new String[]{
Manifest.permission.CAMERA,
Manifest.permission.READ_PHONE_STATE
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_face_attr_preview);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
WindowManager.LayoutParams attributes = getWindow().getAttributes();
attributes.systemUiVisibility = View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
getWindow().setAttributes(attributes);
}
// Activity启动后就锁定为启动时的方向
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED);
previewView = findViewById(R.id.texture_preview);
faceRectView = findViewById(R.id.face_rect_view);
//在布局结束后才做初始化操作
previewView.getViewTreeObserver().addOnGlobalLayoutListener(this);
}
private void initEngine() {
faceEngine = new FaceEngine();
afCode = faceEngine.init(this, DetectMode.ASF_DETECT_MODE_VIDEO, ConfigUtil.getFtOrient(this),
16, 20, FaceEngine.ASF_FACE_DETECT | FaceEngine.ASF_AGE | FaceEngine.ASF_FACE3DANGLE | FaceEngine.ASF_GENDER | FaceEngine.ASF_LIVENESS);
Log.i(TAG, "initEngine: init: " + afCode);
if (afCode != ErrorInfo.MOK) {
showToast( getString(R.string.init_failed, afCode));
}
}
private void unInitEngine() {
if (afCode == 0) {
afCode = faceEngine.unInit();
Log.i(TAG, "unInitEngine: " + afCode);
}
}
@Override
protected void onDestroy() {
if (cameraHelper != null) {
cameraHelper.release();
cameraHelper = null;
}
unInitEngine();
super.onDestroy();
}
private void initCamera() {
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
CameraListener cameraListener = new CameraListener() {
@Override
public void onCameraOpened(Camera camera, int cameraId, int displayOrientation, boolean isMirror) {
Log.i(TAG, "onCameraOpened: " + cameraId + " " + displayOrientation + " " + isMirror);
previewSize = camera.getParameters().getPreviewSize();
drawHelper = new DrawHelper(previewSize.width, previewSize.height, previewView.getWidth(), previewView.getHeight(), displayOrientation
, cameraId, isMirror, false, true);
}
@Override
public void onPreview(byte[] nv21, Camera camera) {
if (faceRectView != null) {
faceRectView.clearFaceInfo();
}
List<FaceInfo> faceInfoList = new ArrayList<>();
// long start = System.currentTimeMillis();
int code = faceEngine.detectFaces(nv21, previewSize.width, previewSize.height, FaceEngine.CP_PAF_NV21, faceInfoList);
if (code == ErrorInfo.MOK && faceInfoList.size() > 0) {
code = faceEngine.process(nv21, previewSize.width, previewSize.height, FaceEngine.CP_PAF_NV21, faceInfoList, processMask);
if (code != ErrorInfo.MOK) {
return;
}
} else {
return;
}
List<AgeInfo> ageInfoList = new ArrayList<>();
List<GenderInfo> genderInfoList = new ArrayList<>();
List<Face3DAngle> face3DAngleList = new ArrayList<>();
List<LivenessInfo> faceLivenessInfoList = new ArrayList<>();
int ageCode = faceEngine.getAge(ageInfoList);
int genderCode = faceEngine.getGender(genderInfoList);
int face3DAngleCode = faceEngine.getFace3DAngle(face3DAngleList);
int livenessCode = faceEngine.getLiveness(faceLivenessInfoList);
// 有其中一个的错误码不为ErrorInfo.MOK,return
if ((ageCode | genderCode | face3DAngleCode | livenessCode) != ErrorInfo.MOK) {
return;
}
if (faceRectView != null && drawHelper != null) {
List<DrawInfo> drawInfoList = new ArrayList<>();
for (int i = 0; i < faceInfoList.size(); i++) {
drawInfoList.add(new DrawInfo(drawHelper.adjustRect(faceInfoList.get(i).getRect()), genderInfoList.get(i).getGender(), ageInfoList.get(i).getAge(), faceLivenessInfoList.get(i).getLiveness(), RecognizeColor.COLOR_UNKNOWN, null));
}
drawHelper.draw(faceRectView, drawInfoList);
}
}
@Override
public void onCameraClosed() {
Log.i(TAG, "onCameraClosed: ");
}
@Override
public void onCameraError(Exception e) {
Log.i(TAG, "onCameraError: " + e.getMessage());
}
@Override
public void onCameraConfigurationChanged(int cameraID, int displayOrientation) {
if (drawHelper != null) {
drawHelper.setCameraDisplayOrientation(displayOrientation);
}
Log.i(TAG, "onCameraConfigurationChanged: " + cameraID + " " + displayOrientation);
}
};
cameraHelper = new CameraHelper.Builder()
.previewViewSize(new Point(previewView.getMeasuredWidth(), previewView.getMeasuredHeight()))
.rotation(getWindowManager().getDefaultDisplay().getRotation())
.specificCameraId(rgbCameraId != null ? rgbCameraId : Camera.CameraInfo.CAMERA_FACING_FRONT)
.isMirror(false)
.previewOn(previewView)
.cameraListener(cameraListener)
.build();
cameraHelper.init();
cameraHelper.start();
}
@Override
void afterRequestPermission(int requestCode, boolean isAllGranted) {
if (requestCode == ACTION_REQUEST_PERMISSIONS) {
if (isAllGranted) {
initEngine();
initCamera();
} else {
showToast(getString( R.string.permission_denied));
}
}
}
/**
* 在{@link #previewView}第一次布局完成后,去除该监听,并且进行引擎和相机的初始化
*/
@Override
public void onGlobalLayout() {
previewView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
if (!checkPermissions(NEEDED_PERMISSIONS)) {
ActivityCompat.requestPermissions(this, NEEDED_PERMISSIONS, ACTION_REQUEST_PERMISSIONS);
} else {
initEngine();
initCamera();
}
}
/**
* 切换相机。注意:若切换相机发现检测不到人脸,则极有可能是检测角度导致的,需要销毁引擎重新创建或者在设置界面修改配置的检测角度
*
* @param view
*/
public void switchCamera(View view) {
if (cameraHelper != null) {
boolean success = cameraHelper.switchCamera();
if (!success) {
showToast(getString(R.string.switch_camera_failed));
} else {
showLongToast(getString(R.string.notice_change_detect_degree));
}
}
}
}
package com.arcsoft.arcfacedemo.activity;
import android.Manifest;
import android.content.DialogInterface;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Environment;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AlertDialog;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.WindowManager;
import android.widget.TextView;
import com.arcsoft.arcfacedemo.R;
import com.arcsoft.arcfacedemo.widget.ProgressDialog;
import com.arcsoft.arcfacedemo.faceserver.FaceServer;
import com.arcsoft.imageutil.ArcSoftImageFormat;
import com.arcsoft.imageutil.ArcSoftImageUtil;
import com.arcsoft.imageutil.ArcSoftImageUtilError;
import java.io.File;
import java.io.FilenameFilter;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 批量注册页面
*/
public class FaceManageActivity extends BaseActivity {
//注册图所在的目录
private static final String ROOT_DIR = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "arcfacedemo";
private static final String REGISTER_DIR = ROOT_DIR + File.separator + "register";
private static final String REGISTER_FAILED_DIR = ROOT_DIR + File.separator + "failed";
private ExecutorService executorService;
private TextView tvNotificationRegisterResult;
ProgressDialog progressDialog = null;
private static final int ACTION_REQUEST_PERMISSIONS = 0x001;
private static String[] NEEDED_PERMISSIONS = new String[]{
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_face_manage);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
executorService = Executors.newSingleThreadExecutor();
tvNotificationRegisterResult = findViewById(R.id.notification_register_result);
progressDialog = new ProgressDialog(this);
FaceServer.getInstance().init(this);
}
@Override
protected void onDestroy() {
if (executorService != null && !executorService.isShutdown()) {
executorService.shutdownNow();
}
if (progressDialog != null && progressDialog.isShowing()) {
progressDialog.dismiss();
}
FaceServer.getInstance().unInit();
super.onDestroy();
}
public void batchRegister(View view) {
if (checkPermissions(NEEDED_PERMISSIONS)) {
doRegister();
} else {
ActivityCompat.requestPermissions(this, NEEDED_PERMISSIONS, ACTION_REQUEST_PERMISSIONS);
}
}
private void doRegister() {
File dir = new File(REGISTER_DIR);
if (!dir.exists()) {
showToast(getString(R.string.batch_process_path_is_not_exists, REGISTER_DIR));
return;
}
if (!dir.isDirectory()) {
showToast(getString(R.string.batch_process_path_is_not_dir, REGISTER_DIR));
return;
}
final File[] jpgFiles = dir.listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return name.endsWith(FaceServer.IMG_SUFFIX);
}
});
executorService.execute(new Runnable() {
@Override
public void run() {
final int totalCount = jpgFiles.length;
int successCount = 0;
runOnUiThread(new Runnable() {
@Override
public void run() {
progressDialog.setMaxProgress(totalCount);
progressDialog.show();
tvNotificationRegisterResult.setText("");
tvNotificationRegisterResult.append(getString(R.string.batch_process_processing_please_wait));
}
});
for (int i = 0; i < totalCount; i++) {
final int finalI = i;
runOnUiThread(new Runnable() {
@Override
public void run() {
if (progressDialog != null) {
progressDialog.refreshProgress(finalI);
}
}
});
final File jpgFile = jpgFiles[i];
Bitmap bitmap = BitmapFactory.decodeFile(jpgFile.getAbsolutePath());
if (bitmap == null) {
File failedFile = new File(REGISTER_FAILED_DIR + File.separator + jpgFile.getName());
if (!failedFile.getParentFile().exists()) {
failedFile.getParentFile().mkdirs();
}
jpgFile.renameTo(failedFile);
continue;
}
bitmap = ArcSoftImageUtil.getAlignedBitmap(bitmap, true);
if (bitmap == null) {
File failedFile = new File(REGISTER_FAILED_DIR + File.separator + jpgFile.getName());
if (!failedFile.getParentFile().exists()) {
failedFile.getParentFile().mkdirs();
}
jpgFile.renameTo(failedFile);
continue;
}
byte[] bgr24 = ArcSoftImageUtil.createImageData(bitmap.getWidth(), bitmap.getHeight(), ArcSoftImageFormat.BGR24);
int transformCode = ArcSoftImageUtil.bitmapToImageData(bitmap, bgr24, ArcSoftImageFormat.BGR24);
if (transformCode != ArcSoftImageUtilError.CODE_SUCCESS) {
runOnUiThread(new Runnable() {
@Override
public void run() {
progressDialog.dismiss();
tvNotificationRegisterResult.append("");
}
});
return;
}
boolean success = FaceServer.getInstance().registerBgr24(FaceManageActivity.this, bgr24, bitmap.getWidth(), bitmap.getHeight(),
jpgFile.getName().substring(0, jpgFile.getName().lastIndexOf(".")));
if (!success) {
File failedFile = new File(REGISTER_FAILED_DIR + File.separator + jpgFile.getName());
if (!failedFile.getParentFile().exists()) {
failedFile.getParentFile().mkdirs();
}
jpgFile.renameTo(failedFile);
} else {
successCount++;
}
}
final int finalSuccessCount = successCount;
runOnUiThread(new Runnable() {
@Override
public void run() {
progressDialog.dismiss();
tvNotificationRegisterResult.append(getString(R.string.batch_process_finished_info, totalCount, finalSuccessCount, totalCount - finalSuccessCount, REGISTER_FAILED_DIR));
}
});
Log.i(FaceManageActivity.class.getSimpleName(), "run: " + executorService.isShutdown());
}
});
}
@Override
void afterRequestPermission(int requestCode, boolean isAllGranted) {
if (requestCode == ACTION_REQUEST_PERMISSIONS) {
if (isAllGranted) {
doRegister();
} else {
showToast(getString(R.string.permission_denied));
}
}
}
public void clearFaces(View view) {
int faceNum = FaceServer.getInstance().getFaceNumber(this);
if (faceNum == 0) {
showToast(getString(R.string.batch_process_no_face_need_to_delete));
} else {
AlertDialog dialog = new AlertDialog.Builder(this)
.setTitle(R.string.batch_process_notification)
.setMessage(getString(R.string.batch_process_confirm_delete, faceNum))
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
int deleteCount = FaceServer.getInstance().clearAllFaces(FaceManageActivity.this);
showToast(deleteCount + " faces cleared!");
}
})
.setNegativeButton(R.string.cancel, null)
.create();
dialog.show();
}
}
}
package com.arcsoft.arcfacedemo.activity;
import android.Manifest;
import android.content.pm.ActivityInfo;
import android.graphics.Color;
import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.Camera;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
import android.widget.CompoundButton;
import android.widget.FrameLayout;
import android.widget.Switch;
import android.widget.TextView;
import com.arcsoft.arcfacedemo.R;
import com.arcsoft.arcfacedemo.common.Constants;
import com.arcsoft.arcfacedemo.faceserver.CompareResult;
import com.arcsoft.arcfacedemo.faceserver.FaceServer;
import com.arcsoft.arcfacedemo.model.DrawInfo;
import com.arcsoft.arcfacedemo.model.FacePreviewInfo;
import com.arcsoft.arcfacedemo.util.ConfigUtil;
import com.arcsoft.arcfacedemo.util.DrawHelper;
import com.arcsoft.arcfacedemo.util.camera.CameraListener;
import com.arcsoft.arcfacedemo.util.camera.DualCameraHelper;
import com.arcsoft.arcfacedemo.util.face.FaceHelper;
import com.arcsoft.arcfacedemo.util.face.FaceListener;
import com.arcsoft.arcfacedemo.util.face.LivenessType;
import com.arcsoft.arcfacedemo.util.face.RecognizeColor;
import com.arcsoft.arcfacedemo.util.face.RequestFeatureStatus;
import com.arcsoft.arcfacedemo.util.face.RequestLivenessStatus;
import com.arcsoft.arcfacedemo.widget.FaceRectView;
import com.arcsoft.arcfacedemo.widget.FaceSearchResultAdapter;
import com.arcsoft.face.AgeInfo;
import com.arcsoft.face.ErrorInfo;
import com.arcsoft.face.FaceEngine;
import com.arcsoft.face.FaceFeature;
import com.arcsoft.face.FaceInfo;
import com.arcsoft.face.GenderInfo;
import com.arcsoft.face.LivenessInfo;
import com.arcsoft.face.VersionInfo;
import com.arcsoft.face.enums.DetectFaceOrientPriority;
import com.arcsoft.face.enums.DetectMode;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import io.reactivex.Observable;
import io.reactivex.ObservableEmitter;
import io.reactivex.ObservableOnSubscribe;
import io.reactivex.Observer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
/**
* 1.活体检测使用IR摄像头数据,其他都使用RGB摄像头数据
* <p>
* 2.本界面仅实现IR数据和RGB数据预览大小相同且画面十分接近的情况(RGB数据和IR数据无旋转、镜像的关系)的活体检测,
* <p>
* 3.若IR数据和RGB数据预览大小不同或两者成像的人脸位置差别很大,需要自己实现人脸框的调整方案。
* <p>
* 4.由于不同的厂商对IR Camera和RGB Camera的CameraId设置可能会有所不同,开发者可能需要根据实际情况修改
* {@link IrRegisterAndRecognizeActivity#cameraRgbId}和
* {@link IrRegisterAndRecognizeActivity#cameraIrId}的值
* <p>
* 5.由于一般情况下android设备的前置摄像头,即cameraId为{@link Camera.CameraInfo#CAMERA_FACING_FRONT}的摄像头在打开后会自动被镜像预览。
* 为了便于开发者们更直观地了解两个摄像头成像的关系,实现人脸框的调整方案,本demo对cameraId为{@link Camera.CameraInfo#CAMERA_FACING_FRONT}
* 的预览画面做了再次镜像的处理,也就是恢复为原画面。
*/
public class IrRegisterAndRecognizeActivity extends BaseActivity implements ViewTreeObserver.OnGlobalLayoutListener {
private static final String TAG = "IrRegisterAndRecognize";
private static final int MAX_DETECT_NUM = 10;
/**
* 当FR成功,活体未成功时,FR等待活体的时间
*/
private static final int WAIT_LIVENESS_INTERVAL = 50;
/**
* 失败重试间隔时间(ms)
*/
private static final long FAIL_RETRY_INTERVAL = 1000;
/**
* 出错重试最大次数
*/
private static final int MAX_RETRY_TIME = 3;
private DualCameraHelper cameraHelper;
private DualCameraHelper cameraHelperIr;
private DrawHelper drawHelperRgb;
private DrawHelper drawHelperIr;
private Camera.Size previewSize;
private Camera.Size previewSizeIr;
/**
* RGB摄像头和IR摄像头的ID,若和实际不符,需要修改以下两个值。
* 同时,可能需要修改默认的VIDEO模式人脸检测角度
*/
private Integer cameraRgbId = Camera.CameraInfo.CAMERA_FACING_BACK;
private Integer cameraIrId = Camera.CameraInfo.CAMERA_FACING_FRONT;
private FaceEngine ftEngine;
private FaceEngine frEngine;
private FaceEngine flEngine;
private int ftInitCode = -1;
private int frInitCode = -1;
private int flInitCode = -1;
private FaceHelper faceHelperIr;
private List<CompareResult> compareResultList;
private FaceSearchResultAdapter adapter;
/**
* 活体检测的开关
*/
private boolean livenessDetect = true;
/**
* 注册人脸状态码,准备注册
*/
private static final int REGISTER_STATUS_READY = 0;
/**
* 注册人脸状态码,注册中
*/
private static final int REGISTER_STATUS_PROCESSING = 1;
/**
* 注册人脸状态码,注册结束(无论成功失败)
*/
private static final int REGISTER_STATUS_DONE = 2;
private int registerStatus = REGISTER_STATUS_DONE;
/**
* 用于记录人脸识别相关状态
*/
private ConcurrentHashMap<Integer, Integer> requestFeatureStatusMap = new ConcurrentHashMap<>();
/**
* 用于记录人脸特征提取出错重试次数
*/
private ConcurrentHashMap<Integer, Integer> extractErrorRetryMap = new ConcurrentHashMap<>();
/**
* 用于存储活体值
*/
private ConcurrentHashMap<Integer, Integer> livenessMap = new ConcurrentHashMap<>();
/**
* 用于存储活体检测出错重试次数
*/
private ConcurrentHashMap<Integer, Integer> livenessErrorRetryMap = new ConcurrentHashMap<>();
private CompositeDisposable getFeatureDelayedDisposables = new CompositeDisposable();
private CompositeDisposable delayFaceTaskCompositeDisposable = new CompositeDisposable();
/**
* 相机预览显示的控件,可为SurfaceView或TextureView
*/
private View previewViewRgb;
private View previewViewIr;
/**
* 绘制人脸框的控件
*/
private FaceRectView faceRectView;
private FaceRectView faceRectViewIr;
private Switch switchLivenessDetect;
private static final int ACTION_REQUEST_PERMISSIONS = 0x001;
/**
* 识别阈值
*/
private static final float SIMILAR_THRESHOLD = 0.8F;
/**
* 所需的所有权限信息
*/
private static final String[] NEEDED_PERMISSIONS = new String[]{
Manifest.permission.CAMERA,
Manifest.permission.READ_PHONE_STATE,
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
};
private volatile byte[] rgbData;
private volatile byte[] irData;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_register_and_recognize_ir);
//保持亮屏
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
WindowManager.LayoutParams attributes = getWindow().getAttributes();
attributes.systemUiVisibility = View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
getWindow().setAttributes(attributes);
}
// Activity启动后就锁定为启动时的方向
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED);
//本地人脸库初始化
FaceServer.getInstance().init(this);
initView();
}
private void initView() {
previewViewRgb = findViewById(R.id.dual_camera_texture_preview_rgb);
//在布局结束后才做初始化操作
previewViewRgb.getViewTreeObserver().addOnGlobalLayoutListener(this);
previewViewIr = findViewById(R.id.dual_camera_texture_previewIr);
faceRectView = findViewById(R.id.dual_camera_face_rect_view);
faceRectViewIr = findViewById(R.id.dual_camera_face_rect_viewIr);
switchLivenessDetect = findViewById(R.id.dual_camera_switch_liveness_detect);
switchLivenessDetect.setChecked(livenessDetect);
switchLivenessDetect.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
livenessDetect = isChecked;
}
});
RecyclerView recyclerShowFaceInfo = findViewById(R.id.dual_camera_recycler_view_person);
compareResultList = new ArrayList<>();
adapter = new FaceSearchResultAdapter(compareResultList, this);
recyclerShowFaceInfo.setAdapter(adapter);
DisplayMetrics dm = getResources().getDisplayMetrics();
int spanCount = (int) (dm.widthPixels / (getResources().getDisplayMetrics().density * 100 + 0.5f));
recyclerShowFaceInfo.setLayoutManager(new GridLayoutManager(this, spanCount));
recyclerShowFaceInfo.setItemAnimator(new DefaultItemAnimator());
}
/**
* 初始化引擎
*/
private void initEngine() {
ftEngine = new FaceEngine();
ftInitCode = ftEngine.init(this, DetectMode.ASF_DETECT_MODE_VIDEO, ConfigUtil.getFtOrient(this),
16, MAX_DETECT_NUM, FaceEngine.ASF_FACE_DETECT);
frEngine = new FaceEngine();
frInitCode = frEngine.init(this, DetectMode.ASF_DETECT_MODE_IMAGE, DetectFaceOrientPriority.ASF_OP_0_ONLY,
16, MAX_DETECT_NUM, FaceEngine.ASF_FACE_RECOGNITION);
flEngine = new FaceEngine();
flInitCode = flEngine.init(this, DetectMode.ASF_DETECT_MODE_IMAGE, DetectFaceOrientPriority.ASF_OP_0_ONLY,
16, MAX_DETECT_NUM, FaceEngine.ASF_IR_LIVENESS);
Log.i(TAG, "initEngine: init: " + ftInitCode);
if (ftInitCode != ErrorInfo.MOK) {
String error = getString(R.string.specific_engine_init_failed, "ftEngine", ftInitCode);
Log.i(TAG, "initEngine: " + error);
showToast(error);
}
if (frInitCode != ErrorInfo.MOK) {
String error = getString(R.string.specific_engine_init_failed, "frEngine", ftInitCode);
Log.i(TAG, "initEngine: " + error);
showToast(error);
}
if (flInitCode != ErrorInfo.MOK) {
String error = getString(R.string.specific_engine_init_failed, "flEngine", ftInitCode);
Log.i(TAG, "initEngine: " + error);
showToast(error);
}
}
/**
* 销毁引擎,faceHelperIr中可能会有特征提取耗时操作仍在执行,加锁防止crash
*/
private void unInitEngine() {
if (ftInitCode == ErrorInfo.MOK && ftEngine != null) {
synchronized (ftEngine) {
int ftUnInitCode = ftEngine.unInit();
Log.i(TAG, "unInitEngine: " + ftUnInitCode);
}
}
if (frInitCode == ErrorInfo.MOK && frEngine != null) {
synchronized (frEngine) {
int frUnInitCode = frEngine.unInit();
Log.i(TAG, "unInitEngine: " + frUnInitCode);
}
}
if (flInitCode == ErrorInfo.MOK && flEngine != null) {
synchronized (flEngine) {
int flUnInitCode = flEngine.unInit();
Log.i(TAG, "unInitEngine: " + flUnInitCode);
}
}
}
@Override
protected void onResume() {
super.onResume();
try {
if (cameraHelper != null) {
cameraHelper.start();
}
if (cameraHelperIr != null) {
cameraHelperIr.start();
}
} catch (RuntimeException e) {
showToast(e.getMessage() + getString(R.string.camera_error_notice));
}
}
@Override
protected void onPause() {
if (cameraHelper != null) {
cameraHelper.stop();
}
if (cameraHelperIr != null) {
cameraHelperIr.stop();
}
super.onPause();
}
@Override
protected void onDestroy() {
if (cameraHelper != null) {
cameraHelper.release();
cameraHelper = null;
}
if (cameraHelperIr != null) {
cameraHelperIr.release();
cameraHelperIr = null;
}
unInitEngine();
if (getFeatureDelayedDisposables != null) {
getFeatureDelayedDisposables.clear();
}
if (delayFaceTaskCompositeDisposable != null) {
delayFaceTaskCompositeDisposable.clear();
}
if (faceHelperIr != null) {
ConfigUtil.setTrackedFaceCount(this, faceHelperIr.getTrackedFaceCount());
faceHelperIr.release();
faceHelperIr = null;
}
FaceServer.getInstance().unInit();
super.onDestroy();
}
private void initRgbCamera() {
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
final FaceListener faceListener = new FaceListener() {
@Override
public void onFail(Exception e) {
Log.e(TAG, "onFail: " + e.getMessage());
}
//请求FR的回调
@Override
public void onFaceFeatureInfoGet(@Nullable final FaceFeature faceFeature, final Integer requestId, final Integer errorCode) {
//FR成功
if (faceFeature != null) {
// Log.i(TAG, "onPreview: fr end = " + System.currentTimeMillis() + " trackId = " + requestId);
Integer liveness = livenessMap.get(requestId);
//不做活体检测的情况,直接搜索
if (!livenessDetect) {
searchFace(faceFeature, requestId);
}
//活体检测通过,搜索特征
else if (liveness != null && liveness == LivenessInfo.ALIVE) {
searchFace(faceFeature, requestId);
}
//活体检测未出结果,或者非活体,延迟执行该函数
else {
if (requestFeatureStatusMap.containsKey(requestId)) {
Observable.timer(WAIT_LIVENESS_INTERVAL, TimeUnit.MILLISECONDS)
.subscribe(new Observer<Long>() {
Disposable disposable;
@Override
public void onSubscribe(Disposable d) {
disposable = d;
getFeatureDelayedDisposables.add(disposable);
}
@Override
public void onNext(Long aLong) {
onFaceFeatureInfoGet(faceFeature, requestId, errorCode);
}
@Override
public void onError(Throwable e) {
}
@Override
public void onComplete() {
getFeatureDelayedDisposables.remove(disposable);
}
});
}
}
}
//特征提取失败
else {
if (increaseAndGetValue(extractErrorRetryMap, requestId) > MAX_RETRY_TIME) {
extractErrorRetryMap.put(requestId, 0);
String msg;
// 传入的FaceInfo在指定的图像上无法解析人脸,此处使用的是RGB人脸数据,一般是人脸模糊
if (errorCode != null && errorCode == ErrorInfo.MERR_FSDK_FACEFEATURE_LOW_CONFIDENCE_LEVEL) {
msg = getString(R.string.low_confidence_level);
} else {
msg = "ExtractCode:" + errorCode;
}
faceHelperIr.setName(requestId, getString(R.string.recognize_failed_notice, msg));
// 在尝试最大次数后,特征提取仍然失败,则认为识别未通过
requestFeatureStatusMap.put(requestId, RequestFeatureStatus.FAILED);
retryRecognizeDelayed(requestId);
} else {
requestFeatureStatusMap.put(requestId, RequestFeatureStatus.TO_RETRY);
}
}
}
@Override
public void onFaceLivenessInfoGet(@Nullable LivenessInfo livenessInfo, final Integer requestId, Integer errorCode) {
if (livenessInfo != null) {
int liveness = livenessInfo.getLiveness();
livenessMap.put(requestId, liveness);
// 非活体,重试
if (liveness == LivenessInfo.NOT_ALIVE) {
faceHelperIr.setName(requestId, getString(R.string.recognize_failed_notice, "NOT_ALIVE"));
// 延迟 FAIL_RETRY_INTERVAL 后,将该人脸状态置为UNKNOWN,帧回调处理时会重新进行活体检测
retryLivenessDetectDelayed(requestId);
}
} else {
if (increaseAndGetValue(livenessErrorRetryMap, requestId) > MAX_RETRY_TIME) {
livenessErrorRetryMap.put(requestId, 0);
String msg;
// 传入的FaceInfo在指定的图像上无法解析人脸,此处使用RGB人脸框 + IR数据,一般是人脸模糊或画面中无人脸
if (errorCode != null && errorCode == ErrorInfo.MERR_FSDK_FACEFEATURE_LOW_CONFIDENCE_LEVEL) {
msg = getString(R.string.low_confidence_level);
} else {
msg = "ProcessCode:" + errorCode;
}
faceHelperIr.setName(requestId, getString(R.string.recognize_failed_notice, msg));
// 在尝试最大次数后,活体检测仍然失败,则认定为非活体
livenessMap.put(requestId, LivenessInfo.NOT_ALIVE);
retryLivenessDetectDelayed(requestId);
} else {
livenessMap.put(requestId, LivenessInfo.UNKNOWN);
}
}
}
};
CameraListener rgbCameraListener = new CameraListener() {
@Override
public void onCameraOpened(Camera camera, int cameraId, int displayOrientation, boolean isMirror) {
previewSize = camera.getParameters().getPreviewSize();
ViewGroup.LayoutParams layoutParams = adjustPreviewViewSize(previewViewRgb, faceRectView, previewSize, displayOrientation);
drawHelperRgb = new DrawHelper(previewSize.width, previewSize.height, layoutParams.width, layoutParams.height, displayOrientation,
cameraId, isMirror, false, false);
if (faceHelperIr == null) {
faceHelperIr = new FaceHelper.Builder()
.ftEngine(ftEngine)
.frEngine(frEngine)
.flEngine(flEngine)
.frQueueSize(MAX_DETECT_NUM)
.flQueueSize(MAX_DETECT_NUM)
.previewSize(previewSize)
.faceListener(faceListener)
.trackedFaceCount(ConfigUtil.getTrackedFaceCount(IrRegisterAndRecognizeActivity.this.getApplicationContext()))
.build();
}
TextView textViewRgb = new TextView(IrRegisterAndRecognizeActivity.this, null);
textViewRgb.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
textViewRgb.setText(getString(R.string.camera_rgb) + "\n" + previewSize.width + "x" + previewSize.height);
textViewRgb.setTextColor(Color.WHITE);
textViewRgb.setBackgroundColor(getResources().getColor(R.color.color_bg_notification));
((FrameLayout) previewViewRgb.getParent()).addView(textViewRgb);
}
@Override
public void onPreview(final byte[] nv21, Camera camera) {
rgbData = nv21;
processPreviewData();
}
@Override
public void onCameraClosed() {
Log.i(TAG, "onCameraClosed: ");
}
@Override
public void onCameraError(Exception e) {
Log.i(TAG, "onCameraError: " + e.getMessage());
}
@Override
public void onCameraConfigurationChanged(int cameraID, int displayOrientation) {
if (drawHelperRgb != null) {
drawHelperRgb.setCameraDisplayOrientation(displayOrientation);
}
Log.i(TAG, "onCameraConfigurationChanged: " + cameraID + " " + displayOrientation);
}
};
cameraHelper = new DualCameraHelper.Builder()
.previewViewSize(new Point(previewViewRgb.getMeasuredWidth(), previewViewRgb.getMeasuredHeight()))
.rotation(getWindowManager().getDefaultDisplay().getRotation())
.specificCameraId(cameraRgbId != null ? cameraRgbId : Camera.CameraInfo.CAMERA_FACING_BACK)
.previewOn(previewViewRgb)
.cameraListener(rgbCameraListener)
.isMirror(cameraRgbId != null && Camera.CameraInfo.CAMERA_FACING_FRONT == cameraRgbId)
.build();
cameraHelper.init();
try {
cameraHelper.start();
} catch (RuntimeException e) {
showToast(e.getMessage() + getString(R.string.camera_error_notice));
}
}
private void initIrCamera() {
CameraListener irCameraListener = new CameraListener() {
@Override
public void onCameraOpened(Camera camera, int cameraId, int displayOrientation, boolean isMirror) {
previewSizeIr = camera.getParameters().getPreviewSize();
ViewGroup.LayoutParams layoutParams = adjustPreviewViewSize(previewViewIr, faceRectViewIr, previewSizeIr, displayOrientation);
drawHelperIr = new DrawHelper(previewSizeIr.width, previewSizeIr.height, layoutParams.width, layoutParams.height, displayOrientation,
cameraId, isMirror, false, false);
TextView textViewIr = new TextView(IrRegisterAndRecognizeActivity.this, null);
textViewIr.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
textViewIr.setText(getString(R.string.camera_ir) + "\n" + previewSizeIr.width + "x" + previewSizeIr.height);
textViewIr.setTextColor(Color.WHITE);
textViewIr.setBackgroundColor(getResources().getColor(R.color.color_bg_notification));
((FrameLayout) previewViewIr.getParent()).addView(textViewIr);
}
@Override
public void onPreview(final byte[] nv21, Camera camera) {
irData = nv21;
}
@Override
public void onCameraClosed() {
Log.i(TAG, "onCameraClosed: ");
}
@Override
public void onCameraError(Exception e) {
Log.i(TAG, "onCameraError: " + e.getMessage());
}
@Override
public void onCameraConfigurationChanged(int cameraID, int displayOrientation) {
if (drawHelperIr != null) {
drawHelperIr.setCameraDisplayOrientation(displayOrientation);
}
Log.i(TAG, "onCameraConfigurationChanged: " + cameraID + " " + displayOrientation);
}
};
cameraHelperIr = new DualCameraHelper.Builder()
.previewViewSize(new Point(previewViewIr.getMeasuredWidth(), previewViewIr.getMeasuredHeight()))
.rotation(getWindowManager().getDefaultDisplay().getRotation())
.specificCameraId(cameraIrId != null ? cameraIrId : Camera.CameraInfo.CAMERA_FACING_FRONT)
.previewOn(previewViewIr)
.cameraListener(irCameraListener)
.isMirror(cameraIrId != null && Camera.CameraInfo.CAMERA_FACING_FRONT == cameraIrId)
// .previewSize(new Point(1280, 960)) //相机预览大小设置,RGB与IR需使用相同大小
// .additionalRotation(270) //额外旋转角度
.build();
cameraHelperIr.init();
try {
cameraHelperIr.start();
} catch (RuntimeException e) {
showToast(e.getMessage() + getString(R.string.camera_error_notice));
}
}
/**
* 调整View的宽高,使2个预览同时显示
*
* @param previewView 显示预览数据的view
* @param faceRectView 画框的view
* @param previewSize 预览大小
* @param displayOrientation 相机旋转角度
* @return 调整后的LayoutParams
*/
private ViewGroup.LayoutParams adjustPreviewViewSize(View previewView, FaceRectView faceRectView, Camera.Size previewSize, int displayOrientation) {
ViewGroup.LayoutParams layoutParams = previewView.getLayoutParams();
int measuredWidth = previewView.getMeasuredWidth();
int measuredHeight = previewView.getMeasuredHeight();
float ratio = ((float) previewSize.height) / (float) previewSize.width;
if (ratio > 1) {
ratio = 1 / ratio;
}
if (displayOrientation % 180 == 0) {
layoutParams.width = measuredWidth;
layoutParams.height = (int) (measuredWidth * ratio);
} else {
layoutParams.height = measuredHeight;
layoutParams.width = (int) (measuredHeight * ratio);
}
Log.i(TAG, "adjustPreviewViewSize: " + layoutParams.width + "x" + layoutParams.height);
previewView.setLayoutParams(layoutParams);
faceRectView.setLayoutParams(layoutParams);
return layoutParams;
}
/**
* 处理预览数据
*/
private synchronized void processPreviewData() {
if (rgbData != null && irData != null) {
final byte[] cloneNv21Rgb = rgbData.clone();
if (faceRectView != null) {
faceRectView.clearFaceInfo();
}
if (faceRectViewIr != null) {
faceRectViewIr.clearFaceInfo();
}
List<FacePreviewInfo> facePreviewInfoList = faceHelperIr.onPreviewFrame(cloneNv21Rgb);
if (facePreviewInfoList != null && faceRectView != null && drawHelperRgb != null
&& faceRectViewIr != null && drawHelperIr != null) {
drawPreviewInfo(facePreviewInfoList);
}
registerFace(cloneNv21Rgb, facePreviewInfoList);
clearLeftFace(facePreviewInfoList);
if (facePreviewInfoList != null && facePreviewInfoList.size() > 0 && previewSize != null) {
for (int i = 0; i < facePreviewInfoList.size(); i++) {
// 注意:这里虽然使用的是IR画面活体检测,RGB画面特征提取,但是考虑到成像接近,所以只用了RGB画面的图像质量检测
Integer status = requestFeatureStatusMap.get(facePreviewInfoList.get(i).getTrackId());
/**
* 在活体检测开启,在人脸活体状态不为处理中(ANALYZING)且不为处理完成(ALIVE、NOT_ALIVE)时重新进行活体检测
*/
if (livenessDetect && (status == null || status != RequestFeatureStatus.SUCCEED)) {
Integer liveness = livenessMap.get(facePreviewInfoList.get(i).getTrackId());
if (liveness == null
|| (liveness != LivenessInfo.ALIVE && liveness != LivenessInfo.NOT_ALIVE && liveness != RequestLivenessStatus.ANALYZING)) {
livenessMap.put(facePreviewInfoList.get(i).getTrackId(), RequestLivenessStatus.ANALYZING);
// IR数据偏移
FaceInfo faceInfo = facePreviewInfoList.get(i).getFaceInfo().clone();
faceInfo.getRect().offset(Constants.HORIZONTAL_OFFSET, Constants.VERTICAL_OFFSET);
faceHelperIr.requestFaceLiveness(irData.clone(), faceInfo, previewSize.width, previewSize.height, FaceEngine.CP_PAF_NV21, facePreviewInfoList.get(i).getTrackId(), LivenessType.IR);
}
}
/**
* 对于每个人脸,若状态为空或者为失败,则请求特征提取(可根据需要添加其他判断以限制特征提取次数),
* 特征提取回传的人脸特征结果在{@link FaceListener#onFaceFeatureInfoGet(FaceFeature, Integer, Integer)}中回传
*/
if (status == null
|| status == RequestFeatureStatus.TO_RETRY) {
requestFeatureStatusMap.put(facePreviewInfoList.get(i).getTrackId(), RequestFeatureStatus.SEARCHING);
faceHelperIr.requestFaceFeature(cloneNv21Rgb, facePreviewInfoList.get(i).getFaceInfo(),
previewSize.width, previewSize.height, FaceEngine.CP_PAF_NV21,
facePreviewInfoList.get(i).getTrackId());
}
}
}
rgbData = null;
irData = null;
}
}
/**
* 绘制预览相关数据
*
* @param facePreviewInfoList {@link FaceHelper#onPreviewFrame(byte[])}回传的处理结果
*/
private void drawPreviewInfo(List<FacePreviewInfo> facePreviewInfoList) {
List<DrawInfo> drawInfoList = new ArrayList<>();
List<DrawInfo> drawInfoListIr = new ArrayList<>();
for (int i = 0; i < facePreviewInfoList.size(); i++) {
int trackId = facePreviewInfoList.get(i).getTrackId();
String name = faceHelperIr.getName(trackId);
Integer liveness = livenessMap.get(trackId);
Rect ftRect = facePreviewInfoList.get(i).getFaceInfo().getRect();
Integer recognizeStatus = requestFeatureStatusMap.get(facePreviewInfoList.get(i).getTrackId());
// 根据识别结果和活体结果设置颜色
int color = RecognizeColor.COLOR_UNKNOWN;
if (recognizeStatus != null) {
if (recognizeStatus == RequestFeatureStatus.FAILED) {
color = RecognizeColor.COLOR_FAILED;
}
if (recognizeStatus == RequestFeatureStatus.SUCCEED) {
color = RecognizeColor.COLOR_SUCCESS;
}
}
if (liveness != null && liveness == LivenessInfo.NOT_ALIVE) {
color = RecognizeColor.COLOR_FAILED;
}
drawInfoList.add(new DrawInfo(drawHelperRgb.adjustRect(ftRect),
GenderInfo.UNKNOWN, AgeInfo.UNKNOWN_AGE,
liveness != null ? liveness : LivenessInfo.UNKNOWN, color,
name == null ? String.valueOf(trackId) : name));
Rect offsetFtRect = new Rect(ftRect);
offsetFtRect.offset(Constants.HORIZONTAL_OFFSET, Constants.VERTICAL_OFFSET);
drawInfoListIr.add(new DrawInfo(drawHelperIr.adjustRect(offsetFtRect),
GenderInfo.UNKNOWN, AgeInfo.UNKNOWN_AGE,
liveness != null ? liveness : LivenessInfo.UNKNOWN, color,
name == null ? String.valueOf(trackId) : name));
}
drawHelperRgb.draw(faceRectView, drawInfoList);
drawHelperIr.draw(faceRectViewIr, drawInfoListIr);
}
/**
* 注册人脸
*
* @param nv21Rgb RGB摄像头的帧数据
* @param facePreviewInfoList {@link FaceHelper#onPreviewFrame(byte[])}回传的处理结果
*/
private void registerFace(final byte[] nv21Rgb, final List<FacePreviewInfo> facePreviewInfoList) {
if (registerStatus == REGISTER_STATUS_READY && facePreviewInfoList != null && facePreviewInfoList.size() > 0) {
registerStatus = REGISTER_STATUS_PROCESSING;
Observable.create(new ObservableOnSubscribe<Boolean>() {
@Override
public void subscribe(ObservableEmitter<Boolean> emitter) {
boolean success = FaceServer.getInstance().registerNv21(
IrRegisterAndRecognizeActivity.this, nv21Rgb,
previewSize.width, previewSize.height, facePreviewInfoList.get(0).getFaceInfo(), "registered " + faceHelperIr.getTrackedFaceCount());
emitter.onNext(success);
}
})
.subscribeOn(Schedulers.computation())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Boolean>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(Boolean success) {
String result = success ? "register success!" : "register failed!";
showToast(result);
registerStatus = REGISTER_STATUS_DONE;
}
@Override
public void onError(Throwable e) {
e.printStackTrace();
showToast("register failed!");
registerStatus = REGISTER_STATUS_DONE;
}
@Override
public void onComplete() {
}
});
}
}
@Override
void afterRequestPermission(int requestCode, boolean isAllGranted) {
if (requestCode == ACTION_REQUEST_PERMISSIONS) {
if (isAllGranted) {
initEngine();
initRgbCamera();
initIrCamera();
} else {
showToast(getString(R.string.permission_denied));
}
}
}
/**
* 删除已经离开的人脸
*
* @param facePreviewInfoList 人脸和trackId列表
*/
private void clearLeftFace(List<FacePreviewInfo> facePreviewInfoList) {
if (compareResultList != null) {
for (int i = compareResultList.size() - 1; i >= 0; i--) {
if (!requestFeatureStatusMap.containsKey(compareResultList.get(i).getTrackId())) {
compareResultList.remove(i);
adapter.notifyItemRemoved(i);
}
}
}
if (facePreviewInfoList == null || facePreviewInfoList.size() == 0) {
requestFeatureStatusMap.clear();
livenessMap.clear();
livenessErrorRetryMap.clear();
extractErrorRetryMap.clear();
if (getFeatureDelayedDisposables != null) {
getFeatureDelayedDisposables.clear();
}
return;
}
Enumeration<Integer> keys = requestFeatureStatusMap.keys();
while (keys.hasMoreElements()) {
int key = keys.nextElement();
boolean contained = false;
for (FacePreviewInfo facePreviewInfo : facePreviewInfoList) {
if (facePreviewInfo.getTrackId() == key) {
contained = true;
break;
}
}
if (!contained) {
requestFeatureStatusMap.remove(key);
livenessMap.remove(key);
livenessErrorRetryMap.remove(key);
extractErrorRetryMap.remove(key);
}
}
}
private void searchFace(final FaceFeature frFace, final Integer requestId) {
Observable
.create(new ObservableOnSubscribe<CompareResult>() {
@Override
public void subscribe(ObservableEmitter<CompareResult> emitter) {
// Log.i(TAG, "subscribe: fr search start = " + System.currentTimeMillis() + " trackId = " + requestId);
CompareResult compareResult = FaceServer.getInstance().getTopOfFaceLib(frFace);
// Log.i(TAG, "subscribe: fr search end = " + System.currentTimeMillis() + " trackId = " + requestId);
emitter.onNext(compareResult);
}
})
.subscribeOn(Schedulers.computation())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<CompareResult>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(CompareResult compareResult) {
if (compareResult == null || compareResult.getUserName() == null) {
requestFeatureStatusMap.put(requestId, RequestFeatureStatus.FAILED);
faceHelperIr.setName(requestId, "VISITOR " + requestId);
return;
}
// Log.i(TAG, "onNext: fr search get result = " + System.currentTimeMillis() + " trackId = " + requestId + " similar = " + compareResult.getSimilar());
if (compareResult.getSimilar() > SIMILAR_THRESHOLD) {
boolean isAdded = false;
if (compareResultList == null) {
requestFeatureStatusMap.put(requestId, RequestFeatureStatus.FAILED);
faceHelperIr.setName(requestId, "VISITOR " + requestId);
return;
}
for (CompareResult compareResult1 : compareResultList) {
if (compareResult1.getTrackId() == requestId) {
isAdded = true;
break;
}
}
if (!isAdded) {
//对于多人脸搜索,假如最大显示数量为 MAX_DETECT_NUM 且有新的人脸进入,则以队列的形式移除
if (compareResultList.size() >= MAX_DETECT_NUM) {
compareResultList.remove(0);
adapter.notifyItemRemoved(0);
}
//添加显示人员时,保存其trackId
compareResult.setTrackId(requestId);
compareResultList.add(compareResult);
adapter.notifyItemInserted(compareResultList.size() - 1);
}
requestFeatureStatusMap.put(requestId, RequestFeatureStatus.SUCCEED);
faceHelperIr.setName(requestId, getString(R.string.recognize_success_notice, compareResult.getUserName()));
} else {
faceHelperIr.setName(requestId, getString(R.string.recognize_failed_notice, "NOT_REGISTERED"));
retryRecognizeDelayed(requestId);
}
}
@Override
public void onError(Throwable e) {
faceHelperIr.setName(requestId, getString(R.string.recognize_failed_notice, "NOT_REGISTERED"));
retryRecognizeDelayed(requestId);
}
@Override
public void onComplete() {
}
});
}
/**
* 将准备注册的状态置为{@link #REGISTER_STATUS_READY}
*
* @param view 注册按钮
*/
public void register(View view) {
if (registerStatus == REGISTER_STATUS_DONE) {
registerStatus = REGISTER_STATUS_READY;
}
}
/**
* 在{@link #previewViewRgb}第一次布局完成后,去除该监听,并且进行引擎和相机的初始化
*/
@Override
public void onGlobalLayout() {
previewViewRgb.getViewTreeObserver().removeOnGlobalLayoutListener(this);
if (!checkPermissions(NEEDED_PERMISSIONS)) {
ActivityCompat.requestPermissions(this, NEEDED_PERMISSIONS, ACTION_REQUEST_PERMISSIONS);
} else {
initEngine();
initRgbCamera();
initIrCamera();
}
}
public void drawIrRectVerticalMirror(View view) {
if (drawHelperIr != null) {
drawHelperIr.setMirrorVertical(!drawHelperIr.isMirrorVertical());
}
}
public void drawIrRectHorizontalMirror(View view) {
if (drawHelperIr != null) {
drawHelperIr.setMirrorHorizontal(!drawHelperIr.isMirrorHorizontal());
}
}
/**
* 将map中key对应的value增1回传
*
* @param countMap map
* @param key key
* @return 增1后的value
*/
public int increaseAndGetValue(Map<Integer, Integer> countMap, int key) {
if (countMap == null) {
return 0;
}
Integer value = countMap.get(key);
if (value == null) {
value = 0;
}
countMap.put(key, ++value);
return value;
}
/**
* 延迟 FAIL_RETRY_INTERVAL 重新进行活体检测
*
* @param requestId 人脸ID
*/
private void retryLivenessDetectDelayed(final Integer requestId) {
Observable.timer(FAIL_RETRY_INTERVAL, TimeUnit.MILLISECONDS)
.subscribe(new Observer<Long>() {
Disposable disposable;
@Override
public void onSubscribe(Disposable d) {
disposable = d;
delayFaceTaskCompositeDisposable.add(disposable);
}
@Override
public void onNext(Long aLong) {
}
@Override
public void onError(Throwable e) {
e.printStackTrace();
}
@Override
public void onComplete() {
// 将该人脸状态置为UNKNOWN,帧回调处理时会重新进行活体检测
if (livenessDetect) {
faceHelperIr.setName(requestId, Integer.toString(requestId));
}
livenessMap.put(requestId, LivenessInfo.UNKNOWN);
delayFaceTaskCompositeDisposable.remove(disposable);
}
});
}
/**
* 延迟 FAIL_RETRY_INTERVAL 重新进行人脸识别
*
* @param requestId 人脸ID
*/
private void retryRecognizeDelayed(final Integer requestId) {
requestFeatureStatusMap.put(requestId, RequestFeatureStatus.FAILED);
Observable.timer(FAIL_RETRY_INTERVAL, TimeUnit.MILLISECONDS)
.subscribe(new Observer<Long>() {
Disposable disposable;
@Override
public void onSubscribe(Disposable d) {
disposable = d;
delayFaceTaskCompositeDisposable.add(disposable);
}
@Override
public void onNext(Long aLong) {
}
@Override
public void onError(Throwable e) {
e.printStackTrace();
}
@Override
public void onComplete() {
// 将该人脸特征提取状态置为FAILED,帧回调处理时会重新进行活体检测
faceHelperIr.setName(requestId, Integer.toString(requestId));
requestFeatureStatusMap.put(requestId, RequestFeatureStatus.TO_RETRY);
delayFaceTaskCompositeDisposable.remove(disposable);
}
});
}
}
package com.arcsoft.arcfacedemo.activity;
import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.os.Build;
import android.os.Bundle;
import android.provider.MediaStore;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.DividerItemDecoration;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.text.style.StyleSpan;
import android.util.Log;
import android.view.View;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.TextView;
import com.arcsoft.arcfacedemo.R;
import com.arcsoft.arcfacedemo.model.ItemShowInfo;
import com.arcsoft.arcfacedemo.widget.MultiFaceInfoAdapter;
import com.arcsoft.face.AgeInfo;
import com.arcsoft.face.ErrorInfo;
import com.arcsoft.face.Face3DAngle;
import com.arcsoft.face.FaceEngine;
import com.arcsoft.face.FaceFeature;
import com.arcsoft.face.FaceInfo;
import com.arcsoft.face.FaceSimilar;
import com.arcsoft.face.GenderInfo;
import com.arcsoft.face.enums.DetectFaceOrientPriority;
import com.arcsoft.face.enums.DetectMode;
import com.arcsoft.face.util.ImageUtils;
import com.arcsoft.imageutil.ArcSoftImageFormat;
import com.arcsoft.imageutil.ArcSoftImageUtil;
import com.arcsoft.imageutil.ArcSoftImageUtilError;
import com.bumptech.glide.Glide;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class MultiImageActivity extends BaseActivity {
private static final String TAG = "MultiImageActivity";
private static final int ACTION_CHOOSE_MAIN_IMAGE = 0x201;
private static final int ACTION_ADD_RECYCLER_ITEM_IMAGE = 0x202;
private static final int ACTION_REQUEST_PERMISSIONS = 0x001;
private ImageView ivMainImage;
private TextView tvMainImageInfo;
/**
* 选择图片时的类型
*/
private static final int TYPE_MAIN = 0;
private static final int TYPE_ITEM = 1;
/**
* 主图的第0张人脸的特征数据
*/
private FaceFeature mainFeature;
private MultiFaceInfoAdapter multiFaceInfoAdapter;
private List<ItemShowInfo> showInfoList;
private FaceEngine faceEngine;
private int faceEngineCode = -1;
private Bitmap mainBitmap;
private static String[] NEEDED_PERMISSIONS = new String[]{
Manifest.permission.READ_PHONE_STATE
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
setContentView(R.layout.activity_multi_image);
/**
* 在选择图片的时候,在android 7.0及以上通过FileProvider获取Uri,不需要文件权限
*/
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
List<String> permissionList = new ArrayList<>(Arrays.asList(NEEDED_PERMISSIONS));
permissionList.add(Manifest.permission.READ_EXTERNAL_STORAGE);
NEEDED_PERMISSIONS = permissionList.toArray(new String[0]);
}
if (!checkPermissions(NEEDED_PERMISSIONS)) {
ActivityCompat.requestPermissions(this, NEEDED_PERMISSIONS, ACTION_REQUEST_PERMISSIONS);
} else {
initEngine();
}
initView();
}
private void initView() {
ivMainImage = findViewById(R.id.iv_main_image);
tvMainImageInfo = findViewById(R.id.tv_main_image_info);
RecyclerView recyclerFaces = findViewById(R.id.recycler_faces);
showInfoList = new ArrayList<>();
multiFaceInfoAdapter = new MultiFaceInfoAdapter(showInfoList, this);
recyclerFaces.setAdapter(multiFaceInfoAdapter);
recyclerFaces.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));
recyclerFaces.setLayoutManager(new LinearLayoutManager(this));
}
private void initEngine() {
faceEngine = new FaceEngine();
faceEngineCode = faceEngine.init(this, DetectMode.ASF_DETECT_MODE_IMAGE, DetectFaceOrientPriority.ASF_OP_0_ONLY,
16, 6, FaceEngine.ASF_FACE_RECOGNITION | FaceEngine.ASF_AGE | FaceEngine.ASF_FACE_DETECT | FaceEngine.ASF_GENDER | FaceEngine.ASF_FACE3DANGLE);
Log.i(TAG, "initEngine: init " + faceEngineCode);
if (faceEngineCode != ErrorInfo.MOK) {
showToast(getString(R.string.init_failed, faceEngineCode));
}
}
private void unInitEngine() {
if (faceEngine != null) {
faceEngineCode = faceEngine.unInit();
Log.i(TAG, "unInitEngine: " + faceEngineCode);
}
}
@Override
protected void onDestroy() {
unInitEngine();
super.onDestroy();
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (data == null || data.getData() == null) {
showToast(getString(R.string.get_picture_failed));
return;
}
if (requestCode == ACTION_CHOOSE_MAIN_IMAGE) {
try {
mainBitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), data.getData());
} catch (IOException e) {
e.printStackTrace();
showToast(getString(R.string.get_picture_failed));
return;
}
if (mainBitmap == null) {
showToast(getString(R.string.get_picture_failed));
return;
}
processImage(mainBitmap, TYPE_MAIN);
} else if (requestCode == ACTION_ADD_RECYCLER_ITEM_IMAGE) {
Bitmap bitmap = null;
try {
bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), data.getData());
} catch (IOException e) {
e.printStackTrace();
showToast(getString(R.string.get_picture_failed));
return;
}
if (bitmap == null) {
showToast(getString(R.string.get_picture_failed));
return;
}
if (mainFeature == null) {
return;
}
processImage(bitmap, TYPE_ITEM);
}
}
public void processImage(Bitmap bitmap, int type) {
if (bitmap == null) {
return;
}
if (faceEngine == null) {
return;
}
// 接口需要的bgr24宽度必须为4的倍数
bitmap = ArcSoftImageUtil.getAlignedBitmap(bitmap, true);
if (bitmap == null) {
return;
}
int width = bitmap.getWidth();
int height = bitmap.getHeight();
// bitmap转bgr24
long start = System.currentTimeMillis();
byte[] bgr24 = ArcSoftImageUtil.createImageData(bitmap.getWidth(), bitmap.getHeight(), ArcSoftImageFormat.BGR24);
int transformCode = ArcSoftImageUtil.bitmapToImageData(bitmap, bgr24, ArcSoftImageFormat.BGR24);
if (transformCode != ArcSoftImageUtilError.CODE_SUCCESS) {
showToast("failed to transform bitmap to imageData, code is " + transformCode);
return;
}
// Log.i(TAG, "processImage:bitmapToBgr24 cost = " + (System.currentTimeMillis() - start));
if (bgr24 != null) {
List<FaceInfo> faceInfoList = new ArrayList<>();
//人脸检测
int detectCode = faceEngine.detectFaces(bgr24, width, height, FaceEngine.CP_PAF_BGR24, faceInfoList);
if (detectCode != 0 || faceInfoList.size() == 0) {
showToast("face detection finished, code is " + detectCode + ", face num is " + faceInfoList.size());
return;
}
//绘制bitmap
bitmap = bitmap.copy(Bitmap.Config.RGB_565, true);
Canvas canvas = new Canvas(bitmap);
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setStrokeWidth(10);
paint.setColor(Color.YELLOW);
if (faceInfoList.size() > 0) {
for (int i = 0; i < faceInfoList.size(); i++) {
//绘制人脸框
paint.setStyle(Paint.Style.STROKE);
canvas.drawRect(faceInfoList.get(i).getRect(), paint);
//绘制人脸序号
paint.setStyle(Paint.Style.FILL_AND_STROKE);
paint.setTextSize(faceInfoList.get(i).getRect().width() / 2);
canvas.drawText("" + i, faceInfoList.get(i).getRect().left, faceInfoList.get(i).getRect().top, paint);
}
}
int faceProcessCode = faceEngine.process(bgr24, width, height, FaceEngine.CP_PAF_BGR24, faceInfoList, FaceEngine.ASF_AGE | FaceEngine.ASF_GENDER | FaceEngine.ASF_FACE3DANGLE);
Log.i(TAG, "processImage: " + faceProcessCode);
if (faceProcessCode != ErrorInfo.MOK) {
showToast("face process finished, code is " + faceProcessCode);
return;
}
//年龄信息结果
List<AgeInfo> ageInfoList = new ArrayList<>();
//性别信息结果
List<GenderInfo> genderInfoList = new ArrayList<>();
//三维角度结果
List<Face3DAngle> face3DAngleList = new ArrayList<>();
//获取年龄、性别、三维角度
int ageCode = faceEngine.getAge(ageInfoList);
int genderCode = faceEngine.getGender(genderInfoList);
int face3DAngleCode = faceEngine.getFace3DAngle(face3DAngleList);
if ((ageCode | genderCode | face3DAngleCode) != ErrorInfo.MOK) {
showToast("at lease one of age、gender、face3DAngle detect failed! codes are: " + ageCode
+ " ," + genderCode + " ," + face3DAngleCode);
return;
}
//人脸比对数据显示
if (faceInfoList.size() > 0) {
if (type == TYPE_MAIN) {
int size = showInfoList.size();
showInfoList.clear();
multiFaceInfoAdapter.notifyItemRangeRemoved(0, size);
mainFeature = new FaceFeature();
int res = faceEngine.extractFaceFeature(bgr24, width, height, FaceEngine.CP_PAF_BGR24, faceInfoList.get(0), mainFeature);
if (res != ErrorInfo.MOK) {
mainFeature = null;
}
Glide.with(ivMainImage.getContext())
.load(bitmap)
.into(ivMainImage);
StringBuilder stringBuilder = new StringBuilder();
if (faceInfoList.size() > 0) {
stringBuilder.append("face info:\n\n");
}
for (int i = 0; i < faceInfoList.size(); i++) {
stringBuilder.append("face[")
.append(i)
.append("]:\n")
.append(faceInfoList.get(i))
.append("\nage:")
.append(ageInfoList.get(i).getAge())
.append("\ngender:")
.append(genderInfoList.get(i).getGender() == GenderInfo.MALE ? "MALE"
: (genderInfoList.get(i).getGender() == GenderInfo.FEMALE ? "FEMALE" : "UNKNOWN"))
.append("\nface3DAngle:")
.append(face3DAngleList.get(i))
.append("\n\n");
}
tvMainImageInfo.setText(stringBuilder);
} else if (type == TYPE_ITEM) {
FaceFeature faceFeature = new FaceFeature();
int res = faceEngine.extractFaceFeature(bgr24, width, height, FaceEngine.CP_PAF_BGR24, faceInfoList.get(0), faceFeature);
if (res == 0) {
FaceSimilar faceSimilar = new FaceSimilar();
int compareResult = faceEngine.compareFaceFeature(mainFeature, faceFeature, faceSimilar);
if (compareResult == ErrorInfo.MOK) {
ItemShowInfo showInfo = new ItemShowInfo(bitmap, ageInfoList.get(0).getAge(), genderInfoList.get(0).getGender(), faceSimilar.getScore());
showInfoList.add(showInfo);
multiFaceInfoAdapter.notifyItemInserted(showInfoList.size() - 1);
} else {
showToast(getString(R.string.compare_failed, compareResult));
}
}
}
} else {
if (type == TYPE_MAIN) {
mainBitmap = null;
}
}
} else {
showToast("can not get bgr24 from bitmap!");
}
}
/**
* 从本地选择文件
*
* @param action 可为选择主图{@link #ACTION_CHOOSE_MAIN_IMAGE}或者选择item图{@link #ACTION_ADD_RECYCLER_ITEM_IMAGE}
*/
public void chooseLocalImage(int action) {
Intent intent = new Intent(Intent.ACTION_PICK);
intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*");
startActivityForResult(intent, action);
}
public void addItemFace(View view) {
if (faceEngineCode != ErrorInfo.MOK) {
showToast(getString(R.string.engine_not_initialized, faceEngineCode));
return;
}
if (mainBitmap == null) {
showToast(getString(R.string.notice_choose_main_img));
return;
}
chooseLocalImage(ACTION_ADD_RECYCLER_ITEM_IMAGE);
}
public void chooseMainImage(View view) {
if (faceEngineCode != ErrorInfo.MOK) {
showToast(getString(R.string.engine_not_initialized, faceEngineCode));
return;
}
chooseLocalImage(ACTION_CHOOSE_MAIN_IMAGE);
}
@Override
void afterRequestPermission(int requestCode, boolean isAllGranted) {
if (requestCode == ACTION_REQUEST_PERMISSIONS) {
if (isAllGranted) {
initEngine();
} else {
showToast(getString(R.string.permission_denied));
}
}
}
}
package com.arcsoft.arcfacedemo.activity;
import android.Manifest;
import android.content.pm.ActivityInfo;
import android.graphics.Point;
import android.hardware.Camera;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
import android.widget.CompoundButton;
import android.widget.Switch;
import com.arcsoft.arcfacedemo.R;
import com.arcsoft.arcfacedemo.faceserver.CompareResult;
import com.arcsoft.arcfacedemo.faceserver.FaceServer;
import com.arcsoft.arcfacedemo.model.DrawInfo;
import com.arcsoft.arcfacedemo.model.FacePreviewInfo;
import com.arcsoft.arcfacedemo.util.ConfigUtil;
import com.arcsoft.arcfacedemo.util.DrawHelper;
import com.arcsoft.arcfacedemo.util.camera.CameraHelper;
import com.arcsoft.arcfacedemo.util.camera.CameraListener;
import com.arcsoft.arcfacedemo.util.face.FaceHelper;
import com.arcsoft.arcfacedemo.util.face.FaceListener;
import com.arcsoft.arcfacedemo.util.face.LivenessType;
import com.arcsoft.arcfacedemo.util.face.RecognizeColor;
import com.arcsoft.arcfacedemo.util.face.RequestFeatureStatus;
import com.arcsoft.arcfacedemo.util.face.RequestLivenessStatus;
import com.arcsoft.arcfacedemo.widget.FaceRectView;
import com.arcsoft.arcfacedemo.widget.FaceSearchResultAdapter;
import com.arcsoft.face.AgeInfo;
import com.arcsoft.face.ErrorInfo;
import com.arcsoft.face.FaceEngine;
import com.arcsoft.face.FaceFeature;
import com.arcsoft.face.GenderInfo;
import com.arcsoft.face.LivenessInfo;
import com.arcsoft.face.enums.DetectFaceOrientPriority;
import com.arcsoft.face.enums.DetectMode;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import io.reactivex.Observable;
import io.reactivex.ObservableEmitter;
import io.reactivex.ObservableOnSubscribe;
import io.reactivex.Observer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
public class RegisterAndRecognizeActivity extends BaseActivity implements ViewTreeObserver.OnGlobalLayoutListener {
private static final String TAG = "RegisterAndRecognize";
private static final int MAX_DETECT_NUM = 10;
/**
* 当FR成功,活体未成功时,FR等待活体的时间
*/
private static final int WAIT_LIVENESS_INTERVAL = 100;
/**
* 失败重试间隔时间(ms)
*/
private static final long FAIL_RETRY_INTERVAL = 1000;
/**
* 出错重试最大次数
*/
private static final int MAX_RETRY_TIME = 3;
private CameraHelper cameraHelper;
private DrawHelper drawHelper;
private Camera.Size previewSize;
/**
* 优先打开的摄像头,本界面主要用于单目RGB摄像头设备,因此默认打开前置
*/
private Integer rgbCameraID = Camera.CameraInfo.CAMERA_FACING_FRONT;
/**
* VIDEO模式人脸检测引擎,用于预览帧人脸追踪
*/
private FaceEngine ftEngine;
/**
* 用于特征提取的引擎
*/
private FaceEngine frEngine;
/**
* IMAGE模式活体检测引擎,用于预览帧人脸活体检测
*/
private FaceEngine flEngine;
private int ftInitCode = -1;
private int frInitCode = -1;
private int flInitCode = -1;
private FaceHelper faceHelper;
private List<CompareResult> compareResultList;
private FaceSearchResultAdapter adapter;
/**
* 活体检测的开关
*/
private boolean livenessDetect = true;
/**
* 注册人脸状态码,准备注册
*/
private static final int REGISTER_STATUS_READY = 0;
/**
* 注册人脸状态码,注册中
*/
private static final int REGISTER_STATUS_PROCESSING = 1;
/**
* 注册人脸状态码,注册结束(无论成功失败)
*/
private static final int REGISTER_STATUS_DONE = 2;
private int registerStatus = REGISTER_STATUS_DONE;
/**
* 用于记录人脸识别相关状态
*/
private ConcurrentHashMap<Integer, Integer> requestFeatureStatusMap = new ConcurrentHashMap<>();
/**
* 用于记录人脸特征提取出错重试次数
*/
private ConcurrentHashMap<Integer, Integer> extractErrorRetryMap = new ConcurrentHashMap<>();
/**
* 用于存储活体值
*/
private ConcurrentHashMap<Integer, Integer> livenessMap = new ConcurrentHashMap<>();
/**
* 用于存储活体检测出错重试次数
*/
private ConcurrentHashMap<Integer, Integer> livenessErrorRetryMap = new ConcurrentHashMap<>();
private CompositeDisposable getFeatureDelayedDisposables = new CompositeDisposable();
private CompositeDisposable delayFaceTaskCompositeDisposable = new CompositeDisposable();
/**
* 相机预览显示的控件,可为SurfaceView或TextureView
*/
private View previewView;
/**
* 绘制人脸框的控件
*/
private FaceRectView faceRectView;
private Switch switchLivenessDetect;
private static final int ACTION_REQUEST_PERMISSIONS = 0x001;
/**
* 识别阈值
*/
private static final float SIMILAR_THRESHOLD = 0.8F;
/**
* 所需的所有权限信息
*/
private static final String[] NEEDED_PERMISSIONS = new String[]{
Manifest.permission.CAMERA,
Manifest.permission.READ_PHONE_STATE
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_register_and_recognize);
//保持亮屏
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
WindowManager.LayoutParams attributes = getWindow().getAttributes();
attributes.systemUiVisibility = View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
getWindow().setAttributes(attributes);
}
// Activity启动后就锁定为启动时的方向
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED);
//本地人脸库初始化
FaceServer.getInstance().init(this);
initView();
}
private void initView() {
previewView = findViewById(R.id.single_camera_texture_preview);
//在布局结束后才做初始化操作
previewView.getViewTreeObserver().addOnGlobalLayoutListener(this);
faceRectView = findViewById(R.id.single_camera_face_rect_view);
switchLivenessDetect = findViewById(R.id.single_camera_switch_liveness_detect);
switchLivenessDetect.setChecked(livenessDetect);
switchLivenessDetect.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
livenessDetect = isChecked;
}
});
RecyclerView recyclerShowFaceInfo = findViewById(R.id.single_camera_recycler_view_person);
compareResultList = new ArrayList<>();
adapter = new FaceSearchResultAdapter(compareResultList, this);
recyclerShowFaceInfo.setAdapter(adapter);
DisplayMetrics dm = getResources().getDisplayMetrics();
int spanCount = (int) (dm.widthPixels / (getResources().getDisplayMetrics().density * 100 + 0.5f));
recyclerShowFaceInfo.setLayoutManager(new GridLayoutManager(this, spanCount));
recyclerShowFaceInfo.setItemAnimator(new DefaultItemAnimator());
}
/**
* 初始化引擎
*/
private void initEngine() {
ftEngine = new FaceEngine();
ftInitCode = ftEngine.init(this, DetectMode.ASF_DETECT_MODE_VIDEO, ConfigUtil.getFtOrient(this),
16, MAX_DETECT_NUM, FaceEngine.ASF_FACE_DETECT);
frEngine = new FaceEngine();
frInitCode = frEngine.init(this, DetectMode.ASF_DETECT_MODE_IMAGE, DetectFaceOrientPriority.ASF_OP_0_ONLY,
16, MAX_DETECT_NUM, FaceEngine.ASF_FACE_RECOGNITION);
flEngine = new FaceEngine();
flInitCode = flEngine.init(this, DetectMode.ASF_DETECT_MODE_IMAGE, DetectFaceOrientPriority.ASF_OP_0_ONLY,
16, MAX_DETECT_NUM, FaceEngine.ASF_LIVENESS);
Log.i(TAG, "initEngine: init: " + ftInitCode);
if (ftInitCode != ErrorInfo.MOK) {
String error = getString(R.string.specific_engine_init_failed, "ftEngine", ftInitCode);
Log.i(TAG, "initEngine: " + error);
showToast(error);
}
if (frInitCode != ErrorInfo.MOK) {
String error = getString(R.string.specific_engine_init_failed, "frEngine", frInitCode);
Log.i(TAG, "initEngine: " + error);
showToast(error);
}
if (flInitCode != ErrorInfo.MOK) {
String error = getString(R.string.specific_engine_init_failed, "flEngine", flInitCode);
Log.i(TAG, "initEngine: " + error);
showToast(error);
}
}
/**
* 销毁引擎,faceHelper中可能会有特征提取耗时操作仍在执行,加锁防止crash
*/
private void unInitEngine() {
if (ftInitCode == ErrorInfo.MOK && ftEngine != null) {
synchronized (ftEngine) {
int ftUnInitCode = ftEngine.unInit();
Log.i(TAG, "unInitEngine: " + ftUnInitCode);
}
}
if (frInitCode == ErrorInfo.MOK && frEngine != null) {
synchronized (frEngine) {
int frUnInitCode = frEngine.unInit();
Log.i(TAG, "unInitEngine: " + frUnInitCode);
}
}
if (flInitCode == ErrorInfo.MOK && flEngine != null) {
synchronized (flEngine) {
int flUnInitCode = flEngine.unInit();
Log.i(TAG, "unInitEngine: " + flUnInitCode);
}
}
}
@Override
protected void onDestroy() {
if (cameraHelper != null) {
cameraHelper.release();
cameraHelper = null;
}
unInitEngine();
if (getFeatureDelayedDisposables != null) {
getFeatureDelayedDisposables.clear();
}
if (delayFaceTaskCompositeDisposable != null) {
delayFaceTaskCompositeDisposable.clear();
}
if (faceHelper != null) {
ConfigUtil.setTrackedFaceCount(this, faceHelper.getTrackedFaceCount());
faceHelper.release();
faceHelper = null;
}
FaceServer.getInstance().unInit();
super.onDestroy();
}
private void initCamera() {
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
final FaceListener faceListener = new FaceListener() {
@Override
public void onFail(Exception e) {
Log.e(TAG, "onFail: " + e.getMessage());
}
//请求FR的回调
@Override
public void onFaceFeatureInfoGet(@Nullable final FaceFeature faceFeature, final Integer requestId, final Integer errorCode) {
//FR成功
if (faceFeature != null) {
// Log.i(TAG, "onPreview: fr end = " + System.currentTimeMillis() + " trackId = " + requestId);
Integer liveness = livenessMap.get(requestId);
//不做活体检测的情况,直接搜索
if (!livenessDetect) {
searchFace(faceFeature, requestId);
}
//活体检测通过,搜索特征
else if (liveness != null && liveness == LivenessInfo.ALIVE) {
searchFace(faceFeature, requestId);
}
//活体检测未出结果,或者非活体,延迟执行该函数
else {
if (requestFeatureStatusMap.containsKey(requestId)) {
Observable.timer(WAIT_LIVENESS_INTERVAL, TimeUnit.MILLISECONDS)
.subscribe(new Observer<Long>() {
Disposable disposable;
@Override
public void onSubscribe(Disposable d) {
disposable = d;
getFeatureDelayedDisposables.add(disposable);
}
@Override
public void onNext(Long aLong) {
onFaceFeatureInfoGet(faceFeature, requestId, errorCode);
}
@Override
public void onError(Throwable e) {
}
@Override
public void onComplete() {
getFeatureDelayedDisposables.remove(disposable);
}
});
}
}
}
//特征提取失败
else {
if (increaseAndGetValue(extractErrorRetryMap, requestId) > MAX_RETRY_TIME) {
extractErrorRetryMap.put(requestId, 0);
String msg;
// 传入的FaceInfo在指定的图像上无法解析人脸,此处使用的是RGB人脸数据,一般是人脸模糊
if (errorCode != null && errorCode == ErrorInfo.MERR_FSDK_FACEFEATURE_LOW_CONFIDENCE_LEVEL) {
msg = getString(R.string.low_confidence_level);
} else {
msg = "ExtractCode:" + errorCode;
}
faceHelper.setName(requestId, getString(R.string.recognize_failed_notice, msg));
// 在尝试最大次数后,特征提取仍然失败,则认为识别未通过
requestFeatureStatusMap.put(requestId, RequestFeatureStatus.FAILED);
retryRecognizeDelayed(requestId);
} else {
requestFeatureStatusMap.put(requestId, RequestFeatureStatus.TO_RETRY);
}
}
}
@Override
public void onFaceLivenessInfoGet(@Nullable LivenessInfo livenessInfo, final Integer requestId, Integer errorCode) {
if (livenessInfo != null) {
int liveness = livenessInfo.getLiveness();
livenessMap.put(requestId, liveness);
// 非活体,重试
if (liveness == LivenessInfo.NOT_ALIVE) {
faceHelper.setName(requestId, getString(R.string.recognize_failed_notice, "NOT_ALIVE"));
// 延迟 FAIL_RETRY_INTERVAL 后,将该人脸状态置为UNKNOWN,帧回调处理时会重新进行活体检测
retryLivenessDetectDelayed(requestId);
}
} else {
if (increaseAndGetValue(livenessErrorRetryMap, requestId) > MAX_RETRY_TIME) {
livenessErrorRetryMap.put(requestId, 0);
String msg;
// 传入的FaceInfo在指定的图像上无法解析人脸,此处使用的是RGB人脸数据,一般是人脸模糊
if (errorCode != null && errorCode == ErrorInfo.MERR_FSDK_FACEFEATURE_LOW_CONFIDENCE_LEVEL) {
msg = getString(R.string.low_confidence_level);
} else {
msg = "ProcessCode:" + errorCode;
}
faceHelper.setName(requestId, getString(R.string.recognize_failed_notice, msg));
retryLivenessDetectDelayed(requestId);
} else {
livenessMap.put(requestId, LivenessInfo.UNKNOWN);
}
}
}
};
CameraListener cameraListener = new CameraListener() {
@Override
public void onCameraOpened(Camera camera, int cameraId, int displayOrientation, boolean isMirror) {
Camera.Size lastPreviewSize = previewSize;
previewSize = camera.getParameters().getPreviewSize();
drawHelper = new DrawHelper(previewSize.width, previewSize.height, previewView.getWidth(), previewView.getHeight(), displayOrientation
, cameraId, isMirror, false, true);
Log.i(TAG, "onCameraOpened: " + drawHelper.toString());
// 切换相机的时候可能会导致预览尺寸发生变化
if (faceHelper == null ||
lastPreviewSize == null ||
lastPreviewSize.width != previewSize.width || lastPreviewSize.height != previewSize.height) {
Integer trackedFaceCount = null;
// 记录切换时的人脸序号
if (faceHelper != null) {
trackedFaceCount = faceHelper.getTrackedFaceCount();
faceHelper.release();
}
faceHelper = new FaceHelper.Builder()
.ftEngine(ftEngine)
.frEngine(frEngine)
.flEngine(flEngine)
.frQueueSize(MAX_DETECT_NUM)
.flQueueSize(MAX_DETECT_NUM)
.previewSize(previewSize)
.faceListener(faceListener)
.trackedFaceCount(trackedFaceCount == null ? ConfigUtil.getTrackedFaceCount(RegisterAndRecognizeActivity.this.getApplicationContext()) : trackedFaceCount)
.build();
}
}
@Override
public void onPreview(final byte[] nv21, Camera camera) {
if (faceRectView != null) {
faceRectView.clearFaceInfo();
}
List<FacePreviewInfo> facePreviewInfoList = faceHelper.onPreviewFrame(nv21);
if (facePreviewInfoList != null && faceRectView != null && drawHelper != null) {
drawPreviewInfo(facePreviewInfoList);
}
registerFace(nv21, facePreviewInfoList);
clearLeftFace(facePreviewInfoList);
if (facePreviewInfoList != null && facePreviewInfoList.size() > 0 && previewSize != null) {
for (int i = 0; i < facePreviewInfoList.size(); i++) {
Integer status = requestFeatureStatusMap.get(facePreviewInfoList.get(i).getTrackId());
/**
* 在活体检测开启,在人脸识别状态不为成功或人脸活体状态不为处理中(ANALYZING)且不为处理完成(ALIVE、NOT_ALIVE)时重新进行活体检测
*/
if (livenessDetect && (status == null || status != RequestFeatureStatus.SUCCEED)) {
Integer liveness = livenessMap.get(facePreviewInfoList.get(i).getTrackId());
if (liveness == null
|| (liveness != LivenessInfo.ALIVE && liveness != LivenessInfo.NOT_ALIVE && liveness != RequestLivenessStatus.ANALYZING)) {
livenessMap.put(facePreviewInfoList.get(i).getTrackId(), RequestLivenessStatus.ANALYZING);
faceHelper.requestFaceLiveness(nv21, facePreviewInfoList.get(i).getFaceInfo(), previewSize.width, previewSize.height, FaceEngine.CP_PAF_NV21, facePreviewInfoList.get(i).getTrackId(), LivenessType.RGB);
}
}
/**
* 对于每个人脸,若状态为空或者为失败,则请求特征提取(可根据需要添加其他判断以限制特征提取次数),
* 特征提取回传的人脸特征结果在{@link FaceListener#onFaceFeatureInfoGet(FaceFeature, Integer, Integer)}中回传
*/
if (status == null
|| status == RequestFeatureStatus.TO_RETRY) {
requestFeatureStatusMap.put(facePreviewInfoList.get(i).getTrackId(), RequestFeatureStatus.SEARCHING);
faceHelper.requestFaceFeature(nv21, facePreviewInfoList.get(i).getFaceInfo(), previewSize.width, previewSize.height, FaceEngine.CP_PAF_NV21, facePreviewInfoList.get(i).getTrackId());
// Log.i(TAG, "onPreview: fr start = " + System.currentTimeMillis() + " trackId = " + facePreviewInfoList.get(i).getTrackedFaceCount());
}
}
}
}
@Override
public void onCameraClosed() {
Log.i(TAG, "onCameraClosed: ");
}
@Override
public void onCameraError(Exception e) {
Log.i(TAG, "onCameraError: " + e.getMessage());
}
@Override
public void onCameraConfigurationChanged(int cameraID, int displayOrientation) {
if (drawHelper != null) {
drawHelper.setCameraDisplayOrientation(displayOrientation);
}
Log.i(TAG, "onCameraConfigurationChanged: " + cameraID + " " + displayOrientation);
}
};
cameraHelper = new CameraHelper.Builder()
.previewViewSize(new Point(previewView.getMeasuredWidth(), previewView.getMeasuredHeight()))
.rotation(getWindowManager().getDefaultDisplay().getRotation())
.specificCameraId(rgbCameraID != null ? rgbCameraID : Camera.CameraInfo.CAMERA_FACING_FRONT)
.isMirror(false)
.previewOn(previewView)
.cameraListener(cameraListener)
.build();
cameraHelper.init();
cameraHelper.start();
}
private void registerFace(final byte[] nv21, final List<FacePreviewInfo> facePreviewInfoList) {
if (registerStatus == REGISTER_STATUS_READY && facePreviewInfoList != null && facePreviewInfoList.size() > 0) {
registerStatus = REGISTER_STATUS_PROCESSING;
Observable.create(new ObservableOnSubscribe<Boolean>() {
@Override
public void subscribe(ObservableEmitter<Boolean> emitter) {
boolean success = FaceServer.getInstance().registerNv21(RegisterAndRecognizeActivity.this, nv21.clone(), previewSize.width, previewSize.height,
facePreviewInfoList.get(0).getFaceInfo(), "registered " + faceHelper.getTrackedFaceCount());
emitter.onNext(success);
}
})
.subscribeOn(Schedulers.computation())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Boolean>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(Boolean success) {
String result = success ? "register success!" : "register failed!";
showToast(result);
registerStatus = REGISTER_STATUS_DONE;
}
@Override
public void onError(Throwable e) {
e.printStackTrace();
showToast("register failed!");
registerStatus = REGISTER_STATUS_DONE;
}
@Override
public void onComplete() {
}
});
}
}
private void drawPreviewInfo(List<FacePreviewInfo> facePreviewInfoList) {
List<DrawInfo> drawInfoList = new ArrayList<>();
for (int i = 0; i < facePreviewInfoList.size(); i++) {
String name = faceHelper.getName(facePreviewInfoList.get(i).getTrackId());
Integer liveness = livenessMap.get(facePreviewInfoList.get(i).getTrackId());
Integer recognizeStatus = requestFeatureStatusMap.get(facePreviewInfoList.get(i).getTrackId());
// 根据识别结果和活体结果设置颜色
int color = RecognizeColor.COLOR_UNKNOWN;
if (recognizeStatus != null) {
if (recognizeStatus == RequestFeatureStatus.FAILED) {
color = RecognizeColor.COLOR_FAILED;
}
if (recognizeStatus == RequestFeatureStatus.SUCCEED) {
color = RecognizeColor.COLOR_SUCCESS;
}
}
if (liveness != null && liveness == LivenessInfo.NOT_ALIVE) {
color = RecognizeColor.COLOR_FAILED;
}
drawInfoList.add(new DrawInfo(drawHelper.adjustRect(facePreviewInfoList.get(i).getFaceInfo().getRect()),
GenderInfo.UNKNOWN, AgeInfo.UNKNOWN_AGE, liveness == null ? LivenessInfo.UNKNOWN : liveness, color,
name == null ? String.valueOf(facePreviewInfoList.get(i).getTrackId()) : name));
}
drawHelper.draw(faceRectView, drawInfoList);
}
@Override
void afterRequestPermission(int requestCode, boolean isAllGranted) {
if (requestCode == ACTION_REQUEST_PERMISSIONS) {
if (isAllGranted) {
initEngine();
initCamera();
} else {
showToast(getString(R.string.permission_denied));
}
}
}
/**
* 删除已经离开的人脸
*
* @param facePreviewInfoList 人脸和trackId列表
*/
private void clearLeftFace(List<FacePreviewInfo> facePreviewInfoList) {
if (compareResultList != null) {
for (int i = compareResultList.size() - 1; i >= 0; i--) {
if (!requestFeatureStatusMap.containsKey(compareResultList.get(i).getTrackId())) {
compareResultList.remove(i);
adapter.notifyItemRemoved(i);
}
}
}
if (facePreviewInfoList == null || facePreviewInfoList.size() == 0) {
requestFeatureStatusMap.clear();
livenessMap.clear();
livenessErrorRetryMap.clear();
extractErrorRetryMap.clear();
if (getFeatureDelayedDisposables != null) {
getFeatureDelayedDisposables.clear();
}
return;
}
Enumeration<Integer> keys = requestFeatureStatusMap.keys();
while (keys.hasMoreElements()) {
int key = keys.nextElement();
boolean contained = false;
for (FacePreviewInfo facePreviewInfo : facePreviewInfoList) {
if (facePreviewInfo.getTrackId() == key) {
contained = true;
break;
}
}
if (!contained) {
requestFeatureStatusMap.remove(key);
livenessMap.remove(key);
livenessErrorRetryMap.remove(key);
extractErrorRetryMap.remove(key);
}
}
}
private void searchFace(final FaceFeature frFace, final Integer requestId) {
Observable
.create(new ObservableOnSubscribe<CompareResult>() {
@Override
public void subscribe(ObservableEmitter<CompareResult> emitter) {
// Log.i(TAG, "subscribe: fr search start = " + System.currentTimeMillis() + " trackId = " + requestId);
CompareResult compareResult = FaceServer.getInstance().getTopOfFaceLib(frFace);
// Log.i(TAG, "subscribe: fr search end = " + System.currentTimeMillis() + " trackId = " + requestId);
emitter.onNext(compareResult);
}
})
.subscribeOn(Schedulers.computation())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<CompareResult>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(CompareResult compareResult) {
if (compareResult == null || compareResult.getUserName() == null) {
requestFeatureStatusMap.put(requestId, RequestFeatureStatus.FAILED);
faceHelper.setName(requestId, "VISITOR " + requestId);
return;
}
// Log.i(TAG, "onNext: fr search get result = " + System.currentTimeMillis() + " trackId = " + requestId + " similar = " + compareResult.getSimilar());
if (compareResult.getSimilar() > SIMILAR_THRESHOLD) {
boolean isAdded = false;
if (compareResultList == null) {
requestFeatureStatusMap.put(requestId, RequestFeatureStatus.FAILED);
faceHelper.setName(requestId, "VISITOR " + requestId);
return;
}
for (CompareResult compareResult1 : compareResultList) {
if (compareResult1.getTrackId() == requestId) {
isAdded = true;
break;
}
}
if (!isAdded) {
//对于多人脸搜索,假如最大显示数量为 MAX_DETECT_NUM 且有新的人脸进入,则以队列的形式移除
if (compareResultList.size() >= MAX_DETECT_NUM) {
compareResultList.remove(0);
adapter.notifyItemRemoved(0);
}
//添加显示人员时,保存其trackId
compareResult.setTrackId(requestId);
compareResultList.add(compareResult);
adapter.notifyItemInserted(compareResultList.size() - 1);
}
requestFeatureStatusMap.put(requestId, RequestFeatureStatus.SUCCEED);
faceHelper.setName(requestId, getString(R.string.recognize_success_notice, compareResult.getUserName()));
} else {
faceHelper.setName(requestId, getString(R.string.recognize_failed_notice, "NOT_REGISTERED"));
retryRecognizeDelayed(requestId);
}
}
@Override
public void onError(Throwable e) {
faceHelper.setName(requestId, getString(R.string.recognize_failed_notice, "NOT_REGISTERED"));
retryRecognizeDelayed(requestId);
}
@Override
public void onComplete() {
}
});
}
/**
* 将准备注册的状态置为{@link #REGISTER_STATUS_READY}
*
* @param view 注册按钮
*/
public void register(View view) {
if (registerStatus == REGISTER_STATUS_DONE) {
registerStatus = REGISTER_STATUS_READY;
}
}
/**
* 切换相机。注意:若切换相机发现检测不到人脸,则极有可能是检测角度导致的,需要销毁引擎重新创建或者在设置界面修改配置的检测角度
*
* @param view
*/
public void switchCamera(View view) {
if (cameraHelper != null) {
boolean success = cameraHelper.switchCamera();
if (!success) {
showToast(getString(R.string.switch_camera_failed));
} else {
showLongToast(getString(R.string.notice_change_detect_degree));
}
}
}
/**
* 在{@link #previewView}第一次布局完成后,去除该监听,并且进行引擎和相机的初始化
*/
@Override
public void onGlobalLayout() {
previewView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
if (!checkPermissions(NEEDED_PERMISSIONS)) {
ActivityCompat.requestPermissions(this, NEEDED_PERMISSIONS, ACTION_REQUEST_PERMISSIONS);
} else {
initEngine();
initCamera();
}
}
/**
* 将map中key对应的value增1回传
*
* @param countMap map
* @param key key
* @return 增1后的value
*/
public int increaseAndGetValue(Map<Integer, Integer> countMap, int key) {
if (countMap == null) {
return 0;
}
Integer value = countMap.get(key);
if (value == null) {
value = 0;
}
countMap.put(key, ++value);
return value;
}
/**
* 延迟 FAIL_RETRY_INTERVAL 重新进行活体检测
*
* @param requestId 人脸ID
*/
private void retryLivenessDetectDelayed(final Integer requestId) {
Observable.timer(FAIL_RETRY_INTERVAL, TimeUnit.MILLISECONDS)
.subscribe(new Observer<Long>() {
Disposable disposable;
@Override
public void onSubscribe(Disposable d) {
disposable = d;
delayFaceTaskCompositeDisposable.add(disposable);
}
@Override
public void onNext(Long aLong) {
}
@Override
public void onError(Throwable e) {
e.printStackTrace();
}
@Override
public void onComplete() {
// 将该人脸状态置为UNKNOWN,帧回调处理时会重新进行活体检测
if (livenessDetect) {
faceHelper.setName(requestId, Integer.toString(requestId));
}
livenessMap.put(requestId, LivenessInfo.UNKNOWN);
delayFaceTaskCompositeDisposable.remove(disposable);
}
});
}
/**
* 延迟 FAIL_RETRY_INTERVAL 重新进行人脸识别
*
* @param requestId 人脸ID
*/
private void retryRecognizeDelayed(final Integer requestId) {
requestFeatureStatusMap.put(requestId, RequestFeatureStatus.FAILED);
Observable.timer(FAIL_RETRY_INTERVAL, TimeUnit.MILLISECONDS)
.subscribe(new Observer<Long>() {
Disposable disposable;
@Override
public void onSubscribe(Disposable d) {
disposable = d;
delayFaceTaskCompositeDisposable.add(disposable);
}
@Override
public void onNext(Long aLong) {
}
@Override
public void onError(Throwable e) {
e.printStackTrace();
}
@Override
public void onComplete() {
// 将该人脸特征提取状态置为FAILED,帧回调处理时会重新进行活体检测
faceHelper.setName(requestId, Integer.toString(requestId));
requestFeatureStatusMap.put(requestId, RequestFeatureStatus.TO_RETRY);
delayFaceTaskCompositeDisposable.remove(disposable);
}
});
}
}
package com.arcsoft.arcfacedemo.activity;
import android.Manifest;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.os.Build;
import android.os.Bundle;
import android.provider.MediaStore;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AlertDialog;
import android.text.ParcelableSpan;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.style.ForegroundColorSpan;
import android.text.style.StyleSpan;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.arcsoft.arcfacedemo.R;
import com.arcsoft.face.AgeInfo;
import com.arcsoft.face.ErrorInfo;
import com.arcsoft.face.Face3DAngle;
import com.arcsoft.face.FaceEngine;
import com.arcsoft.face.FaceFeature;
import com.arcsoft.face.FaceInfo;
import com.arcsoft.face.FaceSimilar;
import com.arcsoft.face.GenderInfo;
import com.arcsoft.face.LivenessInfo;
import com.arcsoft.face.VersionInfo;
import com.arcsoft.face.enums.CompareModel;
import com.arcsoft.face.enums.DetectFaceOrientPriority;
import com.arcsoft.face.enums.DetectMode;
import com.arcsoft.face.enums.DetectModel;
import com.arcsoft.face.model.ArcSoftImageInfo;
import com.arcsoft.face.util.ImageUtils;
import com.arcsoft.imageutil.ArcSoftImageFormat;
import com.arcsoft.imageutil.ArcSoftImageUtil;
import com.arcsoft.imageutil.ArcSoftImageUtilError;
import com.bumptech.glide.Glide;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import io.reactivex.Observable;
import io.reactivex.ObservableEmitter;
import io.reactivex.ObservableOnSubscribe;
import io.reactivex.Observer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
public class SingleImageActivity extends BaseActivity {
private static final String TAG = "SingleImageActivity";
private ImageView ivShow;
private TextView tvNotice;
private FaceEngine faceEngine;
private int faceEngineCode = -1;
/**
* 请求权限的请求码
*/
private static final int ACTION_REQUEST_PERMISSIONS = 0x001;
/**
* 请求选择本地图片文件的请求码
*/
private static final int ACTION_CHOOSE_IMAGE = 0x201;
/**
* 提示对话框
*/
private AlertDialog progressDialog;
/**
* 被处理的图片
*/
private Bitmap mBitmap = null;
/**
* 所需的所有权限信息
*/
private static String[] NEEDED_PERMISSIONS = new String[]{
Manifest.permission.READ_PHONE_STATE
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_image_process);
initView();
/**
* 在选择图片的时候,在android 7.0及以上通过FileProvider获取Uri,不需要文件权限
*/
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
List<String> permissionList = new ArrayList<>(Arrays.asList(NEEDED_PERMISSIONS));
permissionList.add(Manifest.permission.READ_EXTERNAL_STORAGE);
NEEDED_PERMISSIONS = permissionList.toArray(new String[0]);
}
if (!checkPermissions(NEEDED_PERMISSIONS)) {
ActivityCompat.requestPermissions(this, NEEDED_PERMISSIONS, ACTION_REQUEST_PERMISSIONS);
} else {
initEngine();
}
}
private void initEngine() {
faceEngine = new FaceEngine();
faceEngineCode = faceEngine.init(this, DetectMode.ASF_DETECT_MODE_IMAGE, DetectFaceOrientPriority.ASF_OP_ALL_OUT,
16, 10, FaceEngine.ASF_FACE_RECOGNITION | FaceEngine.ASF_FACE_DETECT | FaceEngine.ASF_AGE | FaceEngine.ASF_GENDER | FaceEngine.ASF_FACE3DANGLE | FaceEngine.ASF_LIVENESS);
Log.i(TAG, "initEngine: init: " + faceEngineCode);
if (faceEngineCode != ErrorInfo.MOK) {
showToast(getString(R.string.init_failed, faceEngineCode));
}
}
/**
* 销毁引擎
*/
private void unInitEngine() {
if (faceEngine != null) {
faceEngineCode = faceEngine.unInit();
faceEngine = null;
Log.i(TAG, "unInitEngine: " + faceEngineCode);
}
}
@Override
protected void onDestroy() {
if (mBitmap != null && !mBitmap.isRecycled()) {
mBitmap.recycle();
}
mBitmap = null;
if (progressDialog != null && progressDialog.isShowing()) {
progressDialog.dismiss();
}
progressDialog = null;
unInitEngine();
super.onDestroy();
}
private void initView() {
tvNotice = findViewById(R.id.tv_notice);
ivShow = findViewById(R.id.iv_show);
ivShow.setImageResource(R.drawable.faces);
progressDialog = new AlertDialog.Builder(this)
.setTitle(R.string.processing)
.setView(new ProgressBar(this))
.create();
}
/**
* 按钮点击响应事件
*
* @param view
*/
public void process(final View view) {
view.setClickable(false);
if (progressDialog == null || progressDialog.isShowing()) {
return;
}
progressDialog.show();
//图像转化操作和部分引擎调用比较耗时,建议放子线程操作
Observable.create(new ObservableOnSubscribe<Object>() {
@Override
public void subscribe(ObservableEmitter<Object> emitter) throws Exception {
processImage();
emitter.onComplete();
}
})
.subscribeOn(Schedulers.computation())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Object>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(Object o) {
}
@Override
public void onError(Throwable e) {
e.printStackTrace();
}
@Override
public void onComplete() {
view.setClickable(true);
}
});
}
/**
* 主要操作逻辑部分
*/
public void processImage() {
/**
* 1.准备操作(校验,显示,获取BGR)
*/
if (mBitmap == null) {
mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.faces);
}
// 图像对齐
Bitmap bitmap = ArcSoftImageUtil.getAlignedBitmap(mBitmap, true);
final SpannableStringBuilder notificationSpannableStringBuilder = new SpannableStringBuilder();
if (faceEngineCode != ErrorInfo.MOK) {
addNotificationInfo(notificationSpannableStringBuilder, null, " face engine not initialized!");
showNotificationAndFinish(notificationSpannableStringBuilder);
return;
}
if (bitmap == null) {
addNotificationInfo(notificationSpannableStringBuilder, null, " bitmap is null!");
showNotificationAndFinish(notificationSpannableStringBuilder);
return;
}
if (faceEngine == null) {
addNotificationInfo(notificationSpannableStringBuilder, null, " faceEngine is null!");
showNotificationAndFinish(notificationSpannableStringBuilder);
return;
}
int width = bitmap.getWidth();
int height = bitmap.getHeight();
final Bitmap finalBitmap = bitmap;
runOnUiThread(new Runnable() {
@Override
public void run() {
Glide.with(ivShow.getContext())
.load(finalBitmap)
.into(ivShow);
}
});
// bitmap转bgr24
long start = System.currentTimeMillis();
byte[] bgr24 = ArcSoftImageUtil.createImageData(bitmap.getWidth(), bitmap.getHeight(), ArcSoftImageFormat.BGR24);
int transformCode = ArcSoftImageUtil.bitmapToImageData(bitmap, bgr24, ArcSoftImageFormat.BGR24);
if (transformCode != ArcSoftImageUtilError.CODE_SUCCESS) {
Log.e(TAG, "transform failed, code is " + transformCode);
addNotificationInfo(notificationSpannableStringBuilder, new StyleSpan(Typeface.BOLD), "transform bitmap To ImageData failed", "code is ", String.valueOf(transformCode), "\n");
return;
}
// Log.i(TAG, "processImage:bitmapToBgr24 cost = " + (System.currentTimeMillis() - start));
addNotificationInfo(notificationSpannableStringBuilder, new StyleSpan(Typeface.BOLD), "start face detection,imageWidth is ", String.valueOf(width), ", imageHeight is ", String.valueOf(height), "\n");
List<FaceInfo> faceInfoList = new ArrayList<>();
/**
* 2.成功获取到了BGR24 数据,开始人脸检测
*/
long fdStartTime = System.currentTimeMillis();
// ArcSoftImageInfo arcSoftImageInfo = new ArcSoftImageInfo(width,height,FaceEngine.CP_PAF_BGR24,new byte[][]{bgr24},new int[]{width * 3});
// Log.i(TAG, "processImage: " + arcSoftImageInfo.getPlanes()[0].length);
// int detectCode = faceEngine.detectFaces(arcSoftImageInfo, faceInfoList);
int detectCode = faceEngine.detectFaces(bgr24, width, height, FaceEngine.CP_PAF_BGR24, DetectModel.RGB, faceInfoList);
if (detectCode == ErrorInfo.MOK) {
// Log.i(TAG, "processImage: fd costTime = " + (System.currentTimeMillis() - fdStartTime));
}
//绘制bitmap
Bitmap bitmapForDraw = bitmap.copy(Bitmap.Config.RGB_565, true);
Canvas canvas = new Canvas(bitmapForDraw);
Paint paint = new Paint();
addNotificationInfo(notificationSpannableStringBuilder, null, "detect result:\nerrorCode is :", String.valueOf(detectCode), " face Number is ", String.valueOf(faceInfoList.size()), "\n");
/**
* 3.若检测结果人脸数量大于0,则在bitmap上绘制人脸框并且重新显示到ImageView,若人脸数量为0,则无法进行下一步操作,操作结束
*/
if (faceInfoList.size() > 0) {
addNotificationInfo(notificationSpannableStringBuilder, null, "face list:\n");
paint.setAntiAlias(true);
paint.setStrokeWidth(5);
paint.setColor(Color.YELLOW);
for (int i = 0; i < faceInfoList.size(); i++) {
//绘制人脸框
paint.setStyle(Paint.Style.STROKE);
canvas.drawRect(faceInfoList.get(i).getRect(), paint);
//绘制人脸序号
paint.setStyle(Paint.Style.FILL_AND_STROKE);
int textSize = faceInfoList.get(i).getRect().width() / 2;
paint.setTextSize(textSize);
canvas.drawText(String.valueOf(i), faceInfoList.get(i).getRect().left, faceInfoList.get(i).getRect().top, paint);
addNotificationInfo(notificationSpannableStringBuilder, null, "face[", String.valueOf(i), "]:", faceInfoList.get(i).toString(), "\n");
}
//显示
final Bitmap finalBitmapForDraw = bitmapForDraw;
runOnUiThread(new Runnable() {
@Override
public void run() {
Glide.with(ivShow.getContext())
.load(finalBitmapForDraw)
.into(ivShow);
}
});
} else {
addNotificationInfo(notificationSpannableStringBuilder, null, "can not do further action, exit!");
showNotificationAndFinish(notificationSpannableStringBuilder);
return;
}
addNotificationInfo(notificationSpannableStringBuilder, null, "\n");
/**
* 4.上一步已获取到人脸位置和角度信息,传入给process函数,进行年龄、性别、三维角度、活体检测
*/
long processStartTime = System.currentTimeMillis();
int faceProcessCode = faceEngine.process(bgr24, width, height, FaceEngine.CP_PAF_BGR24, faceInfoList, FaceEngine.ASF_AGE | FaceEngine.ASF_GENDER | FaceEngine.ASF_FACE3DANGLE | FaceEngine.ASF_LIVENESS);
if (faceProcessCode != ErrorInfo.MOK) {
addNotificationInfo(notificationSpannableStringBuilder, new ForegroundColorSpan(Color.RED), "process failed! code is ", String.valueOf(faceProcessCode), "\n");
} else {
// Log.i(TAG, "processImage: process costTime = " + (System.currentTimeMillis() - processStartTime));
}
//年龄信息结果
List<AgeInfo> ageInfoList = new ArrayList<>();
//性别信息结果
List<GenderInfo> genderInfoList = new ArrayList<>();
//人脸三维角度结果
List<Face3DAngle> face3DAngleList = new ArrayList<>();
//活体检测结果
List<LivenessInfo> livenessInfoList = new ArrayList<>();
//获取年龄、性别、三维角度、活体结果
int ageCode = faceEngine.getAge(ageInfoList);
int genderCode = faceEngine.getGender(genderInfoList);
int face3DAngleCode = faceEngine.getFace3DAngle(face3DAngleList);
int livenessCode = faceEngine.getLiveness(livenessInfoList);
if ((ageCode | genderCode | face3DAngleCode | livenessCode) != ErrorInfo.MOK) {
addNotificationInfo(notificationSpannableStringBuilder, null, "at least one of age,gender,face3DAngle detect failed!,codes are:",
String.valueOf(ageCode), " , ", String.valueOf(genderCode), " , ", String.valueOf(face3DAngleCode));
showNotificationAndFinish(notificationSpannableStringBuilder);
return;
}
/**
* 5.年龄、性别、三维角度已获取成功,添加信息到提示文字中
*/
//年龄数据
if (ageInfoList.size() > 0) {
addNotificationInfo(notificationSpannableStringBuilder, new StyleSpan(Typeface.BOLD), "age of each face:\n");
}
for (int i = 0; i < ageInfoList.size(); i++) {
addNotificationInfo(notificationSpannableStringBuilder, null, "face[", String.valueOf(i), "]:", String.valueOf(ageInfoList.get(i).getAge()), "\n");
}
addNotificationInfo(notificationSpannableStringBuilder, null, "\n");
//性别数据
if (genderInfoList.size() > 0) {
addNotificationInfo(notificationSpannableStringBuilder, new StyleSpan(Typeface.BOLD), "gender of each face:\n");
}
for (int i = 0; i < genderInfoList.size(); i++) {
addNotificationInfo(notificationSpannableStringBuilder, null, "face[", String.valueOf(i), "]:"
, genderInfoList.get(i).getGender() == GenderInfo.MALE ?
"MALE" : (genderInfoList.get(i).getGender() == GenderInfo.FEMALE ? "FEMALE" : "UNKNOWN"), "\n");
}
addNotificationInfo(notificationSpannableStringBuilder, null, "\n");
//人脸三维角度数据
if (face3DAngleList.size() > 0) {
addNotificationInfo(notificationSpannableStringBuilder, new StyleSpan(Typeface.BOLD), "face3DAngle of each face:\n");
for (int i = 0; i < face3DAngleList.size(); i++) {
addNotificationInfo(notificationSpannableStringBuilder, null, "face[", String.valueOf(i), "]:", face3DAngleList.get(i).toString(), "\n");
}
}
addNotificationInfo(notificationSpannableStringBuilder, null, "\n");
//活体检测数据
if (livenessInfoList.size() > 0) {
addNotificationInfo(notificationSpannableStringBuilder, new StyleSpan(Typeface.BOLD), "liveness of each face:\n");
for (int i = 0; i < livenessInfoList.size(); i++) {
String liveness = null;
switch (livenessInfoList.get(i).getLiveness()) {
case LivenessInfo.ALIVE:
liveness = "ALIVE";
break;
case LivenessInfo.NOT_ALIVE:
liveness = "NOT_ALIVE";
break;
case LivenessInfo.UNKNOWN:
liveness = "UNKNOWN";
break;
case LivenessInfo.FACE_NUM_MORE_THAN_ONE:
liveness = "FACE_NUM_MORE_THAN_ONE";
break;
default:
liveness = "UNKNOWN";
break;
}
addNotificationInfo(notificationSpannableStringBuilder, null, "face[", String.valueOf(i), "]:", liveness, "\n");
}
}
addNotificationInfo(notificationSpannableStringBuilder, null, "\n");
/**
* 6.最后将图片内的所有人脸进行一一比对并添加到提示文字中
*/
if (faceInfoList.size() > 0) {
FaceFeature[] faceFeatures = new FaceFeature[faceInfoList.size()];
int[] extractFaceFeatureCodes = new int[faceInfoList.size()];
addNotificationInfo(notificationSpannableStringBuilder, new StyleSpan(Typeface.BOLD), "faceFeatureExtract:\n");
for (int i = 0; i < faceInfoList.size(); i++) {
faceFeatures[i] = new FaceFeature();
//从图片解析出人脸特征数据
long frStartTime = System.currentTimeMillis();
extractFaceFeatureCodes[i] = faceEngine.extractFaceFeature(bgr24, width, height, FaceEngine.CP_PAF_BGR24, faceInfoList.get(i), faceFeatures[i]);
if (extractFaceFeatureCodes[i] != ErrorInfo.MOK) {
addNotificationInfo(notificationSpannableStringBuilder, null, "faceFeature of face[", String.valueOf(i), "]",
" extract failed, code is ", String.valueOf(extractFaceFeatureCodes[i]), "\n");
} else {
// Log.i(TAG, "processImage: fr costTime = " + (System.currentTimeMillis() - frStartTime));
addNotificationInfo(notificationSpannableStringBuilder, null, "faceFeature of face[", String.valueOf(i), "]",
" extract success\n");
}
}
addNotificationInfo(notificationSpannableStringBuilder, null, "\n");
//人脸特征的数量大于2,将所有特征进行比较
if (faceFeatures.length >= 2) {
addNotificationInfo(notificationSpannableStringBuilder, new StyleSpan(Typeface.BOLD), "similar of faces:\n");
for (int i = 0; i < faceFeatures.length; i++) {
for (int j = i + 1; j < faceFeatures.length; j++) {
addNotificationInfo(notificationSpannableStringBuilder, new StyleSpan(Typeface.BOLD_ITALIC), "compare face[", String.valueOf(i), "] and face["
, String.valueOf(j), "]:\n");
//若其中一个特征提取失败,则不进行比对
boolean canCompare = true;
if (extractFaceFeatureCodes[i] != 0) {
addNotificationInfo(notificationSpannableStringBuilder, null, "faceFeature of face[", String.valueOf(i), "] extract failed, can not compare!\n");
canCompare = false;
}
if (extractFaceFeatureCodes[j] != 0) {
addNotificationInfo(notificationSpannableStringBuilder, null, "faceFeature of face[", String.valueOf(j), "] extract failed, can not compare!\n");
canCompare = false;
}
if (!canCompare) {
continue;
}
FaceSimilar matching = new FaceSimilar();
//比对两个人脸特征获取相似度信息
faceEngine.compareFaceFeature(faceFeatures[i], faceFeatures[j], CompareModel.LIFE_PHOTO, matching);
//新增相似度比对结果信息
addNotificationInfo(notificationSpannableStringBuilder, null, "similar of face[", String.valueOf(i), "] and face[",
String.valueOf(j), "] is:", String.valueOf(matching.getScore()), "\n");
}
}
}
}
showNotificationAndFinish(notificationSpannableStringBuilder);
}
/**
* 展示提示信息并且关闭提示框
*
* @param stringBuilder 带格式的提示文字
*/
private void showNotificationAndFinish(final SpannableStringBuilder stringBuilder) {
runOnUiThread(new Runnable() {
@Override
public void run() {
if (tvNotice != null) {
tvNotice.setText(stringBuilder);
}
if (progressDialog != null && progressDialog.isShowing()) {
progressDialog.dismiss();
}
}
});
}
/**
* 追加提示信息
*
* @param stringBuilder 提示的字符串的存放对象
* @param styleSpan 添加的字符串的格式
* @param strings 字符串数组
*/
private void addNotificationInfo(SpannableStringBuilder stringBuilder, ParcelableSpan styleSpan, String... strings) {
if (stringBuilder == null || strings == null || strings.length == 0) {
return;
}
int startLength = stringBuilder.length();
for (String string : strings) {
stringBuilder.append(string);
}
int endLength = stringBuilder.length();
if (styleSpan != null) {
stringBuilder.setSpan(styleSpan, startLength, endLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
/**
* 从本地选择文件
*
* @param view
*/
public void chooseLocalImage(View view) {
Intent intent = new Intent(Intent.ACTION_PICK);
intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*");
startActivityForResult(intent, ACTION_CHOOSE_IMAGE);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == ACTION_CHOOSE_IMAGE) {
if (data == null || data.getData() == null) {
showToast(getString(R.string.get_picture_failed));
return;
}
try {
mBitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), data.getData());
} catch (IOException e) {
e.printStackTrace();
return;
}
if (mBitmap == null) {
showToast(getString(R.string.get_picture_failed));
return;
}
Glide.with(ivShow.getContext())
.load(mBitmap)
.into(ivShow);
}
}
@Override
void afterRequestPermission(int requestCode, boolean isAllGranted) {
if (requestCode == ACTION_REQUEST_PERMISSIONS) {
if (isAllGranted) {
initEngine();
} else {
showToast(getString(R.string.permission_denied));
}
}
}
}
<resources>
<!--app名称-->
<string name="app_name">ArcFaceDemo</string>
<!--配置、功能选择界面-->
<string name="page_preview">人脸属性检测(视频)</string>
<string name="page_ir_preview">红外活体检测(视频)</string>
<string name="page_face_recognize">人脸比对1:n(视频vs人脸库,RGB活体)</string>
<string name="page_ir_face_recognize">人脸比对1:n(视频vs人脸库,IR活体)</string>
<string name="page_single_image">人脸属性检测(图片)</string>
<string name="page_multi_image">人脸比对1:n(图片vs图片)</string>
<string name="page_face_manage">人脸批量注册管理</string>
<string name="get_device_finger_failed">获取设备指纹失败,错误码:%d</string>
<string name="choose_detect_degree">选择视频模式检测角度</string>
<string name="copy_device_fingerprint">复制设备指纹信息</string>
<string name="device_fingerprint_copied">设备指纹信息:\n%s\n已复制</string>
<string name="active_engine">激活引擎</string>
<string name="active_success">激活引擎成功</string>
<string name="already_activated">引擎已激活,无需再次激活</string>
<string name="active_failed">引擎激活失败,错误码为 %d</string>
<string name="library_not_found">未找到库文件,请检查是否有将.so文件放至工程的 app\\src\\main\\jniLibs 目录下</string>
<string name="ft_op_0">视频模式仅检测0度</string>
<string name="ft_op_90">视频模式仅检测90度</string>
<string name="ft_op_180">视频模式仅检测180度</string>
<string name="ft_op_270">视频模式仅检测270度</string>
<string name="ft_op_all">视频模式全方向人脸检测</string>
<!--人脸属性检测(图片) 界面-->
<string name="start_process">开始处理</string>
<string name="choose_local_image">选择本地图片</string>
<string name="processing">处理中</string>
<!--人脸比对1:n(图片vs图片) 界面-->
<string name="choose_main_image">选择主图</string>
<string name="add_item_image">添加比对图</string>
<string name="notice_choose_main_img">请先选择主图</string>
<string name="compare_failed">比对失败,错误码为 %d</string>
<!--各个界面获取本地图片失败提示-->
<string name="get_picture_failed">获取图片失败</string>
<!--各个界面获取权限失败时的提示-->
<string name="permission_denied">权限被拒绝!</string>
<!--各个界面引擎初始化失败的提示-->
<string name="init_failed">引擎初始化失败,错误码为 %d</string>
<string name="engine_not_initialized">引擎未初始化,错误码为 %d</string>
<!--单目、双目识别注册界面-->
<string name="register">注册</string>
<string name="switch_camera">切换相机</string>
<string name="switch_camera_failed">切换相机失败</string>
<string name="recognize_failed_notice">未通过:%s</string>
<string name="recognize_success_notice">通过:%s</string>
<string name="low_confidence_level">人脸置信度低</string>
<string name="specific_engine_init_failed">%s 初始化失败,错误码为:%d</string>
<string name="notice_change_detect_degree">相机已切换,若无法检测到人脸,需要在首页修改视频模式人脸检测角度</string>
<string name="liveness_detect">活体检测</string>
<string name="camera_rgb">RGB CAMERA</string>
<string name="camera_ir">IR CAMERA</string>
<string name="camera_rgb_preview_size">RGB CAMERA\n%dx%d</string>
<string name="camera_ir_preview_size">IR CAMERA\n%dx%d</string>
<string name="camera_error_notice">\n可能的原因:该设备不支持同时打开两个摄像头</string>
<string name="draw_ir_rect_mirror_horizontal">IR人脸框水平镜像绘制</string>
<string name="draw_ir_rect_mirror_vertical">IR人脸框垂直镜像绘制</string>
<!--人脸批量注册进度框-->
<string name="progress_dialog_batch_register">进度: %d / %d</string>
<string name="progress_dialog_registering_please_wait">注册中,请稍等</string>
<!--批量处理-->
<string name="ok">确认</string>
<string name="cancel">取消</string>
<string name="batch_process_notification_register">请将需要注册的图片放在\nsdcard/arcfacedemo/register\n目录下</string>
<string name="batch_process_no_face_need_to_delete">无人脸需要删除</string>
<string name="batch_process_confirm_delete">确认删除这%d张图片及人脸特征?</string>
<string name="batch_process_batch_register">批量注册</string>
<string name="batch_process_notification">提示</string>
<string name="batch_process_clear_faces">清空人脸库</string>
<string name="batch_process_path_is_not_exists">路径 \n%s\n 不存在</string>
<string name="batch_process_path_is_not_dir">路径 \n%s\n 不是文件夹</string>
<string name="batch_process_processing_please_wait">处理中,请稍等</string>
<string name="batch_process_finished_info">处理完成!\n处理总数 = %d \n成功数 = %d \n失败数 = %d \n处理失败的图片已保存在文件夹 \' %s \'</string>
</resources>
<resources>
<!--app name-->
<!--app名称-->
<string name="app_name">ArcFaceDemo</string>
<!--settings、choose function page-->
<string name="page_preview">face attributes detect(video)</string>
<string name="page_ir_preview">ir liveness detect(video)</string>
<string name="page_face_recognize">face compare 1:n(video vs database,rgb liveness detect)</string>
<string name="page_ir_face_recognize">face compare 1:n(video vs database,ir liveness detect)</string>
<string name="page_single_image">face attributes detect(image)</string>
<string name="page_multi_image">face compare 1:n(image vs image)</string>
<string name="page_face_manage">face manage</string>
<string name="get_device_finger_failed">get device finger failed, code is: %d</string>
<string name="choose_detect_degree">choose video-mode detect degree</string>
<string name="copy_device_fingerprint">copy device fingerprint</string>
<string name="device_fingerprint_copied">device fingerprint::\n%s\n copied</string>
<string name="active_engine">active engine</string>
<string name="active_success">active success</string>
<string name="already_activated">already activated</string>
<string name="active_failed">active failed,code is %d</string>
<string name="library_not_found">library not found, please check if you put .so files into the project directory app\\src\\main\\jniLibs</string>
<string name="ft_op_0">only detect 0 degree in video mode</string>
<string name="ft_op_90">only detect 90 degree in video mode</string>
<string name="ft_op_180">only detect 180 degree in video mode</string>
<string name="ft_op_270">only detect 270 degree in video mode</string>
<string name="ft_op_all">detect all degrees in video mode</string>
<!--配置、功能选择界面-->
<string name="page_preview">人脸属性检测(视频)</string>
<string name="page_ir_preview">红外活体检测(视频)</string>
<string name="page_face_recognize">人脸比对1:n(视频vs人脸库,RGB活体)</string>
<string name="page_ir_face_recognize">人脸比对1:n(视频vs人脸库,IR活体)</string>
<string name="page_single_image">人脸属性检测(图片)</string>
<string name="page_multi_image">人脸比对1:n(图片vs图片)</string>
<string name="page_face_manage">人脸批量注册管理</string>
<string name="get_device_finger_failed">获取设备指纹失败,错误码:%d</string>
<string name="choose_detect_degree">选择视频模式检测角度</string>
<string name="copy_device_fingerprint">复制设备指纹信息</string>
<string name="device_fingerprint_copied">设备指纹信息:\n%s\n已复制</string>
<string name="active_engine">激活引擎</string>
<string name="active_success">激活引擎成功</string>
<string name="already_activated">引擎已激活,无需再次激活</string>
<string name="active_failed">引擎激活失败,错误码为 %d</string>
<string name="library_not_found">未找到库文件,请检查是否有将.so文件放至工程的 app\\src\\main\\jniLibs 目录下</string>
<string name="ft_op_0">视频模式仅检测0度</string>
<string name="ft_op_90">视频模式仅检测90度</string>
<string name="ft_op_180">视频模式仅检测180度</string>
<string name="ft_op_270">视频模式仅检测270度</string>
<string name="ft_op_all">视频模式全方向人脸检测</string>
<!--face attributes detect(image) page-->
<string name="start_process">process</string>
<string name="choose_local_image">choose local image</string>
<string name="processing">processing</string>
<!--人脸属性检测(图片) 界面-->
<string name="start_process">开始处理</string>
<string name="choose_local_image">选择本地图片</string>
<string name="processing">处理中</string>
<!--face compare 1:n(image vs image) page-->
<string name="choose_main_image">choose main image</string>
<string name="add_item_image">add item image</string>
<string name="notice_choose_main_img">please choose main image first</string>
<string name="compare_failed">compare failed!code is %d</string>
<!--人脸比对1:n(图片vs图片) 界面-->
<string name="choose_main_image">选择主图</string>
<string name="add_item_image">添加比对图</string>
<string name="notice_choose_main_img">请先选择主图</string>
<string name="compare_failed">比对失败,错误码为 %d</string>
<!--global get picture failed notification-->
<string name="get_picture_failed">failed to get picture!</string>
<!--各个界面获取本地图片失败提示-->
<string name="get_picture_failed">获取图片失败</string>
<!--global permission denied notification-->
<string name="permission_denied">permission denied!</string>
<!--各个界面获取权限失败时的提示-->
<string name="permission_denied">权限被拒绝!</string>
<!--global init failed notification-->
<string name="init_failed">init failed,code is %d</string>
<string name="engine_not_initialized">engine not initialized! code is %d</string>
<!--各个界面引擎初始化失败的提示-->
<string name="init_failed">引擎初始化失败,错误码为 %d</string>
<string name="engine_not_initialized">引擎未初始化,错误码为 %d</string>
<!--single/dual camera register&recognize page-->
<string name="register">register</string>
<string name="switch_camera">switch camera</string>
<string name="switch_camera_failed">switch camera failed</string>
<string name="notice_change_detect_degree">camera switched, if no face detected, please change face detect degree in homepage</string>
<string name="recognize_failed_notice">DENIED:%s</string>
<string name="recognize_success_notice">PASS:%s</string>
<string name="low_confidence_level">face low confidence level</string>
<string name="specific_engine_init_failed">%s init failed, code is:%d</string>
<string name="liveness_detect">liveness detect</string>
<!--单目、双目识别注册界面-->
<string name="register">注册</string>
<string name="switch_camera">切换相机</string>
<string name="switch_camera_failed">切换相机失败</string>
<string name="recognize_failed_notice">未通过:%s</string>
<string name="recognize_success_notice">通过:%s</string>
<string name="low_confidence_level">人脸置信度低</string>
<string name="specific_engine_init_failed">%s 初始化失败,错误码为:%d</string>
<string name="notice_change_detect_degree">相机已切换,若无法检测到人脸,需要在首页修改视频模式人脸检测角度</string>
<string name="liveness_detect">活体检测</string>
<string name="camera_rgb">RGB CAMERA</string>
<string name="camera_ir">IR CAMERA</string>
<string name="camera_rgb_preview_size">RGB CAMERA\n%dx%d</string>
<string name="camera_ir_preview_size">IR CAMERA\n%dx%d</string>
<string name="camera_error_notice">\npossible reason:open two cameras at the same time is not allowed on this device</string>
<string name="draw_ir_rect_mirror_horizontal">draw IR face rect mirror horizontal</string>
<string name="draw_ir_rect_mirror_vertical">draw IR face rect mirror vertical</string>
<string name="camera_error_notice">\n可能的原因:该设备不支持同时打开两个摄像头</string>
<string name="draw_ir_rect_mirror_horizontal">IR人脸框水平镜像绘制</string>
<string name="draw_ir_rect_mirror_vertical">IR人脸框垂直镜像绘制</string>
<!--batch process dialog-->
<string name="progress_dialog_batch_register">progress: %d / %d</string>
<string name="progress_dialog_registering_please_wait">registering,please wait</string>
<!--人脸批量注册进度框-->
<string name="progress_dialog_batch_register">进度: %d / %d</string>
<string name="progress_dialog_registering_please_wait">注册中,请稍等</string>
<!--batch process-->
<string name="ok">OK</string>
<string name="cancel">Cancel</string>
<string name="batch_process_notification_register">please put the images that need to register into directory \nsdcard/arcfacedemo/register</string>
<string name="batch_process_no_face_need_to_delete">no face need to delete</string>
<string name="batch_process_confirm_delete">delete those %d images and face features?</string>
<string name="batch_process_batch_register">batch register</string>
<string name="batch_process_notification">notification</string>
<string name="batch_process_clear_faces">clear faces</string>
<string name="batch_process_path_is_not_exists">path \n%s\n is not exists</string>
<string name="batch_process_path_is_not_dir">path \n%s\n is not a directory</string>
<string name="batch_process_processing_please_wait">process start,please wait</string>
<string name="batch_process_finished_info">process finished!\ntotal count = %d \nsuccess count = %d \nfailed count = %d \nfailed images are in directory \' %s \'</string>
<!--批量处理-->
<string name="ok">确认</string>
<string name="cancel">取消</string>
<string name="batch_process_notification_register">请将需要注册的图片放在\nsdcard/arcfacedemo/register\n目录下</string>
<string name="batch_process_no_face_need_to_delete">无人脸需要删除</string>
<string name="batch_process_confirm_delete">确认删除这%d张图片及人脸特征?</string>
<string name="batch_process_batch_register">批量注册</string>
<string name="batch_process_notification">提示</string>
<string name="batch_process_clear_faces">清空人脸库</string>
<string name="batch_process_path_is_not_exists">路径 \n%s\n 不存在</string>
<string name="batch_process_path_is_not_dir">路径 \n%s\n 不是文件夹</string>
<string name="batch_process_processing_please_wait">处理中,请稍等</string>
<string name="batch_process_finished_info">处理完成!\n处理总数 = %d \n成功数 = %d \n失败数 = %d \n处理失败的图片已保存在文件夹 \' %s \'</string>
... ...
package com.arcsoft.arcfacedemo;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() {
assertEquals(4, 2 + 2);
}
}
\ No newline at end of file
... ... @@ -43,5 +43,5 @@ android {
dependencies {
api fileTree(include: ['*.jar'], dir: 'libs')
implementation 'org.jetbrains:annotations-java5:15.0'
implementation 'org.jetbrains:annotations:13.0'
}
... ...
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.baidu.idl.main.facesdk">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application android:allowBackup="false"
android:label="@string/app_name"
android:supportsRtl="true">
... ...
/*
* Copyright (C) 2018 Baidu, Inc. All Rights Reserved.
*/
package com.baidu.idl.main.facesdk.api;
import android.graphics.Bitmap;
import android.text.TextUtils;
import com.baidu.idl.main.facesdk.FaceInfo;
import com.baidu.idl.main.facesdk.db.DBManager;
import com.baidu.idl.main.facesdk.manager.FaceSDKManager;
import com.baidu.idl.main.facesdk.model.BDFaceImageInstance;
import com.baidu.idl.main.facesdk.model.BDFaceSDKCommon;
import com.baidu.idl.main.facesdk.model.Feature;
import com.baidu.idl.main.facesdk.model.Group;
import com.baidu.idl.main.facesdk.model.ResponseGetRecords;
import com.baidu.idl.main.facesdk.model.User;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class FaceApi {
private static FaceApi instance;
private ExecutorService es = Executors.newSingleThreadExecutor();
private Future future;
private int mUserNum;
private boolean isinitSuccess = false;
private FaceApi() {
}
public static synchronized FaceApi getInstance() {
if (instance == null) {
instance = new FaceApi();
}
return instance;
}
/**
* 添加用户组
*/
public boolean groupAdd(Group group) {
if (group == null || TextUtils.isEmpty(group.getGroupId())) {
return false;
}
Pattern pattern = Pattern.compile("^[0-9a-zA-Z_-]{1,}$");
Matcher matcher = pattern.matcher(group.getGroupId());
if (!matcher.matches()) {
return false;
}
boolean ret = DBManager.getInstance().addGroup(group);
return ret;
}
/**
* 查询用户组(默认最多取1000个组)
*/
public List<Group> getGroupList(int start, int length) {
if (start < 0 || length < 0) {
return null;
}
if (length > 1000) {
length = 1000;
}
List<Group> groupList = DBManager.getInstance().queryGroups(start, length);
return groupList;
}
/**
* 根据groupId查询用户组
*/
public List<Group> getGroupListByGroupId(String groupId) {
if (TextUtils.isEmpty(groupId)) {
return null;
}
return DBManager.getInstance().queryGroupsByGroupId(groupId);
}
/**
* 根据groupId删除用户组
*/
public boolean groupDelete(String groupId) {
if (TextUtils.isEmpty(groupId)) {
return false;
}
boolean ret = DBManager.getInstance().deleteGroup(groupId);
return ret;
}
/**
* 添加用户
*/
public boolean userAdd(User user) {
if (user == null || TextUtils.isEmpty(user.getGroupId())) {
return false;
}
Pattern pattern = Pattern.compile("^[0-9a-zA-Z_-]{1,}$");
Matcher matcher = pattern.matcher(user.getUserId());
if (!matcher.matches()) {
return false;
}
boolean ret = DBManager.getInstance().addUser(user);
return ret;
}
/**
* 根据groupId查找用户
*/
public List<User> getUserList(String groupId) {
if (TextUtils.isEmpty(groupId)) {
return null;
}
List<User> userList = DBManager.getInstance().queryUserByGroupId(groupId);
return userList;
}
/**
* 根据groupId、userName查找用户
*/
public List<User> getUserListByUserName(String groupId, String userName) {
if (TextUtils.isEmpty(groupId) || TextUtils.isEmpty(userName)) {
return null;
}
List<User> userList = DBManager.getInstance().queryUserByUserName(groupId, userName);
return userList;
}
/**
* 根据_id查找用户
*/
public User getUserListById(int _id) {
if (_id < 0) {
return null;
}
List<User> userList = DBManager.getInstance().queryUserById(_id);
if (userList != null && userList.size() > 0) {
return userList.get(0);
}
return null;
}
/**
* 更新用户
*/
public boolean userUpdate(User user) {
if (user == null) {
return false;
}
boolean ret = DBManager.getInstance().updateUser(user);
return ret;
}
/**
* 更新用户
*/
public boolean userUpdate(String groupId, String userName, String imageName, byte[] feature) {
if (groupId == null || userName == null || imageName == null || feature == null) {
return false;
}
boolean ret = DBManager.getInstance().updateUser(groupId, userName, imageName, feature);
return ret;
}
/**
* 删除用户
*/
public boolean userDelete(String userId, String groupId) {
if (TextUtils.isEmpty(userId) || TextUtils.isEmpty(groupId)) {
return false;
}
boolean ret = DBManager.getInstance().deleteUser(userId, groupId);
return ret;
}
/**
* 远程删除用户
*/
public boolean userDeleteByName(String userName, String groupId) {
if (TextUtils.isEmpty(userName) || TextUtils.isEmpty(groupId)) {
return false;
}
boolean ret = DBManager.getInstance().userDeleteByName(userName, groupId);
return ret;
}
/**
* 是否是有效姓名
*
* @param username 用户名
* @return 有效或无效信息
*/
public String isValidName(String username) {
if (username == null || "".equals(username.trim())) {
return "姓名为空";
}
// 姓名过长
if (username.length() > 10) {
return "姓名过长";
}
// 含有特殊符号
String regex = "[ _`~!@#$%^&*()+=|{}':;',\\[\\].<>/?~!@#¥%……&*()—"
+ "—+|{}【】‘;:”“’。,、?]|\n|\r|\t";
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(username);
if (m.find()) {
return "姓名中含有特殊符号";
}
return "0";
}
/**
* 提取特征值
*/
public float getFeature(Bitmap bitmap, byte[] feature, BDFaceSDKCommon.FeatureType featureType) {
if (bitmap == null) {
return -1;
}
BDFaceImageInstance imageInstance = new BDFaceImageInstance(bitmap);
// 最大检测人脸,获取人脸信息
FaceInfo[] faceInfos = FaceSDKManager.getInstance().getFaceDetect()
.detect(BDFaceSDKCommon.DetectType.DETECT_VIS, imageInstance);
float ret = -1;
if (faceInfos != null && faceInfos.length > 0) {
FaceInfo faceInfo = faceInfos[0];
// 人脸识别,提取人脸特征值
ret = FaceSDKManager.getInstance().getFaceFeature().feature(
featureType, imageInstance,
faceInfo.landmarks, feature);
}
imageInstance.destory();
return ret;
}
public boolean registerUserIntoDBmanager(String groupName, String userName, String picName,
String userInfo, byte[] faceFeature) {
boolean isSuccess = false;
Group group = new Group();
group.setGroupId(groupName);
User user = new User();
user.setGroupId(groupName);
/*
* 用户id(由数字、字母、下划线组成),长度限制128B
* uid为用户的id,百度对uid不做限制和处理,应该与您的帐号系统中的用户id对应。
*/
final String uid = UUID.randomUUID().toString();
user.setUserId(uid);
user.setUserName(userName);
user.setFeature(faceFeature);
user.setImageName(picName);
if (userInfo != null) {
user.setUserInfo(userInfo);
}
// 添加用户信息到数据库
boolean importUserSuccess = FaceApi.getInstance().userAdd(user);
if (importUserSuccess) {
// 如果添加到数据库成功,则添加用户组信息到数据库
// 如果当前图片组名和上一张图片组名相同,则不添加数据库到组表
if (FaceApi.getInstance().groupAdd(group)) {
isSuccess = true;
} else {
isSuccess = false;
}
} else {
isSuccess = false;
}
return isSuccess;
}
/**
* 获取底库数量
*
* @return
*/
public int getmUserNum() {
return mUserNum;
}
public boolean isinitSuccess() {
return isinitSuccess;
}
/**
* 数据库发现变化时候,重新把数据库中的人脸信息添加到内存中,id+feature
*/
public void initDatabases(final boolean isFeaturePush) {
if (future != null && !future.isDone()) {
future.cancel(true);
}
isinitSuccess = false;
future = es.submit(new Runnable() {
@Override
public void run() {
List<Group> listGroup = FaceApi.getInstance().getGroupList(0, 100);
if (listGroup != null && listGroup.size() > 0) {
ArrayList<Feature> features = new ArrayList<>();
for (int i = 0; i < listGroup.size(); i++) {
List<User> listUser = FaceApi.getInstance().getUserList(listGroup.get(i).getGroupId());
for (int j = 0; j < listUser.size(); j++) {
Feature feature = new Feature();
feature.setId(listUser.get(j).getId());
feature.setFeature(listUser.get(j).getFeature());
features.add(feature);
}
}
if (isFeaturePush) {
FaceSDKManager.getInstance().getFaceFeature().featurePush(features);
}
mUserNum = features.size();
}
isinitSuccess = true;
}
});
}
// 查询识别记录
public List<ResponseGetRecords> getRecords(String startTime, String endTime) {
// if (TextUtils.isEmpty(startTime) || TextUtils.isEmpty(endTime)) {
// return null;
// }
List<ResponseGetRecords> responseGetRecords = DBManager.getInstance().queryRecords(startTime, endTime);
if (responseGetRecords != null && responseGetRecords.size() > 0) {
return responseGetRecords;
}
return null;
}
// 添加识别记录
public boolean addRecords(ResponseGetRecords responseGetRecords) {
boolean ret = false;
if (responseGetRecords == null) {
return ret;
}
ret = DBManager.getInstance().addResponseGetRecords(responseGetRecords);
return ret;
}
// 删除识别记录
public boolean deleteRecords(String userName) {
boolean ret = false;
if (TextUtils.isEmpty(userName)) {
return ret;
}
ret = DBManager.getInstance().deleteRecords(userName);
return ret;
}
// 删除识别记录
public boolean deleteRecords(String startTime, String endTime) {
boolean ret = false;
if (TextUtils.isEmpty(startTime) && TextUtils.isEmpty(endTime)) {
return ret;
}
ret = DBManager.getInstance().deleteRecords(startTime, endTime);
return ret;
}
// 清除识别记录
public int cleanRecords() {
boolean ret = false;
int num = DBManager.getInstance().cleanRecords();
return num;
}
}
... ...
package com.baidu.idl.main.facesdk.callback;
import android.hardware.Camera;
/**
* Time: 2019/1/25
* Author: v_chaixiaogang
* Description: camera1数据结果回调
*/
public interface CameraDataCallback {
/**
* @param data 预览数据
* @param camera 相机设备
* @param width 预览宽
* @param height 预览高
*/
void onGetCameraData(byte[] data, Camera camera, int width, int height);
}
... ...
/*
* Copyright (C) 2018 Baidu, Inc. All Rights Reserved.
*/
package com.baidu.idl.main.facesdk.callback;
import com.baidu.idl.main.facesdk.model.LivenessModel;
/**
* 人脸检测回调接口。
*
* @Time: 2019/1/25
* @Author: v_chaixiaogang
*/
public interface FaceDetectCallBack {
void onFaceDetectCallback(LivenessModel livenessModel);
void onTip(int code, String msg);
void onFaceDetectDarwCallback(LivenessModel livenessModel);
}
... ...
/*
* Copyright (C) 2019 Baidu, Inc. All Rights Reserved.
*/
package com.baidu.idl.main.facesdk.callback;
/**
* 人脸特征抽取回调接口。
*
* @Time: 2019/5/30
* @Author: v_zhangxiaoqing01
*/
public interface FaceFeatureCallBack {
public void onFaceFeatureCallBack(float featureSize, byte[] feature);
}
... ...
/*
* Copyright (C) 2017 Baidu, Inc. All Rights Reserved.
*/
package com.baidu.idl.main.facesdk.camera;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Path;
import android.graphics.Region;
import android.os.Handler;
import android.os.Looper;
import android.util.AttributeSet;
import android.view.TextureView;
import android.widget.FrameLayout;
import com.baidu.idl.main.facesdk.model.SingleBaseConfig;
/**
* 基于 系统TextureView实现的预览View。
*
* @Time: 2019/1/28
* @Author: v_chaixiaogang
*/
public class AutoTexturePreviewView extends FrameLayout {
public TextureView textureView;
private int videoWidth = 0;
private int videoHeight = 0;
private int previewWidth = 0;
private int previewHeight = 0;
private static int scale = 2;
public static float circleRadius;
public static float circleX;
public static float circleY;
private float[] pointXY = new float[3];
public AutoTexturePreviewView(Context context) {
super(context);
init();
}
public AutoTexturePreviewView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public AutoTexturePreviewView(Context context, AttributeSet attrs,
int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private Handler handler = new Handler(Looper.getMainLooper());
private void init() {
setWillNotDraw(false);
textureView = new TextureView(getContext());
addView(textureView);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
previewWidth = getWidth();
previewHeight = getHeight();
if (videoWidth == 0 || videoHeight == 0 || previewWidth == 0 || previewHeight == 0) {
return;
}
if (previewWidth * videoHeight > previewHeight * videoWidth) {
int scaledChildHeight = videoHeight * previewWidth / videoWidth;
textureView.layout(0, (previewHeight - scaledChildHeight) / scale,
previewWidth, (previewHeight + scaledChildHeight) / scale);
} else {
int scaledChildWidth = videoWidth * previewHeight / videoHeight;
textureView.layout((previewWidth - scaledChildWidth) / scale, 0,
(previewWidth + scaledChildWidth) / scale, previewHeight);
}
}
public TextureView getTextureView() {
return textureView;
}
public int getPreviewWidth() {
return previewWidth;
}
public int getPreviewHeight() {
return previewHeight;
}
public void setPreviewSize(int width, int height) {
if (this.videoWidth == width && this.videoHeight == height) {
return;
}
this.videoWidth = width;
this.videoHeight = height;
handler.post(new Runnable() {
@Override
public void run() {
requestLayout();
}
});
}
@Override
protected void onDraw(Canvas canvas) {
String displayType = SingleBaseConfig.getBaseConfig().getDetectFrame();
if (displayType.equals("fixedarea")) {
Path path = new Path();
// 设置裁剪的圆心坐标,半径
path.addCircle(getWidth() / 2, getHeight() / 2, getWidth() / 3, Path.Direction.CCW);
// 裁剪画布,并设置其填充方式
canvas.clipPath(path, Region.Op.REPLACE);
circleRadius = getWidth() / 3;
circleX = (getRight() - getLeft()) / 2;
circleY = (getBottom() - getTop()) / 2;
}
super.onDraw(canvas);
}
}
... ...
package com.baidu.idl.main.facesdk.camera;
import android.content.Context;
import android.graphics.SurfaceTexture;
import android.hardware.Camera;
import android.util.Log;
import android.util.SparseIntArray;
import android.view.Surface;
import android.view.TextureView;
import com.baidu.idl.main.facesdk.callback.CameraDataCallback;
import com.baidu.idl.main.facesdk.model.SingleBaseConfig;
import java.util.List;
/**
* Time: 2019/1/24
* Author: v_chaixiaogang
* Description:
*/
public class CameraPreviewManager implements TextureView.SurfaceTextureListener {
private static final String TAG = "camera_preview";
AutoTexturePreviewView mTextureView;
boolean mPreviewed = false;
private boolean mSurfaceCreated = false;
private SurfaceTexture mSurfaceTexture;
public static final int CAMERA_FACING_BACK = 0;
public static final int CAMERA_FACING_FRONT = 1;
public static final int CAMERA_USB = 2;
public static final int CAMERA_ORBBEC = 3;
/**
* 垂直方向
*/
public static final int ORIENTATION_PORTRAIT = 0;
/**
* 水平方向
*/
public static final int ORIENTATION_HORIZONTAL = 1;
/**
* 当前相机的ID。
*/
private int cameraFacing = CAMERA_FACING_FRONT;
private int previewWidth;
private int previewHeight;
private int videoWidth;
private int videoHeight;
private int tempWidth;
private int tempHeight;
private int textureWidth;
private int textureHeight;
private Camera mCamera;
private int mCameraNum;
private int displayOrientation = 0;
private int cameraId = 0;
private int mirror = 1; // 镜像处理
private CameraDataCallback mCameraDataCallback;
private static volatile CameraPreviewManager instance = null;
private static final SparseIntArray ORIENTATIONS = new SparseIntArray();
static {
ORIENTATIONS.append(Surface.ROTATION_0, 0);
ORIENTATIONS.append(Surface.ROTATION_90, 90);
ORIENTATIONS.append(Surface.ROTATION_180, 180);
ORIENTATIONS.append(Surface.ROTATION_270, 270);
}
public static CameraPreviewManager getInstance() {
synchronized (CameraPreviewManager.class) {
if (instance == null) {
instance = new CameraPreviewManager();
}
}
return instance;
}
public int getCameraFacing() {
return cameraFacing;
}
public void setCameraFacing(int cameraFacing) {
this.cameraFacing = cameraFacing;
}
public int getDisplayOrientation() {
return displayOrientation;
}
public void setDisplayOrientation(int displayOrientation) {
this.displayOrientation = displayOrientation;
}
/**
* 开启预览
*
* @param context
* @param textureView
*/
public void startPreview(Context context, AutoTexturePreviewView textureView, int width,
int height, CameraDataCallback cameraDataCallback) {
Log.e(TAG, "开启预览模式");
Context mContext = context;
this.mCameraDataCallback = cameraDataCallback;
mTextureView = textureView;
this.previewWidth = width;
this.previewHeight = height;
mSurfaceTexture = mTextureView.getTextureView().getSurfaceTexture();
mTextureView.getTextureView().setSurfaceTextureListener(this);
}
@Override
public void onSurfaceTextureAvailable(SurfaceTexture texture, int i, int i1) {
Log.e(TAG, "--surfaceTexture--SurfaceTextureAvailable");
mSurfaceTexture = texture;
mSurfaceCreated = true;
textureWidth = i;
textureHeight = i1;
openCamera();
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture texture, int i, int i1) {
Log.e(TAG, "--surfaceTexture--TextureSizeChanged");
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture texture) {
Log.e(TAG, "--surfaceTexture--destroyed");
mSurfaceCreated = false;
if (mCamera != null) {
mCamera.setPreviewCallback(null);
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}
return true;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture texture) {
// Log.e(TAG, "--surfaceTexture--Updated");
}
/**
* 关闭预览
*/
public void stopPreview() {
if (mCamera != null) {
try {
mCamera.setPreviewTexture(null);
mSurfaceCreated = false;
mTextureView = null;
mCamera.setPreviewCallback(null);
mCamera.stopPreview();
mCamera.release();
mCamera = null;
} catch (Exception e) {
Log.e("qing", "camera destory error");
e.printStackTrace();
}
}
}
/**
* 开启摄像头
*/
private void openCamera() {
try {
if (mCamera == null) {
Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
for (int i = 0; i < Camera.getNumberOfCameras(); i++) {
Camera.getCameraInfo(i, cameraInfo);
if (cameraInfo.facing == cameraFacing) {
cameraId = i;
}
}
mCamera = Camera.open(cameraId);
Log.e(TAG, "initCamera---open camera");
}
// 摄像头图像预览角度
int cameraRotation = SingleBaseConfig.getBaseConfig().getVideoDirection();
switch (cameraFacing) {
case CAMERA_FACING_FRONT: {
// cameraRotation = ORIENTATIONS.get(displayOrientation);
// cameraRotation = getCameraDisplayOrientation(cameraRotation, cameraId);
mCamera.setDisplayOrientation(cameraRotation);
break;
}
case CAMERA_FACING_BACK: {
// cameraRotation = ORIENTATIONS.get(displayOrientation);
// cameraRotation = getCameraDisplayOrientation(cameraRotation, cameraId);
mCamera.setDisplayOrientation(cameraRotation);
break;
}
case CAMERA_USB: {
mCamera.setDisplayOrientation(cameraRotation);
break;
}
default:
break;
}
if (cameraRotation == 90 || cameraRotation == 270) {
boolean isRgbRevert = SingleBaseConfig.getBaseConfig().getRgbRevert();
if (isRgbRevert) {
mTextureView.setRotationY(180);
} else {
mTextureView.setRotationY(0);
}
// 旋转90度或者270,需要调整宽高
mTextureView.setPreviewSize(previewHeight, previewWidth);
} else {
boolean isRgbRevert = SingleBaseConfig.getBaseConfig().getRgbRevert();
if (isRgbRevert) {
mTextureView.setRotationY(180);
} else {
mTextureView.setRotationY(0);
}
mTextureView.setPreviewSize(previewWidth, previewHeight);
}
Camera.Parameters params = mCamera.getParameters();
List<Camera.Size> sizeList = params.getSupportedPreviewSizes(); // 获取所有支持的camera尺寸
final Camera.Size optionSize = getOptimalPreviewSize(sizeList, previewWidth,
previewHeight); // 获取一个最为适配的camera.size
if (optionSize.width == previewWidth && optionSize.height == previewHeight) {
videoWidth = previewWidth;
videoHeight = previewHeight;
} else {
videoWidth = optionSize.width;
videoHeight = optionSize.height;
}
params.setPreviewSize(videoWidth, videoHeight);
mCamera.setParameters(params);
try {
mCamera.setPreviewTexture(mSurfaceTexture);
mCamera.setPreviewCallback(new Camera.PreviewCallback() {
@Override
public void onPreviewFrame(byte[] bytes, Camera camera) {
if (mCameraDataCallback != null) {
mCameraDataCallback.onGetCameraData(bytes, camera,
videoWidth, videoHeight);
}
}
});
mCamera.startPreview();
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG, e.getMessage());
}
} catch (RuntimeException e) {
Log.e(TAG, e.getMessage());
}
}
private int getCameraDisplayOrientation(int degrees, int cameraId) {
Camera.CameraInfo info = new Camera.CameraInfo();
Camera.getCameraInfo(cameraId, info);
int rotation = 0;
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
rotation = (info.orientation + degrees) % 360;
rotation = (360 - rotation) % 360; // compensate the mirror
} else { // back-facing
rotation = (info.orientation - degrees + 360) % 360;
}
return rotation;
}
/**
* 解决预览变形问题
*
* @param sizes
* @param w
* @param h
* @return
*/
private Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int w, int h) {
final double aspectTolerance = 0.1;
double targetRatio = (double) w / h;
if (sizes == null) {
return null;
}
Camera.Size optimalSize = null;
double minDiff = Double.MAX_VALUE;
int targetHeight = h;
// Try to find an size match aspect ratio and size
for (Camera.Size size : sizes) {
double ratio = (double) size.width / size.height;
if (Math.abs(ratio - targetRatio) > aspectTolerance) {
continue;
}
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
// Cannot find the one match the aspect ratio, ignore the requirement
if (optimalSize == null) {
minDiff = Double.MAX_VALUE;
for (Camera.Size size : sizes) {
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
}
return optimalSize;
}
}
... ...
/*
* Copyright (C) 2017 Baidu, Inc. All Rights Reserved.
*/
package com.baidu.idl.main.facesdk.camera;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.os.Handler;
import android.os.Looper;
import android.util.AttributeSet;
import android.view.TextureView;
import android.widget.FrameLayout;
public class PicoAutoTexturePreviewView extends FrameLayout {
public TextureView textureView;
private int videoWidth = 0;
private int videoHeight = 0;
private int previewWidth = 0;
private int previewHeight = 0;
private static int scale = 2;
private Paint mPaint;
private Rect mSrcRect;
private Rect mDstRect;
public PicoAutoTexturePreviewView(Context context) {
super(context);
init();
}
public PicoAutoTexturePreviewView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public PicoAutoTexturePreviewView(Context context, AttributeSet attrs,
int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private Handler handler = new Handler(Looper.getMainLooper());
private void init() {
textureView = new TextureView(getContext());
addView(textureView);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
mSrcRect = new Rect();
mDstRect = new Rect();
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
previewWidth = getWidth();
previewHeight = getHeight();
if (videoWidth == 0 || videoHeight == 0 || previewWidth == 0 || previewHeight == 0) {
return;
}
if (previewWidth * videoHeight > previewHeight * videoWidth) {
int scaledChildHeight = videoHeight * previewWidth / videoWidth;
textureView.layout(0, (previewHeight - scaledChildHeight) / scale,
previewWidth, (previewHeight + scaledChildHeight) / scale);
} else {
int scaledChildWidth = videoWidth * previewHeight / videoHeight;
textureView.layout((previewWidth - scaledChildWidth) / scale, 0,
(previewWidth + scaledChildWidth) / scale, previewHeight);
}
}
public TextureView getTextureView() {
return textureView;
}
public int getPreviewWidth() {
return previewWidth;
}
public int getPreviewHeight() {
return previewHeight;
}
public void setPreviewSize(int width, int height) {
if (this.videoWidth == width && this.videoHeight == height) {
return;
}
this.videoWidth = width;
this.videoHeight = height;
handler.post(new Runnable() {
@Override
public void run() {
requestLayout();
}
});
}
public void draw(Bitmap bm) {
Canvas canvas = textureView.lockCanvas();
if (canvas != null) {
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
mSrcRect.set(0, 0, bm.getWidth(), bm.getHeight());
mDstRect.set(0, 0, textureView.getWidth(), bm.getHeight() * textureView.getWidth() / bm.getWidth());
canvas.drawBitmap(bm, mSrcRect, mDstRect, mPaint);
}
textureView.unlockCanvasAndPost(canvas);
}
}
... ...
/*
* Copyright (C) 2018 Baidu, Inc. All Rights Reserved.
*/
package com.baidu.idl.main.facesdk.db;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
/**
* 数据库创建工具
*/
public class DBHelper extends SQLiteOpenHelper {
private static final String CREATE_TABLE_START_SQL = "CREATE TABLE IF NOT EXISTS ";
private static final String CREATE_TABLE_PRIMIRY_SQL = " integer primary key autoincrement,";
/**
* 数据库名称
*/
private static final String DB_NAME = "face.db";
/**
* 数据库版本
*/
private static final int VERSION = 1;
/** 人脸特征表 */
// public static final String TABLE_FEATURE = "feature";
/**
* 用户组表
*/
public static final String TABLE_USER_GROUP = "user_group";
/**
* 用户表
*/
public static final String TABLE_USER = "user";
/**
* 识别记录表
*/
public static final String TABLE_Records = "records";
public DBHelper(Context context) {
super(context, DB_NAME, null, VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
createTables(db);
}
@Override
public synchronized void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if (newVersion > oldVersion) {
// db.execSQL("DROP TABLE IF EXISTS " + TABLE_FEATURE);
db.execSQL("DROP TABLE IF EXISTS " + TABLE_USER_GROUP);
db.execSQL("DROP TABLE IF EXISTS " + TABLE_USER);
db.execSQL("DROP TABLE IF EXISTS " + TABLE_Records);
onCreate(db);
}
}
public synchronized void createTables(SQLiteDatabase db) {
if (db == null || db.isReadOnly()) {
db = getWritableDatabase();
}
// 创建人脸特征表的SQL语句
// StringBuffer featureSql = new StringBuffer();
// featureSql.append(CREATE_TABLE_START_SQL).append(TABLE_FEATURE).append(" ( ");
// featureSql.append(" _id").append(CREATE_TABLE_PRIMIRY_SQL);
// featureSql.append(" face_token").append(" varchar(128) default \"\" ,");
// featureSql.append(" group_id").append(" varchar(32) default \"\" ,");
// featureSql.append(" user_id").append(" varchar(32) default \"\" ,");
// featureSql.append(" feature").append(" blob ,");
// featureSql.append(" image_name").append(" varchar(64) default \"\" ,");
// featureSql.append(" ctime").append(" long ,");
// featureSql.append(" update_time").append(" long )");
// 创建用户组表的SQL语句
StringBuffer groupSql = new StringBuffer();
groupSql.append(CREATE_TABLE_START_SQL).append(TABLE_USER_GROUP).append(" ( ");
groupSql.append(" _id").append(CREATE_TABLE_PRIMIRY_SQL);
groupSql.append(" group_id").append(" varchar(32) default \"\" ,");
groupSql.append(" desc").append(" varchar(32) default \"\" ,");
groupSql.append(" ctime").append(" long ,");
groupSql.append(" update_time").append(" long )");
// 创建用户表的SQL语句
StringBuffer userSql = new StringBuffer();
userSql.append(CREATE_TABLE_START_SQL).append(TABLE_USER).append(" ( ");
userSql.append(" _id").append(CREATE_TABLE_PRIMIRY_SQL);
userSql.append(" user_id").append(" varchar(32) default \"\" ,");
userSql.append(" user_name").append(" varchar(32) default \"\" ,");
userSql.append(" user_info").append(" varchar(32) default \"\" ,");
userSql.append(" group_id").append(" varchar(32) default \"\" ,");
userSql.append(" face_token").append(" varchar(128) default \"\" ,");
userSql.append(" feature").append(" blob ,");
userSql.append(" image_name").append(" varchar(64) default \"\" ,");
userSql.append(" ctime").append(" long ,");
userSql.append(" update_time").append(" long )");
// 创建识别记录的SQL语句
StringBuffer recordSql = new StringBuffer();
recordSql.append(CREATE_TABLE_START_SQL).append(TABLE_Records).append(" ( ");
recordSql.append(" _id").append(CREATE_TABLE_PRIMIRY_SQL);
recordSql.append(" deviceid").append(" varchar(32) default \"\" ,");
recordSql.append(" user_id").append(" varchar(32) default \"\" ,");
recordSql.append(" user_name").append(" varchar(32) default \"\" ,");
recordSql.append(" group_id").append(" varchar(32) default \"\" ,");
recordSql.append(" face_token").append(" varchar(128) default \"\" ,");
recordSql.append(" time").append(" datetime ,");
recordSql.append(" records").append(" varchar(32) default \"\" ,");
recordSql.append(" longId").append(" varchar(32) default \"\" ,");
recordSql.append(" score").append(" varchar(32) default \"\" )");
try {
db.execSQL(groupSql.toString());
db.execSQL(userSql.toString());
db.execSQL(recordSql.toString());
// db.execSQL(featureSql.toString());
} catch (Exception e) {
e.printStackTrace();
}
}
}
... ...
package com.baidu.idl.main.facesdk.db;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.text.TextUtils;
import android.util.Log;
import com.baidu.idl.main.facesdk.model.Group;
import com.baidu.idl.main.facesdk.model.ResponseGetRecords;
import com.baidu.idl.main.facesdk.model.User;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 数据库管理类
*/
public class DBManager {
/**
* The constant TAG
*/
private static final String TAG = "DBManager";
private AtomicInteger mOpenCounter = new AtomicInteger();
private static DBManager instance;
private static SQLiteOpenHelper mDBHelper;
private SQLiteDatabase mDatabase;
private boolean allowTransaction = true;
private Lock writeLock = new ReentrantLock();
private volatile boolean writeLocked = false;
/**
* 单例模式,初始化DBManager
*
* @return DBManager实例
*/
public static synchronized DBManager getInstance() {
if (instance == null) {
instance = new DBManager();
}
return instance;
}
/**
* 数据库初始化
*
* @param context 当前上下文
*/
public void init(Context context) {
if (context == null) {
return;
}
if (mDBHelper == null) {
mDBHelper = new DBHelper(context.getApplicationContext());
}
}
/**
* 释放数据库
*/
public void release() {
if (mDBHelper != null) {
mDBHelper.close();
mDBHelper = null;
}
instance = null;
}
/**
* 打开数据库
*/
public synchronized SQLiteDatabase openDatabase() {
if (mOpenCounter.incrementAndGet() == 1) {
// Opening new database
try {
mDatabase = mDBHelper.getWritableDatabase();
} catch (Exception e) {
Log.e(TAG, "openDatabase e = " + e.getMessage());
mDatabase = mDBHelper.getReadableDatabase();
}
}
return mDatabase;
}
/**
* 关闭数据库
*/
public synchronized void closeDatabase() {
if (mOpenCounter.decrementAndGet() == 0) {
// Closing database
mDatabase.close();
}
}
// ---------------------------------------用户组相关 start--------------------------------------
/**
* 添加用户组
*/
public boolean addGroup(Group group) {
if (mDBHelper == null) {
return false;
}
Cursor cursor = null;
SQLiteDatabase db = mDBHelper.getReadableDatabase();
String where = "group_id = ? ";
String[] whereValue = {group.getGroupId()};
// 查询该groupId是否在数据库中,如果在,则不添加
cursor = db.query(DBHelper.TABLE_USER_GROUP, null, where, whereValue,
null, null, null);
if (cursor == null) {
return false;
}
if (cursor.getCount() > 0) {
return true;
}
mDatabase = mDBHelper.getWritableDatabase();
ContentValues cv = new ContentValues();
cv.put("group_id", group.getGroupId());
cv.put("desc", group.getDesc() == null ? "" : group.getDesc());
cv.put("update_time", System.currentTimeMillis());
cv.put("ctime", System.currentTimeMillis());
long rowId = -1;
try {
rowId = mDatabase.insert(DBHelper.TABLE_USER_GROUP, null, cv);
} catch (Exception e) {
Log.e(TAG, "addGroup e = " + e.getMessage());
e.printStackTrace();
}
if (rowId < 0) {
return false;
}
Log.i(TAG, "insert group success:" + rowId);
closeCursor(cursor);
return true;
}
/**
* 查询用户组
*/
public List<Group> queryGroups(int start, int offset) {
Cursor cursor = null;
List<Group> groupList = new ArrayList<>();
try {
if (mDBHelper == null) {
return null;
}
SQLiteDatabase db = mDBHelper.getReadableDatabase();
String limit = start + " , " + offset;
cursor = db.query(DBHelper.TABLE_USER_GROUP, null, null, null, null, null, null, limit);
while (cursor != null && cursor.getCount() > 0 && cursor.moveToNext()) {
int dbId = cursor.getInt(cursor.getColumnIndex("_id"));
String groupId = cursor.getString(cursor.getColumnIndex("group_id"));
String desc = cursor.getString(cursor.getColumnIndex("desc"));
long updateTime = cursor.getLong(cursor.getColumnIndex("update_time"));
long ctime = cursor.getLong(cursor.getColumnIndex("ctime"));
Group group = new Group();
group.setGroupId(groupId);
group.setDesc(desc);
group.setCtime(ctime);
groupList.add(group);
}
} finally {
closeCursor(cursor);
}
return groupList;
}
/**
* 查询用户组(根据groupId)
*/
public List<Group> queryGroupsByGroupId(String groupId) {
ArrayList<Group> groupList = new ArrayList<>();
Cursor cursor = null;
try {
if (mDBHelper == null) {
return groupList;
}
SQLiteDatabase db = mDBHelper.getReadableDatabase();
String where = "group_id = ? ";
String[] whereValue = {groupId};
cursor = db.query(DBHelper.TABLE_USER_GROUP, null, where, whereValue, null, null, null);
while (cursor != null && cursor.getCount() > 0 && cursor.moveToNext()) {
int dbId = cursor.getInt(cursor.getColumnIndex("_id"));
String desc = cursor.getString(cursor.getColumnIndex("desc"));
long updateTime = cursor.getLong(cursor.getColumnIndex("update_time"));
long ctime = cursor.getLong(cursor.getColumnIndex("ctime"));
Group group = new Group();
group.setGroupId(groupId);
group.setDesc(desc);
group.setCtime(ctime);
groupList.add(group);
}
} catch (Exception e) {
Log.e(TAG, "queryGroupsByGroupId e = " + e.getMessage());
} finally {
closeCursor(cursor);
}
return groupList;
}
/**
* 删除用户组
*/
public boolean deleteGroup(String groupId) {
boolean success = false;
try {
mDatabase = mDBHelper.getWritableDatabase();
beginTransaction(mDatabase);
if (!TextUtils.isEmpty(groupId)) {
String where = "group_id = ?";
String[] whereValue = {groupId};
if (mDatabase.delete(DBHelper.TABLE_USER, where, whereValue) < 0) {
return false;
}
if (mDatabase.delete(DBHelper.TABLE_USER_GROUP, where, whereValue) < 0) {
return false;
}
setTransactionSuccessful(mDatabase);
success = true;
}
} finally {
endTransaction(mDatabase);
}
return success;
}
// ---------------------------------------用户组相关 end----------------------------------------
// ---------------------------------------用户相关 start----------------------------------------
/**
* 添加用户
*/
public boolean addUser(User user) {
if (mDBHelper == null) {
return false;
}
try {
mDatabase = mDBHelper.getWritableDatabase();
beginTransaction(mDatabase);
ContentValues cv = new ContentValues();
cv.put("user_id", user.getUserId());
cv.put("user_name", user.getUserName());
cv.put("user_info", user.getUserInfo());
cv.put("group_id", user.getGroupId());
cv.put("face_token", user.getFaceToken());
cv.put("feature", user.getFeature());
cv.put("image_name", user.getImageName());
cv.put("ctime", System.currentTimeMillis());
cv.put("update_time", System.currentTimeMillis());
long rowId = mDatabase.insert(DBHelper.TABLE_USER, null, cv);
if (rowId < 0) {
return false;
}
setTransactionSuccessful(mDatabase);
Log.i(TAG, "insert user success:" + rowId);
} catch (Exception e) {
Log.e(TAG, "addUser e = " + e.getMessage());
return false;
} finally {
endTransaction(mDatabase);
}
return true;
}
/**
* 查询用户(根据groupId、userId)
*/
public User queryUser(String groupId, String userId) {
Cursor cursor = null;
try {
if (mDBHelper == null) {
return null;
}
SQLiteDatabase db = mDBHelper.getReadableDatabase();
String where = "user_id = ? and group_id = ? ";
String[] whereValue = {userId, groupId};
cursor = db.query(DBHelper.TABLE_USER, null, where, whereValue, null, null, null);
if (cursor != null && cursor.getCount() > 0 && cursor.moveToNext()) {
int dbId = cursor.getInt(cursor.getColumnIndex("_id"));
String userName = cursor.getString(cursor.getColumnIndex("user_name"));
long updateTime = cursor.getLong(cursor.getColumnIndex("update_time"));
long ctime = cursor.getLong(cursor.getColumnIndex("ctime"));
User user = new User();
user.setId(dbId);
user.setUserId(userId);
user.setGroupId(groupId);
user.setUserName(userName);
user.setCtime(ctime);
user.setUpdateTime(updateTime);
return user;
}
} finally {
closeCursor(cursor);
}
return null;
}
/**
* 查询用户(根据groupId)
*/
public List<User> queryUserByGroupId(String groupId) {
Cursor cursor = null;
List<User> users = new ArrayList<>();
try {
if (mDBHelper == null) {
return null;
}
SQLiteDatabase db = mDBHelper.getReadableDatabase();
String where = "group_id = ? order by ctime desc";
String[] whereValue = {groupId};
cursor = db.query(DBHelper.TABLE_USER, null, where, whereValue, null, null, null);
while (cursor != null && cursor.getCount() > 0 && cursor.moveToNext()) {
int dbId = cursor.getInt(cursor.getColumnIndex("_id"));
String userId = cursor.getString(cursor.getColumnIndex("user_id"));
String userName = cursor.getString(cursor.getColumnIndex("user_name"));
String userInfo = cursor.getString(cursor.getColumnIndex("user_info"));
String faceToken = cursor.getString(cursor.getColumnIndex("face_token"));
byte[] feature = cursor.getBlob(cursor.getColumnIndex("feature"));
String imageName = cursor.getString(cursor.getColumnIndex("image_name"));
long updateTime = cursor.getLong(cursor.getColumnIndex("update_time"));
long ctime = cursor.getLong(cursor.getColumnIndex("ctime"));
User user = new User();
user.setId(dbId);
user.setUserId(userId);
user.setGroupId(groupId);
user.setUserName(userName);
user.setCtime(ctime);
user.setUpdateTime(updateTime);
user.setUserInfo(userInfo);
user.setFaceToken(faceToken);
user.setFeature(feature);
user.setImageName(imageName);
users.add(user);
}
} finally {
closeCursor(cursor);
}
return users;
}
/**
* 查询用户(根据groupId、userName)
*/
public List<User> queryUserByUserName(String groupId, String userName) {
Cursor cursor = null;
List<User> users = new ArrayList<>();
try {
if (mDBHelper == null) {
return null;
}
SQLiteDatabase db = mDBHelper.getReadableDatabase();
String where = "user_name = ? and group_id = ? ";
String[] whereValue = {userName, groupId};
cursor = db.query(DBHelper.TABLE_USER, null, where, whereValue, null, null, null);
if (cursor != null && cursor.getCount() > 0 && cursor.moveToNext()) {
int dbId = cursor.getInt(cursor.getColumnIndex("_id"));
String userId = cursor.getString(cursor.getColumnIndex("user_id"));
String userInfo = cursor.getString(cursor.getColumnIndex("user_info"));
String faceToken = cursor.getString(cursor.getColumnIndex("face_token"));
byte[] feature = cursor.getBlob(cursor.getColumnIndex("feature"));
String imageName = cursor.getString(cursor.getColumnIndex("image_name"));
long updateTime = cursor.getLong(cursor.getColumnIndex("update_time"));
long ctime = cursor.getLong(cursor.getColumnIndex("ctime"));
User user = new User();
user.setId(dbId);
user.setUserId(userId);
user.setGroupId(groupId);
user.setUserName(userName);
user.setCtime(ctime);
user.setUpdateTime(updateTime);
user.setUserInfo(userInfo);
user.setFeature(feature);
user.setImageName(imageName);
user.setFaceToken(faceToken);
users.add(user);
}
} finally {
closeCursor(cursor);
}
return users;
}
/**
* 查询用户(根据dbId)
*/
public List<User> queryUserById(int _id) {
List<User> users = new ArrayList<>();
Cursor cursor = null;
try {
if (mDBHelper == null) {
return null;
}
SQLiteDatabase db = mDBHelper.getReadableDatabase();
String where = "_id = ? ";
String[] whereValue = {String.valueOf(_id)};
cursor = db.query(DBHelper.TABLE_USER, null, where, whereValue, null, null, null);
if (cursor != null && cursor.getCount() > 0 && cursor.moveToNext()) {
String groupId = cursor.getString(cursor.getColumnIndex("group_id"));
String userId = cursor.getString(cursor.getColumnIndex("user_id"));
String userName = cursor.getString(cursor.getColumnIndex("user_name"));
String userInfo = cursor.getString(cursor.getColumnIndex("user_info"));
String faceToken = cursor.getString(cursor.getColumnIndex("face_token"));
byte[] feature = cursor.getBlob(cursor.getColumnIndex("feature"));
String imageName = cursor.getString(cursor.getColumnIndex("image_name"));
long updateTime = cursor.getLong(cursor.getColumnIndex("update_time"));
long ctime = cursor.getLong(cursor.getColumnIndex("ctime"));
User user = new User();
user.setId(_id);
user.setUserId(userId);
user.setGroupId(groupId);
user.setUserName(userName);
user.setCtime(ctime);
user.setUpdateTime(updateTime);
user.setUserInfo(userInfo);
user.setFeature(feature);
user.setImageName(imageName);
user.setFaceToken(faceToken);
users.add(user);
}
} finally {
closeCursor(cursor);
}
return users;
}
/**
* 更新用户
*/
public boolean updateUser(User user) {
boolean success = false;
if (mDBHelper == null) {
return success;
}
try {
mDatabase = mDBHelper.getWritableDatabase();
beginTransaction(mDatabase);
if (user != null) {
mDatabase.beginTransaction();
String where = "user_id = ? and group_id = ?";
String[] whereValue = {user.getUserId(), user.getGroupId()};
ContentValues cv = new ContentValues();
cv.put("user_id", user.getUserId());
cv.put("user_name", user.getUserName());
cv.put("group_id", user.getGroupId());
cv.put("image_name", user.getImageName());
cv.put("update_time", System.currentTimeMillis());
if (mDatabase.update(DBHelper.TABLE_USER, cv, where, whereValue) < 0) {
return false;
}
}
setTransactionSuccessful(mDatabase);
success = true;
} finally {
endTransaction(mDatabase);
}
return success;
}
/**
* 更新用户
*/
public boolean updateUser(String groupId, String userName, String imageName, byte[] feature) {
if (mDBHelper == null) {
return false;
}
try {
mDatabase = mDBHelper.getWritableDatabase();
beginTransaction(mDatabase);
String where = "user_name = ? and group_id = ?";
String[] whereValue = {userName, groupId};
ContentValues cv = new ContentValues();
cv.put("user_name", userName);
cv.put("group_id", groupId);
cv.put("image_name", imageName);
cv.put("update_time", System.currentTimeMillis());
cv.put("feature", feature);
if (mDatabase.update(DBHelper.TABLE_USER, cv, where, whereValue) < 0) {
return false;
}
setTransactionSuccessful(mDatabase);
} finally {
endTransaction(mDatabase);
}
return true;
}
/**
* 删除用户
*/
public boolean deleteUser(String userId, String groupId) {
boolean success = false;
try {
mDatabase = mDBHelper.getWritableDatabase();
beginTransaction(mDatabase);
if (!TextUtils.isEmpty(userId) && !TextUtils.isEmpty(groupId)) {
String where = "user_id = ? and group_id = ?";
String[] whereValue = {userId, groupId};
if (mDatabase.delete(DBHelper.TABLE_USER, where, whereValue) < 0) {
return false;
}
setTransactionSuccessful(mDatabase);
success = true;
}
} finally {
endTransaction(mDatabase);
}
return success;
}
// ---------------------------------------用户相关 end------------------------------------------
private void beginTransaction(SQLiteDatabase mDatabase) {
if (allowTransaction) {
mDatabase.beginTransaction();
} else {
writeLock.lock();
writeLocked = true;
}
}
private void setTransactionSuccessful(SQLiteDatabase mDatabase) {
if (allowTransaction) {
mDatabase.setTransactionSuccessful();
}
}
private void endTransaction(SQLiteDatabase mDatabase) {
if (allowTransaction) {
mDatabase.endTransaction();
}
if (writeLocked) {
writeLock.unlock();
writeLocked = false;
}
}
private void closeCursor(Cursor cursor) {
if (cursor != null) {
try {
cursor.close();
} catch (Throwable e) {
Log.e(TAG, "closeCursor e = " + e.getMessage());
}
}
}
// 获取识别记录
public List<ResponseGetRecords> queryRecords(String startTime, String endTime) {
List<ResponseGetRecords> responseGetRecords = new ArrayList<>();
Cursor cursor = null;
try {
if (mDBHelper == null) {
return null;
}
SQLiteDatabase db = mDBHelper.getReadableDatabase();
if (!TextUtils.isEmpty(startTime) && !TextUtils.isEmpty(endTime)) {
String where = "time > ? and time < ? ";
String[] whereValue = {startTime, endTime};
cursor = db.query(DBHelper.TABLE_Records, null, where, whereValue, null, null, null);
} else {
String sql = "select * from " + DBHelper.TABLE_Records;
cursor = db.rawQuery(sql, null);
}
if (cursor != null && cursor.getCount() > 0) {
while (cursor.moveToNext()) {
int dbId = cursor.getInt(cursor.getColumnIndex("_id"));
String userId = cursor.getString(cursor.getColumnIndex("user_id"));
String records = cursor.getString(cursor.getColumnIndex("records"));
String longId = cursor.getString(cursor.getColumnIndex("longId"));
String score = cursor.getString(cursor.getColumnIndex("score"));
String deviceId = cursor.getString(cursor.getColumnIndex("deviceid"));
String time = cursor.getString(cursor.getColumnIndex("time"));
String faceToken = cursor.getString(cursor.getColumnIndex("face_token"));
String groupId = cursor.getString(cursor.getColumnIndex("group_id"));
String userName = cursor.getString(cursor.getColumnIndex("user_name"));
ResponseGetRecords responseGetRecord = new ResponseGetRecords();
responseGetRecord.setIndex(String.valueOf(dbId));
responseGetRecord.setUserId(userId);
responseGetRecord.setDeviceId(deviceId);
responseGetRecord.setRecords(records);
responseGetRecord.setLogId(longId);
responseGetRecord.setScore(score);
responseGetRecord.setTime(time);
responseGetRecord.setFaceToken("");
responseGetRecord.setGroupId(groupId);
responseGetRecord.setUserName(userName);
responseGetRecords.add(responseGetRecord);
}
}
} catch (Exception e) {
Log.e("shangtest", e.getMessage());
} finally {
closeCursor(cursor);
}
return responseGetRecords;
}
// 添加识别记录
public boolean addResponseGetRecords(ResponseGetRecords responseGetRecords) {
if (mDBHelper == null) {
return false;
}
try {
mDatabase = mDBHelper.getWritableDatabase();
beginTransaction(mDatabase);
ContentValues cv = new ContentValues();
cv.put("user_id", responseGetRecords.getUserId());
cv.put("records", responseGetRecords.getRecords());
cv.put("longId", responseGetRecords.getLogId());
cv.put("score", responseGetRecords.getScore());
cv.put("deviceid", responseGetRecords.getDeviceId());
cv.put("time", responseGetRecords.getTime());
cv.put("user_name", responseGetRecords.getUserName());
cv.put("group_id", responseGetRecords.getGroupId());
cv.put("face_token", responseGetRecords.getFaceToken());
long rowId = mDatabase.insert(DBHelper.TABLE_Records, null, cv);
if (rowId < 0) {
return false;
}
setTransactionSuccessful(mDatabase);
} catch (Exception e) {
return false;
} finally {
endTransaction(mDatabase);
}
return true;
}
// 根据时间删除识别记录
public boolean deleteRecords(String startTime, String endTime) {
boolean success = false;
try {
mDatabase = mDBHelper.getWritableDatabase();
beginTransaction(mDatabase);
if (!TextUtils.isEmpty(startTime) && !TextUtils.isEmpty(endTime)) {
String where = " time > ? and time < ?";
String[] whereValue = {startTime, endTime};
// String[] whereValue = {"2019-09-05 10:50:09", "2019-09-05 10:50:13"};
if (mDatabase.delete(DBHelper.TABLE_Records, where, whereValue) < 0) {
return false;
}
setTransactionSuccessful(mDatabase);
success = true;
}
} finally {
endTransaction(mDatabase);
}
return success;
}
// 根据userId删除识别记录
public boolean deleteRecords(String userName) {
boolean success = false;
try {
mDatabase = mDBHelper.getWritableDatabase();
beginTransaction(mDatabase);
if (!TextUtils.isEmpty(userName)) {
String where = "user_name = ?";
String[] whereValue = {userName};
if (mDatabase.delete(DBHelper.TABLE_Records, where, whereValue) < 0) {
return false;
}
setTransactionSuccessful(mDatabase);
success = true;
}
} finally {
endTransaction(mDatabase);
}
return success;
}
// 根据userId删除识别记录
public int cleanRecords() {
int number = getRecordsNum();
boolean success = false;
try {
mDatabase = mDBHelper.getWritableDatabase();
beginTransaction(mDatabase);
String where = "1 = 1";
String[] whereValue = {};
if (mDatabase.delete(DBHelper.TABLE_Records, where, whereValue) < 0) {
return 0;
}
// 自增恢复为0
String resoreIdSql = "update sqlite_sequence set seq=0 where name='" + DBHelper.TABLE_Records + "'";
mDatabase.execSQL(resoreIdSql);
setTransactionSuccessful(mDatabase);
success = true;
} finally {
endTransaction(mDatabase);
}
return number;
}
public int getRecordsNum() {
int number = 0;
{
Cursor cursor = null;
try {
if (mDBHelper == null) {
return 0;
}
SQLiteDatabase db = mDBHelper.getReadableDatabase();
cursor = db.rawQuery("select count(*) from " + DBHelper.TABLE_Records, null);
if (!cursor.moveToFirst()) {
number = 0;
} else {
number = (int) cursor.getLong(0);
}
} finally {
closeCursor(cursor);
}
return number;
}
}
/**
* 远程删除用户
*/
public boolean userDeleteByName(String userNmae, String groupId) {
boolean success = false;
try {
mDatabase = mDBHelper.getWritableDatabase();
beginTransaction(mDatabase);
if (!TextUtils.isEmpty(userNmae) && !TextUtils.isEmpty(groupId)) {
String where = "user_name = ? and group_id = ?";
String[] whereValue = {userNmae, groupId};
if (mDatabase.delete(DBHelper.TABLE_USER, where, whereValue) < 0) {
return false;
}
setTransactionSuccessful(mDatabase);
success = true;
}
} finally {
endTransaction(mDatabase);
}
return success;
}
}
\ No newline at end of file
... ...
package com.baidu.idl.main.facesdk.listener;
public interface SdkInitListener {
public void initStart();
public void initLicenseSuccess();
public void initLicenseFail(int errorCode, String msg);
public void initModelSuccess();
public void initModelFail(int errorCode, String msg);
}
... ...
package com.baidu.idl.main.facesdk.manager;
import android.content.Context;
import android.text.TextUtils;
import android.util.Log;
import com.baidu.idl.main.facesdk.FaceAuth;
import com.baidu.idl.main.facesdk.FaceDetect;
import com.baidu.idl.main.facesdk.FaceFeature;
import com.baidu.idl.main.facesdk.FaceInfo;
import com.baidu.idl.main.facesdk.FaceLive;
import com.baidu.idl.main.facesdk.FaceMouthMask;
import com.baidu.idl.main.facesdk.api.FaceApi;
import com.baidu.idl.main.facesdk.callback.Callback;
import com.baidu.idl.main.facesdk.callback.FaceDetectCallBack;
import com.baidu.idl.main.facesdk.callback.FaceFeatureCallBack;
import com.baidu.idl.main.facesdk.db.DBManager;
import com.baidu.idl.main.facesdk.listener.SdkInitListener;
import com.baidu.idl.main.facesdk.model.BDFaceDetectListConf;
import com.baidu.idl.main.facesdk.model.BDFaceImageInstance;
import com.baidu.idl.main.facesdk.model.BDFaceInstance;
import com.baidu.idl.main.facesdk.model.BDFaceOcclusion;
import com.baidu.idl.main.facesdk.model.BDFaceSDKCommon;
import com.baidu.idl.main.facesdk.model.BDFaceSDKConfig;
import com.baidu.idl.main.facesdk.model.Feature;
import com.baidu.idl.main.facesdk.model.GlobalSet;
import com.baidu.idl.main.facesdk.model.LivenessModel;
import com.baidu.idl.main.facesdk.model.SingleBaseConfig;
import com.baidu.idl.main.facesdk.model.User;
import com.baidu.idl.main.facesdk.utils.LogUtils;
import com.baidu.idl.main.facesdk.utils.PreferencesUtil;
import com.baidu.idl.main.facesdk.utils.ToastUtils;
import java.util.ArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import static com.baidu.idl.main.facesdk.model.BDFaceSDKCommon.BDFaceLogInfo.BDFACE_LOG_ALL_MESSAGE;
import static com.baidu.idl.main.facesdk.model.GlobalSet.FEATURE_SIZE;
import static com.baidu.idl.main.facesdk.model.GlobalSet.TIME_TAG;
public class FaceSDKManager {
public static final int SDK_MODEL_LOAD_SUCCESS = 0;
public static final int SDK_UNACTIVATION = 1;
public static final int SDK_UNINIT = 2;
public static final int SDK_INITING = 3;
public static final int SDK_INITED = 4;
public static final int SDK_INIT_FAIL = 5;
public static final int SDK_INIT_SUCCESS = 6;
public static volatile int initStatus = SDK_UNACTIVATION;
private FaceAuth faceAuth;
private FaceDetect faceDetect;
private FaceFeature faceFeature;
private FaceLive faceLiveness;
private ExecutorService es = Executors.newSingleThreadExecutor();
private Future future;
private ExecutorService es2 = Executors.newSingleThreadExecutor();
private Future future2;
private BDFaceDetectListConf bdFaceDetectListConfig;
private FaceDetect faceDetectNir;
private FaceMouthMask faceMouthMask;
private float[] scores;
private FaceSDKManager() {
faceAuth = new FaceAuth();
faceAuth.setActiveLog(BDFACE_LOG_ALL_MESSAGE);
faceAuth.setCoreConfigure(BDFaceSDKCommon.BDFaceCoreRunMode.BDFACE_LITE_POWER_LOW, 2);
// faceDetect = new FaceDetect();
// faceFeature = new FaceFeature();
// faceLiveness = new FaceLive();
}
private static class HolderClass {
private static final FaceSDKManager instance = new FaceSDKManager();
}
public static FaceSDKManager getInstance() {
return HolderClass.instance;
}
public FaceDetect getFaceDetect() {
return faceDetect;
}
public FaceFeature getFaceFeature() {
return faceFeature;
}
public FaceLive getFaceLiveness() {
return faceLiveness;
}
/**
* 初始化鉴权,如果鉴权通过,直接初始化模型
*
* @param context
* @param listener
*/
public void init(final Context context, final SdkInitListener listener) {
PreferencesUtil.initPrefs(context.getApplicationContext());
final String licenseOfflineKey = PreferencesUtil.getString("activate_offline_key", "");
final String licenseOnlineKey = PreferencesUtil.getString("activate_online_key", "HDDJ-PARX-TMWX-HUYX");
// 如果licenseKey 不存在提示授权码为空,并跳转授权页面授权
if (TextUtils.isEmpty(licenseOfflineKey) && TextUtils.isEmpty(licenseOnlineKey)) {
ToastUtils.toast(context, "未授权设备,请完成授权激活");
if (listener != null) {
listener.initLicenseFail(-1, "授权码不存在,请重新输入!");
}
return;
}
// todo 增加判空处理
if (listener != null) {
listener.initStart();
}
if (!TextUtils.isEmpty(licenseOfflineKey)) {
// 离线激活
faceAuth.initLicenseOffLine(context, new Callback() {
@Override
public void onResponse(int code, String response) {
if (code == 0) {
initStatus = SDK_INIT_SUCCESS;
if (listener != null) {
listener.initLicenseSuccess();
}
initModel(context, listener);
return;
}
}
});
} else if (!TextUtils.isEmpty(licenseOnlineKey)) {
// 在线激活
faceAuth.initLicenseOnLine(context, licenseOnlineKey, new Callback() {
@Override
public void onResponse(int code, String response) {
if (code == 0) {
initStatus = SDK_INIT_SUCCESS;
if (listener != null) {
listener.initLicenseSuccess();
}
initModel(context, listener);
return;
}
}
});
} else {
if (listener != null) {
listener.initLicenseFail(-1, "授权码不存在,请重新输入!");
}
}
}
/**
* 初始化模型,目前包含检查,活体,识别模型;因为初始化是顺序执行,可以在最好初始化回掉中返回状态结果
*
* @param context
* @param listener
*/
public void initModel(final Context context, final SdkInitListener listener) {
ToastUtils.toast(context, "模型初始化中,请稍后片刻");
faceDetect = new FaceDetect();
BDFaceInstance bdFaceInstance = new BDFaceInstance();
bdFaceInstance.creatInstance();
faceDetectNir = new FaceDetect(bdFaceInstance);
faceFeature = new FaceFeature();
faceLiveness = new FaceLive();
faceMouthMask = new FaceMouthMask();
initConfig();
final long startInitModelTime = System.currentTimeMillis();
faceDetect.initModel(context,
GlobalSet.DETECT_VIS_MODEL,
GlobalSet.ALIGN_TRACK_MODEL,
BDFaceSDKCommon.DetectType.DETECT_VIS,
BDFaceSDKCommon.AlignType.BDFACE_ALIGN_TYPE_RGB_FAST,
new Callback() {
@Override
public void onResponse(int code, String response) {
if (code != 0 && listener != null) {
listener.initModelFail(code, response);
}
}
});
faceDetect.initModel(context,
GlobalSet.DETECT_VIS_MODEL,
GlobalSet.ALIGN_RGB_MODEL, BDFaceSDKCommon.DetectType.DETECT_VIS,
BDFaceSDKCommon.AlignType.BDFACE_ALIGN_TYPE_RGB_ACCURATE,
new Callback() {
@Override
public void onResponse(int code, String response) {
// ToastUtils.toast(context, code + " " + response);
if (code != 0 && listener != null) {
listener.initModelFail(code, response);
}
}
});
// faceDetect.initModel(context,
// GlobalSet.DETECT_VIS_MODEL,
// "",
// GlobalSet.ALIGN_MODEL, BDFaceSDKCommon.DetectType.DETECT_VIS,
// BDFaceSDKCommon.AlignType.BDFACE_FULL_ALIGN_TYPE_GENERAL_RGB,
// new Callback() {
// @Override
// public void onResponse(int code, String response) {
// // ToastUtils.toast(context, code + " " + response);
// if (code != 0 && listener != null) {
// listener.initModelFail(code, response);
// }
// }
// });
faceDetectNir.initModel(context,
GlobalSet.DETECT_NIR_MODE,
GlobalSet.ALIGN_NIR_MODEL, BDFaceSDKCommon.DetectType.DETECT_NIR,
BDFaceSDKCommon.AlignType.BDFACE_ALIGN_TYPE_NIR_ACCURATE,
new Callback() {
@Override
public void onResponse(int code, String response) {
// ToastUtils.toast(context, code + " " + response);
if (code != 0 && listener != null) {
listener.initModelFail(code, response);
}
}
});
faceDetect.initQuality(context,
GlobalSet.BLUR_MODEL,
GlobalSet.OCCLUSION_MODEL, new Callback() {
@Override
public void onResponse(int code, String response) {
if (code != 0 && listener != null) {
listener.initModelFail(code, response);
}
}
});
faceDetect.initAttrEmo(context, GlobalSet.ATTRIBUTE_MODEL, GlobalSet.EMOTION_MODEL, new Callback() {
@Override
public void onResponse(int code, String response) {
if (code != 0 && listener != null) {
listener.initModelFail(code, response);
}
}
});
faceLiveness.initModel(context,
GlobalSet.LIVE_VIS_MODEL,
GlobalSet.LIVE_NIR_MODEL,
GlobalSet.LIVE_DEPTH_MODEL,
new Callback() {
@Override
public void onResponse(int code, String response) {
// ToastUtils.toast(context, code + " " + response);
if (code != 0 && listener != null) {
listener.initModelFail(code, response);
}
}
});
faceMouthMask.initModel(context, GlobalSet.MOUTH_MASK, new Callback() {
@Override
public void onResponse(int code, String response) {
if (code != 0 && listener != null) {
listener.initModelFail(code, response);
}
}
});
faceFeature.initModel(context,
GlobalSet.RECOGNIZE_IDPHOTO_MODEL,
GlobalSet.RECOGNIZE_VIS_MODEL,
"",
new Callback() {
@Override
public void onResponse(int code, String response) {
long endInitModelTime = System.currentTimeMillis();
LogUtils.e(TIME_TAG, "init model time = " + (endInitModelTime - startInitModelTime));
if (code != 0) {
ToastUtils.toast(context, "模型加载失败");
if (listener != null) {
listener.initModelFail(code, response);
}
} else {
initStatus = SDK_MODEL_LOAD_SUCCESS;
// 模型初始化成功,加载人脸数据
initDataBases(context);
ToastUtils.toast(context, "模型加载完毕,欢迎使用");
if (listener != null) {
listener.initModelSuccess();
}
}
}
});
}
/**
* 初始化配置
*
* @return
*/
public boolean initConfig() {
if (faceDetect != null) {
BDFaceSDKConfig config = new BDFaceSDKConfig();
// TODO: 最小人脸个数检查,默认设置为1,用户根据自己需求调整
config.maxDetectNum = 1;
// TODO: 默认为80px。可传入大于30px的数值,小于此大小的人脸不予检测,生效时间第一次加载模型
config.minFaceSize = SingleBaseConfig.getBaseConfig().getMinimumFace();
// 是否进行属性检测,默认关闭
config.isAttribute = SingleBaseConfig.getBaseConfig().isAttribute();
//
// // TODO: 模糊,遮挡,光照三个质量检测和姿态角查默认关闭,如果要开启,设置页启动
// config.isCheckBlur = config.isOcclusion
// = config.isIllumination = config.isHeadPose
// = SingleBaseConfig.getBaseConfig().isQualityControl();
bdFaceDetectListConfig = new BDFaceDetectListConf();
bdFaceDetectListConfig.usingQuality = bdFaceDetectListConfig.usingHeadPose
= SingleBaseConfig.getBaseConfig().isQualityControl();
bdFaceDetectListConfig.usingAttribute = SingleBaseConfig.getBaseConfig().isAttribute();
bdFaceDetectListConfig.usingEyeClose = false;
bdFaceDetectListConfig.usingMouthClose = false;
faceDetect.loadConfig(config);
return true;
}
return false;
}
public void initDataBases(Context context) {
// 初始化数据库
DBManager.getInstance().init(context);
// 数据变化,更新内存
FaceApi.getInstance().initDatabases(true);
}
/**
* 检测-活体-特征-人脸检索流程
*
* @param rgbData 可见光YUV 数据流
* @param nirData 红外YUV 数据流
* @param depthData 深度depth 数据流
* @param srcHeight 可见光YUV 数据流-高度
* @param srcWidth 可见光YUV 数据流-宽度
* @param liveCheckMode 活体检测类型【不使用活体:1】;【RGB活体:2】;【RGB+NIR活体:3】;【RGB+Depth活体:4】
* @param faceDetectCallBack
*/
public void onDetectCheck(final byte[] rgbData,
final byte[] nirData,
final byte[] depthData,
final int srcHeight,
final int srcWidth,
final int liveCheckMode,
final FaceDetectCallBack faceDetectCallBack) {
// 【【提取特征+1:N检索:3】
onDetectCheck(rgbData, nirData, depthData, srcHeight, srcWidth, liveCheckMode, 3, faceDetectCallBack);
}
/**
* 检测-活体-特征- 全流程
*
* @param rgbData 可见光YUV 数据流
* @param nirData 红外YUV 数据流
* @param depthData 深度depth 数据流
* @param srcHeight 可见光YUV 数据流-高度
* @param srcWidth 可见光YUV 数据流-宽度
* @param liveCheckMode 活体检测模式【不使用活体:1】;【RGB活体:2】;【RGB+NIR活体:3】;【RGB+Depth活体:4】
* @param featureCheckMode 特征抽取模式【不提取特征:1】;【提取特征:2】;【提取特征+1:N检索:3】;
* @param faceDetectCallBack
*/
public void onDetectCheck(final byte[] rgbData,
final byte[] nirData,
final byte[] depthData,
final int srcHeight,
final int srcWidth,
final int liveCheckMode,
final int featureCheckMode,
final FaceDetectCallBack faceDetectCallBack) {
if (future != null && !future.isDone()) {
return;
}
future = es.submit(new Runnable() {
@Override
public void run() {
long startTime = System.currentTimeMillis();
// 创建检测结果存储数据
LivenessModel livenessModel = new LivenessModel();
// 创建检测对象,如果原始数据YUV,转为算法检测的图片BGR
// TODO: 用户调整旋转角度和是否镜像,手机和开发版需要动态适配
BDFaceImageInstance rgbInstance;
if (SingleBaseConfig.getBaseConfig().getType() == 4
&& SingleBaseConfig.getBaseConfig().getCameraType() == 6) {
rgbInstance = new BDFaceImageInstance(rgbData, srcHeight,
srcWidth, BDFaceSDKCommon.BDFaceImageType.BDFACE_IMAGE_TYPE_RGB,
SingleBaseConfig.getBaseConfig().getDetectDirection(),
SingleBaseConfig.getBaseConfig().getMirrorRGB());
} else {
rgbInstance = new BDFaceImageInstance(rgbData, srcHeight,
srcWidth, BDFaceSDKCommon.BDFaceImageType.BDFACE_IMAGE_TYPE_YUV_NV21,
SingleBaseConfig.getBaseConfig().getDetectDirection(),
SingleBaseConfig.getBaseConfig().getMirrorRGB());
}
// TODO: getImage() 获取送检图片,如果检测数据有问题,可以通过image view 展示送检图片
livenessModel.setBdFaceImageInstance(rgbInstance.getImage());
// 检查函数调用,返回检测结果
long startDetectTime = System.currentTimeMillis();
// 快速检测获取人脸信息,仅用于绘制人脸框,详细人脸数据后续获取
FaceInfo[] faceInfos = FaceSDKManager.getInstance().getFaceDetect()
.track(BDFaceSDKCommon.DetectType.DETECT_VIS,
BDFaceSDKCommon.AlignType.BDFACE_ALIGN_TYPE_RGB_FAST, rgbInstance);
livenessModel.setRgbDetectDuration(System.currentTimeMillis() - startDetectTime);
LogUtils.e(TIME_TAG, "detect vis time = " + livenessModel.getRgbDetectDuration());
// 检测结果判断
if (faceInfos != null && faceInfos.length > 0) {
livenessModel.setTrackFaceInfo(faceInfos);
livenessModel.setFaceInfo(faceInfos[0]);
livenessModel.setLandmarks(faceInfos[0].landmarks);
if (faceDetectCallBack != null) {
faceDetectCallBack.onFaceDetectDarwCallback(livenessModel);
}
onLivenessCheck(rgbInstance, nirData, depthData, srcHeight,
srcWidth, livenessModel.getLandmarks(),
livenessModel, startTime, liveCheckMode, featureCheckMode,
faceDetectCallBack, faceInfos);
} else {
// 流程结束销毁图片,开始下一帧图片检测,否着内存泄露
rgbInstance.destory();
if (faceDetectCallBack != null) {
faceDetectCallBack.onFaceDetectCallback(null);
faceDetectCallBack.onFaceDetectDarwCallback(null);
faceDetectCallBack.onTip(0, "未检测到人脸");
}
}
}
});
}
/**
* 质量检测结果过滤,如果需要质量检测,
* 需要调用 SingleBaseConfig.getBaseConfig().setQualityControl(true);设置为true,
* 再调用 FaceSDKManager.getInstance().initConfig() 加载到底层配置项中
*
* @param livenessModel
* @param faceDetectCallBack
* @return
*/
public boolean onQualityCheck(final LivenessModel livenessModel,
final FaceDetectCallBack faceDetectCallBack) {
if (!SingleBaseConfig.getBaseConfig().isQualityControl()) {
return true;
}
if (livenessModel != null && livenessModel.getFaceInfo() != null) {
// 角度过滤
if (Math.abs(livenessModel.getFaceInfo().yaw) > SingleBaseConfig.getBaseConfig().getYaw()) {
faceDetectCallBack.onTip(-1, "人脸左右偏转角超出限制");
return false;
} else if (Math.abs(livenessModel.getFaceInfo().roll) > SingleBaseConfig.getBaseConfig().getRoll()) {
faceDetectCallBack.onTip(-1, "人脸平行平面内的头部旋转角超出限制");
return false;
} else if (Math.abs(livenessModel.getFaceInfo().pitch) > SingleBaseConfig.getBaseConfig().getPitch()) {
faceDetectCallBack.onTip(-1, "人脸上下偏转角超出限制");
return false;
}
// 模糊结果过滤
float blur = livenessModel.getFaceInfo().bluriness;
if (blur > SingleBaseConfig.getBaseConfig().getBlur()) {
faceDetectCallBack.onTip(-1, "图片模糊");
return false;
}
// 光照结果过滤
float illum = livenessModel.getFaceInfo().illum;
if (illum < SingleBaseConfig.getBaseConfig().getIllumination()) {
faceDetectCallBack.onTip(-1, "图片光照不通过");
return false;
}
// 遮挡结果过滤
if (livenessModel.getFaceInfo().occlusion != null) {
BDFaceOcclusion occlusion = livenessModel.getFaceInfo().occlusion;
if (occlusion.leftEye > SingleBaseConfig.getBaseConfig().getLeftEye()) {
// 左眼遮挡置信度
faceDetectCallBack.onTip(-1, "左眼遮挡");
} else if (occlusion.rightEye > SingleBaseConfig.getBaseConfig().getRightEye()) {
// 右眼遮挡置信度
faceDetectCallBack.onTip(-1, "右眼遮挡");
} else if (occlusion.nose > SingleBaseConfig.getBaseConfig().getNose()) {
// 鼻子遮挡置信度
faceDetectCallBack.onTip(-1, "鼻子遮挡");
} else if (occlusion.mouth > SingleBaseConfig.getBaseConfig().getMouth()) {
// 嘴巴遮挡置信度
faceDetectCallBack.onTip(-1, "嘴巴遮挡");
} else if (occlusion.leftCheek > SingleBaseConfig.getBaseConfig().getLeftCheek()) {
// 左脸遮挡置信度
faceDetectCallBack.onTip(-1, "左脸遮挡");
} else if (occlusion.rightCheek > SingleBaseConfig.getBaseConfig().getRightCheek()) {
// 右脸遮挡置信度
faceDetectCallBack.onTip(-1, "右脸遮挡");
} else if (occlusion.chin > SingleBaseConfig.getBaseConfig().getChinContour()) {
// 下巴遮挡置信度
faceDetectCallBack.onTip(-1, "下巴遮挡");
} else {
return true;
}
}
}
return false;
}
/**
* 活体-特征-人脸检索全流程
*
* @param rgbInstance 可见光底层送检对象
* @param nirData 红外YUV 数据流
* @param depthData 深度depth 数据流
* @param srcHeight 可见光YUV 数据流-高度
* @param srcWidth 可见光YUV 数据流-宽度
* @param landmark 检测眼睛,嘴巴,鼻子,72个关键点
* @param livenessModel 检测结果数据集合
* @param startTime 开始检测时间
* @param liveCheckMode 活体检测模式【不使用活体:1】;【RGB活体:2】;【RGB+NIR活体:3】;【RGB+Depth活体:4】
* @param featureCheckMode 特征抽取模式【不提取特征:1】;【提取特征:2】;【提取特征+1:N检索:3】;
* @param faceDetectCallBack
*/
public void onLivenessCheck(final BDFaceImageInstance rgbInstance,
final byte[] nirData,
final byte[] depthData,
final int srcHeight,
final int srcWidth,
final float[] landmark,
final LivenessModel livenessModel,
final long startTime,
final int liveCheckMode,
final int featureCheckMode,
final FaceDetectCallBack faceDetectCallBack,
final FaceInfo[] fastFaceInfos) {
if (future2 != null && !future2.isDone()) {
// 流程结束销毁图片,开始下一帧图片检测,否着内存泄露
rgbInstance.destory();
return;
}
future2 = es2.submit(new Runnable() {
@Override
public void run() {
FaceInfo[] faceInfos = FaceSDKManager.getInstance()
.getFaceDetect()
.detect(BDFaceSDKCommon.DetectType.DETECT_VIS,
BDFaceSDKCommon.AlignType.BDFACE_ALIGN_TYPE_RGB_ACCURATE,
rgbInstance,
fastFaceInfos, bdFaceDetectListConfig);
// 重新赋予详细人脸信息
if (faceInfos != null && faceInfos.length > 0) {
livenessModel.setTrackFaceInfo(faceInfos);
livenessModel.setFaceInfo(faceInfos[0]);
livenessModel.setLandmarks(faceInfos[0].landmarks);
scores = faceMouthMask.checkMask(rgbInstance, faceInfos);
if (scores != null) {
Log.e("FaceMouthMask", scores[0] + "");
}
} else {
rgbInstance.destory();
return;
}
// 质量检测未通过,销毁BDFaceImageInstance,结束函数
if (!onQualityCheck(livenessModel, faceDetectCallBack)) {
rgbInstance.destory();
return;
}
// 获取LivenessConfig liveCheckMode 配置选项:【不使用活体:1】;【RGB活体:2】;【RGB+NIR活体:3】;【RGB+Depth活体:4】
// TODO 活体检测
float rgbScore = -1;
if (liveCheckMode != 1) {
long startRgbTime = System.currentTimeMillis();
rgbScore = FaceSDKManager.getInstance().getFaceLiveness().silentLive(
BDFaceSDKCommon.LiveType.BDFACE_SILENT_LIVE_TYPE_RGB,
rgbInstance, faceInfos[0].landmarks);
livenessModel.setRgbLivenessScore(rgbScore);
livenessModel.setRgbLivenessDuration(System.currentTimeMillis() - startRgbTime);
LogUtils.e(TIME_TAG, "live rgb time = " + livenessModel.getRgbLivenessDuration());
}
float nirScore = -1;
if (liveCheckMode == 3 && nirData != null) {
// 创建检测对象,如果原始数据YUV-IR,转为算法检测的图片BGR
// TODO: 用户调整旋转角度和是否镜像,手机和开发版需要动态适配
BDFaceImageInstance nirInstance = new BDFaceImageInstance(nirData, srcHeight,
srcWidth, BDFaceSDKCommon.BDFaceImageType.BDFACE_IMAGE_TYPE_YUV_NV21,
SingleBaseConfig.getBaseConfig().getDetectDirection(),
SingleBaseConfig.getBaseConfig().getMirrorNIR());
// 避免RGB检测关键点在IR对齐活体稳定,增加红外检测
long startIrDetectTime = System.currentTimeMillis();
// FaceInfo[] faceInfosIr = faceDetectNir.detect(BDFaceSDKCommon.DetectType.DETECT_NIR,
// nirInstance);
BDFaceDetectListConf bdFaceDetectListConf = new BDFaceDetectListConf();
bdFaceDetectListConf.usingDetect = true;
bdFaceDetectListConf.usingAlign = true;
FaceInfo[] faceInfosIr = faceDetectNir.detect(BDFaceSDKCommon.DetectType.DETECT_NIR,
BDFaceSDKCommon.AlignType.BDFACE_ALIGN_TYPE_NIR_ACCURATE,
nirInstance,
null, bdFaceDetectListConf);
bdFaceDetectListConf.usingDetect = false;
livenessModel.setIrLivenessDuration(System.currentTimeMillis() - startIrDetectTime);
LogUtils.e(TIME_TAG, "detect ir time = " + livenessModel.getIrLivenessDuration());
if (faceInfosIr != null && faceInfosIr.length > 0) {
FaceInfo faceInfoIr = faceInfosIr[0];
long startNirTime = System.currentTimeMillis();
nirScore = FaceSDKManager.getInstance().getFaceLiveness().silentLive(
BDFaceSDKCommon.LiveType.BDFACE_SILENT_LIVE_TYPE_NIR,
nirInstance, faceInfoIr.landmarks);
livenessModel.setIrLivenessScore(nirScore);
livenessModel.setIrLivenessDuration(System.currentTimeMillis() - startNirTime);
LogUtils.e(TIME_TAG, "live ir time = " + livenessModel.getIrLivenessDuration());
}
nirInstance.destory();
}
float depthScore = -1;
if (liveCheckMode == 4 && depthData != null) {
// TODO: 用户调整旋转角度和是否镜像,适配Atlas 镜头,目前宽和高400*640,其他摄像头需要动态调整,人脸72 个关键点x 坐标向左移动80个像素点
float[] depthLandmark = new float[faceInfos[0].landmarks.length];
BDFaceImageInstance depthInstance;
if (SingleBaseConfig.getBaseConfig().getCameraType() == 2) {
System.arraycopy(faceInfos[0].landmarks, 0, depthLandmark, 0, faceInfos[0].landmarks.length);
if (SingleBaseConfig.getBaseConfig().getCameraType() == 2) {
for (int i = 0; i < 144; i = i + 2) {
depthLandmark[i] -= 80;
}
}
depthInstance = new BDFaceImageInstance(depthData,
SingleBaseConfig.getBaseConfig().getDepthWidth(),
SingleBaseConfig.getBaseConfig().getDepthHeight(),
BDFaceSDKCommon.BDFaceImageType.BDFACE_IMAGE_TYPE_DEPTH,
0, 0);
} else {
depthInstance = new BDFaceImageInstance(depthData,
SingleBaseConfig.getBaseConfig().getDepthHeight(),
SingleBaseConfig.getBaseConfig().getDepthWidth(),
BDFaceSDKCommon.BDFaceImageType.BDFACE_IMAGE_TYPE_DEPTH,
0, 0);
}
// 创建检测对象,如果原始数据Depth
long startDepthTime = System.currentTimeMillis();
if (SingleBaseConfig.getBaseConfig().getCameraType() == 2) {
depthScore = FaceSDKManager.getInstance().getFaceLiveness().silentLive(
BDFaceSDKCommon.LiveType.BDFACE_SILENT_LIVE_TYPE_DEPTH,
depthInstance, depthLandmark);
} else {
depthScore = FaceSDKManager.getInstance().getFaceLiveness().silentLive(
BDFaceSDKCommon.LiveType.BDFACE_SILENT_LIVE_TYPE_DEPTH,
depthInstance, faceInfos[0].landmarks);
}
livenessModel.setDepthLivenessScore(depthScore);
livenessModel.setDepthtLivenessDuration(System.currentTimeMillis() - startDepthTime);
LogUtils.e(TIME_TAG, "live depth time = " + livenessModel.getDepthtLivenessDuration());
depthInstance.destory();
}
// TODO 特征提取+人脸检索
if (liveCheckMode == 1) {
onFeatureCheck(rgbInstance, faceInfos[0].landmarks, livenessModel, featureCheckMode);
} else {
if (liveCheckMode == 2 && rgbScore > SingleBaseConfig.getBaseConfig().getRgbLiveScore()) {
onFeatureCheck(rgbInstance, faceInfos[0].landmarks, livenessModel, featureCheckMode);
} else if (liveCheckMode == 3 && rgbScore > SingleBaseConfig.getBaseConfig().getRgbLiveScore()
&& nirScore > SingleBaseConfig.getBaseConfig().getNirLiveScore()) {
onFeatureCheck(rgbInstance, faceInfos[0].landmarks, livenessModel, featureCheckMode);
} else if (liveCheckMode == 4 && rgbScore > SingleBaseConfig.getBaseConfig().getRgbLiveScore()
&& depthScore > SingleBaseConfig.getBaseConfig().getDepthLiveScore()) {
onFeatureCheck(rgbInstance, faceInfos[0].landmarks, livenessModel, featureCheckMode);
}
}
// 流程结束,记录最终时间
livenessModel.setAllDetectDuration(System.currentTimeMillis() - startTime);
LogUtils.e(TIME_TAG, "all process time = " + livenessModel.getAllDetectDuration());
// 流程结束销毁图片,开始下一帧图片检测,否着内存泄露
rgbInstance.destory();
// 显示最终结果提示
if (faceDetectCallBack != null) {
faceDetectCallBack.onFaceDetectCallback(livenessModel);
}
}
});
}
/**
* 特征提取-人脸识别比对
*
* @param rgbInstance 可见光底层送检对象
* @param landmark 检测眼睛,嘴巴,鼻子,72个关键点
* @param livenessModel 检测结果数据集合
* @param featureCheckMode 特征抽取模式【不提取特征:1】;【提取特征:2】;【提取特征+1:N检索:3】;
*/
public void onFeatureCheck(BDFaceImageInstance rgbInstance,
float[] landmark,
LivenessModel livenessModel,
final int featureCheckMode) {
// 如果不抽取特征,直接返回
if (featureCheckMode == 1) {
return;
}
byte[] feature = new byte[512];
long startFeatureTime = System.currentTimeMillis();
float featureSize = FaceSDKManager.getInstance().getFaceFeature().feature(
BDFaceSDKCommon.FeatureType.BDFACE_FEATURE_TYPE_LIVE_PHOTO, rgbInstance, landmark, feature);
livenessModel.setFeatureDuration(System.currentTimeMillis() - startFeatureTime);
LogUtils.e(TIME_TAG, "feature live time = " + livenessModel.getFeatureDuration());
livenessModel.setFeature(feature);
// 如果只提去特征,不做检索,此处返回
if (featureCheckMode == 2) {
livenessModel.setFeatureCode(featureSize);
return;
}
// 如果提取特征+检索,调用search 方法
if (featureSize == FEATURE_SIZE / 4) {
// 特征提取成功
// TODO 阈值可以根据不同模型调整
long startFeature = System.currentTimeMillis();
ArrayList<Feature> featureResult = FaceSDKManager.getInstance().getFaceFeature().featureSearch(feature,
BDFaceSDKCommon.FeatureType.BDFACE_FEATURE_TYPE_LIVE_PHOTO,
1, true);
// TODO 返回top num = 1 个数据集合,此处可以任意设置,会返回比对从大到小排序的num 个数据集合
if (featureResult != null && featureResult.size() > 0) {
// 获取第一个数据
Feature topFeature = featureResult.get(0);
// 判断第一个阈值是否大于设定阈值,如果大于,检索成功
if (topFeature != null && topFeature.getScore() >
SingleBaseConfig.getBaseConfig().getThreshold()) {
// 当前featureEntity 只有id+feature 索引,在数据库中查到完整信息
User user = FaceApi.getInstance().getUserListById(topFeature.getId());
if (user != null) {
livenessModel.setUser(user);
livenessModel.setFeatureScore(topFeature.getScore());
}
}
}
livenessModel.setCheckDuration(System.currentTimeMillis() - startFeature);
LogUtils.e(TIME_TAG, "feature search time = " + livenessModel.getCheckDuration());
}
}
/**
* 单独调用 特征提取
*
* @param imageInstance 可见光底层送检对象
* @param landmark 检测眼睛,嘴巴,鼻子,72个关键点
* @param featureCheckMode 特征提取模式
* @param faceFeatureCallBack 回掉方法
*/
public void onFeatureCheck(BDFaceImageInstance imageInstance, float[] landmark,
BDFaceSDKCommon.FeatureType featureCheckMode,
final FaceFeatureCallBack faceFeatureCallBack) {
BDFaceImageInstance rgbInstance = new BDFaceImageInstance(imageInstance.data,
imageInstance.height, imageInstance.width,
imageInstance.imageType, 0, 0);
byte[] feature = new byte[512];
float featureSize = FaceSDKManager.getInstance().getFaceFeature().feature(
featureCheckMode, rgbInstance, landmark, feature);
if (featureSize == FEATURE_SIZE / 4) {
// 特征提取成功
if (faceFeatureCallBack != null) {
faceFeatureCallBack.onFaceFeatureCallBack(featureSize, feature);
}
}
// 流程结束销毁图片
rgbInstance.destory();
}
}
\ No newline at end of file
... ...
package com.baidu.idl.main.facesdk.manager;
import com.baidu.idl.main.facesdk.FaceInfo;
import com.baidu.idl.main.facesdk.callback.FaceDetectCallBack;
import com.baidu.idl.main.facesdk.model.BDFaceImageInstance;
import com.baidu.idl.main.facesdk.model.BDFaceOcclusion;
import com.baidu.idl.main.facesdk.model.BDFaceSDKCommon;
import com.baidu.idl.main.facesdk.model.LivenessModel;
import com.baidu.idl.main.facesdk.model.SingleBaseConfig;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/**
* Time: 2019/1/25
* Author: v_chaixiaogang
* rgb单目检测管理类
*/
public class FaceTrackManager {
private static volatile FaceTrackManager instance = null;
private ExecutorService es;
private Future future;
private boolean isAliving;
private boolean isQualityControl = false;
private boolean isDetect = true;
private FaceDetectCallBack mFaceDetectCallBack;
public FaceDetectCallBack getFaceDetectCallBack() {
return mFaceDetectCallBack;
}
public void setFaceDetectCallBack(FaceDetectCallBack faceDetectCallBack) {
mFaceDetectCallBack = faceDetectCallBack;
}
public boolean isQualityControl() {
return isQualityControl;
}
public void setQualityControl(boolean qualityControl) {
isQualityControl = qualityControl;
}
public boolean isAliving() {
return isAliving;
}
public void setAliving(boolean aliving) {
isAliving = aliving;
}
public boolean isDetect() {
return isDetect;
}
public void setDetect(boolean detect) {
isDetect = detect;
}
public FaceTrackManager() {
es = Executors.newSingleThreadExecutor();
}
public static FaceTrackManager getInstance() {
synchronized (FaceTrackManager.class) {
if (instance == null) {
instance = new FaceTrackManager();
}
}
return instance;
}
public void faceTrack(final byte[] argb, final int width, final int height,
final FaceDetectCallBack faceDetectCallBack) {
if (future != null && !future.isDone()) {
return;
}
future = es.submit(new Runnable() {
@Override
public void run() {
faceDataDetect(argb, width, height, faceDetectCallBack);
}
});
}
/**
* 人脸检测
*
* @param argb
* @param width
* @param height
* @param faceDetectCallBack
*/
private void faceDataDetect(final byte[] argb, int width, int height, FaceDetectCallBack faceDetectCallBack) {
LivenessModel livenessModel = new LivenessModel();
BDFaceImageInstance rgbInstance;
if (SingleBaseConfig.getBaseConfig().getType() == 4 && SingleBaseConfig.getBaseConfig().getCameraType() == 6) {
rgbInstance = new BDFaceImageInstance(argb, height, width,
BDFaceSDKCommon.BDFaceImageType.BDFACE_IMAGE_TYPE_RGB,
SingleBaseConfig.getBaseConfig().getDetectDirection(),
SingleBaseConfig.getBaseConfig().getMirrorRGB());
} else {
rgbInstance = new BDFaceImageInstance(argb, height, width,
BDFaceSDKCommon.BDFaceImageType.BDFACE_IMAGE_TYPE_YUV_NV21,
SingleBaseConfig.getBaseConfig().getDetectDirection(),
SingleBaseConfig.getBaseConfig().getMirrorRGB());
}
livenessModel.setBdFaceImageInstance(rgbInstance);
long startTime = System.currentTimeMillis();
FaceInfo[] faceInfos = FaceSDKManager.getInstance().getFaceDetect()
.track(BDFaceSDKCommon.DetectType.DETECT_VIS, rgbInstance);
livenessModel.setRgbDetectDuration(System.currentTimeMillis() - startTime);
// getImage() 获取送检图片
livenessModel.setBdFaceImageInstance(rgbInstance.getImage());
if (faceInfos != null && faceInfos.length > 0) {
livenessModel.setTrackFaceInfo(faceInfos);
FaceInfo faceInfo = faceInfos[0];
livenessModel.setFaceInfo(faceInfo);
livenessModel.setLandmarks(faceInfo.landmarks);
// 质量检测,针对模糊度、遮挡、角度
if (onQualityCheck(livenessModel, faceDetectCallBack)) {
// 活体检测
if (isAliving) {
startTime = System.currentTimeMillis();
float rgbScore = FaceSDKManager.getInstance().getFaceLiveness().silentLive(
BDFaceSDKCommon.LiveType.BDFACE_SILENT_LIVE_TYPE_RGB,
rgbInstance, faceInfo.landmarks);
livenessModel.setRgbLivenessScore(rgbScore);
livenessModel.setRgbLivenessDuration(System.currentTimeMillis() - startTime);
}
// 流程结束销毁图片,开始下一帧图片检测,否着内存泄露
rgbInstance.destory();
if (faceDetectCallBack != null) {
faceDetectCallBack.onFaceDetectCallback(livenessModel);
}
} else {
// 流程结束销毁图片,开始下一帧图片检测,否着内存泄露
rgbInstance.destory();
}
} else {
// 流程结束销毁图片,开始下一帧图片检测,否着内存泄露
rgbInstance.destory();
if (faceDetectCallBack != null) {
faceDetectCallBack.onTip(0, "未检测到人脸");
faceDetectCallBack.onFaceDetectCallback(null);
}
}
}
/**
* 质量检测
*
* @param livenessModel
* @param faceDetectCallBack
* @return
*/
public boolean onQualityCheck(final LivenessModel livenessModel,
final FaceDetectCallBack faceDetectCallBack) {
if (!SingleBaseConfig.getBaseConfig().isQualityControl()) {
return true;
}
if (livenessModel != null && livenessModel.getFaceInfo() != null) {
// 角度过滤
if (Math.abs(livenessModel.getFaceInfo().yaw) > SingleBaseConfig.getBaseConfig().getYaw()) {
faceDetectCallBack.onTip(-1, "人脸左右偏转角超出限制");
return false;
} else if (Math.abs(livenessModel.getFaceInfo().roll) > SingleBaseConfig.getBaseConfig().getRoll()) {
faceDetectCallBack.onTip(-1, "人脸平行平面内的头部旋转角超出限制");
return false;
} else if (Math.abs(livenessModel.getFaceInfo().pitch) > SingleBaseConfig.getBaseConfig().getPitch()) {
faceDetectCallBack.onTip(-1, "人脸上下偏转角超出限制");
return false;
}
// 模糊结果过滤
float blur = livenessModel.getFaceInfo().bluriness;
if (blur > SingleBaseConfig.getBaseConfig().getBlur()) {
faceDetectCallBack.onTip(-1, "图片模糊");
return false;
}
// 光照结果过滤
float illum = livenessModel.getFaceInfo().illum;
if (illum < SingleBaseConfig.getBaseConfig().getIllumination()) {
faceDetectCallBack.onTip(-1, "图片光照不通过");
return false;
}
// 遮挡结果过滤
if (livenessModel.getFaceInfo().occlusion != null) {
BDFaceOcclusion occlusion = livenessModel.getFaceInfo().occlusion;
if (occlusion.leftEye > SingleBaseConfig.getBaseConfig().getLeftEye()) {
// 左眼遮挡置信度
faceDetectCallBack.onTip(-1, "左眼遮挡");
} else if (occlusion.rightEye > SingleBaseConfig.getBaseConfig().getRightEye()) {
// 右眼遮挡置信度
faceDetectCallBack.onTip(-1, "右眼遮挡");
} else if (occlusion.nose > SingleBaseConfig.getBaseConfig().getNose()) {
// 鼻子遮挡置信度
faceDetectCallBack.onTip(-1, "鼻子遮挡");
} else if (occlusion.mouth > SingleBaseConfig.getBaseConfig().getMouth()) {
// 嘴巴遮挡置信度
faceDetectCallBack.onTip(-1, "嘴巴遮挡");
} else if (occlusion.leftCheek > SingleBaseConfig.getBaseConfig().getLeftCheek()) {
// 左脸遮挡置信度
faceDetectCallBack.onTip(-1, "左脸遮挡");
} else if (occlusion.rightCheek > SingleBaseConfig.getBaseConfig().getRightCheek()) {
// 右脸遮挡置信度
faceDetectCallBack.onTip(-1, "右脸遮挡");
} else if (occlusion.chin > SingleBaseConfig.getBaseConfig().getChinContour()) {
// 下巴遮挡置信度
faceDetectCallBack.onTip(-1, "下巴遮挡");
} else {
return true;
}
}
}
return false;
}
}
... ...
package com.baidu.idl.main.facesdk.model;
/**
* author : shangrong
* date : 2019/5/22 9:10 PM
* description :配置文件
*/
public class BaseConfig {
// 设备通信密码
private String dPass = "";
// RGB检测帧回显
private Boolean display = true;
// RGB预览Y轴转向falese为0,true为180
private Boolean rgbRevert = false;
// NIR或depth实时视频预览
private Boolean isNirOrDepth = true;
// 默认为false。可选项为"true"、"false",是否开启调试显示,将会作用到所有视频流识别页面,包含1:N、1:1采集人脸图片环节。
private boolean debug = false;
// 默认为0。可传入0、90、180、270四个选项。
private int videoDirection = 270;
// 默认为wireframe。可选项为"wireframe"、"fixedarea",如选择fixed_area,需要传入半径,px像素为单位
private String detectFrame = "wireframe";
// 当选择fixed_area,需要传入半径信息,以px为单位,如50px
// private int radius = 50;
// 默认为0。可传入0、90、180、270四个选项
private int detectDirection = 90;
// 默认为max。分为"max" 、"none"三个方式,分别是最大人脸 ,和不检测人脸
private String trackType = "max";
// 默认为80px。可传入大于50px的数值,小于此大小的人脸不予检测
private int minimumFace = 60;
// 模糊度设置,默认0.5。取值范围[0~1],0是最清晰,1是最模糊
private float blur = 0.5f;
// 光照设置,默认40.取值范围[0~255], 数值越大,光线越强
private int illumination = 40;
// 姿态阈值
private float gesture = 15;
// 三维旋转之俯仰角度[-90(上), 90(下)],默认20
private float pitch = 20;
// 平面内旋转角[-180(逆时针), 180(顺时针)],默认20
private float roll = 20;
// 三维旋转之左右旋转角[-90(左), 90(右)],默认20
private float yaw = 20;
// 遮挡阈值
private float occlusion = 0.6f;
// 左眼被遮挡的阈值,默认0.6
private float leftEye = 0.6f;
// 右眼被遮挡的阈值,默认0.6
private float rightEye = 0.6f;
// 鼻子被遮挡的阈值,默认0.7
private float nose = 0.7f;
// 嘴巴被遮挡的阈值,默认0.7
private float mouth = 0.7f;
// 左脸颊被遮挡的阈值,默认0.8
private float leftCheek = 0.8f;
// 右脸颊被遮挡的阈值,默认0.8
private float rightCheek = 0.8f;
// 下巴被遮挡阈值,默认为0.6
private float chinContour = 0.6f;
// 人脸完整度,默认为1。0为人脸溢出图像边界,1为人脸都在图像边界内
private float completeness = 1f;
// 识别阈值,0-100,默认为80分,需要选择具体模型的阈值。live:80、idcard:80
private int threshold = 80;
// 使用的特征抽取模型默认为生活照:1;证件照:2
private int activeModel = 1;
// 识别结果出来后的演示展示,默认为0ms
private int timeLapse = 0;
// 活体选择模式,默认为不使用活体,"1"
//不使用活体:1
//RGB活体:2
//RGB+NIR活体:3
//RGB+Depth活体:4
private int type = 1;
// 是否开启质量检测开关
private boolean qualityControl = false;
// RGB活体阀值
private float rgbLiveScore = 0.90f;
// NIR活体阀值
private float nirLiveScore = 0.90f;
// Depth活体阀值
private float depthLiveScore = 0.90f;
// 0:奥比中光Astra Mini、Mini S系列(结构光)
// 1:奥比中光 Astra Pro 、Pro S 、蝴蝶(结构光)
// 2:奥比中光Atlas(结构光)
// 3:奥比中光大白、海燕(结构光)
// 4:奥比中光Deeyea(结构光)
// 5:华捷艾米A100S、A200(结构光)
// 6:Pico DCAM710(ToF)
private int cameraType = 1;
// 0:RGB无镜像,1:有镜像
private int mirrorRGB = 1;
// 0:NIR无镜像,1:有镜像
private int mirrorNIR = 0;
// 是否开启属性检测
private boolean attribute = false;
// rgb和nir摄像头宽
private int rgbAndNirWidth = 640;
// rgb和nir摄像头高
private int rgbAndNirHeight = 480;
// depth摄像头宽
private int depthWidth = 640;
// depth摄像头高
private int depthHeight = 480;
public int getRgbAndNirWidth() {
return rgbAndNirWidth;
}
public void setRgbAndNirWidth(int rgbAndNirWidth) {
this.rgbAndNirWidth = rgbAndNirWidth;
}
public int getRgbAndNirHeight() {
return rgbAndNirHeight;
}
public void setRgbAndNirHeight(int rgbAndNirHeight) {
this.rgbAndNirHeight = rgbAndNirHeight;
}
public int getDepthWidth() {
return depthWidth;
}
public void setDepthWidth(int depthWidth) {
this.depthWidth = depthWidth;
}
public int getDepthHeight() {
return depthHeight;
}
public void setDepthHeight(int depthHeight) {
this.depthHeight = depthHeight;
}
public int getCameraType() {
return cameraType;
}
public void setCameraType(int cameraType) {
this.cameraType = cameraType;
}
public float getRgbLiveScore() {
return rgbLiveScore;
}
public void setRgbLiveScore(float rgbLiveScore) {
this.rgbLiveScore = rgbLiveScore;
}
public float getNirLiveScore() {
return nirLiveScore;
}
public void setNirLiveScore(float nirLiveScore) {
this.nirLiveScore = nirLiveScore;
}
public float getDepthLiveScore() {
return depthLiveScore;
}
public void setDepthLiveScore(float depthLiveScore) {
this.depthLiveScore = depthLiveScore;
}
public String getdPass() {
return dPass;
}
public void setdPass(String dPass) {
this.dPass = dPass;
}
public boolean isDebug() {
return debug;
}
public void setDebug(boolean debug) {
this.debug = debug;
}
public int getVideoDirection() {
return videoDirection;
}
public void setVideoDirection(int videoDirection) {
this.videoDirection = videoDirection;
}
public String getDetectFrame() {
return detectFrame;
}
public void setDetectFrame(String detectFrame) {
this.detectFrame = detectFrame;
}
// public int getRadius() {
// return radius;
// }
//
// public void setRadius(int radius) {
// this.radius = radius;
// }
public int getDetectDirection() {
return detectDirection;
}
public void setDetectDirection(int detectDirection) {
this.detectDirection = detectDirection;
}
public String getTrackType() {
return trackType;
}
public void setTrackType(String trackType) {
this.trackType = trackType;
}
public int getMinimumFace() {
return minimumFace;
}
public void setMinimumFace(int minimumFace) {
this.minimumFace = minimumFace;
}
public float getBlur() {
return blur;
}
public void setBlur(float blur) {
this.blur = blur;
}
public int getIllumination() {
return illumination;
}
public void setIllumination(int illumination) {
this.illumination = illumination;
}
public float getGesture() {
return gesture;
}
public void setGesture(float gesture) {
this.gesture = gesture;
}
public float getPitch() {
return pitch;
}
public void setPitch(float pitch) {
this.pitch = pitch;
}
public float getRoll() {
return roll;
}
public void setRoll(float roll) {
this.roll = roll;
}
public float getYaw() {
return yaw;
}
public void setYaw(float yaw) {
this.yaw = yaw;
}
public float getOcclusion() {
return occlusion;
}
public void setOcclusion(float occlusion) {
this.occlusion = occlusion;
}
public float getLeftEye() {
return leftEye;
}
public void setLeftEye(float leftEye) {
this.leftEye = leftEye;
}
public float getRightEye() {
return rightEye;
}
public void setRightEye(float rightEye) {
this.rightEye = rightEye;
}
public float getNose() {
return nose;
}
public void setNose(float nose) {
this.nose = nose;
}
public float getMouth() {
return mouth;
}
public void setMouth(float mouth) {
this.mouth = mouth;
}
public float getLeftCheek() {
return leftCheek;
}
public void setLeftCheek(float leftCheek) {
this.leftCheek = leftCheek;
}
public float getRightCheek() {
return rightCheek;
}
public void setRightCheek(float rightCheek) {
this.rightCheek = rightCheek;
}
public float getChinContour() {
return chinContour;
}
public void setChinContour(float chinContour) {
this.chinContour = chinContour;
}
public float getCompleteness() {
return completeness;
}
public void setCompleteness(float completeness) {
this.completeness = completeness;
}
public int getThreshold() {
return threshold;
}
public void setThreshold(int threshold) {
this.threshold = threshold;
}
public int getActiveModel() {
return activeModel;
}
public void setActiveModel(int activeModel) {
this.activeModel = activeModel;
}
public int getTimeLapse() {
return timeLapse;
}
public void setTimeLapse(int timeLapse) {
this.timeLapse = timeLapse;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public boolean isQualityControl() {
return qualityControl;
}
public void setQualityControl(boolean qualityControl) {
this.qualityControl = qualityControl;
}
public Boolean getDisplay() {
return display;
}
public void setDisplay(Boolean display) {
this.display = display;
}
public Boolean getNirOrDepth() {
return isNirOrDepth;
}
public void setNirOrDepth(Boolean nirOrDepth) {
isNirOrDepth = nirOrDepth;
}
public int getMirrorRGB() {
return mirrorRGB;
}
public void setMirrorRGB(int mirrorRGB) {
this.mirrorRGB = mirrorRGB;
}
public int getMirrorNIR() {
return mirrorNIR;
}
public void setMirrorNIR(int mirrorNIR) {
this.mirrorNIR = mirrorNIR;
}
public Boolean getRgbRevert() {
return rgbRevert;
}
public void setRgbRevert(Boolean rgbRevert) {
this.rgbRevert = rgbRevert;
}
public boolean isAttribute() {
return attribute;
}
public void setAttribute(boolean attribute) {
this.attribute = attribute;
}
}
... ...
package com.baidu.idl.main.facesdk.model;
/**
* @Time: 2019/5/24
* @Author: v_zhangxiaoqing01
*/
public class GlobalSet {
// 模型在asset 下path 为空
public static final String PATH = "";
// 模型在SD 卡下写对应的绝对路径
// public static final String PATH = "/storage/emulated/0/baidu_face/model/";
public static final int FEATURE_SIZE = 512;
public static final String TIME_TAG = "face_time";
// 遮罩比例
public static final float SURFACE_RATIO = 0.6f;
public static final String ACTIVATE_KEY = "faceexample-face-android-1";
public static final String LICENSE_FILE_NAME = "idl-license.face-android";
public static final String DETECT_VIS_MODEL = PATH
+ "detect/detect_rgb-customized-pa-faceid4_0.model.int8-0.0.6.1";
public static final String DETECT_NIR_MODE = PATH
+ "detect/detect_nir-faceboxes-pa-faceid4_0.model.int8-0.0.4.2";
public static final String ALIGN_RGB_MODEL = PATH
+ "align/align_rgb-customized-ca-paddle_6_4_0_1.model.float32-6.4.0.2";
public static final String ALIGN_NIR_MODEL = PATH
+ "align/align-mobilenet-pa-nir_faceid4_0.model.int8-0.7.4.2";
public static final String ALIGN_TRACK_MODEL = PATH
+ "align/align-customized-pa-mobile.model.float32-0.7.5.2";
public static final String LIVE_VIS_MODEL = PATH
+ "silent_live/liveness_rgb-customized-pa-mobile.model.float32-4.1.10.1";
public static final String LIVE_NIR_MODEL = PATH
+ "silent_live/liveness_nir-customized-pa-paddle_lite.model.int8-4.1.5.1";
public static final String LIVE_DEPTH_MODEL = PATH
+ "silent_live/liveness_depth-customized-pa-autodl_mobile.model.int8-4.1.7.1.lite";
public static final String RECOGNIZE_VIS_MODEL = PATH
+ "feature/feature_live-mnasnet-pa-mnasv4_int8.model.int8-1.0.27.1";
public static final String RECOGNIZE_IDPHOTO_MODEL = PATH
+ "feature/feature_id-mnasnet-pa-dynamic_norm.model.float32-1.0.16.1";
public static final String RECOGNIZE_NIR_MODEL = PATH
+ "feature/feature_nir-customized-pa-nir_91p5.model.float32-1.0.11.2";
public static final String RECOGNIZE_RGBD_MODEL = PATH
+ "feature/feature_rgbd-mnasnet-pa-RGBD98p54.model.float32-1.0.15.2";
public static final String OCCLUSION_MODEL = PATH
+ "occlusion/occlusion-customized-pa-mobile.model.float32-2.0.4.1";
public static final String BLUR_MODEL = PATH
+ "blur/blur-customized-pa-server.model.float32-3.0.2.1.lite";
public static final String ATTRIBUTE_MODEL = PATH
+ "attribute/attribute-customized-pa-mobile.model.float32-1.0.9.2";
public static final String EMOTION_MODEL = PATH
+ "";
public static final String GAZE_MODEL = PATH
+ "gaze/gaze-customized-pa-mobile.model.float32.lite";
public static final String MOUTH_MASK = PATH
+ "mouth_mask/mouth_mask-customized-pa-paddlie_lite.model.float32-1.0.0.1";
// 图片尺寸限制大小
public static final int PICTURE_SIZE = 1000000;
// 摄像头类型
public static final String TYPE_CAMERA = "TYPE_CAMERA";
public static final int ORBBEC = 1;
public static final int IMIMECT = 2;
public static final int ORBBECPRO = 3;
public static final int ORBBECPROS1 = 4;
public static final int ORBBECPRODABAI = 5;
public static final int ORBBECPRODEEYEA = 6;
public static final int ORBBECATLAS = 7;
}
... ...
/*
* Copyright (C) 2018 Baidu, Inc. All Rights Reserved.
*/
package com.baidu.idl.main.facesdk.model;
/**
* 组实体类
*/
public class Group {
private String groupId = "";
private String desc = "";
private long ctime;
private boolean isChecked; // 用于多选框
public Group() {
}
public String getGroupId() {
return groupId;
}
public void setGroupId(String groupId) {
this.groupId = groupId;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
public long getCtime() {
return ctime;
}
public void setCtime(long ctime) {
this.ctime = ctime;
}
public boolean isChecked() {
return isChecked;
}
public void setChecked(boolean checked) {
isChecked = checked;
}
}
... ...
/*
* Copyright (C) 2018 Baidu, Inc. All Rights Reserved.
*/
package com.baidu.idl.main.facesdk.model;
import com.baidu.idl.main.facesdk.FaceInfo;
public class LivenessModel {
private int faceDetectCode;
private FaceInfo faceInfo;
private long rgbDetectDuration;
private long irDetectDuration;
private long rgbLivenessDuration;
private float irLivenessScore;
private long irLivenessDuration;
private long depthtLivenessDuration;
private float rgbLivenessScore;
private float depthLivenessScore;
private int liveType;
private float[] landmarks;
private byte[] feature;
private float featureScore;
private float featureCode;
private long featureDuration;
private long checkDuration;
private BDFaceImageInstance bdFaceImageInstance;
private User user;
private int[] shape;
private FaceInfo[] trackFaceInfo;
private long allDetectDuration;
public BDFaceImageInstance getBdFaceImageInstance() {
return bdFaceImageInstance;
}
public void setBdFaceImageInstance(BDFaceImageInstance bdFaceImageInstance) {
this.bdFaceImageInstance = bdFaceImageInstance;
}
public long getAllDetectDuration() {
return allDetectDuration;
}
public void setAllDetectDuration(long allDetectDuration) {
this.allDetectDuration = allDetectDuration;
}
public byte[] getFeature() {
return feature;
}
public void setFeature(byte[] feature) {
this.feature = feature;
}
public float[] getLandmarks() {
return landmarks;
}
public void setLandmarks(float[] landmarks) {
this.landmarks = landmarks;
}
public int[] getShape() {
return shape;
}
public void setShape(int[] shape) {
this.shape = shape;
}
public FaceInfo[] getTrackFaceInfo() {
return trackFaceInfo;
}
public void setTrackFaceInfo(FaceInfo[] trackFaceInfo) {
this.trackFaceInfo = trackFaceInfo;
}
public int getFaceDetectCode() {
return faceDetectCode;
}
public void setFaceDetectCode(int faceDetectCode) {
this.faceDetectCode = faceDetectCode;
}
public FaceInfo getFaceInfo() {
return faceInfo;
}
public void setFaceInfo(FaceInfo faceInfo) {
this.faceInfo = faceInfo;
}
public long getRgbDetectDuration() {
return rgbDetectDuration;
}
public void setRgbDetectDuration(long rgbDetectDuration) {
this.rgbDetectDuration = rgbDetectDuration;
}
public long getIrDetectDuration() {
return irDetectDuration;
}
public void setIrDetectDuration(long irDetectDuration) {
this.irDetectDuration = irDetectDuration;
}
public float getRgbLivenessDuration() {
return rgbLivenessDuration;
}
public void setRgbLivenessDuration(long rgbLivenessDuration) {
this.rgbLivenessDuration = rgbLivenessDuration;
}
public float getIrLivenessScore() {
return irLivenessScore;
}
public void setIrLivenessScore(float irLivenessScore) {
this.irLivenessScore = irLivenessScore;
}
public long getIrLivenessDuration() {
return irLivenessDuration;
}
public void setIrLivenessDuration(long irLivenessDuration) {
this.irLivenessDuration = irLivenessDuration;
}
public long getDepthtLivenessDuration() {
return depthtLivenessDuration;
}
public void setDepthtLivenessDuration(long depthtLivenessDuration) {
this.depthtLivenessDuration = depthtLivenessDuration;
}
public float getRgbLivenessScore() {
return rgbLivenessScore;
}
public void setRgbLivenessScore(float rgbLivenessScore) {
this.rgbLivenessScore = rgbLivenessScore;
}
public float getDepthLivenessScore() {
return depthLivenessScore;
}
public void setDepthLivenessScore(float depthLivenessScore) {
this.depthLivenessScore = depthLivenessScore;
}
public int getLiveType() {
return liveType;
}
public void setLiveType(int liveType) {
this.liveType = liveType;
}
public float getFeatureScore() {
return featureScore;
}
public void setFeatureScore(float featureScore) {
this.featureScore = featureScore;
}
public long getFeatureDuration() {
return featureDuration;
}
public void setFeatureDuration(long featureDuration) {
this.featureDuration = featureDuration;
}
public long getCheckDuration() {
return checkDuration;
}
public void setCheckDuration(long checkDuration) {
this.checkDuration = checkDuration;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public float getFeatureCode() {
return featureCode;
}
public void setFeatureCode(float featureCode) {
this.featureCode = featureCode;
}
}
... ...
package com.baidu.idl.main.facesdk.model;
public class ResponseGetRecords {
// 设备指纹
private String deviceId;
// 记录详细内容
private String records;
// 记录的唯一记录
private String logId;
// 用户id
private String userId;
// 识别分数
private String score;
// 具体识别结果,时间戳形式,如:1529485386691
private String time;
// 用户名
private String userName;
// 用户所在组
private String groupId;
private String faceToken;
// 所在序列
private String index;
public String getIndex() {
return index;
}
public void setIndex(String index) {
this.index = index;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getGroupId() {
return groupId;
}
public void setGroupId(String groupId) {
this.groupId = groupId;
}
public String getFaceToken() {
return faceToken;
}
public void setFaceToken(String faceToken) {
this.faceToken = faceToken;
}
public String getDeviceId() {
return deviceId;
}
public void setDeviceId(String deviceId) {
this.deviceId = deviceId;
}
public String getRecords() {
return records;
}
public void setRecords(String records) {
this.records = records;
}
public String getLogId() {
return logId;
}
public void setLogId(String logId) {
this.logId = logId;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getScore() {
return score;
}
public void setScore(String score) {
this.score = score;
}
public String getTime() {
return time;
}
public void setTime(String time) {
this.time = time;
}
}
... ...
package com.baidu.idl.main.facesdk.model;
/**
* author : shangrong
* date : 2019/5/23 11:23 AM
* description :配置BaseConfig单例
*/
public class SingleBaseConfig {
private static BaseConfig baseConfig;
private SingleBaseConfig() {
}
public static BaseConfig getBaseConfig() {
if (baseConfig == null) {
baseConfig = new BaseConfig();
}
return baseConfig;
}
public static void copyInstance(BaseConfig result) {
baseConfig = result;
}
}
... ...
/*
* Copyright (C) 2018 Baidu, Inc. All Rights Reserved.
*/
package com.baidu.idl.main.facesdk.model;
import android.util.Base64;
/**
* 用户实体类
*/
public class User {
private int id;
private String userId = "";
private String userName = "";
private String groupId = "";
private long ctime;
private long updateTime;
private String userInfo = "";
private String faceToken = "";
private String imageName = "";
private byte[] feature;
private boolean isChecked;
public User() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getGroupId() {
return groupId;
}
public void setGroupId(String groupId) {
this.groupId = groupId;
}
public long getCtime() {
return ctime;
}
public void setCtime(long ctime) {
this.ctime = ctime;
}
public long getUpdateTime() {
return updateTime;
}
public void setUpdateTime(long updateTime) {
this.updateTime = updateTime;
}
public String getUserInfo() {
return userInfo;
}
public void setUserInfo(String userInfo) {
this.userInfo = userInfo;
}
public String getFaceToken() {
if (feature != null) {
byte[] base = Base64.encode(feature, Base64.NO_WRAP);
faceToken = new String(base);
}
return faceToken;
}
public void setFaceToken(String faceToken) {
this.faceToken = faceToken;
}
public String getImageName() {
return imageName;
}
public void setImageName(String imageName) {
this.imageName = imageName;
}
public byte[] getFeature() {
return feature;
}
public void setFeature(byte[] feature) {
this.feature = feature;
}
public boolean isChecked() {
return isChecked;
}
public void setChecked(boolean checked) {
isChecked = checked;
}
}
... ...
/*
* Copyright (C) 2011 Baidu Inc. All rights reserved.
*/
package com.baidu.idl.main.facesdk.utils;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.media.ExifInterface;
import android.net.Uri;
import android.util.Base64;
import android.util.Log;
import com.baidu.idl.main.facesdk.model.BDFaceImageInstance;
import com.baidu.idl.main.facesdk.model.BDFaceSDKCommon;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
/**
* 这个类提供一些操作Bitmap的方法
*/
public final class BitmapUtils {
/**
* 图像的旋转方向是0
*/
public static final int ROTATE0 = 0;
/**
* 图像的旋转方向是90
*/
public static final int ROTATE90 = 90;
/**
* 图像的旋转方向是180
*/
public static final int ROTATE180 = 180;
/**
* 图像的旋转方向是270
*/
public static final int ROTATE270 = 270;
/**
* 图像的旋转方向是360
*/
public static final int ROTATE360 = 360;
/**
* 图片太大内存溢出后压缩的比例
*/
public static final int PIC_COMPRESS_SIZE = 4;
/**
* 图像压缩边界
*/
public static final int IMAGEBOUND = 128;
/**
* 图片显示最大边的像素
*/
public static final int MAXLENTH = 1024;
/**
* Log TAG
*/
private static final String TAG = "ImageUtils";
/**
* Log switch
*/
private static final boolean DEBUG = false;
/**
* 保存图片的质量:100
*/
private static final int QUALITY = 100;
/**
* 默认的最大尺寸
*/
private static final int DEFAULT_MAX_SIZE_CELL_NETWORK = 600;
/**
* 题编辑wifi环境下压缩的最大尺寸
*/
private static final int QUESTION_MAX_SIZE_CELL_NETWORK = 1024;
/**
* 图片压缩的质量
*/
private static final int QUESTION_IMAGE_JPG_QUALITY = 75;
/**
* 默认的图片压缩的质量
*/
private static final int DEFAULT_IMAGE_JPG_QUALITY = 50;
/**
* 网络请求超时时间
*/
private static final int CONNECTTIMEOUT = 3000;
/**
* Private constructor to prohibit nonsense instance creation.
*/
private BitmapUtils() {
}
/**
* 得到要显示的图片数据
*/
public static Bitmap createBitmap(byte[] data, float orientation) {
Bitmap bitmap = null;
Bitmap transformed = null;
BitmapFactory.Options opts = new BitmapFactory.Options();
try {
int width = 2000;
int hight = 2000;
int min = Math.min(width, hight);
opts.inJustDecodeBounds = true;
BitmapFactory.decodeByteArray(data, 0, data.length, opts);
opts.inSampleSize =
BitmapUtils.computeSampleSize(opts, min, BitmapUtils.MAXLENTH * BitmapUtils.MAXLENTH);
opts.inJustDecodeBounds = false;
bitmap = BitmapFactory.decodeByteArray(data, 0, data.length, opts);
transformed = BitmapUtils.rotateBitmap(orientation, bitmap);
} catch (OutOfMemoryError e) {
e.printStackTrace();
if (bitmap != null && !bitmap.isRecycled()) {
bitmap.recycle();
bitmap = null;
}
if (transformed != null && !transformed.isRecycled()) {
transformed.recycle();
transformed = null;
}
opts.inJustDecodeBounds = true;
BitmapFactory.decodeByteArray(data, 0, data.length, opts);
opts.inSampleSize =
BitmapUtils.computeSampleSize(opts, -1, opts.outWidth * opts.outHeight
/ BitmapUtils.PIC_COMPRESS_SIZE);
opts.inJustDecodeBounds = false;
bitmap = BitmapFactory.decodeByteArray(data, 0, data.length, opts);
transformed = BitmapUtils.rotateBitmap(orientation, bitmap);
}
if (transformed != bitmap && bitmap != null) {
bitmap.recycle();
bitmap = null;
}
return transformed;
}
/**
* 根据从数据中读到的方向旋转图片
*
* @param orientation 图片方向
* @param bitmap 要旋转的bitmap
* @return 旋转后的图片
*/
public static Bitmap rotateBitmap(float orientation, Bitmap bitmap) {
Bitmap transformed;
Matrix m = new Matrix();
if (orientation == 0) {
transformed = bitmap;
} else {
m.setRotate(orientation);
transformed = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), m, true);
}
return transformed;
}
/**
* 获取无损压缩图片合适的压缩比例
*
* @param options 图片的一些设置项
* @param minSideLength 最小边长
* @param maxNumOfPixels 最大的像素数目
* @return 返回合适的压缩值
*/
public static int computeSampleSize(BitmapFactory.Options options, int minSideLength, int maxNumOfPixels) {
int initialSize = BitmapUtils.computeInitialSampleSize(options, minSideLength, maxNumOfPixels);
int roundedSize;
if (initialSize <= 8) { // SUPPRESS CHECKSTYLE
roundedSize = 1;
while (roundedSize < initialSize) {
roundedSize <<= 1;
}
} else {
roundedSize = (initialSize + 7) / 8 * 8; // SUPPRESS CHECKSTYLE
}
return roundedSize;
}
/**
* 获取无损压缩图片的压缩比
*
* @param options 图片的一些设置项
* @param minSideLength 最小边长
* @param maxNumOfPixels 最大的像素数目
* @return 返回合适的压缩值
*/
public static int computeInitialSampleSize(BitmapFactory.Options options, int minSideLength,
int maxNumOfPixels) {
double w = options.outWidth;
double h = options.outHeight;
int lowerBound = (maxNumOfPixels == -1) ? 1 : (int) Math.ceil(Math.sqrt(w * h / maxNumOfPixels));
int upperBound =
(minSideLength == -1) ? BitmapUtils.IMAGEBOUND : (int) Math.min(
Math.floor(w / minSideLength), Math.floor(h / minSideLength));
if (upperBound < lowerBound) {
return lowerBound;
}
if ((maxNumOfPixels == -1) && (minSideLength == -1)) {
return 1;
} else if (minSideLength == -1) {
return lowerBound;
} else {
return upperBound;
}
}
/**
* 解析图片的旋转方向
*
* @param path 图片的路径
* @return 旋转角度
*/
public static int decodeImageDegree(String path) {
int degree = ROTATE0;
try {
ExifInterface exifInterface = new ExifInterface(path);
int orientation =
exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_NORMAL);
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
degree = ROTATE90;
break;
case ExifInterface.ORIENTATION_ROTATE_180:
degree = ROTATE180;
break;
case ExifInterface.ORIENTATION_ROTATE_270:
degree = ROTATE270;
break;
default:
degree = ROTATE0;
break;
}
} catch (Exception e) {
e.printStackTrace();
degree = ROTATE0;
}
return degree;
}
/**
* 等比压缩图片
*
* @param bitmap 原图
* @param scale 压缩因子
* @return 压缩后的图片
*/
private static Bitmap scale(Bitmap bitmap, float scale) {
Matrix matrix = new Matrix();
matrix.postScale(scale, scale);
return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
}
/**
* 尺寸缩放
*
* @param bitmap bitmap
* @param w width
* @param h height
* @return scaleBitmap
*/
public static Bitmap scale(Bitmap bitmap, int w, int h) {
if (bitmap == null) {
return null;
}
int width = bitmap.getWidth();
int height = bitmap.getHeight();
Matrix matrix = new Matrix();
float scaleWidth = ((float) w / width);
float scaleHeight = ((float) h / height);
matrix.postScale(scaleWidth, scaleHeight);
return Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true);
}
/**
* 等比压缩图片
*
* @param resBitmap 原图
* @param desWidth 压缩后图片的宽度
* @param desHeight 压缩后图片的高度
* @return 压缩后的图片
*/
public static Bitmap calculateInSampleSize(Bitmap resBitmap, int desWidth, int desHeight) {
int resWidth = resBitmap.getWidth();
int resHeight = resBitmap.getHeight();
if (resHeight > desHeight || resWidth > desWidth) {
// 计算出实际宽高和目标宽高的比率
final float heightRatio = (float) desHeight / (float) resHeight;
final float widthRatio = (float) desWidth / (float) resWidth;
float scale = heightRatio < widthRatio ? heightRatio : widthRatio;
return scale(resBitmap, scale);
}
return resBitmap;
}
public static Uri saveTakePictureImage(Context context, byte[] data) {
File file = context.getExternalFilesDir("file1");
file = new File(file.getAbsolutePath() + File.separator + System.currentTimeMillis() + ".jpg");
FileOutputStream fout = null;
try {
fout = new FileOutputStream(file);
fout.write(data);
fout.flush();
} catch (Exception e) {
e.printStackTrace();
// 异常时删除保存失败的文件
try {
if (file != null && file.exists() && file.isFile()) {
file.delete();
}
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
} finally {
if (fout != null) {
try {
fout.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Log.e(TAG, file.getAbsolutePath());
return Uri.fromFile(file);
}
public static Bitmap yuv2Bitmap(byte[] data, int width, int height) {
int frameSize = width * height;
int[] rgba = new int[frameSize];
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
int y = (0xff & ((int) data[i * width + j]));
int u = (0xff & ((int) data[frameSize + (i >> 1) * width + (j & ~1) + 0]));
int v = (0xff & ((int) data[frameSize + (i >> 1) * width + (j & ~1) + 1]));
y = y < 16 ? 16 : y;
int r = Math.round(1.164f * (y - 16) + 1.596f * (v - 128));
int g = Math.round(1.164f * (y - 16) - 0.813f * (v - 128) - 0.391f * (u - 128));
int b = Math.round(1.164f * (y - 16) + 2.018f * (u - 128));
r = r < 0 ? 0 : (r > 255 ? 255 : r);
g = g < 0 ? 0 : (g > 255 ? 255 : g);
b = b < 0 ? 0 : (b > 255 ? 255 : b);
rgba[i * width + j] = 0xff000000 + (b << 16) + (g << 8) + r;
}
}
Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
bmp.setPixels(rgba, 0, width, 0, 0, width, height);
return bmp;
}
public static Bitmap Depth2Bitmap(byte[] depthBytes, int width, int height) {
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
int[] argbData = new int[width * height];
for (int i = 0; i < width * height; i++) {
argbData[i] = (((int) depthBytes[i * 2] + depthBytes[i * 2 + 1] * 256) / 10 & 0x000000ff)
| ((((int) depthBytes[i * 2] + depthBytes[i * 2 + 1] * 256) / 10) & 0x000000ff) << 8
| ((((int) depthBytes[i * 2] + depthBytes[i * 2 + 1] * 256) / 10) & 0x000000ff) << 16
| 0xff000000;
}
bitmap.setPixels(argbData, 0, width, 0, 0, width, height);
return bitmap;
}
public static Bitmap RGB2Bitmap(byte[] bytes, int width, int height) {
// use Bitmap.Config.ARGB_8888 instead of type is OK
Bitmap stitchBmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
byte[] rgba = new byte[width * height * 4];
for (int i = 0; i < width * height; i++) {
byte b1 = bytes[i * 3 + 0];
byte b2 = bytes[i * 3 + 1];
byte b3 = bytes[i * 3 + 2];
// set value
rgba[i * 4 + 0] = b1;
rgba[i * 4 + 1] = b2;
rgba[i * 4 + 2] = b3;
rgba[i * 4 + 3] = (byte) 255;
}
stitchBmp.copyPixelsFromBuffer(ByteBuffer.wrap(rgba));
return stitchBmp;
}
public static Bitmap BGR2Bitmap(byte[] bytes, int width, int height) {
// use Bitmap.Config.ARGB_8888 instead of type is OK
Bitmap stitchBmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
byte[] rgba = new byte[width * height * 4];
for (int i = 0; i < width * height; i++) {
byte b1 = bytes[i * 3 + 0];
byte b2 = bytes[i * 3 + 1];
byte b3 = bytes[i * 3 + 2];
// set value
rgba[i * 4 + 0] = b3;
rgba[i * 4 + 1] = b2;
rgba[i * 4 + 2] = b1;
rgba[i * 4 + 3] = (byte) 255;
}
stitchBmp.copyPixelsFromBuffer(ByteBuffer.wrap(rgba));
return stitchBmp;
}
public static byte[] ARGB2R(byte[] bytes, int width, int height) {
byte[] IR = new byte[width * height];
for (int i = 0; i < width * height; i++) {
IR[i] = bytes[i * 4];
}
return IR;
}
public static Bitmap getInstaceBmp(BDFaceImageInstance newInstance) {
Bitmap transBmp = null;
if (newInstance.imageType == BDFaceSDKCommon.BDFaceImageType.BDFACE_IMAGE_TYPE_RGBA) {
transBmp = Bitmap.createBitmap(newInstance.width, newInstance.height, Bitmap.Config.ARGB_8888);
transBmp.copyPixelsFromBuffer(ByteBuffer.wrap(newInstance.data));
} else if (newInstance.imageType == BDFaceSDKCommon.BDFaceImageType.BDFACE_IMAGE_TYPE_BGR) {
transBmp = BitmapUtils.BGR2Bitmap(newInstance.data, newInstance.width, newInstance.height);
} else if (newInstance.imageType == BDFaceSDKCommon.BDFaceImageType.BDFACE_IMAGE_TYPE_YUV_NV21) {
transBmp = BitmapUtils.yuv2Bitmap(newInstance.data, newInstance.width, newInstance.height);
} else if (newInstance.imageType == BDFaceSDKCommon.BDFaceImageType.BDFACE_IMAGE_TYPE_GRAY) {
transBmp = Depth2Bitmap(newInstance.data, newInstance.width, newInstance.height);
}
return transBmp;
}
/**
* 获取图片数据
* @param path
* @return
*/
public static Bitmap getBitmap(String path) {
FileInputStream fis;
Bitmap bm = null;
try {
fis = new FileInputStream(path);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 8;//图片的长宽都是原来的1/8
BufferedInputStream bis = new BufferedInputStream(fis);
bm = BitmapFactory.decodeStream(bis, null, options);
fis.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return bm;
}
public static Bitmap base64ToBitmap(String base64String) {
base64String = Uri.decode(base64String);
byte[] decode = Base64.decode(base64String, Base64.DEFAULT);
Bitmap bitmap = BitmapFactory.decodeByteArray(decode, 0, decode.length);
return bitmap;
}
}
... ...
/**
* Copyright (C) 2017 Baidu Inc. All rights reserved.
*/
package com.baidu.idl.main.facesdk.utils;
import android.content.Context;
import android.os.Build;
import android.text.TextUtils;
/**
* 显示设备信息工具类
*/
public final class DensityUtils {
/**
* 四舍五入
*/
private static final float DOT_FIVE = 0.5f;
/**
* portrait degree:90
*/
private static final int PORTRAIT_DEGREE_90 = 90;
/**
* portrait degree:270
*/
private static final int PORTRAIT_DEGREE_270 = 270;
/**
* 需要比较的设置型号
*/
private static final String[] BUILD_MODELS = {
"i700v", // 斐讯i700v
"A862W", // 夏新A862W
"V8526" // 桑菲V8526
};
/**
* Private constructor to prohibit nonsense instance creation.
*/
private DensityUtils() {
}
/**
* sp转px.
*
* @param context context
* @param spValue spValue
* @return 换算后的px值
*/
public static int sp2px(Context context, float spValue) {
final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
return (int) (spValue * fontScale + 0.5f);
}
/**
* dip转换成px
*
* @param context Context
* @param dip dip Value
* @return 换算后的px值
*/
public static int dip2px(Context context, float dip) {
float density = getDensity(context);
return (int) (dip * density + DensityUtils.DOT_FIVE);
}
/**
* px转换成dip
*
* @param context Context
* @param px px Value
* @return 换算后的dip值
*/
public static int px2dip(Context context, float px) {
float density = getDensity(context);
return (int) (px / density + DOT_FIVE);
}
/**
* 得到显示宽度
*
* @param context Context
* @return 宽度
*/
public static int getDisplayWidth(Context context) {
return context.getResources().getDisplayMetrics().widthPixels;
}
/**
* 得到显示高度
*
* @param context Context
* @return 高度
*/
public static int getDisplayHeight(Context context) {
return context.getResources().getDisplayMetrics().heightPixels;
}
/**
* 得到显示密度
*
* @param context Context
* @return 密度
*/
public static float getDensity(Context context) {
return context.getResources().getDisplayMetrics().density;
}
/**
* 得到DPI
*
* @param context Context
* @return DPI
*/
public static int getDensityDpi(Context context) {
return context.getResources().getDisplayMetrics().densityDpi;
}
/**
* 判断当前Android系统摄像头旋转多少度
*
* @return 当前Android系统能竖着屏幕,
* 正常应该旋转90度,
* 但是斐讯i700v、夏新A862W、桑菲V8526需要旋转270度
*/
public static int getPortraitDegree() {
int degree = PORTRAIT_DEGREE_90;
// 为了更好的扩展更多的特殊设置型号,将要比较的设备型号提成一个数组,遍历这个数据。
for (String model : BUILD_MODELS) {
if (TextUtils.equals(model, Build.MODEL)) {
degree = PORTRAIT_DEGREE_270;
break;
}
}
return degree;
}
}
\ No newline at end of file
... ...
package com.baidu.idl.main.facesdk.utils;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.RectF;
import android.view.TextureView;
import com.baidu.idl.main.facesdk.FaceInfo;
import com.baidu.idl.main.facesdk.camera.AutoTexturePreviewView;
import com.baidu.idl.main.facesdk.camera.PicoAutoTexturePreviewView;
import com.baidu.idl.main.facesdk.model.BDFaceImageInstance;
/**
* Created by ShiShuaiFeng on 2019/6/5.
*/
public class FaceOnDrawTexturViewUtil {
private FaceOnDrawTexturViewUtil() {
}
/**
* 通过中心点坐标(x,y) 和 width ,绘制Rect
*
* @param faceInfo
* @return
*/
public static Rect getFaceRectTwo(FaceInfo faceInfo) {
Rect rect = new Rect();
rect.top = (int) ((faceInfo.centerY - faceInfo.width / 2));
rect.left = (int) ((faceInfo.centerX - faceInfo.width / 2));
rect.right = (int) ((faceInfo.centerX + faceInfo.width / 2));
rect.bottom = (int) ((faceInfo.centerY + faceInfo.width / 2));
return rect;
}
public static void mapFromOriginalRect(RectF rectF,
AutoTexturePreviewView autoTexturePreviewView,
BDFaceImageInstance imageFrame) {
// 获取屏幕的宽
int selfWidth = autoTexturePreviewView.getPreviewWidth();
// 获取屏幕的高
int selfHeight = autoTexturePreviewView.getPreviewHeight();
// 新建矩阵对象
Matrix matrix = new Matrix();
// 当屏幕宽度/图像宽度>屏幕高度/图像高度时
if (selfWidth * imageFrame.height > selfHeight * imageFrame.width) {
// 将高度按照宽度比进行缩放
int targetHeight = imageFrame.height * selfWidth / imageFrame.width;
// 计算平移距离
int delta = (targetHeight - selfHeight) / 2;
// 计算宽度比
float ratio = 1.0f * selfWidth / imageFrame.width;
// 设置矩阵变换缩放比
matrix.postScale(ratio, ratio);
// 设置变换矩阵的平移距离
matrix.postTranslate(0, -delta);
} else {
// 将宽度按照高度比进行缩放
int targetWith = imageFrame.width * selfHeight / imageFrame.height;
// 计算平移距离
int delta = (targetWith - selfWidth) / 2;
// 计算宽度比
float ratio = 1.0f * selfHeight / imageFrame.height;
// 设置矩阵变换缩放比
matrix.postScale(ratio, ratio);
// 设置变换矩阵的平移距离
matrix.postTranslate(-delta, 0);
}
// 对人脸框数据进行矩阵变换
matrix.mapRect(rectF);
}
public static void mapFromOriginalRect(RectF rectF,
TextureView textureView,
BDFaceImageInstance imageFrame) {
int selfWidth = textureView.getWidth();
int selfHeight = textureView.getHeight();
Matrix matrix = new Matrix();
if (selfWidth * imageFrame.height > selfHeight * imageFrame.width) {
int targetHeight = imageFrame.height * selfWidth / imageFrame.width;
int delta = (targetHeight - selfHeight) / 2;
float ratio = 1.0f * selfWidth / imageFrame.width;
matrix.postScale(ratio, ratio);
matrix.postTranslate(0, -delta);
} else {
int targetWith = imageFrame.width * selfHeight / imageFrame.height;
int delta = (targetWith - selfWidth) / 2;
float ratio = 1.0f * selfHeight / imageFrame.height;
matrix.postScale(ratio, ratio);
matrix.postTranslate(-delta, 0);
}
matrix.mapRect(rectF);
}
public static void mapFromOriginalRect(RectF rectF,
PicoAutoTexturePreviewView textureView,
BDFaceImageInstance imageFrame) {
int selfWidth = textureView.getWidth();
int selfHeight = textureView.getHeight();
Matrix matrix = new Matrix();
if (selfWidth * imageFrame.height > selfHeight * imageFrame.width) {
int targetHeight = imageFrame.height * selfWidth / imageFrame.width;
int delta = (targetHeight - selfHeight) / 2;
float ratio = 1.0f * selfWidth / imageFrame.width;
matrix.postScale(ratio, ratio);
matrix.postTranslate(0, -delta);
} else {
int targetWith = imageFrame.width * selfHeight / imageFrame.height;
int delta = (targetWith - selfWidth) / 2;
float ratio = 1.0f * selfHeight / imageFrame.height;
matrix.postScale(ratio, ratio);
matrix.postTranslate(-delta, 0);
}
matrix.mapRect(rectF);
}
public static void converttPointXY(float[] pointXY, AutoTexturePreviewView textureView,
BDFaceImageInstance imageFrame, float width) {
int selfWidth = textureView.getWidth();
int selfHeight = textureView.getHeight();
if (selfWidth * imageFrame.height > selfHeight * imageFrame.width) {
int targetHeight = imageFrame.height * selfWidth / imageFrame.width;
int delta = (targetHeight - selfHeight) / 2;
float ratio = 1.0f * selfWidth / imageFrame.width;
pointXY[0] = pointXY[0] * ratio;
pointXY[1] = pointXY[1] * ratio;
pointXY[1] = pointXY[1] - delta;
pointXY[2] = width * ratio;
} else {
int targetWith = imageFrame.width * selfHeight / imageFrame.height;
int delta = (targetWith - selfWidth) / 2;
float ratio = 1.0f * selfHeight / imageFrame.height;
pointXY[0] = pointXY[0] * ratio;
pointXY[1] = pointXY[1] * ratio;
pointXY[0] = pointXY[0] - delta;
pointXY[2] = width * ratio;
}
}
}
... ...
package com.baidu.idl.main.facesdk.utils;
import android.graphics.Bitmap;
import android.os.Environment;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
/**
* author : shangrong
* date : 2019/5/23 2:05 PM
* description :文件工具类
*/
public class FileUtils {
/**
* 读取txt文件的内容
*
* @param filePath 想要读取的文件对象
* @return 返回文件内容
*/
public static String txt2String(String filePath) {
File file = new File(filePath);
if (!file.exists()) {
return "";
}
StringBuilder result = new StringBuilder();
try {
BufferedReader br = new BufferedReader(new FileReader(file));//构造一个BufferedReader类来读取文件
String s = null;
while ((s = br.readLine()) != null) {//使用readLine方法,一次读一行
result.append(System.lineSeparator() + s);
}
br.close();
} catch (Exception e) {
e.printStackTrace();
}
return result.toString();
}
/**
* 写入TXT文件
*/
public static boolean writeTxtFile(String content, String filePath) {
File file = new File(filePath);
if (!file.exists()) {
return false;
}
RandomAccessFile mm = null;
boolean flag = false;
FileOutputStream fileOutputStream = null;
try {
fileOutputStream = new FileOutputStream(file);
fileOutputStream.write(content.getBytes("utf-8"));
fileOutputStream.close();
flag = true;
} catch (Exception e) {
e.printStackTrace();
}
return flag;
}
/**
* Checks if is sd card available.检查SD卡是否可用
*/
public static boolean isSdCardAvailable() {
return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);
}
/**
* Gets the SD root file.获取SD卡根目录
*/
public static File getSDRootFile() {
if (isSdCardAvailable()) {
return Environment.getExternalStorageDirectory();
} else {
return null;
}
}
/**
* 获取导入图片文件的目录信息
*/
public static File getBatchImportDirectory() {
File sdRootFile = getSDRootFile();
File file = null;
if (sdRootFile != null && sdRootFile.exists()) {
file = new File(sdRootFile, "Face-Import");
if (!file.exists()) {
file.mkdirs();
}
}
return file;
}
/**
* 获取导入图片成功的目录信息
*/
public static File getBatchImportSuccessDirectory() {
File sdRootFile = getSDRootFile();
File file = null;
if (sdRootFile != null && sdRootFile.exists()) {
file = new File(sdRootFile, "Success-Import");
if (!file.exists()) {
file.mkdirs();
}
}
return file;
}
/**
* 判断文件是否存在
*/
public static File isFileExist(String fileDirectory, String fileName) {
File file = new File(fileDirectory + "/" + fileName);
try {
if (!file.exists()) {
return null;
}
} catch (Exception e) {
return null;
}
return file;
}
/**
* 删除文件
*/
public static void deleteFile(String filePath) {
try {
// 找到文件所在的路径并删除该文件
File file = new File(filePath);
file.delete();
} catch (Exception e) {
e.printStackTrace();
}
}
/*
* 获取不带扩展名的文件名
* */
public static String getFileNameNoEx(String filename) {
if ((filename != null) && (filename.length() > 0)) {
int dot = filename.lastIndexOf('.');
if ((dot > -1) && (dot < (filename.length()))) {
return filename.substring(0, dot);
}
}
return filename;
}
/**
* 保存图片
*/
public static boolean saveBitmap(File file, Bitmap bitmap) {
FileOutputStream out = null;
try {
out = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);
return true;
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (out != null) {
out.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
return false;
}
public static boolean copyFile(String oldPath, String newPath) {
InputStream inStream = null;
FileOutputStream fs = null;
boolean result = false;
try {
int bytesum = 0;
int byteread = 0;
File oldfile = new File(oldPath);
// 判断目录是否存在
File newfile = new File(newPath);
File newFileDir = new File(newfile.getPath().replace(newfile.getName(), ""));
if (!newFileDir.exists()) {
newFileDir.mkdirs();
}
if (oldfile.exists()) { // 文件存在时
inStream = new FileInputStream(oldPath); // 读入原文件
fs = new FileOutputStream(newPath);
byte[] buffer = new byte[1444];
int length;
while ((byteread = inStream.read(buffer)) != -1) {
bytesum += byteread; // 字节数 文件大小
System.out.println(bytesum);
fs.write(buffer, 0, byteread);
}
result = true;
} else {
result = false;
}
} catch (Exception e) {
System.out.println("复制单个文件操作出错");
e.printStackTrace();
} finally {
if (inStream != null) {
try {
inStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fs != null) {
try {
fs.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return result;
}
}
... ...
package com.baidu.idl.main.facesdk.utils;
import android.util.Log;
import com.baidu.idl.main.facesdk.model.SingleBaseConfig;
public class LogUtils {
private static boolean isDebug = SingleBaseConfig.getBaseConfig().isDebug();
public static void setIsDebug(boolean isDebug) {
LogUtils.isDebug = isDebug;
}
public static int v(String tag, String msg) {
if (isDebug) {
return Log.v(tag, msg);
} else {
return -1;
}
}
public static int d(String tag, String msg) {
if (isDebug) {
return Log.d(tag, msg);
} else {
return -1;
}
}
public static int i(String tag, String msg) {
if (isDebug) {
return Log.i(tag, msg);
} else {
return -1;
}
}
public static int w(String tag, String msg) {
if (isDebug) {
return Log.w(tag, msg);
} else {
return -1;
}
}
public static int e(String tag, String msg) {
if (isDebug) {
return Log.e(tag, msg);
} else {
return -1;
}
}
}
... ...
package com.baidu.idl.main.facesdk.utils;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.widget.Toast;
public class ToastUtils {
private static Handler handler = new Handler(Looper.getMainLooper());
public static void toast(final Context context, final String text) {
handler.post(new Runnable() {
@Override
public void run() {
Toast.makeText(context, text, Toast.LENGTH_SHORT).show();
}
});
}
public static void toast(final Context context, final int resId) {
handler.post(new Runnable() {
@Override
public void run() {
Toast.makeText(context, resId, Toast.LENGTH_SHORT).show();
}
});
}
}
... ...
package com.baidu.idl.main.facesdk.view;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.util.AttributeSet;
import android.widget.ImageView;
import com.baidu.idl.main.facesdk.R;
import com.baidu.idl.main.facesdk.utils.LogUtils;
/**
* Created by v_liujialu01 on 2018/12/3.
*/
@SuppressLint("AppCompatCustomView")
public class CircleImageView extends ImageView {
private static final String TAG = CircleImageView.class.getSimpleName();
private static final ScaleType SCALE_TYPE = ScaleType.CENTER_CROP;
private static final Bitmap.Config BITMAP_CONFIG = Bitmap.Config.ARGB_8888;
private static final int COLORDRAWABLE_DIMENSION = 2;
private static final int DEFAULT_BORDER_WIDTH = 0;
private static final int DEFAULT_BORDER_COLOR = Color.BLACK;
private final RectF mDrawableRect = new RectF();
private final RectF mBorderRect = new RectF();
private final Matrix mShaderMatrix = new Matrix();
private final Paint mBitmapPaint = new Paint();
private final Paint mBorderPaint = new Paint();
private int mBorderColor = DEFAULT_BORDER_COLOR;
private int mBorderWidth = DEFAULT_BORDER_WIDTH;
private Bitmap mBitmap;
private BitmapShader mBitmapShader;
private int mBitmapWidth;
private int mBitmapHeight;
private float mDrawableRadius;
private float mBorderRadius;
private boolean mReady;
private boolean mSetupPending;
public CircleImageView(Context context) {
super(context);
init();
}
public CircleImageView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CircleImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircleImageView,
defStyle, 0);
mBorderWidth = a.getDimensionPixelSize(R.styleable.CircleImageView_border_width,
DEFAULT_BORDER_WIDTH);
mBorderColor = a.getColor(R.styleable.CircleImageView_border_color, DEFAULT_BORDER_COLOR);
a.recycle();
init();
}
private void init() {
super.setScaleType(SCALE_TYPE);
mReady = true;
if (mSetupPending) {
setup();
mSetupPending = false;
}
}
@Override
public ScaleType getScaleType() {
return SCALE_TYPE;
}
@Override
public void setScaleType(ScaleType scaleType) {
LogUtils.i(TAG, "scaleType = " + scaleType);
}
@Override
public void setAdjustViewBounds(boolean adjustViewBounds) {
if (adjustViewBounds) {
throw new IllegalArgumentException("adjustViewBounds not supported.");
}
}
@Override
protected void onDraw(Canvas canvas) {
if (getDrawable() == null) {
return;
}
canvas.drawCircle(getWidth() / 2, getHeight() / 2, mDrawableRadius, mBitmapPaint);
if (mBorderWidth != 0) {
canvas.drawCircle(getWidth() / 2, getHeight() / 2, mBorderRadius, mBorderPaint);
}
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
setup();
}
public int getBorderColor() {
return mBorderColor;
}
public void setBorderColor(int borderColor) {
if (borderColor == mBorderColor) {
return;
}
mBorderColor = borderColor;
mBorderPaint.setColor(mBorderColor);
invalidate();
}
public int getBorderWidth() {
return mBorderWidth;
}
public void setBorderWidth(int borderWidth) {
if (borderWidth == mBorderWidth) {
return;
}
mBorderWidth = borderWidth;
setup();
}
@Override
public void setImageBitmap(Bitmap bm) {
super.setImageBitmap(bm);
mBitmap = bm;
setup();
}
@Override
public void setImageDrawable(Drawable drawable) {
super.setImageDrawable(drawable);
mBitmap = getBitmapFromDrawable(drawable);
setup();
}
@Override
public void setImageResource(int resId) {
super.setImageResource(resId);
mBitmap = getBitmapFromDrawable(getDrawable());
setup();
}
@Override
public void setImageURI(Uri uri) {
super.setImageURI(uri);
mBitmap = getBitmapFromDrawable(getDrawable());
setup();
}
private Bitmap getBitmapFromDrawable(Drawable drawable) {
try {
if (drawable instanceof BitmapDrawable) {
return ((BitmapDrawable) drawable).getBitmap();
}
Bitmap bitmap;
if (drawable instanceof ColorDrawable) {
bitmap = Bitmap.createBitmap(COLORDRAWABLE_DIMENSION, COLORDRAWABLE_DIMENSION,
BITMAP_CONFIG);
} else {
bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(),
BITMAP_CONFIG);
}
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
drawable.draw(canvas);
return bitmap;
} catch (Exception e) {
LogUtils.e(TAG, "e = " + e.getMessage());
return null;
}
}
private void setup() {
if (!mReady) {
mSetupPending = true;
return;
}
if (mBitmap == null) {
return;
}
int power = 2;
mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
mBitmapPaint.setAntiAlias(true);
mBitmapPaint.setShader(mBitmapShader);
mBorderPaint.setStyle(Paint.Style.STROKE);
mBorderPaint.setAntiAlias(true);
mBorderPaint.setColor(mBorderColor);
mBorderPaint.setStrokeWidth(mBorderWidth);
mBitmapHeight = mBitmap.getHeight();
mBitmapWidth = mBitmap.getWidth();
mBorderRect.set(0, 0, getWidth(), getHeight());
mBorderRadius = Math.min((mBorderRect.height() - mBorderWidth) / power,
(mBorderRect.width() - mBorderWidth) / power);
mDrawableRect.set(mBorderWidth, mBorderWidth,
mBorderRect.width() - mBorderWidth,
mBorderRect.height() - mBorderWidth);
mDrawableRadius = Math.min(mDrawableRect.height() / power, mDrawableRect.width() / power);
updateShaderMatrix();
invalidate();
}
private void updateShaderMatrix() {
float scale;
float dx = 0;
float dy = 0;
float power = 0.5f;
mShaderMatrix.set(null);
if (mBitmapWidth * mDrawableRect.height() > mDrawableRect.width() * mBitmapHeight) {
scale = mDrawableRect.height() / (float) mBitmapHeight;
dx = (mDrawableRect.width() - mBitmapWidth * scale) * power;
} else {
scale = mDrawableRect.width() / (float) mBitmapWidth;
dy = (mDrawableRect.height() - mBitmapHeight * scale) * power;
}
mShaderMatrix.setScale(scale, scale);
mShaderMatrix.postTranslate((int) (dx + power) + mBorderWidth,
(int) (dy + power) + mBorderWidth);
mBitmapShader.setLocalMatrix(mShaderMatrix);
}
}
... ...
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="CircleImageView">
<attr name="border_width" format="dimension" />
<attr name="border_color" format="color" />
</declare-styleable>
</resources>
\ No newline at end of file
... ...
... ... @@ -32,11 +32,7 @@ android {
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'com.android.support:appcompat-v7:28.0.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
implementation 'com.apkfuns.logutils:library:1.4.0'
implementation project(path: ':arcface')
implementation project(path: ':bdface')
// api project(path: ':arcface')
api project(path: ':bdface')
}
... ...
package com.yhkj.rebotsdk.engine
/**
* 状态码
*/
internal class EngineStatusCode {
companion object {
const val SUCCEED = 1000 //操作成功
const val FAILED = -1000 //操作失败
}
}
/**
* 状态
* @param code 状态码
* @param msg 状态消息
*/
internal data class EngineStatusData(val code: Int, val msg: String)
/**
* 人脸识别结果
* @param faceId 人脸记录Id
* @param score 比对分数
*/
internal data class IdentifyData(val faceId: String, val score: Float)
\ No newline at end of file
... ... @@ -2,25 +2,22 @@ package com.yhkj.rebotsdk.engine
import android.content.Context
import android.graphics.Bitmap
import com.baidu.idl.main.facesdk.callback.Callback
import com.baidu.idl.main.facesdk.model.BaseConfig
import com.yhkj.rebotsdk.face.ResultCallBack
internal interface EngineInterface {
/**
* 激活人脸license
* 激活人脸license和初始化model
*/
fun initLicense(
fun init(
context: Context, active_key: String, appId: String,
sdkKey: String
): Int
sdkKey: String,callback: ResultCallBack
)
/**
* 初始化
* @param context 上下文对象
* @return 是否初始化成功
*/
fun init(context: Context?): EngineStatusData
/**
* 销毁
... ... @@ -41,4 +38,5 @@ internal interface EngineInterface {
* 获取视频流中符合条件的人脸
*/
fun getBitmap(byteArray: ByteArray,width: Int,height: Int): Bitmap
}
\ No newline at end of file
... ...
package com.yhkj.rebotsdk.engine
import com.yhkj.rebotsdk.engine.arcface.ArcFaceEngine
import com.yhkj.rebotsdk.engine.baidu.BDFaceEngine
internal class FaceEngineFactory {
fun createEngine() : EngineInterface = ArcFaceEngine()
fun createEngine() : EngineInterface = BDFaceEngine()
fun createArcEngine() : EngineInterface = ArcFaceEngine()
}
\ No newline at end of file
... ...
package com.yhkj.rebotsdk.engine.arcface
//import com.arcsoft.face.FaceEngine
import android.content.Context
import android.graphics.Bitmap
import com.arcsoft.face.FaceEngine
import com.yhkj.rebotsdk.engine.EngineInterface
import com.yhkj.rebotsdk.engine.EngineStatusCode
import com.yhkj.rebotsdk.engine.EngineStatusData
import java.io.File
import java.io.FileInputStream
import java.io.IOException
import com.yhkj.rebotsdk.face.ResultCallBack
/**
* Created by zhangweiwei on 2020/3/16.
... ... @@ -17,18 +13,19 @@ internal class ArcFaceEngine : EngineInterface {
override fun initLicense(
override fun init(
context: Context,
active_key: String,
appId: String,
sdkKey: String
): Int {
return FaceEngine.activeOnline(context, appId, sdkKey)
sdkKey: String, callback: ResultCallBack
) {
// val code=FaceEngine.activeOnline(context, appId, sdkKey)
// callback.onLicenseResult(code,"")
// if(code==0){
// //todo 初始化Model
// }
}
override fun init(context: Context?): EngineStatusData {
TODO("Not yet implemented")
}
override fun unInit() {
TODO("Not yet implemented")
... ... @@ -46,5 +43,4 @@ internal class ArcFaceEngine : EngineInterface {
TODO("Not yet implemented")
}
}
\ No newline at end of file
... ...
... ... @@ -4,37 +4,60 @@ import android.content.Context
import android.graphics.Bitmap
import com.baidu.idl.main.facesdk.FaceAuth
import com.baidu.idl.main.facesdk.callback.Callback
import com.baidu.idl.main.facesdk.listener.SdkInitListener
import com.baidu.idl.main.facesdk.manager.FaceSDKManager
import com.baidu.idl.main.facesdk.model.BaseConfig
import com.baidu.idl.main.facesdk.model.SingleBaseConfig
import com.yhkj.rebotsdk.engine.EngineInterface
import com.yhkj.rebotsdk.engine.EngineStatusData
import com.yhkj.rebotsdk.face.ResultCallBack
/**
* Created by zhangweiwei on 2020/3/16.
*/
internal class BDFaceEngine : EngineInterface {
private var initCode = -1
override fun initLicense(
context: Context,
active_key: String,
appId: String,
sdkKey: String
): Int {
override fun init(
context: Context, active_key: String, appId: String,
sdkKey: String, callback: ResultCallBack
) {
val faceAuth = FaceAuth()
// 在线激活
faceAuth.initLicenseOnLine(
context,
active_key,
Callback { code, response ->
initCode = code
return@Callback
active_key
) { code, response ->
callback.onLicenseResult(code,response)
if(code==0){
FaceSDKManager.getInstance().initModel(context,object :SdkInitListener{
override fun initStart() {
}
override fun initLicenseSuccess() {
}
override fun initLicenseFail(errorCode: Int, msg: String?) {
}
override fun initModelSuccess() {
callback.onModelResult(0,"")
}
override fun initModelFail(errorCode: Int, msg: String?) {
callback.onModelResult(errorCode,msg)
}
})
return initCode
}
override fun init(context: Context?): EngineStatusData {
TODO("Not yet implemented")
}
}
override fun unInit() {
TODO("Not yet implemented")
... ... @@ -53,4 +76,5 @@ internal class BDFaceEngine : EngineInterface {
}
}
\ No newline at end of file
... ...
... ... @@ -3,15 +3,12 @@ package com.yhkj.rebotsdk.face
import android.content.Context
import android.graphics.Bitmap
import com.yhkj.rebotsdk.engine.EngineInterface
import com.yhkj.rebotsdk.engine.EngineStatusCode
import com.yhkj.rebotsdk.engine.FaceEngineFactory
internal class FaceApi private constructor() : FaceInterface {
class FaceApi private constructor() : FaceInterface {
private lateinit var mFaceEngine: EngineInterface
private var mOnEnrollListener: FaceInterface.OnEnrollListener? = null
private var mOnVerifyListener: FaceInterface.OnVerifyListener? = null
companion object {
... ... @@ -21,47 +18,28 @@ internal class FaceApi private constructor() : FaceInterface {
}
override fun initLicense(
context: Context,
active_key: String,
appId: String,
sdkKey: String
): Int {
override fun init(
context: Context, active_key: String, appId: String,
sdkKey: String,callBack: ResultCallBack
) {
mFaceEngine = FaceEngineFactory().createEngine()
return mFaceEngine.initLicense(context, active_key, appId, sdkKey)
mFaceEngine.init(context, active_key, appId, sdkKey,callBack)
}
override fun initFace(context: Context): StatusData {
return mFaceEngine.init(context).let {
when (it.code) {
EngineStatusCode.SUCCEED -> {
StatusData(StatusCode.SUCCEED, it.msg)
}
else -> {
StatusData(StatusCode.FAILED, it.msg)
}
}
}
}
override fun initConfig(context: Context): Boolean {
TODO("Not yet implemented")
}
override fun setEnrollListener(listener: FaceInterface.OnEnrollListener) {
mOnEnrollListener=listener
}
override fun setVerifyListener(listener: FaceInterface.OnVerifyListener) {
mOnVerifyListener=listener
}
override fun release() {
mFaceEngine.unInit()
}
override fun registerFace(context: Context?, nv21: ByteArray?, width: Int, height: Int,
faceData: ByteArray,faceId: String): Boolean {
override fun registerFace(
context: Context?, nv21: ByteArray?, width: Int, height: Int,
faceData: ByteArray, faceId: String
): Boolean {
TODO("Not yet implemented")
}
... ...
... ... @@ -2,77 +2,23 @@ package com.yhkj.rebotsdk.face
import android.content.Context
import android.graphics.Bitmap
import com.baidu.idl.main.facesdk.model.BaseConfig
interface FaceInterface {
/**
* 录入模式监听
*/
interface OnEnrollListener {
/**
* 录入结束(并未注册到人脸引擎中,仅获取到符合录入的人脸数据)
*/
fun onEnrollFinished(fingerData : ByteArray)
/**
* 录入异常
* @param statusData 提示信息
*/
fun onEnrollException(statusData: StatusData)
}
/**
* 识别模式监听
* 激活人脸license并初始化模块
*/
interface OnVerifyListener {
fun init(context:Context,active_key:String,appId:String,
sdkKey:String,callBack: ResultCallBack)
/**
* 人脸识别成功
* @param faceId 人脸Id
*/
fun onVerifySucceed(faceId: String)
/**
* 人脸识别失败
* @param msg 提示消息
*/
fun onVerifyFailed(msg: String)
}
/**
* 激活人脸license
*/
fun initLicense(context:Context,active_key:String,appId:String,
sdkKey:String): Int
/**
* 初始化人脸模块
* @param appId
* @param appKey
*/
fun initFace(context:Context): StatusData
/**
* 初始化配置模块
*/
fun initConfig(context: Context):Boolean
/**
* 设置录入模式监听
* @param listener 录入模式监听
*/
fun setEnrollListener(listener: OnEnrollListener)
/**
* 设置识别模式监听
* @param listener 识别模式监听
*/
fun setVerifyListener(listener: OnVerifyListener)
/**
... ... @@ -106,4 +52,6 @@ interface FaceInterface {
*/
fun getRecognizeBitmap(byteArray: ByteArray,width: Int,height: Int):Bitmap
}
\ No newline at end of file
... ...
package com.yhkj.rebotsdk.face
/**
* Created by zhangweiwei on 2020/3/18.
*/
interface ResultCallBack {
/**
* 回调函数 code 0 : 成功;code 1 加载失败
* @param code
* @param response
*/
fun onLicenseResult(code: Int, response: String?)
fun onModelResult(code: Int,response: String?)
}
\ No newline at end of file
... ...
rootProject.name='rebot_sdk'
include ':sdk', ':arcface', ':bdface'
include ':test'
... ...
/build
... ...
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion 28
buildToolsVersion "29.0.3"
defaultConfig {
applicationId "com.yhkj.test"
minSdkVersion 23
targetSdkVersion 28
versionCode 1
versionName "1.0"
multiDexEnabled true
ndk {
moduleName "facesdk"
ldLibs "log"
abiFilters "armeabi-v7a" // "armeabi", "x86", "arm64-v8a"
}
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
implementation 'com.apkfuns.logutils:library:1.4.0'
api project(path: ':sdk')
}
... ...
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
... ...
package com.yhkj.rebotsdk
package com.yhkj.test
import android.support.test.InstrumentationRegistry
import android.support.test.runner.AndroidJUnit4
... ... @@ -19,6 +19,6 @@ class ExampleInstrumentedTest {
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.yhkj.rebotsdk", appContext.packageName)
assertEquals("com.yhkj.test", appContext.packageName)
}
}
... ...
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.yhkj.test">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme"
tools:replace="android:allowBackup">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".FaceAttributeRGBActivity" />
<activity android:name=".FaceRGBCloseDebugSearchActivity" />
</application>
</manifest>
\ No newline at end of file
... ...
package com.yhkj.test
import android.Manifest
import android.app.Activity
import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle
import android.view.KeyEvent
import android.view.Window
import android.view.WindowManager
import java.util.*
/**
* Created by zhangweiwei on 2020/3/18.
*/
open class BaseActivity: Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 将activity设置为全屏显示
requestWindowFeature(Window.FEATURE_NO_TITLE)
window.setFlags(
WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN
)
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
requestPermissions(99)
}
// 请求权限
fun requestPermissions(requestCode: Int) {
try {
if (Build.VERSION.SDK_INT >= 23) {
val requestPerssionArr =
ArrayList<String>()
val hasCamera =
checkSelfPermission(Manifest.permission.CAMERA)
if (hasCamera != PackageManager.PERMISSION_GRANTED) {
requestPerssionArr.add(Manifest.permission.CAMERA)
}
val hasPhoneStateRead =
checkSelfPermission(Manifest.permission.READ_PHONE_STATE)
if (hasPhoneStateRead != PackageManager.PERMISSION_GRANTED) {
requestPerssionArr.add(Manifest.permission.READ_PHONE_STATE)
}
val hasSdcardRead =
checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE)
if (hasSdcardRead != PackageManager.PERMISSION_GRANTED) {
requestPerssionArr.add(Manifest.permission.READ_EXTERNAL_STORAGE)
}
val hasSdcardWrite =
checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
if (hasSdcardWrite != PackageManager.PERMISSION_GRANTED) {
requestPerssionArr.add(Manifest.permission.WRITE_EXTERNAL_STORAGE)
}
// 是否应该显示权限请求
if (requestPerssionArr.size >= 1) {
val requestArray =
arrayOfNulls<String>(requestPerssionArr.size)
for (i in requestArray.indices) {
requestArray[i] = requestPerssionArr[i]
}
requestPermissions(requestArray, requestCode)
}
}
} catch (e: Exception) {
e.printStackTrace()
}
}
override fun onRequestPermissionsResult(
requestCode: Int, permissions: Array<String?>,
grantResults: IntArray
) {
var flag = false
for (i in permissions.indices) {
if (PackageManager.PERMISSION_GRANTED == grantResults[i]) {
flag = true
}
}
}
override fun dispatchKeyEvent(event: KeyEvent): Boolean {
return if (event.keyCode == KeyEvent.KEYCODE_BACK) {
true
} else {
super.dispatchKeyEvent(event)
}
}
}
\ No newline at end of file
... ...
package com.yhkj.test;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.RectF;
import android.hardware.Camera;
import android.os.Bundle;
import android.util.Log;
import android.view.TextureView;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import com.baidu.idl.main.facesdk.FaceInfo;
import com.baidu.idl.main.facesdk.callback.CameraDataCallback;
import com.baidu.idl.main.facesdk.callback.FaceDetectCallBack;
import com.baidu.idl.main.facesdk.camera.AutoTexturePreviewView;
import com.baidu.idl.main.facesdk.camera.CameraPreviewManager;
import com.baidu.idl.main.facesdk.manager.FaceSDKManager;
import com.baidu.idl.main.facesdk.manager.FaceTrackManager;
import com.baidu.idl.main.facesdk.model.BDFaceImageInstance;
import com.baidu.idl.main.facesdk.model.BDFaceSDKCommon;
import com.baidu.idl.main.facesdk.model.LivenessModel;
import com.baidu.idl.main.facesdk.model.SingleBaseConfig;
import com.baidu.idl.main.facesdk.utils.BitmapUtils;
import com.baidu.idl.main.facesdk.utils.FaceOnDrawTexturViewUtil;
public class FaceAttributeRGBActivity extends BaseActivity {
private AutoTexturePreviewView mPreviewView;
private TextView textAttr;
private TextView rgbLivenessScoreTv;
private TextView rgbLivenssDurationTv;
private TextView tipTv;
private TextView tvDetectDuration;
private ImageView imagePreview;
// 图片越大,性能消耗越大,也可以选择640*480, 1280*720
private static final int mWidth = SingleBaseConfig.getBaseConfig().getRgbAndNirWidth();
private static final int mHeight = SingleBaseConfig.getBaseConfig().getRgbAndNirHeight();
// textureView用于绘制人脸框等。
private TextureView textureView;
private RectF rectF;
private Paint paint;
// 包含适配屏幕后后的人脸的x坐标,y坐标,和width
private float[] pointXY = new float[3];
private boolean requestToInner = false;
private TextView detectText;
private Button btnBack;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_attribute_track);
// 属性开启属性检测
SingleBaseConfig.getBaseConfig().setAttribute(true);
FaceSDKManager.getInstance().initConfig();
findView();
}
private void findView() {
btnBack = findViewById(R.id.btn_back);
mPreviewView = findViewById(R.id.preview_view);
textureView = findViewById(R.id.texture_view);
textureView.setOpaque(false);
// 不需要屏幕自动变黑。
textureView.setKeepScreenOn(true);
textAttr = findViewById(R.id.text_attr);
rgbLivenessScoreTv = findViewById(R.id.text_rgb_liveness_score);
rgbLivenssDurationTv = findViewById(R.id.text_rgb_livenss_duration);
tipTv = findViewById(R.id.text_tip);
tvDetectDuration = findViewById(R.id.text_face_detect_duration);
imagePreview = findViewById(R.id.image_preview);
// 画人脸框
rectF = new RectF();
paint = new Paint();
detectText = findViewById(R.id.detect_text);
btnBack.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
finish();
}
});
}
@Override
protected void onResume() {
super.onResume();
// 摄像头图像预览
startCameraPreview();
Log.e("qing", "start camera");
}
/**
* 摄像头图像预览
*/
private void startCameraPreview() {
// 设置前置摄像头
// CameraPreviewManager.getInstance().setCameraFacing(CameraPreviewManager.CAMERA_FACING_FRONT);
// 设置后置摄像头
// CameraPreviewManager.getInstance().setCameraFacing(CameraPreviewManager.CAMERA_FACING_BACK);
// 设置USB摄像头
CameraPreviewManager.getInstance().setCameraFacing(CameraPreviewManager.CAMERA_USB);
CameraPreviewManager.getInstance().startPreview(this, mPreviewView, mWidth, mHeight, new CameraDataCallback() {
@Override
public void onGetCameraData(byte[] data, Camera camera, int width, int height) {
// 拿到相机帧数据
faceDetect(data, width, height);
// 调试模式打开 显示实际送检图片的方向,SDK只检测人脸朝上的图
boolean isRGBDisplay = SingleBaseConfig.getBaseConfig().getDisplay();
if (isRGBDisplay) {
showDetectImage(data);
}
}
});
}
/**
* 摄像头数据处理
*
* @param data
* @param width
* @param height
*/
private void faceDetect(byte[] data, int width, int height) {
// 无活体检测
FaceTrackManager.getInstance().setAliving(true);
FaceTrackManager.getInstance().faceTrack(data, width, height, new FaceDetectCallBack() {
@Override
public void onFaceDetectCallback(LivenessModel livenessModel) {
// ToastUtils.toast(getApplicationContext(), String.valueOf(livenessModel.getRgbLivenessScore()));
// 输出结果
if (SingleBaseConfig.getBaseConfig().getDetectFrame().equals("fixedarea")) {
isInserLimit(livenessModel);
// 输出结果
checkResult(livenessModel);
}
if (SingleBaseConfig.getBaseConfig().getDetectFrame().equals("wireframe")) {
showFrame(livenessModel);
checkResult(livenessModel);
}
}
@Override
public void onTip(int code, final String msg) {
displayTip(msg);
}
@Override
public void onFaceDetectDarwCallback(LivenessModel livenessModel) {
}
});
}
/**
* 显示检测的图片。用于调试,如果人脸sdk检测的人脸需要朝上,可以通过该图片判断。实际应用中可注释掉
*
* @param rgb
*/
private void showDetectImage(byte[] rgb) {
if (rgb == null) {
return;
}
BDFaceImageInstance rgbInstance = new BDFaceImageInstance(rgb, mHeight,
mWidth, BDFaceSDKCommon.BDFaceImageType.BDFACE_IMAGE_TYPE_YUV_NV21,
SingleBaseConfig.getBaseConfig().getDetectDirection(),
SingleBaseConfig.getBaseConfig().getMirrorRGB());
BDFaceImageInstance imageInstance = rgbInstance.getImage();
final Bitmap bitmap = BitmapUtils.getInstaceBmp(imageInstance);
runOnUiThread(new Runnable() {
@Override
public void run() {
imagePreview.setVisibility(View.VISIBLE);
imagePreview.setImageBitmap(bitmap);
}
});
// 流程结束销毁图片,开始下一帧图片检测,否则内存泄露
rgbInstance.destory();
}
@Override
protected void onDestroy() {
super.onDestroy();
CameraPreviewManager.getInstance().stopPreview();
// 关闭属性检测
SingleBaseConfig.getBaseConfig().setAttribute(false);
FaceSDKManager.getInstance().initConfig();
}
// 检测结果输出
private void checkResult(LivenessModel model) {
if (model == null) {
clearTip();
return;
} else {
displayTip("");
}
if (requestToInner) {
runOnUiThread(new Runnable() {
@Override
public void run() {
clearTip();
detectText.setText("预览区域内人脸不全");
}
});
return;
} else {
runOnUiThread(new Runnable() {
@Override
public void run() {
detectText.setText("");
}
});
}
displayResult(model, null);
attrCheck(model);
}
/**
* 人脸属性检测
*
* @param model
*/
private void attrCheck(LivenessModel model) {
final FaceInfo faceInfo = model.getFaceInfo();
// todo 人脸属性数据获取
runOnUiThread(new Runnable() {
@Override
public void run() {
textAttr.setText("人脸属性:" + getMsg(faceInfo));
}
});
}
public String getMsg(FaceInfo faceInfo) {
StringBuilder msg = new StringBuilder();
if (faceInfo != null) {
msg.append(faceInfo.age);
msg.append(",").append(faceInfo.emotionThree == BDFaceSDKCommon.BDFaceEmotion.BDFACE_EMOTION_CALM ?
"平静"
: faceInfo.emotionThree == BDFaceSDKCommon.BDFaceEmotion.BDFACE_EMOTION_SMILE ? "笑"
: faceInfo.emotionThree == BDFaceSDKCommon.BDFaceEmotion.BDFACE_EMOTION_FROWN ? "皱眉" : "没有表情");
msg.append(",").append(faceInfo.gender == BDFaceSDKCommon.BDFaceGender.BDFACE_GENDER_FEMALE ? "女性" :
faceInfo.gender == BDFaceSDKCommon.BDFaceGender.BDFACE_GENDER_MALE ? "男性" : "婴儿");
msg.append(",").append(faceInfo.glasses == BDFaceSDKCommon.BDFaceGlasses.BDFACE_NO_GLASSES ? "无眼镜"
: faceInfo.glasses == BDFaceSDKCommon.BDFaceGlasses.BDFACE_GLASSES ? "有眼镜"
: faceInfo.glasses == BDFaceSDKCommon.BDFaceGlasses.BDFACE_SUN_GLASSES ? "墨镜" : "太阳镜");
msg.append(",").append(faceInfo.race == BDFaceSDKCommon.BDFaceRace.BDFACE_RACE_YELLOW ? "黄种人"
: faceInfo.race == BDFaceSDKCommon.BDFaceRace.BDFACE_RACE_WHITE ? "白种人"
: faceInfo.race == BDFaceSDKCommon.BDFaceRace.BDFACE_RACE_BLACK ? "黑种人"
: faceInfo.race == BDFaceSDKCommon.BDFaceRace.BDFACE_RACE_INDIAN ? "印度人"
: "地球人");
}
return msg.toString();
}
private void displayResult(final LivenessModel livenessModel, final String livess) {
runOnUiThread(new Runnable() {
@Override
public void run() {
if (livess != null && livess.equals("livess")) {
rgbLivenessScoreTv.setVisibility(View.VISIBLE);
rgbLivenssDurationTv.setVisibility(View.VISIBLE);
rgbLivenessScoreTv.setText("RGB活体得分:" + livenessModel.getRgbLivenessScore());
rgbLivenssDurationTv.setText("RGB活体耗时:" + livenessModel.getRgbLivenessDuration());
} else {
rgbLivenessScoreTv.setVisibility(View.GONE);
rgbLivenssDurationTv.setVisibility(View.GONE);
}
tvDetectDuration.setText("人脸检测耗时:" + livenessModel.getRgbDetectDuration());
}
});
}
private void displayTip(final String tip) {
runOnUiThread(new Runnable() {
@Override
public void run() {
tipTv.setText(tip);
}
});
}
private void clearTip() {
runOnUiThread(new Runnable() {
@Override
public void run() {
tvDetectDuration.setText("");
rgbLivenessScoreTv.setText("");
rgbLivenssDurationTv.setText("");
textAttr.setText("");
if (SingleBaseConfig.getBaseConfig().getDetectFrame().equals("wireframe")) {
detectText.setText("");
}
}
});
}
/**
* 绘制人脸框
*/
private void showFrame(final LivenessModel model) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Canvas canvas = textureView.lockCanvas();
if (canvas == null) {
textureView.unlockCanvasAndPost(canvas);
return;
}
if (model == null) {
// 清空canvas
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
textureView.unlockCanvasAndPost(canvas);
return;
}
FaceInfo[] faceInfos = model.getTrackFaceInfo();
if (faceInfos == null || faceInfos.length == 0) {
// 清空canvas
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
textureView.unlockCanvasAndPost(canvas);
return;
}
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
FaceInfo faceInfo = faceInfos[0];
rectF.set(FaceOnDrawTexturViewUtil.getFaceRectTwo(faceInfo));
// 检测图片的坐标和显示的坐标不一样,需要转换。
FaceOnDrawTexturViewUtil.mapFromOriginalRect(rectF,
mPreviewView, model.getBdFaceImageInstance());
paint.setColor(Color.GREEN);
paint.setStyle(Paint.Style.STROKE);
// 绘制框
canvas.drawRect(rectF, paint);
textureView.unlockCanvasAndPost(canvas);
}
});
}
// 判断人脸是否在园内
private void isInserLimit(final LivenessModel livenessModel) {
if (livenessModel == null) {
requestToInner = false;
return;
}
FaceInfo[] faceInfos = livenessModel.getTrackFaceInfo();
if (faceInfos == null || faceInfos.length == 0) {
requestToInner = false;
return;
}
pointXY[0] = livenessModel.getFaceInfo().centerX;
pointXY[1] = livenessModel.getFaceInfo().centerY;
pointXY[2] = livenessModel.getFaceInfo().width;
FaceOnDrawTexturViewUtil.converttPointXY(pointXY, mPreviewView,
livenessModel.getBdFaceImageInstance(), livenessModel.getFaceInfo().width);
float lfetLimitX = AutoTexturePreviewView.circleX - AutoTexturePreviewView.circleRadius;
float rightLimitX = AutoTexturePreviewView.circleX + AutoTexturePreviewView.circleRadius;
float topLimitY = AutoTexturePreviewView.circleY - AutoTexturePreviewView.circleRadius;
float bottomLimitY = AutoTexturePreviewView.circleY + AutoTexturePreviewView.circleRadius;
if (pointXY[0] - pointXY[2] / 2 < lfetLimitX
|| pointXY[0] + pointXY[2] / 2 > rightLimitX
|| pointXY[1] - pointXY[2] / 2 < topLimitY
|| pointXY[1] + pointXY[2] / 2 > bottomLimitY) {
requestToInner = true;
} else {
requestToInner = false;
}
}
}
... ...
package com.yhkj.test;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.RectF;
import android.hardware.Camera;
import android.os.Bundle;
import android.view.Gravity;
import android.view.TextureView;
import android.view.View;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.baidu.idl.main.facesdk.FaceInfo;
import com.baidu.idl.main.facesdk.callback.CameraDataCallback;
import com.baidu.idl.main.facesdk.callback.FaceDetectCallBack;
import com.baidu.idl.main.facesdk.camera.AutoTexturePreviewView;
import com.baidu.idl.main.facesdk.camera.CameraPreviewManager;
import com.baidu.idl.main.facesdk.manager.FaceSDKManager;
import com.baidu.idl.main.facesdk.model.LivenessModel;
import com.baidu.idl.main.facesdk.model.SingleBaseConfig;
import com.baidu.idl.main.facesdk.model.User;
import com.baidu.idl.main.facesdk.utils.DensityUtils;
import com.baidu.idl.main.facesdk.utils.FaceOnDrawTexturViewUtil;
import com.baidu.idl.main.facesdk.utils.FileUtils;
import com.baidu.idl.main.facesdk.view.CircleImageView;
/**
* @Time 2019/06/02
* @Author v_shishuaifeng
* @Description RGB关闭Debug页面
*/
public class FaceRGBCloseDebugSearchActivity extends BaseActivity implements View.OnClickListener {
// 图片越大,性能消耗越大,也可以选择640*480, 1280*720
private static final int PREFER_WIDTH = SingleBaseConfig.getBaseConfig().getRgbAndNirWidth();
private static final int PERFER_HEIGH = SingleBaseConfig.getBaseConfig().getRgbAndNirHeight();
private Context mContext;
// 关闭Debug 模式
private AutoTexturePreviewView mAutoCameraPreviewView;
private TextView mDetectText;
private CircleImageView mDetectImage;
private TextView mTrackText;
private TextureView mFaceDetectImageView;
private Paint paint;
private RectF rectF;
private RelativeLayout relativeLayout;
private int mLiveType;
private float mRgbLiveScore;
// 包含适配屏幕后后的人脸的x坐标,y坐标,和width
private float[] pointXY = new float[3];
private boolean requestToInner = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_face_rgb_close_debug);
mContext = this;
initView();
// 屏幕的宽
int displayWidth = DensityUtils.getDisplayWidth(mContext);
// 屏幕的高
int displayHeight = DensityUtils.getDisplayHeight(mContext);
// 当屏幕的宽大于屏幕宽时
if (displayHeight < displayWidth) {
// 获取高
int height = displayHeight;
// 获取宽
int width = (int) (displayHeight * ((9.0f / 16.0f)));
// 设置布局的宽和高
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(width, height);
// 设置布局居中
params.gravity = Gravity.CENTER;
relativeLayout.setLayoutParams(params);
}
}
/***
* 关闭Debug 模式view
*/
private void initView() {
// 活体状态
mLiveType = SingleBaseConfig.getBaseConfig().getType();
// 活体阈值
mRgbLiveScore = SingleBaseConfig.getBaseConfig().getRgbLiveScore();
// 获取整个布局
relativeLayout = findViewById(R.id.all_relative);
// 画人脸框
paint = new Paint();
rectF = new RectF();
mFaceDetectImageView = findViewById(R.id.draw_detect_face_view);
mFaceDetectImageView.setOpaque(false);
mFaceDetectImageView.setKeepScreenOn(true);
// 返回
Button mButReturn = findViewById(R.id.btn_back);
mButReturn.setOnClickListener(this);
// 设置
Button mBtSetting = findViewById(R.id.btn_setting);
mBtSetting.setOnClickListener(this);
// 单目摄像头RGB 图像预览
mAutoCameraPreviewView = findViewById(R.id.auto_camera_preview_view);
mAutoCameraPreviewView.setVisibility(View.VISIBLE);
mDetectText = findViewById(R.id.detect_text);
mDetectImage = findViewById(R.id.detect_reg_image_item);
mTrackText = findViewById(R.id.track_txt);
// 调试按钮
findViewById(R.id.debug_btn).setOnClickListener(this);
}
@Override
protected void onResume() {
super.onResume();
startTestCloseDebugRegisterFunction();
}
private void startTestCloseDebugRegisterFunction() {
// TODO : 临时放置
CameraPreviewManager.getInstance().setCameraFacing(CameraPreviewManager.CAMERA_USB);
CameraPreviewManager.getInstance().startPreview(this, mAutoCameraPreviewView,
PREFER_WIDTH, PERFER_HEIGH, new CameraDataCallback() {
@Override
public void onGetCameraData(byte[] data, Camera camera, int width, int height) {
// 摄像头预览数据进行人脸检测
FaceSDKManager.getInstance().onDetectCheck(data, null, null,
height, width, mLiveType, new FaceDetectCallBack() {
@Override
public void onFaceDetectCallback(LivenessModel livenessModel) {
if (SingleBaseConfig.getBaseConfig().getDetectFrame().equals("fixedarea")) {
isInserLimit(livenessModel);
// 输出结果
checkCloseResult(livenessModel);
}
if (SingleBaseConfig.getBaseConfig().getDetectFrame().equals("wireframe")) {
checkCloseResult(livenessModel);
}
}
@Override
public void onTip(int code, String msg) {
displayTip(code, msg);
}
@Override
public void onFaceDetectDarwCallback(LivenessModel livenessModel) {
if (SingleBaseConfig.getBaseConfig().getDetectFrame().equals("wireframe")) {
showFrame(livenessModel);
}
}
});
}
});
}
private void displayTip(final int code, final String tip) {
runOnUiThread(new Runnable() {
@Override
public void run() {
if (code == 0) {
mTrackText.setVisibility(View.GONE);
} else {
mTrackText.setVisibility(View.VISIBLE);
mTrackText.setText("识别失败");
mTrackText.setBackgroundColor(Color.RED);
mDetectImage.setImageResource(R.mipmap.ic_littleicon);
}
mDetectText.setText(tip);
}
});
}
private void checkCloseResult(final LivenessModel livenessModel) {
// 当未检测到人脸UI显示
runOnUiThread(new Runnable() {
@Override
public void run() {
if (livenessModel == null || livenessModel.getFaceInfo() == null) {
mTrackText.setVisibility(View.GONE);
mDetectText.setText("未检测到人脸");
mDetectImage.setImageResource(R.mipmap.ic_littleicon);
return;
} else {
if (requestToInner) {
mTrackText.setVisibility(View.VISIBLE);
mTrackText.setText("预览区域内人脸不全");
mTrackText.setBackgroundColor(Color.RED);
mDetectText.setText("");
mDetectText.setVisibility(View.VISIBLE);
mDetectImage.setImageResource(R.mipmap.ic_littleicon);
return;
}
if (mLiveType == 1) {
User user = livenessModel.getUser();
if (user == null) {
mTrackText.setVisibility(View.VISIBLE);
mTrackText.setText("识别失败");
mTrackText.setBackgroundColor(Color.RED);
mDetectText.setText("搜索不到用户");
mDetectText.setVisibility(View.VISIBLE);
mDetectImage.setImageResource(R.mipmap.ic_littleicon);
} else {
String absolutePath = FileUtils.getBatchImportSuccessDirectory()
+ "/" + user.getImageName();
Bitmap bitmap = BitmapFactory.decodeFile(absolutePath);
mDetectImage.setImageBitmap(bitmap);
mTrackText.setVisibility(View.VISIBLE);
mTrackText.setText("识别成功");
mTrackText.setBackgroundColor(Color.rgb(66, 147, 136));
mDetectText.setText("欢迎您, " + user.getUserName());
}
} else {
float rgbLivenessScore = livenessModel.getRgbLivenessScore();
if (rgbLivenessScore < mRgbLiveScore) {
mTrackText.setVisibility(View.VISIBLE);
mTrackText.setText("识别失败");
mTrackText.setBackgroundColor(Color.RED);
mDetectText.setText("活体检测未通过");
mDetectImage.setImageResource(R.mipmap.ic_littleicon);
} else {
User user = livenessModel.getUser();
if (user == null) {
mTrackText.setVisibility(View.VISIBLE);
mTrackText.setText("识别失败");
mTrackText.setBackgroundColor(Color.RED);
mDetectText.setText("搜索不到用户");
mDetectText.setVisibility(View.VISIBLE);
mDetectImage.setImageResource(R.mipmap.ic_littleicon);
} else {
String absolutePath = FileUtils.getBatchImportSuccessDirectory()
+ "/" + user.getImageName();
Bitmap bitmap = BitmapFactory.decodeFile(absolutePath);
mDetectImage.setImageBitmap(bitmap);
mTrackText.setVisibility(View.VISIBLE);
mTrackText.setText("识别成功");
mTrackText.setBackgroundColor(Color.rgb(66, 147, 136));
mDetectText.setText("欢迎您, " + user.getUserName());
}
}
}
}
}
});
}
@Override
public void onClick(View v) {
switch (v.getId()) {
// 返回
case R.id.btn_back:
finish();
break;
// 设置
case R.id.btn_setting:
// Intent intent = new Intent(mContext, SettingMainActivity.class);
// intent.putExtra("page_type", "search");
// startActivityForResult(intent, PAGE_TYPE);
// finish();
break;
case R.id.debug_btn:
// mAutoCameraPreviewView.removeAllViews();
// startActivity(new Intent(this, FaceRGBOpenDebugSearchActivity.class));
// finish();
break;
default:
break;
}
}
/**
* 绘制人脸框。
*/
private void showFrame(final LivenessModel model) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Canvas canvas = mFaceDetectImageView.lockCanvas();
if (canvas == null) {
mFaceDetectImageView.unlockCanvasAndPost(canvas);
return;
}
if (model == null) {
// 清空canvas
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
mFaceDetectImageView.unlockCanvasAndPost(canvas);
return;
}
FaceInfo[] faceInfos = model.getTrackFaceInfo();
if (faceInfos == null || faceInfos.length == 0) {
// 清空canvas
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
mFaceDetectImageView.unlockCanvasAndPost(canvas);
return;
}
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
FaceInfo faceInfo = faceInfos[0];
rectF.set(FaceOnDrawTexturViewUtil.getFaceRectTwo(faceInfo));
// 检测图片的坐标和显示的坐标不一样,需要转换。
FaceOnDrawTexturViewUtil.mapFromOriginalRect(rectF,
mAutoCameraPreviewView, model.getBdFaceImageInstance());
paint.setColor(Color.GREEN);
paint.setStyle(Paint.Style.STROKE);
// 绘制框
canvas.drawRect(rectF, paint);
mFaceDetectImageView.unlockCanvasAndPost(canvas);
}
});
}
// 判断人脸是否在园内
private void isInserLimit(final LivenessModel livenessModel) {
if (livenessModel == null) {
requestToInner = false;
return;
}
FaceInfo[] faceInfos = livenessModel.getTrackFaceInfo();
if (faceInfos == null || faceInfos.length == 0) {
requestToInner = false;
return;
}
pointXY[0] = livenessModel.getFaceInfo().centerX;
pointXY[1] = livenessModel.getFaceInfo().centerY;
pointXY[2] = livenessModel.getFaceInfo().width;
FaceOnDrawTexturViewUtil.converttPointXY(pointXY, mAutoCameraPreviewView,
livenessModel.getBdFaceImageInstance(), livenessModel.getFaceInfo().width);
float lfetLimitX = AutoTexturePreviewView.circleX - AutoTexturePreviewView.circleRadius;
float rightLimitX = AutoTexturePreviewView.circleX + AutoTexturePreviewView.circleRadius;
float topLimitY = AutoTexturePreviewView.circleY - AutoTexturePreviewView.circleRadius;
float bottomLimitY = AutoTexturePreviewView.circleY + AutoTexturePreviewView.circleRadius;
if (pointXY[0] - pointXY[2] / 2 < lfetLimitX
|| pointXY[0] + pointXY[2] / 2 > rightLimitX
|| pointXY[1] - pointXY[2] / 2 < topLimitY
|| pointXY[1] + pointXY[2] / 2 > bottomLimitY) {
requestToInner = true;
} else {
requestToInner = false;
}
}
}
... ...
package com.yhkj.test
import android.content.Intent
import android.os.Bundle
import com.yhkj.rebotsdk.face.FaceApi
import com.yhkj.rebotsdk.face.ResultCallBack
import com.yhkj.test.util.ToastUtils
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : BaseActivity() {
private val faceApi=FaceApi.INSTANCE
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
test.setOnClickListener {
faceApi.init(applicationContext,
"HDDJ-PARX-TMWX-HUYX", "",
"", object : ResultCallBack {
override fun onLicenseResult(code: Int, response: String?) {
if(code==0){
ToastUtils.toast(applicationContext,"激活成功")
}else{
ToastUtils.toast(applicationContext,response)
}
}
override fun onModelResult(code: Int, response: String?) {
if(code!=0){
ToastUtils.toast(applicationContext,response)
}else{
val intent=Intent(this@MainActivity,
FaceRGBCloseDebugSearchActivity::class.java)
startActivity(intent)
}
}
})
}
}
}
... ...
package com.yhkj.test.util;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.widget.Toast;
public class ToastUtils {
private static Handler handler = new Handler(Looper.getMainLooper());
public static void toast(final Context context, final String text) {
handler.post(new Runnable() {
@Override
public void run() {
Toast.makeText(context, text, Toast.LENGTH_SHORT).show();
}
});
}
public static void toast(final Context context, final int resId) {
handler.post(new Runnable() {
@Override
public void run() {
Toast.makeText(context, resId, Toast.LENGTH_SHORT).show();
}
});
}
}
... ...
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
<aapt:attr name="android:fillColor">
<gradient
android:endX="85.84757"
android:endY="92.4963"
android:startX="42.9492"
android:startY="49.59793"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>
\ No newline at end of file
... ...
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="2dp" />
<solid android:color="#036838"></solid>
</shape>
\ No newline at end of file
... ...
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="2dp" />
<solid android:color="#ec808d"></solid>
</shape>
\ No newline at end of file
... ...
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval"
android:useLevel="false">
<stroke
android:width="3dp"
android:color="#016838" />
<size android:width="60dp"
android:height="60dp" />
</shape>
\ No newline at end of file
... ...
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
</vector>
... ...
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@null" />
<padding
android:bottom="1dip"
android:left="1dip"
android:right="1dip" />
<stroke
android:width="1dip"
android:color="#000" />
<!--边框宽度-->
<size
android:width="10dip"
android:height="10dip" />
</shape>
\ No newline at end of file
... ...
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2018 Baidu, Inc. All Rights Reserved.
-->
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.baidu.idl.main.facesdk.camera.AutoTexturePreviewView
android:id="@+id/preview_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<TextureView
android:id="@+id/texture_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ImageView
android:id="@+id/image_preview"
android:layout_width="150dp"
android:layout_height="150dp"
android:layout_alignParentRight="true"
android:layout_marginBottom="30dp"
android:layout_marginRight="0dp"
android:visibility="invisible" />
<TextView
android:id="@+id/text_tip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginTop="20dp"
android:textColor="#ffffff"
android:textSize="20dp"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:orientation="vertical">
<TextView
android:id="@+id/detect_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginTop="20dp"
android:textColor="#87CEFA"
android:textSize="20dp"/>
<TextView
android:id="@+id/text_face_detect_duration"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginTop="20dp"
android:textColor="#87CEFA"
android:textSize="20dp"/>
<TextView
android:id="@+id/text_rgb_liveness_score"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginTop="8dp"
android:textColor="#87CEFA"
android:textSize="20dp"/>
<TextView
android:id="@+id/text_rgb_livenss_duration"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginTop="8dp"
android:textColor="#87CEFA"
android:textSize="20dp"/>
<TextView
android:id="@+id/text_attr"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginTop="8dp"
android:textColor="#87CEFA"
android:textSize="20dp"/>
</LinearLayout>
<Button
android:id="@+id/btn_back"
android:layout_width="60dp"
android:layout_height="30dp"
android:layout_marginLeft="15dp"
android:layout_marginTop="15dp"
android:background="@drawable/fillet_style_return_selector"
android:text="返回"
android:textColor="#ffffff" />
</RelativeLayout>
... ...
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/all_relative"
android:layout_width="match_parent"
android:background="@color/white"
android:layout_height="match_parent">
<include
android:id="@+id/search_title"
layout="@layout/layout_search_title" />
<Button
android:id="@+id/debug_btn"
android:layout_width="wrap_content"
android:layout_height="35dp"
android:layout_alignParentRight="true"
android:layout_below="@id/search_title"
android:layout_margin="5dp"
android:textColor="#000"
android:background="@drawable/logout_selector"
android:text="开启调试" />
<TextureView
android:id="@+id/ir_camera_preview_view"
android:layout_width="1dp"
android:layout_height="1dp"
android:layout_above="@+id/layout_info"
android:layout_below="@id/search_title"
android:visibility="invisible" />
<TextureView
android:id="@+id/camera_preview_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@+id/layout_info"
android:layout_below="@id/search_title"
android:visibility="gone" />
<com.baidu.idl.main.facesdk.camera.AutoTexturePreviewView
android:id="@+id/auto_camera_preview_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@id/layout_info"
android:layout_below="@id/search_title" />
<TextureView
android:id="@+id/draw_detect_face_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@+id/layout_info"
android:layout_below="@id/search_title" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_below="@id/auto_camera_preview_view"
android:background="#898989" />
<TextView
android:id="@+id/track_txt"
android:layout_width="match_parent"
android:layout_height="30dp"
android:layout_above="@+id/layout_info"
android:layout_alignParentStart="true"
android:background="#016838"
android:gravity="center"
android:textColor="#ffffff"
android:visibility="gone" />
<RelativeLayout
android:id="@+id/layout_info"
android:layout_width="match_parent"
android:layout_height="195dp"
android:layout_alignParentBottom="true"
android:gravity="center_horizontal">
<include layout="@layout/activity_search_itme" />
</RelativeLayout>
</RelativeLayout>
... ...
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/test"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/active_license"
android:textSize="@dimen/sp_30"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
\ No newline at end of file
... ...
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/detect_text"
android:layout_width="match_parent"
android:layout_height="30dp"
android:layout_alignParentStart="true"
android:layout_marginBottom="12dp"
android:gravity="center"
android:textColor="#000000" />
<RelativeLayout
android:layout_width="140dp"
android:layout_height="140dp"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="40dp"
android:background="@drawable/fillet_style_search_item">
<com.baidu.idl.main.facesdk.view.CircleImageView
android:id="@+id/detect_reg_image_item"
android:layout_width="134dp"
android:layout_height="134dp"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:src="@mipmap/ic_littleicon"
/>
</RelativeLayout>
</RelativeLayout>
... ...
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<Button
android:id="@+id/btn_back"
android:layout_width="60dp"
android:layout_height="30dp"
android:layout_marginLeft="15dp"
android:layout_marginTop="15dp"
android:background="@drawable/fillet_style_return_selector"
android:text="返回"
android:textColor="#ffffff" />
<TextView
android:id="@+id/textView4"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:gravity="center"
android:text="视频流动态人脸搜索"
android:textColor="#363636"
android:textSize="22dp"
android:textStyle="bold" />
<Button
android:id="@+id/btn_setting"
android:layout_width="60dp"
android:layout_height="30dp"
android:layout_alignParentEnd="true"
android:layout_alignTop="@+id/textView4"
android:layout_marginEnd="14dp"
android:background="@drawable/fillet_style_button_selector"
android:text="设置"
android:textColor="#ffffff" />
<View
android:id="@+id/shang_view"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_below="@id/btn_back"
android:layout_marginTop="15dp"
android:background="#898989" />
</RelativeLayout>
\ No newline at end of file
... ...
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>
\ No newline at end of file
... ...
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>
\ No newline at end of file
... ...
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#6200EE</color>
<color name="colorPrimaryDark">#3700B3</color>
<color name="colorAccent">#03DAC5</color>
<color name="buttonColor">#FF6600</color>
<color name="white">#FFFFFF</color>
<color name="black">#000000</color>
<color name="buttonBg">#016838</color>
<color name="red">#FF0033</color>
</resources>
... ...
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="dp_30">30dp</dimen>
<dimen name="sp_30">30sp</dimen>
</resources>
\ No newline at end of file
... ...
<resources>
<string name="app_name">test</string>
<string name="active_license">激活人脸算法</string>
</resources>
... ...
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
</resources>
... ...