作者 张卫卫

sdk架构修改

正在显示 23 个修改的文件 包含 1003 行增加3837 行删除

要显示太多修改。

为保证性能只显示 23 of 23+ 个文件。

@@ -13,6 +13,7 @@ @@ -13,6 +13,7 @@
13 <option value="$PROJECT_DIR$/arcface" /> 13 <option value="$PROJECT_DIR$/arcface" />
14 <option value="$PROJECT_DIR$/bdface" /> 14 <option value="$PROJECT_DIR$/bdface" />
15 <option value="$PROJECT_DIR$/sdk" /> 15 <option value="$PROJECT_DIR$/sdk" />
  16 + <option value="$PROJECT_DIR$/test" />
16 </set> 17 </set>
17 </option> 18 </option>
18 <option name="resolveModulePerSourceSet" value="false" /> 19 <option name="resolveModulePerSourceSet" value="false" />
@@ -24,9 +24,6 @@ android { @@ -24,9 +24,6 @@ android {
24 dependencies { 24 dependencies {
25 api fileTree(dir: 'libs', include: ['*.jar']) 25 api fileTree(dir: 'libs', include: ['*.jar'])
26 implementation 'com.android.support:appcompat-v7:28.0.0' 26 implementation 'com.android.support:appcompat-v7:28.0.0'
27 - testImplementation 'junit:junit:4.12'  
28 - androidTestImplementation 'com.android.support.test:runner:1.0.2'  
29 - androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'  
30 implementation 'com.android.support:recyclerview-v7:28.0.0' 27 implementation 'com.android.support:recyclerview-v7:28.0.0'
31 implementation 'com.github.bumptech.glide:glide:4.9.0' 28 implementation 'com.github.bumptech.glide:glide:4.9.0'
32 implementation 'io.reactivex.rxjava2:rxjava:2.2.6' 29 implementation 'io.reactivex.rxjava2:rxjava:2.2.6'
1 -package com.arcsoft.arcfacedemo;  
2 -  
3 -import android.content.Context;  
4 -import android.support.test.InstrumentationRegistry;  
5 -import android.support.test.runner.AndroidJUnit4;  
6 -  
7 -import org.junit.Test;  
8 -import org.junit.runner.RunWith;  
9 -  
10 -import static org.junit.Assert.*;  
11 -  
12 -/**  
13 - * Instrumented test, which will execute on an Android device.  
14 - *  
15 - * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>  
16 - */  
17 -@RunWith(AndroidJUnit4.class)  
18 -public class ExampleInstrumentedTest {  
19 - @Test  
20 - public void useAppContext() {  
21 - // Context of the app under test.  
22 - Context appContext = InstrumentationRegistry.getTargetContext();  
23 -  
24 - assertEquals("com.arcsoft.arcfacedemo", appContext.getPackageName());  
25 - }  
26 -}  
@@ -2,61 +2,11 @@ @@ -2,61 +2,11 @@
2 <manifest xmlns:android="http://schemas.android.com/apk/res/android" 2 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
3 package="com.arcsoft.arcfacedemo"> 3 package="com.arcsoft.arcfacedemo">
4 4
5 - <uses-permission android:name="android.permission.CAMERA" />  
6 - <uses-permission android:name="android.permission.READ_PHONE_STATE" />  
7 - <uses-permission android:name="android.permission.INTERNET" />  
8 - <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />  
9 - <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />  
10 5
11 - <application  
12 - android:allowBackup="true"  
13 - android:icon="@mipmap/ic_launcher" 6 + <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
  7 + <application android:allowBackup="false"
14 android:label="@string/app_name" 8 android:label="@string/app_name"
15 - android:supportsRtl="true"  
16 - android:theme="@style/AppTheme">  
17 - <activity  
18 - android:name=".activity.FaceAttrPreviewActivity"  
19 - android:launchMode="singleTop" />  
20 - <activity  
21 - android:name=".activity.ChooseFunctionActivity"  
22 - android:launchMode="singleTop">  
23 - <intent-filter>  
24 - <action android:name="android.intent.action.MAIN" />  
25 -  
26 - <category android:name="android.intent.category.LAUNCHER" />  
27 - </intent-filter>  
28 - </activity>  
29 -  
30 - <activity  
31 - android:name=".activity.SingleImageActivity"  
32 - android:configChanges="orientation|screenSize"  
33 - android:launchMode="singleTop" />  
34 -  
35 - <activity  
36 - android:name=".activity.MultiImageActivity"  
37 - android:launchMode="singleTop" />  
38 -  
39 - <activity  
40 - android:name=".activity.IrRegisterAndRecognizeActivity"  
41 - android:launchMode="singleTop" />  
42 -  
43 - <activity  
44 - android:name=".activity.RegisterAndRecognizeActivity"  
45 - android:launchMode="singleTop" />  
46 -  
47 - <activity  
48 - android:name=".activity.FaceManageActivity"  
49 - android:launchMode="singleTop" />  
50 -  
51 - <provider  
52 - android:name="android.support.v4.content.FileProvider"  
53 - android:authorities="${applicationId}.provider"  
54 - android:exported="false"  
55 - android:grantUriPermissions="true">  
56 - <meta-data  
57 - android:name="android.support.FILE_PROVIDER_PATHS"  
58 - android:resource="@xml/provider_paths" />  
59 - </provider> 9 + android:supportsRtl="true">
60 10
61 </application> 11 </application>
62 12
1 -package com.arcsoft.arcfacedemo.activity;  
2 -  
3 -import android.content.pm.PackageManager;  
4 -import android.support.annotation.NonNull;  
5 -import android.support.v4.content.ContextCompat;  
6 -import android.support.v7.app.AppCompatActivity;  
7 -import android.widget.Toast;  
8 -  
9 -public abstract class BaseActivity extends AppCompatActivity {  
10 - /**  
11 - * 权限检查  
12 - *  
13 - * @param neededPermissions 需要的权限  
14 - * @return 是否全部被允许  
15 - */  
16 - protected boolean checkPermissions(String[] neededPermissions) {  
17 - if (neededPermissions == null || neededPermissions.length == 0) {  
18 - return true;  
19 - }  
20 - boolean allGranted = true;  
21 - for (String neededPermission : neededPermissions) {  
22 - allGranted &= ContextCompat.checkSelfPermission(this, neededPermission) == PackageManager.PERMISSION_GRANTED;  
23 - }  
24 - return allGranted;  
25 - }  
26 -  
27 - @Override  
28 - public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {  
29 - super.onRequestPermissionsResult(requestCode, permissions, grantResults);  
30 - boolean isAllGranted = true;  
31 - for (int grantResult : grantResults) {  
32 - isAllGranted &= (grantResult == PackageManager.PERMISSION_GRANTED);  
33 - }  
34 - afterRequestPermission(requestCode, isAllGranted);  
35 - }  
36 -  
37 - /**  
38 - * 请求权限的回调  
39 - *  
40 - * @param requestCode 请求码  
41 - * @param isAllGranted 是否全部被同意  
42 - */  
43 - abstract void afterRequestPermission(int requestCode, boolean isAllGranted);  
44 -  
45 - protected void showToast(String s) {  
46 - Toast.makeText(getApplicationContext(), s, Toast.LENGTH_SHORT).show();  
47 - }  
48 - protected void showLongToast(String s) {  
49 - Toast.makeText(getApplicationContext(), s, Toast.LENGTH_LONG).show();  
50 - }  
51 -  
52 -}  
1 -package com.arcsoft.arcfacedemo.activity;  
2 -  
3 -import android.Manifest;  
4 -import android.content.Intent;  
5 -import android.content.pm.ApplicationInfo;  
6 -import android.os.Bundle;  
7 -import android.support.v4.app.ActivityCompat;  
8 -import android.util.Log;  
9 -import android.view.View;  
10 -  
11 -import com.arcsoft.arcfacedemo.R;  
12 -import com.arcsoft.arcfacedemo.common.Constants;  
13 -import com.arcsoft.arcfacedemo.fragment.ChooseDetectDegreeDialog;  
14 -import com.arcsoft.face.ActiveFileInfo;  
15 -import com.arcsoft.face.ErrorInfo;  
16 -import com.arcsoft.face.FaceEngine;  
17 -import com.arcsoft.face.VersionInfo;  
18 -import com.arcsoft.face.enums.RuntimeABI;  
19 -  
20 -import java.io.File;  
21 -import java.util.ArrayList;  
22 -import java.util.List;  
23 -  
24 -import io.reactivex.Observable;  
25 -import io.reactivex.ObservableEmitter;  
26 -import io.reactivex.ObservableOnSubscribe;  
27 -import io.reactivex.Observer;  
28 -import io.reactivex.android.schedulers.AndroidSchedulers;  
29 -import io.reactivex.disposables.Disposable;  
30 -import io.reactivex.schedulers.Schedulers;  
31 -  
32 -  
33 -public class ChooseFunctionActivity extends BaseActivity {  
34 - private static final String TAG = "ChooseFunctionActivity";  
35 - private static final int ACTION_REQUEST_PERMISSIONS = 0x001;  
36 - // 在线激活所需的权限  
37 - private static final String[] NEEDED_PERMISSIONS = new String[]{  
38 - Manifest.permission.READ_PHONE_STATE  
39 - };  
40 - boolean libraryExists = true;  
41 - // Demo 所需的动态库文件  
42 - private static final String[] LIBRARIES = new String[]{  
43 - // 人脸相关  
44 - "libarcsoft_face_engine.so",  
45 - "libarcsoft_face.so",  
46 - // 图像库相关  
47 - "libarcsoft_image_util.so",  
48 - };  
49 - // 修改配置项的对话框  
50 - ChooseDetectDegreeDialog chooseDetectDegreeDialog;  
51 -  
52 - @Override  
53 - protected void onCreate(Bundle savedInstanceState) {  
54 - super.onCreate(savedInstanceState);  
55 - setContentView(R.layout.activity_choose_function);  
56 - libraryExists = checkSoFile(LIBRARIES);  
57 - ApplicationInfo applicationInfo = getApplicationInfo();  
58 - Log.i(TAG, "onCreate: " + applicationInfo.nativeLibraryDir);  
59 - if (!libraryExists) {  
60 - showToast(getString(R.string.library_not_found));  
61 - }else {  
62 - VersionInfo versionInfo = new VersionInfo();  
63 - int code = FaceEngine.getVersion(versionInfo);  
64 - Log.i(TAG, "onCreate: getVersion, code is: " + code + ", versionInfo is: " + versionInfo);  
65 - }  
66 - }  
67 -  
68 - /**  
69 - * 检查能否找到动态链接库,如果找不到,请修改工程配置  
70 - *  
71 - * @param libraries 需要的动态链接库  
72 - * @return 动态库是否存在  
73 - */  
74 - private boolean checkSoFile(String[] libraries) {  
75 - File dir = new File(getApplicationInfo().nativeLibraryDir);  
76 - File[] files = dir.listFiles();  
77 - if (files == null || files.length == 0) {  
78 - return false;  
79 - }  
80 - List<String> libraryNameList = new ArrayList<>();  
81 - for (File file : files) {  
82 - libraryNameList.add(file.getName());  
83 - }  
84 - boolean exists = true;  
85 - for (String library : libraries) {  
86 - exists &= libraryNameList.contains(library);  
87 - }  
88 - return exists;  
89 - }  
90 -  
91 - /**  
92 - * 打开相机,显示年龄性别  
93 - *  
94 - * @param view  
95 - */  
96 - public void jumpToPreviewActivity(View view) {  
97 - checkLibraryAndJump(FaceAttrPreviewActivity.class);  
98 - }  
99 -  
100 - /**  
101 - * 处理单张图片,显示图片中所有人脸的信息,并且一一比对相似度  
102 - *  
103 - * @param view  
104 - */  
105 - public void jumpToSingleImageActivity(View view) {  
106 - checkLibraryAndJump(SingleImageActivity.class);  
107 - }  
108 -  
109 - /**  
110 - * 选择一张主照,显示主照中人脸的详细信息,然后选择图片和主照进行比对  
111 - *  
112 - * @param view  
113 - */  
114 - public void jumpToMultiImageActivity(View view) {  
115 - checkLibraryAndJump(MultiImageActivity.class);  
116 - }  
117 -  
118 - /**  
119 - * 打开相机,RGB活体检测,人脸注册,人脸识别  
120 - *  
121 - * @param view  
122 - */  
123 - public void jumpToFaceRecognizeActivity(View view) {  
124 - checkLibraryAndJump(RegisterAndRecognizeActivity.class);  
125 - }  
126 -  
127 - /**  
128 - * 打开相机,IR活体检测,人脸注册,人脸识别  
129 - *  
130 - * @param view  
131 - */  
132 - public void jumpToIrFaceRecognizeActivity(View view) {  
133 - checkLibraryAndJump(IrRegisterAndRecognizeActivity.class);  
134 - }  
135 -  
136 - /**  
137 - * 批量注册和删除功能  
138 - *  
139 - * @param view  
140 - */  
141 - public void jumpToBatchRegisterActivity(View view) {  
142 - checkLibraryAndJump(FaceManageActivity.class);  
143 - }  
144 -  
145 - /**  
146 - * 激活引擎  
147 - *  
148 - * @param view  
149 - */  
150 - public void activeEngine(final View view) {  
151 - if (!libraryExists) {  
152 - showToast(getString(R.string.library_not_found));  
153 - return;  
154 - }  
155 - if (!checkPermissions(NEEDED_PERMISSIONS)) {  
156 - ActivityCompat.requestPermissions(this, NEEDED_PERMISSIONS, ACTION_REQUEST_PERMISSIONS);  
157 - return;  
158 - }  
159 - if (view != null) {  
160 - view.setClickable(false);  
161 - }  
162 - Observable.create(new ObservableOnSubscribe<Integer>() {  
163 - @Override  
164 - public void subscribe(ObservableEmitter<Integer> emitter) {  
165 - RuntimeABI runtimeABI = FaceEngine.getRuntimeABI();  
166 - Log.i(TAG, "subscribe: getRuntimeABI() " + runtimeABI);  
167 -  
168 - long start = System.currentTimeMillis();  
169 - int activeCode = FaceEngine.activeOnline(ChooseFunctionActivity.this, Constants.APP_ID, Constants.SDK_KEY);  
170 - Log.i(TAG, "subscribe cost: " + (System.currentTimeMillis() - start));  
171 - emitter.onNext(activeCode);  
172 - }  
173 - })  
174 - .subscribeOn(Schedulers.io())  
175 - .observeOn(AndroidSchedulers.mainThread())  
176 - .subscribe(new Observer<Integer>() {  
177 - @Override  
178 - public void onSubscribe(Disposable d) {  
179 -  
180 - }  
181 -  
182 - @Override  
183 - public void onNext(Integer activeCode) {  
184 - if (activeCode == ErrorInfo.MOK) {  
185 - showToast(getString(R.string.active_success));  
186 - } else if (activeCode == ErrorInfo.MERR_ASF_ALREADY_ACTIVATED) {  
187 - showToast(getString(R.string.already_activated));  
188 - } else {  
189 - showToast(getString(R.string.active_failed, activeCode));  
190 - }  
191 -  
192 - if (view != null) {  
193 - view.setClickable(true);  
194 - }  
195 - ActiveFileInfo activeFileInfo = new ActiveFileInfo();  
196 - int res = FaceEngine.getActiveFileInfo(ChooseFunctionActivity.this, activeFileInfo);  
197 - if (res == ErrorInfo.MOK) {  
198 - Log.i(TAG, activeFileInfo.toString());  
199 - }  
200 -  
201 - }  
202 -  
203 - @Override  
204 - public void onError(Throwable e) {  
205 - showToast(e.getMessage());  
206 - if (view != null) {  
207 - view.setClickable(true);  
208 - }  
209 - }  
210 -  
211 - @Override  
212 - public void onComplete() {  
213 -  
214 - }  
215 - });  
216 -  
217 - }  
218 -  
219 - @Override  
220 - void afterRequestPermission(int requestCode, boolean isAllGranted) {  
221 - if (requestCode == ACTION_REQUEST_PERMISSIONS) {  
222 - if (isAllGranted) {  
223 - activeEngine(null);  
224 - } else {  
225 - showToast(getString(R.string.permission_denied));  
226 - }  
227 - }  
228 - }  
229 -  
230 - void checkLibraryAndJump(Class activityClass) {  
231 - if (!libraryExists) {  
232 - showToast(getString(R.string.library_not_found));  
233 - return;  
234 - }  
235 - startActivity(new Intent(this, activityClass));  
236 - }  
237 -  
238 -  
239 - public void chooseDetectDegree(View view) {  
240 - if (chooseDetectDegreeDialog == null) {  
241 - chooseDetectDegreeDialog = new ChooseDetectDegreeDialog();  
242 - }  
243 - if (chooseDetectDegreeDialog.isAdded()) {  
244 - chooseDetectDegreeDialog.dismiss();  
245 - }  
246 - chooseDetectDegreeDialog.show(getSupportFragmentManager(), ChooseDetectDegreeDialog.class.getSimpleName());  
247 - }  
248 -  
249 -}  
1 -package com.arcsoft.arcfacedemo.activity;  
2 -  
3 -import android.Manifest;  
4 -import android.content.pm.ActivityInfo;  
5 -import android.graphics.Point;  
6 -import android.hardware.Camera;  
7 -import android.os.Build;  
8 -import android.os.Bundle;  
9 -import android.support.v4.app.ActivityCompat;  
10 -import android.util.DisplayMetrics;  
11 -import android.util.Log;  
12 -import android.view.View;  
13 -import android.view.ViewTreeObserver;  
14 -import android.view.WindowManager;  
15 -  
16 -import com.arcsoft.arcfacedemo.R;  
17 -import com.arcsoft.arcfacedemo.model.DrawInfo;  
18 -import com.arcsoft.arcfacedemo.util.ConfigUtil;  
19 -import com.arcsoft.arcfacedemo.util.DrawHelper;  
20 -import com.arcsoft.arcfacedemo.util.camera.CameraHelper;  
21 -import com.arcsoft.arcfacedemo.util.camera.CameraListener;  
22 -import com.arcsoft.arcfacedemo.util.face.RecognizeColor;  
23 -import com.arcsoft.arcfacedemo.widget.FaceRectView;  
24 -import com.arcsoft.face.AgeInfo;  
25 -import com.arcsoft.face.ErrorInfo;  
26 -import com.arcsoft.face.Face3DAngle;  
27 -import com.arcsoft.face.FaceEngine;  
28 -import com.arcsoft.face.FaceInfo;  
29 -import com.arcsoft.face.GenderInfo;  
30 -import com.arcsoft.face.LivenessInfo;  
31 -import com.arcsoft.face.enums.DetectMode;  
32 -  
33 -import java.util.ArrayList;  
34 -import java.util.List;  
35 -  
36 -public class FaceAttrPreviewActivity extends BaseActivity implements ViewTreeObserver.OnGlobalLayoutListener {  
37 - private static final String TAG = "FaceAttrPreviewActivity";  
38 - private CameraHelper cameraHelper;  
39 - private DrawHelper drawHelper;  
40 - private Camera.Size previewSize;  
41 - private Integer rgbCameraId = Camera.CameraInfo.CAMERA_FACING_BACK;  
42 - private FaceEngine faceEngine;  
43 - private int afCode = -1;  
44 - private int processMask = FaceEngine.ASF_AGE | FaceEngine.ASF_FACE3DANGLE | FaceEngine.ASF_GENDER | FaceEngine.ASF_LIVENESS;  
45 - /**  
46 - * 相机预览显示的控件,可为SurfaceView或TextureView  
47 - */  
48 - private View previewView;  
49 - private FaceRectView faceRectView;  
50 -  
51 - private static final int ACTION_REQUEST_PERMISSIONS = 0x001;  
52 - /**  
53 - * 所需的所有权限信息  
54 - */  
55 - private static final String[] NEEDED_PERMISSIONS = new String[]{  
56 - Manifest.permission.CAMERA,  
57 - Manifest.permission.READ_PHONE_STATE  
58 - };  
59 -  
60 - @Override  
61 - protected void onCreate(Bundle savedInstanceState) {  
62 - super.onCreate(savedInstanceState);  
63 - setContentView(R.layout.activity_face_attr_preview);  
64 - getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);  
65 - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {  
66 - WindowManager.LayoutParams attributes = getWindow().getAttributes();  
67 - attributes.systemUiVisibility = View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;  
68 - getWindow().setAttributes(attributes);  
69 - }  
70 -  
71 - // Activity启动后就锁定为启动时的方向  
72 - setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED);  
73 -  
74 - previewView = findViewById(R.id.texture_preview);  
75 - faceRectView = findViewById(R.id.face_rect_view);  
76 - //在布局结束后才做初始化操作  
77 - previewView.getViewTreeObserver().addOnGlobalLayoutListener(this);  
78 -  
79 - }  
80 -  
81 - private void initEngine() {  
82 - faceEngine = new FaceEngine();  
83 - afCode = faceEngine.init(this, DetectMode.ASF_DETECT_MODE_VIDEO, ConfigUtil.getFtOrient(this),  
84 - 16, 20, FaceEngine.ASF_FACE_DETECT | FaceEngine.ASF_AGE | FaceEngine.ASF_FACE3DANGLE | FaceEngine.ASF_GENDER | FaceEngine.ASF_LIVENESS);  
85 - Log.i(TAG, "initEngine: init: " + afCode);  
86 - if (afCode != ErrorInfo.MOK) {  
87 - showToast( getString(R.string.init_failed, afCode));  
88 - }  
89 - }  
90 -  
91 - private void unInitEngine() {  
92 -  
93 - if (afCode == 0) {  
94 - afCode = faceEngine.unInit();  
95 - Log.i(TAG, "unInitEngine: " + afCode);  
96 - }  
97 - }  
98 -  
99 -  
100 - @Override  
101 - protected void onDestroy() {  
102 - if (cameraHelper != null) {  
103 - cameraHelper.release();  
104 - cameraHelper = null;  
105 - }  
106 - unInitEngine();  
107 - super.onDestroy();  
108 - }  
109 -  
110 - private void initCamera() {  
111 - DisplayMetrics metrics = new DisplayMetrics();  
112 - getWindowManager().getDefaultDisplay().getMetrics(metrics);  
113 -  
114 - CameraListener cameraListener = new CameraListener() {  
115 - @Override  
116 - public void onCameraOpened(Camera camera, int cameraId, int displayOrientation, boolean isMirror) {  
117 - Log.i(TAG, "onCameraOpened: " + cameraId + " " + displayOrientation + " " + isMirror);  
118 - previewSize = camera.getParameters().getPreviewSize();  
119 - drawHelper = new DrawHelper(previewSize.width, previewSize.height, previewView.getWidth(), previewView.getHeight(), displayOrientation  
120 - , cameraId, isMirror, false, true);  
121 - }  
122 -  
123 -  
124 - @Override  
125 - public void onPreview(byte[] nv21, Camera camera) {  
126 -  
127 - if (faceRectView != null) {  
128 - faceRectView.clearFaceInfo();  
129 - }  
130 - List<FaceInfo> faceInfoList = new ArrayList<>();  
131 -// long start = System.currentTimeMillis();  
132 - int code = faceEngine.detectFaces(nv21, previewSize.width, previewSize.height, FaceEngine.CP_PAF_NV21, faceInfoList);  
133 - if (code == ErrorInfo.MOK && faceInfoList.size() > 0) {  
134 - code = faceEngine.process(nv21, previewSize.width, previewSize.height, FaceEngine.CP_PAF_NV21, faceInfoList, processMask);  
135 - if (code != ErrorInfo.MOK) {  
136 - return;  
137 - }  
138 - } else {  
139 - return;  
140 - }  
141 -  
142 - List<AgeInfo> ageInfoList = new ArrayList<>();  
143 - List<GenderInfo> genderInfoList = new ArrayList<>();  
144 - List<Face3DAngle> face3DAngleList = new ArrayList<>();  
145 - List<LivenessInfo> faceLivenessInfoList = new ArrayList<>();  
146 - int ageCode = faceEngine.getAge(ageInfoList);  
147 - int genderCode = faceEngine.getGender(genderInfoList);  
148 - int face3DAngleCode = faceEngine.getFace3DAngle(face3DAngleList);  
149 - int livenessCode = faceEngine.getLiveness(faceLivenessInfoList);  
150 -  
151 - // 有其中一个的错误码不为ErrorInfo.MOK,return  
152 - if ((ageCode | genderCode | face3DAngleCode | livenessCode) != ErrorInfo.MOK) {  
153 - return;  
154 - }  
155 - if (faceRectView != null && drawHelper != null) {  
156 - List<DrawInfo> drawInfoList = new ArrayList<>();  
157 - for (int i = 0; i < faceInfoList.size(); i++) {  
158 - 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));  
159 - }  
160 - drawHelper.draw(faceRectView, drawInfoList);  
161 - }  
162 - }  
163 -  
164 - @Override  
165 - public void onCameraClosed() {  
166 - Log.i(TAG, "onCameraClosed: ");  
167 - }  
168 -  
169 - @Override  
170 - public void onCameraError(Exception e) {  
171 - Log.i(TAG, "onCameraError: " + e.getMessage());  
172 - }  
173 -  
174 - @Override  
175 - public void onCameraConfigurationChanged(int cameraID, int displayOrientation) {  
176 - if (drawHelper != null) {  
177 - drawHelper.setCameraDisplayOrientation(displayOrientation);  
178 - }  
179 - Log.i(TAG, "onCameraConfigurationChanged: " + cameraID + " " + displayOrientation);  
180 - }  
181 - };  
182 - cameraHelper = new CameraHelper.Builder()  
183 - .previewViewSize(new Point(previewView.getMeasuredWidth(), previewView.getMeasuredHeight()))  
184 - .rotation(getWindowManager().getDefaultDisplay().getRotation())  
185 - .specificCameraId(rgbCameraId != null ? rgbCameraId : Camera.CameraInfo.CAMERA_FACING_FRONT)  
186 - .isMirror(false)  
187 - .previewOn(previewView)  
188 - .cameraListener(cameraListener)  
189 - .build();  
190 - cameraHelper.init();  
191 - cameraHelper.start();  
192 - }  
193 -  
194 - @Override  
195 - void afterRequestPermission(int requestCode, boolean isAllGranted) {  
196 - if (requestCode == ACTION_REQUEST_PERMISSIONS) {  
197 - if (isAllGranted) {  
198 - initEngine();  
199 - initCamera();  
200 - } else {  
201 - showToast(getString( R.string.permission_denied));  
202 - }  
203 - }  
204 - }  
205 -  
206 - /**  
207 - * 在{@link #previewView}第一次布局完成后,去除该监听,并且进行引擎和相机的初始化  
208 - */  
209 - @Override  
210 - public void onGlobalLayout() {  
211 - previewView.getViewTreeObserver().removeOnGlobalLayoutListener(this);  
212 - if (!checkPermissions(NEEDED_PERMISSIONS)) {  
213 - ActivityCompat.requestPermissions(this, NEEDED_PERMISSIONS, ACTION_REQUEST_PERMISSIONS);  
214 - } else {  
215 - initEngine();  
216 - initCamera();  
217 - }  
218 - }  
219 -  
220 -  
221 - /**  
222 - * 切换相机。注意:若切换相机发现检测不到人脸,则极有可能是检测角度导致的,需要销毁引擎重新创建或者在设置界面修改配置的检测角度  
223 - *  
224 - * @param view  
225 - */  
226 - public void switchCamera(View view) {  
227 - if (cameraHelper != null) {  
228 - boolean success = cameraHelper.switchCamera();  
229 - if (!success) {  
230 - showToast(getString(R.string.switch_camera_failed));  
231 - } else {  
232 - showLongToast(getString(R.string.notice_change_detect_degree));  
233 - }  
234 - }  
235 - }  
236 -}  
1 -package com.arcsoft.arcfacedemo.activity;  
2 -  
3 -import android.Manifest;  
4 -import android.content.DialogInterface;  
5 -import android.graphics.Bitmap;  
6 -import android.graphics.BitmapFactory;  
7 -import android.os.Environment;  
8 -import android.support.v4.app.ActivityCompat;  
9 -import android.support.v7.app.AlertDialog;  
10 -import android.os.Bundle;  
11 -import android.util.Log;  
12 -import android.view.View;  
13 -import android.view.WindowManager;  
14 -import android.widget.TextView;  
15 -  
16 -import com.arcsoft.arcfacedemo.R;  
17 -import com.arcsoft.arcfacedemo.widget.ProgressDialog;  
18 -import com.arcsoft.arcfacedemo.faceserver.FaceServer;  
19 -import com.arcsoft.imageutil.ArcSoftImageFormat;  
20 -import com.arcsoft.imageutil.ArcSoftImageUtil;  
21 -import com.arcsoft.imageutil.ArcSoftImageUtilError;  
22 -  
23 -import java.io.File;  
24 -import java.io.FilenameFilter;  
25 -import java.util.concurrent.ExecutorService;  
26 -import java.util.concurrent.Executors;  
27 -  
28 -/**  
29 - * 批量注册页面  
30 - */  
31 -public class FaceManageActivity extends BaseActivity {  
32 - //注册图所在的目录  
33 - private static final String ROOT_DIR = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "arcfacedemo";  
34 - private static final String REGISTER_DIR = ROOT_DIR + File.separator + "register";  
35 - private static final String REGISTER_FAILED_DIR = ROOT_DIR + File.separator + "failed";  
36 - private ExecutorService executorService;  
37 -  
38 - private TextView tvNotificationRegisterResult;  
39 -  
40 - ProgressDialog progressDialog = null;  
41 - private static final int ACTION_REQUEST_PERMISSIONS = 0x001;  
42 - private static String[] NEEDED_PERMISSIONS = new String[]{  
43 - Manifest.permission.READ_EXTERNAL_STORAGE,  
44 - Manifest.permission.WRITE_EXTERNAL_STORAGE  
45 - };  
46 -  
47 - @Override  
48 - protected void onCreate(Bundle savedInstanceState) {  
49 - super.onCreate(savedInstanceState);  
50 - setContentView(R.layout.activity_face_manage);  
51 - getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);  
52 - executorService = Executors.newSingleThreadExecutor();  
53 - tvNotificationRegisterResult = findViewById(R.id.notification_register_result);  
54 - progressDialog = new ProgressDialog(this);  
55 - FaceServer.getInstance().init(this);  
56 - }  
57 -  
58 - @Override  
59 - protected void onDestroy() {  
60 - if (executorService != null && !executorService.isShutdown()) {  
61 - executorService.shutdownNow();  
62 - }  
63 - if (progressDialog != null && progressDialog.isShowing()) {  
64 - progressDialog.dismiss();  
65 - }  
66 -  
67 - FaceServer.getInstance().unInit();  
68 - super.onDestroy();  
69 - }  
70 -  
71 - public void batchRegister(View view) {  
72 - if (checkPermissions(NEEDED_PERMISSIONS)) {  
73 - doRegister();  
74 - } else {  
75 - ActivityCompat.requestPermissions(this, NEEDED_PERMISSIONS, ACTION_REQUEST_PERMISSIONS);  
76 - }  
77 - }  
78 -  
79 - private void doRegister() {  
80 - File dir = new File(REGISTER_DIR);  
81 - if (!dir.exists()) {  
82 - showToast(getString(R.string.batch_process_path_is_not_exists, REGISTER_DIR));  
83 - return;  
84 - }  
85 - if (!dir.isDirectory()) {  
86 - showToast(getString(R.string.batch_process_path_is_not_dir, REGISTER_DIR));  
87 - return;  
88 - }  
89 - final File[] jpgFiles = dir.listFiles(new FilenameFilter() {  
90 - @Override  
91 - public boolean accept(File dir, String name) {  
92 - return name.endsWith(FaceServer.IMG_SUFFIX);  
93 - }  
94 - });  
95 - executorService.execute(new Runnable() {  
96 - @Override  
97 - public void run() {  
98 - final int totalCount = jpgFiles.length;  
99 -  
100 - int successCount = 0;  
101 - runOnUiThread(new Runnable() {  
102 - @Override  
103 - public void run() {  
104 - progressDialog.setMaxProgress(totalCount);  
105 - progressDialog.show();  
106 - tvNotificationRegisterResult.setText("");  
107 - tvNotificationRegisterResult.append(getString(R.string.batch_process_processing_please_wait));  
108 - }  
109 - });  
110 - for (int i = 0; i < totalCount; i++) {  
111 - final int finalI = i;  
112 - runOnUiThread(new Runnable() {  
113 - @Override  
114 - public void run() {  
115 - if (progressDialog != null) {  
116 - progressDialog.refreshProgress(finalI);  
117 - }  
118 - }  
119 - });  
120 - final File jpgFile = jpgFiles[i];  
121 - Bitmap bitmap = BitmapFactory.decodeFile(jpgFile.getAbsolutePath());  
122 - if (bitmap == null) {  
123 - File failedFile = new File(REGISTER_FAILED_DIR + File.separator + jpgFile.getName());  
124 - if (!failedFile.getParentFile().exists()) {  
125 - failedFile.getParentFile().mkdirs();  
126 - }  
127 - jpgFile.renameTo(failedFile);  
128 - continue;  
129 - }  
130 - bitmap = ArcSoftImageUtil.getAlignedBitmap(bitmap, true);  
131 - if (bitmap == null) {  
132 - File failedFile = new File(REGISTER_FAILED_DIR + File.separator + jpgFile.getName());  
133 - if (!failedFile.getParentFile().exists()) {  
134 - failedFile.getParentFile().mkdirs();  
135 - }  
136 - jpgFile.renameTo(failedFile);  
137 - continue;  
138 - }  
139 - byte[] bgr24 = ArcSoftImageUtil.createImageData(bitmap.getWidth(), bitmap.getHeight(), ArcSoftImageFormat.BGR24);  
140 - int transformCode = ArcSoftImageUtil.bitmapToImageData(bitmap, bgr24, ArcSoftImageFormat.BGR24);  
141 - if (transformCode != ArcSoftImageUtilError.CODE_SUCCESS) {  
142 - runOnUiThread(new Runnable() {  
143 - @Override  
144 - public void run() {  
145 - progressDialog.dismiss();  
146 - tvNotificationRegisterResult.append("");  
147 - }  
148 - });  
149 - return;  
150 - }  
151 - boolean success = FaceServer.getInstance().registerBgr24(FaceManageActivity.this, bgr24, bitmap.getWidth(), bitmap.getHeight(),  
152 - jpgFile.getName().substring(0, jpgFile.getName().lastIndexOf(".")));  
153 - if (!success) {  
154 - File failedFile = new File(REGISTER_FAILED_DIR + File.separator + jpgFile.getName());  
155 - if (!failedFile.getParentFile().exists()) {  
156 - failedFile.getParentFile().mkdirs();  
157 - }  
158 - jpgFile.renameTo(failedFile);  
159 - } else {  
160 - successCount++;  
161 - }  
162 - }  
163 - final int finalSuccessCount = successCount;  
164 - runOnUiThread(new Runnable() {  
165 - @Override  
166 - public void run() {  
167 - progressDialog.dismiss();  
168 - tvNotificationRegisterResult.append(getString(R.string.batch_process_finished_info, totalCount, finalSuccessCount, totalCount - finalSuccessCount, REGISTER_FAILED_DIR));  
169 - }  
170 - });  
171 - Log.i(FaceManageActivity.class.getSimpleName(), "run: " + executorService.isShutdown());  
172 - }  
173 - });  
174 - }  
175 -  
176 - @Override  
177 - void afterRequestPermission(int requestCode, boolean isAllGranted) {  
178 - if (requestCode == ACTION_REQUEST_PERMISSIONS) {  
179 - if (isAllGranted) {  
180 - doRegister();  
181 - } else {  
182 - showToast(getString(R.string.permission_denied));  
183 - }  
184 - }  
185 - }  
186 -  
187 - public void clearFaces(View view) {  
188 - int faceNum = FaceServer.getInstance().getFaceNumber(this);  
189 - if (faceNum == 0) {  
190 - showToast(getString(R.string.batch_process_no_face_need_to_delete));  
191 - } else {  
192 - AlertDialog dialog = new AlertDialog.Builder(this)  
193 - .setTitle(R.string.batch_process_notification)  
194 - .setMessage(getString(R.string.batch_process_confirm_delete, faceNum))  
195 - .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {  
196 - @Override  
197 - public void onClick(DialogInterface dialog, int which) {  
198 - int deleteCount = FaceServer.getInstance().clearAllFaces(FaceManageActivity.this);  
199 - showToast(deleteCount + " faces cleared!");  
200 - }  
201 - })  
202 - .setNegativeButton(R.string.cancel, null)  
203 - .create();  
204 - dialog.show();  
205 - }  
206 - }  
207 -}  
1 -package com.arcsoft.arcfacedemo.activity;  
2 -  
3 -import android.Manifest;  
4 -import android.content.pm.ActivityInfo;  
5 -import android.graphics.Color;  
6 -import android.graphics.Point;  
7 -import android.graphics.Rect;  
8 -import android.hardware.Camera;  
9 -import android.os.Build;  
10 -import android.os.Bundle;  
11 -import android.support.annotation.Nullable;  
12 -import android.support.v4.app.ActivityCompat;  
13 -import android.support.v7.widget.DefaultItemAnimator;  
14 -import android.support.v7.widget.GridLayoutManager;  
15 -import android.support.v7.widget.RecyclerView;  
16 -import android.util.DisplayMetrics;  
17 -import android.util.Log;  
18 -import android.view.View;  
19 -import android.view.ViewGroup;  
20 -import android.view.ViewTreeObserver;  
21 -import android.view.WindowManager;  
22 -import android.widget.CompoundButton;  
23 -import android.widget.FrameLayout;  
24 -import android.widget.Switch;  
25 -import android.widget.TextView;  
26 -  
27 -import com.arcsoft.arcfacedemo.R;  
28 -import com.arcsoft.arcfacedemo.common.Constants;  
29 -import com.arcsoft.arcfacedemo.faceserver.CompareResult;  
30 -import com.arcsoft.arcfacedemo.faceserver.FaceServer;  
31 -import com.arcsoft.arcfacedemo.model.DrawInfo;  
32 -import com.arcsoft.arcfacedemo.model.FacePreviewInfo;  
33 -import com.arcsoft.arcfacedemo.util.ConfigUtil;  
34 -import com.arcsoft.arcfacedemo.util.DrawHelper;  
35 -import com.arcsoft.arcfacedemo.util.camera.CameraListener;  
36 -import com.arcsoft.arcfacedemo.util.camera.DualCameraHelper;  
37 -import com.arcsoft.arcfacedemo.util.face.FaceHelper;  
38 -import com.arcsoft.arcfacedemo.util.face.FaceListener;  
39 -import com.arcsoft.arcfacedemo.util.face.LivenessType;  
40 -import com.arcsoft.arcfacedemo.util.face.RecognizeColor;  
41 -import com.arcsoft.arcfacedemo.util.face.RequestFeatureStatus;  
42 -import com.arcsoft.arcfacedemo.util.face.RequestLivenessStatus;  
43 -import com.arcsoft.arcfacedemo.widget.FaceRectView;  
44 -import com.arcsoft.arcfacedemo.widget.FaceSearchResultAdapter;  
45 -import com.arcsoft.face.AgeInfo;  
46 -import com.arcsoft.face.ErrorInfo;  
47 -import com.arcsoft.face.FaceEngine;  
48 -import com.arcsoft.face.FaceFeature;  
49 -import com.arcsoft.face.FaceInfo;  
50 -import com.arcsoft.face.GenderInfo;  
51 -import com.arcsoft.face.LivenessInfo;  
52 -import com.arcsoft.face.VersionInfo;  
53 -import com.arcsoft.face.enums.DetectFaceOrientPriority;  
54 -import com.arcsoft.face.enums.DetectMode;  
55 -  
56 -import java.util.ArrayList;  
57 -import java.util.Enumeration;  
58 -import java.util.List;  
59 -import java.util.Map;  
60 -import java.util.concurrent.ConcurrentHashMap;  
61 -import java.util.concurrent.TimeUnit;  
62 -  
63 -import io.reactivex.Observable;  
64 -import io.reactivex.ObservableEmitter;  
65 -import io.reactivex.ObservableOnSubscribe;  
66 -import io.reactivex.Observer;  
67 -import io.reactivex.android.schedulers.AndroidSchedulers;  
68 -import io.reactivex.disposables.CompositeDisposable;  
69 -import io.reactivex.disposables.Disposable;  
70 -import io.reactivex.schedulers.Schedulers;  
71 -  
72 -/**  
73 - * 1.活体检测使用IR摄像头数据,其他都使用RGB摄像头数据  
74 - * <p>  
75 - * 2.本界面仅实现IR数据和RGB数据预览大小相同且画面十分接近的情况(RGB数据和IR数据无旋转、镜像的关系)的活体检测,  
76 - * <p>  
77 - * 3.若IR数据和RGB数据预览大小不同或两者成像的人脸位置差别很大,需要自己实现人脸框的调整方案。  
78 - * <p>  
79 - * 4.由于不同的厂商对IR Camera和RGB Camera的CameraId设置可能会有所不同,开发者可能需要根据实际情况修改  
80 - * {@link IrRegisterAndRecognizeActivity#cameraRgbId}和  
81 - * {@link IrRegisterAndRecognizeActivity#cameraIrId}的值  
82 - * <p>  
83 - * 5.由于一般情况下android设备的前置摄像头,即cameraId为{@link Camera.CameraInfo#CAMERA_FACING_FRONT}的摄像头在打开后会自动被镜像预览。  
84 - * 为了便于开发者们更直观地了解两个摄像头成像的关系,实现人脸框的调整方案,本demo对cameraId为{@link Camera.CameraInfo#CAMERA_FACING_FRONT}  
85 - * 的预览画面做了再次镜像的处理,也就是恢复为原画面。  
86 - */  
87 -public class IrRegisterAndRecognizeActivity extends BaseActivity implements ViewTreeObserver.OnGlobalLayoutListener {  
88 - private static final String TAG = "IrRegisterAndRecognize";  
89 - private static final int MAX_DETECT_NUM = 10;  
90 - /**  
91 - * 当FR成功,活体未成功时,FR等待活体的时间  
92 - */  
93 - private static final int WAIT_LIVENESS_INTERVAL = 50;  
94 - /**  
95 - * 失败重试间隔时间(ms)  
96 - */  
97 - private static final long FAIL_RETRY_INTERVAL = 1000;  
98 - /**  
99 - * 出错重试最大次数  
100 - */  
101 - private static final int MAX_RETRY_TIME = 3;  
102 -  
103 - private DualCameraHelper cameraHelper;  
104 - private DualCameraHelper cameraHelperIr;  
105 - private DrawHelper drawHelperRgb;  
106 - private DrawHelper drawHelperIr;  
107 - private Camera.Size previewSize;  
108 - private Camera.Size previewSizeIr;  
109 -  
110 - /**  
111 - * RGB摄像头和IR摄像头的ID,若和实际不符,需要修改以下两个值。  
112 - * 同时,可能需要修改默认的VIDEO模式人脸检测角度  
113 - */  
114 - private Integer cameraRgbId = Camera.CameraInfo.CAMERA_FACING_BACK;  
115 - private Integer cameraIrId = Camera.CameraInfo.CAMERA_FACING_FRONT;  
116 -  
117 - private FaceEngine ftEngine;  
118 - private FaceEngine frEngine;  
119 - private FaceEngine flEngine;  
120 -  
121 - private int ftInitCode = -1;  
122 - private int frInitCode = -1;  
123 - private int flInitCode = -1;  
124 -  
125 - private FaceHelper faceHelperIr;  
126 - private List<CompareResult> compareResultList;  
127 - private FaceSearchResultAdapter adapter;  
128 - /**  
129 - * 活体检测的开关  
130 - */  
131 - private boolean livenessDetect = true;  
132 -  
133 - /**  
134 - * 注册人脸状态码,准备注册  
135 - */  
136 - private static final int REGISTER_STATUS_READY = 0;  
137 - /**  
138 - * 注册人脸状态码,注册中  
139 - */  
140 - private static final int REGISTER_STATUS_PROCESSING = 1;  
141 - /**  
142 - * 注册人脸状态码,注册结束(无论成功失败)  
143 - */  
144 - private static final int REGISTER_STATUS_DONE = 2;  
145 -  
146 - private int registerStatus = REGISTER_STATUS_DONE;  
147 -  
148 -  
149 - /**  
150 - * 用于记录人脸识别相关状态  
151 - */  
152 - private ConcurrentHashMap<Integer, Integer> requestFeatureStatusMap = new ConcurrentHashMap<>();  
153 - /**  
154 - * 用于记录人脸特征提取出错重试次数  
155 - */  
156 - private ConcurrentHashMap<Integer, Integer> extractErrorRetryMap = new ConcurrentHashMap<>();  
157 - /**  
158 - * 用于存储活体值  
159 - */  
160 - private ConcurrentHashMap<Integer, Integer> livenessMap = new ConcurrentHashMap<>();  
161 - /**  
162 - * 用于存储活体检测出错重试次数  
163 - */  
164 - private ConcurrentHashMap<Integer, Integer> livenessErrorRetryMap = new ConcurrentHashMap<>();  
165 -  
166 - private CompositeDisposable getFeatureDelayedDisposables = new CompositeDisposable();  
167 - private CompositeDisposable delayFaceTaskCompositeDisposable = new CompositeDisposable();  
168 - /**  
169 - * 相机预览显示的控件,可为SurfaceView或TextureView  
170 - */  
171 - private View previewViewRgb;  
172 - private View previewViewIr;  
173 - /**  
174 - * 绘制人脸框的控件  
175 - */  
176 - private FaceRectView faceRectView;  
177 - private FaceRectView faceRectViewIr;  
178 -  
179 - private Switch switchLivenessDetect;  
180 -  
181 - private static final int ACTION_REQUEST_PERMISSIONS = 0x001;  
182 -  
183 - /**  
184 - * 识别阈值  
185 - */  
186 - private static final float SIMILAR_THRESHOLD = 0.8F;  
187 -  
188 - /**  
189 - * 所需的所有权限信息  
190 - */  
191 - private static final String[] NEEDED_PERMISSIONS = new String[]{  
192 - Manifest.permission.CAMERA,  
193 - Manifest.permission.READ_PHONE_STATE,  
194 - Manifest.permission.READ_EXTERNAL_STORAGE,  
195 - Manifest.permission.WRITE_EXTERNAL_STORAGE  
196 - };  
197 -  
198 - private volatile byte[] rgbData;  
199 - private volatile byte[] irData;  
200 -  
201 - @Override  
202 - protected void onCreate(Bundle savedInstanceState) {  
203 - super.onCreate(savedInstanceState);  
204 - setContentView(R.layout.activity_register_and_recognize_ir);  
205 -  
206 - //保持亮屏  
207 - getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);  
208 -  
209 - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {  
210 - WindowManager.LayoutParams attributes = getWindow().getAttributes();  
211 - attributes.systemUiVisibility = View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;  
212 - getWindow().setAttributes(attributes);  
213 - }  
214 -  
215 - // Activity启动后就锁定为启动时的方向  
216 - setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED);  
217 - //本地人脸库初始化  
218 - FaceServer.getInstance().init(this);  
219 -  
220 - initView();  
221 - }  
222 -  
223 - private void initView() {  
224 - previewViewRgb = findViewById(R.id.dual_camera_texture_preview_rgb);  
225 - //在布局结束后才做初始化操作  
226 - previewViewRgb.getViewTreeObserver().addOnGlobalLayoutListener(this);  
227 - previewViewIr = findViewById(R.id.dual_camera_texture_previewIr);  
228 - faceRectView = findViewById(R.id.dual_camera_face_rect_view);  
229 - faceRectViewIr = findViewById(R.id.dual_camera_face_rect_viewIr);  
230 - switchLivenessDetect = findViewById(R.id.dual_camera_switch_liveness_detect);  
231 -  
232 - switchLivenessDetect.setChecked(livenessDetect);  
233 - switchLivenessDetect.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {  
234 - @Override  
235 - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {  
236 - livenessDetect = isChecked;  
237 - }  
238 - });  
239 - RecyclerView recyclerShowFaceInfo = findViewById(R.id.dual_camera_recycler_view_person);  
240 - compareResultList = new ArrayList<>();  
241 - adapter = new FaceSearchResultAdapter(compareResultList, this);  
242 - recyclerShowFaceInfo.setAdapter(adapter);  
243 - DisplayMetrics dm = getResources().getDisplayMetrics();  
244 - int spanCount = (int) (dm.widthPixels / (getResources().getDisplayMetrics().density * 100 + 0.5f));  
245 - recyclerShowFaceInfo.setLayoutManager(new GridLayoutManager(this, spanCount));  
246 - recyclerShowFaceInfo.setItemAnimator(new DefaultItemAnimator());  
247 - }  
248 -  
249 -  
250 - /**  
251 - * 初始化引擎  
252 - */  
253 - private void initEngine() {  
254 - ftEngine = new FaceEngine();  
255 - ftInitCode = ftEngine.init(this, DetectMode.ASF_DETECT_MODE_VIDEO, ConfigUtil.getFtOrient(this),  
256 - 16, MAX_DETECT_NUM, FaceEngine.ASF_FACE_DETECT);  
257 -  
258 - frEngine = new FaceEngine();  
259 - frInitCode = frEngine.init(this, DetectMode.ASF_DETECT_MODE_IMAGE, DetectFaceOrientPriority.ASF_OP_0_ONLY,  
260 - 16, MAX_DETECT_NUM, FaceEngine.ASF_FACE_RECOGNITION);  
261 -  
262 - flEngine = new FaceEngine();  
263 - flInitCode = flEngine.init(this, DetectMode.ASF_DETECT_MODE_IMAGE, DetectFaceOrientPriority.ASF_OP_0_ONLY,  
264 - 16, MAX_DETECT_NUM, FaceEngine.ASF_IR_LIVENESS);  
265 -  
266 - Log.i(TAG, "initEngine: init: " + ftInitCode);  
267 -  
268 - if (ftInitCode != ErrorInfo.MOK) {  
269 - String error = getString(R.string.specific_engine_init_failed, "ftEngine", ftInitCode);  
270 - Log.i(TAG, "initEngine: " + error);  
271 - showToast(error);  
272 - }  
273 - if (frInitCode != ErrorInfo.MOK) {  
274 - String error = getString(R.string.specific_engine_init_failed, "frEngine", ftInitCode);  
275 - Log.i(TAG, "initEngine: " + error);  
276 - showToast(error);  
277 - }  
278 - if (flInitCode != ErrorInfo.MOK) {  
279 - String error = getString(R.string.specific_engine_init_failed, "flEngine", ftInitCode);  
280 - Log.i(TAG, "initEngine: " + error);  
281 - showToast(error);  
282 - }  
283 - }  
284 -  
285 - /**  
286 - * 销毁引擎,faceHelperIr中可能会有特征提取耗时操作仍在执行,加锁防止crash  
287 - */  
288 - private void unInitEngine() {  
289 - if (ftInitCode == ErrorInfo.MOK && ftEngine != null) {  
290 - synchronized (ftEngine) {  
291 - int ftUnInitCode = ftEngine.unInit();  
292 - Log.i(TAG, "unInitEngine: " + ftUnInitCode);  
293 - }  
294 - }  
295 - if (frInitCode == ErrorInfo.MOK && frEngine != null) {  
296 - synchronized (frEngine) {  
297 - int frUnInitCode = frEngine.unInit();  
298 - Log.i(TAG, "unInitEngine: " + frUnInitCode);  
299 - }  
300 - }  
301 - if (flInitCode == ErrorInfo.MOK && flEngine != null) {  
302 - synchronized (flEngine) {  
303 - int flUnInitCode = flEngine.unInit();  
304 - Log.i(TAG, "unInitEngine: " + flUnInitCode);  
305 - }  
306 - }  
307 - }  
308 -  
309 - @Override  
310 - protected void onResume() {  
311 - super.onResume();  
312 - try {  
313 - if (cameraHelper != null) {  
314 - cameraHelper.start();  
315 - }  
316 - if (cameraHelperIr != null) {  
317 - cameraHelperIr.start();  
318 - }  
319 - } catch (RuntimeException e) {  
320 - showToast(e.getMessage() + getString(R.string.camera_error_notice));  
321 - }  
322 - }  
323 -  
324 - @Override  
325 - protected void onPause() {  
326 - if (cameraHelper != null) {  
327 - cameraHelper.stop();  
328 - }  
329 - if (cameraHelperIr != null) {  
330 - cameraHelperIr.stop();  
331 - }  
332 - super.onPause();  
333 - }  
334 -  
335 - @Override  
336 - protected void onDestroy() {  
337 -  
338 - if (cameraHelper != null) {  
339 - cameraHelper.release();  
340 - cameraHelper = null;  
341 - }  
342 - if (cameraHelperIr != null) {  
343 - cameraHelperIr.release();  
344 - cameraHelperIr = null;  
345 - }  
346 -  
347 - unInitEngine();  
348 -  
349 - if (getFeatureDelayedDisposables != null) {  
350 - getFeatureDelayedDisposables.clear();  
351 - }  
352 - if (delayFaceTaskCompositeDisposable != null) {  
353 - delayFaceTaskCompositeDisposable.clear();  
354 - }  
355 -  
356 - if (faceHelperIr != null) {  
357 - ConfigUtil.setTrackedFaceCount(this, faceHelperIr.getTrackedFaceCount());  
358 - faceHelperIr.release();  
359 - faceHelperIr = null;  
360 - }  
361 -  
362 - FaceServer.getInstance().unInit();  
363 - super.onDestroy();  
364 - }  
365 -  
366 - private void initRgbCamera() {  
367 - DisplayMetrics metrics = new DisplayMetrics();  
368 - getWindowManager().getDefaultDisplay().getMetrics(metrics);  
369 - final FaceListener faceListener = new FaceListener() {  
370 - @Override  
371 - public void onFail(Exception e) {  
372 - Log.e(TAG, "onFail: " + e.getMessage());  
373 - }  
374 -  
375 - //请求FR的回调  
376 - @Override  
377 - public void onFaceFeatureInfoGet(@Nullable final FaceFeature faceFeature, final Integer requestId, final Integer errorCode) {  
378 - //FR成功  
379 - if (faceFeature != null) {  
380 -// Log.i(TAG, "onPreview: fr end = " + System.currentTimeMillis() + " trackId = " + requestId);  
381 - Integer liveness = livenessMap.get(requestId);  
382 - //不做活体检测的情况,直接搜索  
383 - if (!livenessDetect) {  
384 - searchFace(faceFeature, requestId);  
385 - }  
386 - //活体检测通过,搜索特征  
387 - else if (liveness != null && liveness == LivenessInfo.ALIVE) {  
388 - searchFace(faceFeature, requestId);  
389 - }  
390 - //活体检测未出结果,或者非活体,延迟执行该函数  
391 - else {  
392 -  
393 - if (requestFeatureStatusMap.containsKey(requestId)) {  
394 - Observable.timer(WAIT_LIVENESS_INTERVAL, TimeUnit.MILLISECONDS)  
395 - .subscribe(new Observer<Long>() {  
396 - Disposable disposable;  
397 -  
398 - @Override  
399 - public void onSubscribe(Disposable d) {  
400 - disposable = d;  
401 - getFeatureDelayedDisposables.add(disposable);  
402 - }  
403 -  
404 - @Override  
405 - public void onNext(Long aLong) {  
406 - onFaceFeatureInfoGet(faceFeature, requestId, errorCode);  
407 - }  
408 -  
409 - @Override  
410 - public void onError(Throwable e) {  
411 -  
412 - }  
413 -  
414 - @Override  
415 - public void onComplete() {  
416 - getFeatureDelayedDisposables.remove(disposable);  
417 - }  
418 - });  
419 - }  
420 - }  
421 -  
422 - }  
423 - //特征提取失败  
424 - else {  
425 - if (increaseAndGetValue(extractErrorRetryMap, requestId) > MAX_RETRY_TIME) {  
426 - extractErrorRetryMap.put(requestId, 0);  
427 - String msg;  
428 - // 传入的FaceInfo在指定的图像上无法解析人脸,此处使用的是RGB人脸数据,一般是人脸模糊  
429 - if (errorCode != null && errorCode == ErrorInfo.MERR_FSDK_FACEFEATURE_LOW_CONFIDENCE_LEVEL) {  
430 - msg = getString(R.string.low_confidence_level);  
431 - } else {  
432 - msg = "ExtractCode:" + errorCode;  
433 - }  
434 - faceHelperIr.setName(requestId, getString(R.string.recognize_failed_notice, msg));  
435 - // 在尝试最大次数后,特征提取仍然失败,则认为识别未通过  
436 - requestFeatureStatusMap.put(requestId, RequestFeatureStatus.FAILED);  
437 - retryRecognizeDelayed(requestId);  
438 - } else {  
439 - requestFeatureStatusMap.put(requestId, RequestFeatureStatus.TO_RETRY);  
440 - }  
441 - }  
442 - }  
443 -  
444 - @Override  
445 - public void onFaceLivenessInfoGet(@Nullable LivenessInfo livenessInfo, final Integer requestId, Integer errorCode) {  
446 - if (livenessInfo != null) {  
447 - int liveness = livenessInfo.getLiveness();  
448 - livenessMap.put(requestId, liveness);  
449 - // 非活体,重试  
450 - if (liveness == LivenessInfo.NOT_ALIVE) {  
451 - faceHelperIr.setName(requestId, getString(R.string.recognize_failed_notice, "NOT_ALIVE"));  
452 - // 延迟 FAIL_RETRY_INTERVAL 后,将该人脸状态置为UNKNOWN,帧回调处理时会重新进行活体检测  
453 - retryLivenessDetectDelayed(requestId);  
454 - }  
455 - } else {  
456 - if (increaseAndGetValue(livenessErrorRetryMap, requestId) > MAX_RETRY_TIME) {  
457 - livenessErrorRetryMap.put(requestId, 0);  
458 - String msg;  
459 - // 传入的FaceInfo在指定的图像上无法解析人脸,此处使用RGB人脸框 + IR数据,一般是人脸模糊或画面中无人脸  
460 - if (errorCode != null && errorCode == ErrorInfo.MERR_FSDK_FACEFEATURE_LOW_CONFIDENCE_LEVEL) {  
461 - msg = getString(R.string.low_confidence_level);  
462 - } else {  
463 - msg = "ProcessCode:" + errorCode;  
464 - }  
465 - faceHelperIr.setName(requestId, getString(R.string.recognize_failed_notice, msg));  
466 - // 在尝试最大次数后,活体检测仍然失败,则认定为非活体  
467 - livenessMap.put(requestId, LivenessInfo.NOT_ALIVE);  
468 - retryLivenessDetectDelayed(requestId);  
469 - } else {  
470 - livenessMap.put(requestId, LivenessInfo.UNKNOWN);  
471 - }  
472 - }  
473 - }  
474 -  
475 - };  
476 - CameraListener rgbCameraListener = new CameraListener() {  
477 - @Override  
478 - public void onCameraOpened(Camera camera, int cameraId, int displayOrientation, boolean isMirror) {  
479 - previewSize = camera.getParameters().getPreviewSize();  
480 - ViewGroup.LayoutParams layoutParams = adjustPreviewViewSize(previewViewRgb, faceRectView, previewSize, displayOrientation);  
481 - drawHelperRgb = new DrawHelper(previewSize.width, previewSize.height, layoutParams.width, layoutParams.height, displayOrientation,  
482 - cameraId, isMirror, false, false);  
483 - if (faceHelperIr == null) {  
484 - faceHelperIr = new FaceHelper.Builder()  
485 - .ftEngine(ftEngine)  
486 - .frEngine(frEngine)  
487 - .flEngine(flEngine)  
488 - .frQueueSize(MAX_DETECT_NUM)  
489 - .flQueueSize(MAX_DETECT_NUM)  
490 - .previewSize(previewSize)  
491 - .faceListener(faceListener)  
492 - .trackedFaceCount(ConfigUtil.getTrackedFaceCount(IrRegisterAndRecognizeActivity.this.getApplicationContext()))  
493 - .build();  
494 - }  
495 -  
496 - TextView textViewRgb = new TextView(IrRegisterAndRecognizeActivity.this, null);  
497 - textViewRgb.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));  
498 - textViewRgb.setText(getString(R.string.camera_rgb) + "\n" + previewSize.width + "x" + previewSize.height);  
499 - textViewRgb.setTextColor(Color.WHITE);  
500 - textViewRgb.setBackgroundColor(getResources().getColor(R.color.color_bg_notification));  
501 - ((FrameLayout) previewViewRgb.getParent()).addView(textViewRgb);  
502 - }  
503 -  
504 -  
505 - @Override  
506 - public void onPreview(final byte[] nv21, Camera camera) {  
507 - rgbData = nv21;  
508 - processPreviewData();  
509 - }  
510 -  
511 - @Override  
512 - public void onCameraClosed() {  
513 - Log.i(TAG, "onCameraClosed: ");  
514 - }  
515 -  
516 - @Override  
517 - public void onCameraError(Exception e) {  
518 - Log.i(TAG, "onCameraError: " + e.getMessage());  
519 - }  
520 -  
521 - @Override  
522 - public void onCameraConfigurationChanged(int cameraID, int displayOrientation) {  
523 - if (drawHelperRgb != null) {  
524 - drawHelperRgb.setCameraDisplayOrientation(displayOrientation);  
525 - }  
526 - Log.i(TAG, "onCameraConfigurationChanged: " + cameraID + " " + displayOrientation);  
527 - }  
528 - };  
529 - cameraHelper = new DualCameraHelper.Builder()  
530 - .previewViewSize(new Point(previewViewRgb.getMeasuredWidth(), previewViewRgb.getMeasuredHeight()))  
531 - .rotation(getWindowManager().getDefaultDisplay().getRotation())  
532 - .specificCameraId(cameraRgbId != null ? cameraRgbId : Camera.CameraInfo.CAMERA_FACING_BACK)  
533 - .previewOn(previewViewRgb)  
534 - .cameraListener(rgbCameraListener)  
535 - .isMirror(cameraRgbId != null && Camera.CameraInfo.CAMERA_FACING_FRONT == cameraRgbId)  
536 - .build();  
537 - cameraHelper.init();  
538 - try {  
539 - cameraHelper.start();  
540 - } catch (RuntimeException e) {  
541 - showToast(e.getMessage() + getString(R.string.camera_error_notice));  
542 - }  
543 - }  
544 -  
545 - private void initIrCamera() {  
546 - CameraListener irCameraListener = new CameraListener() {  
547 - @Override  
548 - public void onCameraOpened(Camera camera, int cameraId, int displayOrientation, boolean isMirror) {  
549 - previewSizeIr = camera.getParameters().getPreviewSize();  
550 - ViewGroup.LayoutParams layoutParams = adjustPreviewViewSize(previewViewIr, faceRectViewIr, previewSizeIr, displayOrientation);  
551 - drawHelperIr = new DrawHelper(previewSizeIr.width, previewSizeIr.height, layoutParams.width, layoutParams.height, displayOrientation,  
552 - cameraId, isMirror, false, false);  
553 - TextView textViewIr = new TextView(IrRegisterAndRecognizeActivity.this, null);  
554 - textViewIr.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));  
555 - textViewIr.setText(getString(R.string.camera_ir) + "\n" + previewSizeIr.width + "x" + previewSizeIr.height);  
556 - textViewIr.setTextColor(Color.WHITE);  
557 - textViewIr.setBackgroundColor(getResources().getColor(R.color.color_bg_notification));  
558 - ((FrameLayout) previewViewIr.getParent()).addView(textViewIr);  
559 - }  
560 -  
561 -  
562 - @Override  
563 - public void onPreview(final byte[] nv21, Camera camera) {  
564 - irData = nv21;  
565 - }  
566 -  
567 - @Override  
568 - public void onCameraClosed() {  
569 - Log.i(TAG, "onCameraClosed: ");  
570 - }  
571 -  
572 - @Override  
573 - public void onCameraError(Exception e) {  
574 - Log.i(TAG, "onCameraError: " + e.getMessage());  
575 - }  
576 -  
577 - @Override  
578 - public void onCameraConfigurationChanged(int cameraID, int displayOrientation) {  
579 - if (drawHelperIr != null) {  
580 - drawHelperIr.setCameraDisplayOrientation(displayOrientation);  
581 - }  
582 - Log.i(TAG, "onCameraConfigurationChanged: " + cameraID + " " + displayOrientation);  
583 - }  
584 - };  
585 -  
586 - cameraHelperIr = new DualCameraHelper.Builder()  
587 - .previewViewSize(new Point(previewViewIr.getMeasuredWidth(), previewViewIr.getMeasuredHeight()))  
588 - .rotation(getWindowManager().getDefaultDisplay().getRotation())  
589 - .specificCameraId(cameraIrId != null ? cameraIrId : Camera.CameraInfo.CAMERA_FACING_FRONT)  
590 - .previewOn(previewViewIr)  
591 - .cameraListener(irCameraListener)  
592 - .isMirror(cameraIrId != null && Camera.CameraInfo.CAMERA_FACING_FRONT == cameraIrId)  
593 -// .previewSize(new Point(1280, 960)) //相机预览大小设置,RGB与IR需使用相同大小  
594 -// .additionalRotation(270) //额外旋转角度  
595 - .build();  
596 - cameraHelperIr.init();  
597 - try {  
598 - cameraHelperIr.start();  
599 - } catch (RuntimeException e) {  
600 - showToast(e.getMessage() + getString(R.string.camera_error_notice));  
601 - }  
602 - }  
603 -  
604 - /**  
605 - * 调整View的宽高,使2个预览同时显示  
606 - *  
607 - * @param previewView 显示预览数据的view  
608 - * @param faceRectView 画框的view  
609 - * @param previewSize 预览大小  
610 - * @param displayOrientation 相机旋转角度  
611 - * @return 调整后的LayoutParams  
612 - */  
613 - private ViewGroup.LayoutParams adjustPreviewViewSize(View previewView, FaceRectView faceRectView, Camera.Size previewSize, int displayOrientation) {  
614 - ViewGroup.LayoutParams layoutParams = previewView.getLayoutParams();  
615 - int measuredWidth = previewView.getMeasuredWidth();  
616 - int measuredHeight = previewView.getMeasuredHeight();  
617 - float ratio = ((float) previewSize.height) / (float) previewSize.width;  
618 - if (ratio > 1) {  
619 - ratio = 1 / ratio;  
620 - }  
621 - if (displayOrientation % 180 == 0) {  
622 - layoutParams.width = measuredWidth;  
623 - layoutParams.height = (int) (measuredWidth * ratio);  
624 - } else {  
625 - layoutParams.height = measuredHeight;  
626 - layoutParams.width = (int) (measuredHeight * ratio);  
627 - }  
628 - Log.i(TAG, "adjustPreviewViewSize: " + layoutParams.width + "x" + layoutParams.height);  
629 - previewView.setLayoutParams(layoutParams);  
630 - faceRectView.setLayoutParams(layoutParams);  
631 - return layoutParams;  
632 - }  
633 -  
634 - /**  
635 - * 处理预览数据  
636 - */  
637 - private synchronized void processPreviewData() {  
638 - if (rgbData != null && irData != null) {  
639 - final byte[] cloneNv21Rgb = rgbData.clone();  
640 - if (faceRectView != null) {  
641 - faceRectView.clearFaceInfo();  
642 - }  
643 - if (faceRectViewIr != null) {  
644 - faceRectViewIr.clearFaceInfo();  
645 - }  
646 - List<FacePreviewInfo> facePreviewInfoList = faceHelperIr.onPreviewFrame(cloneNv21Rgb);  
647 - if (facePreviewInfoList != null && faceRectView != null && drawHelperRgb != null  
648 - && faceRectViewIr != null && drawHelperIr != null) {  
649 - drawPreviewInfo(facePreviewInfoList);  
650 - }  
651 - registerFace(cloneNv21Rgb, facePreviewInfoList);  
652 - clearLeftFace(facePreviewInfoList);  
653 -  
654 - if (facePreviewInfoList != null && facePreviewInfoList.size() > 0 && previewSize != null) {  
655 - for (int i = 0; i < facePreviewInfoList.size(); i++) {  
656 - // 注意:这里虽然使用的是IR画面活体检测,RGB画面特征提取,但是考虑到成像接近,所以只用了RGB画面的图像质量检测  
657 - Integer status = requestFeatureStatusMap.get(facePreviewInfoList.get(i).getTrackId());  
658 - /**  
659 - * 在活体检测开启,在人脸活体状态不为处理中(ANALYZING)且不为处理完成(ALIVE、NOT_ALIVE)时重新进行活体检测  
660 - */  
661 - if (livenessDetect && (status == null || status != RequestFeatureStatus.SUCCEED)) {  
662 - Integer liveness = livenessMap.get(facePreviewInfoList.get(i).getTrackId());  
663 - if (liveness == null  
664 - || (liveness != LivenessInfo.ALIVE && liveness != LivenessInfo.NOT_ALIVE && liveness != RequestLivenessStatus.ANALYZING)) {  
665 - livenessMap.put(facePreviewInfoList.get(i).getTrackId(), RequestLivenessStatus.ANALYZING);  
666 - // IR数据偏移  
667 - FaceInfo faceInfo = facePreviewInfoList.get(i).getFaceInfo().clone();  
668 - faceInfo.getRect().offset(Constants.HORIZONTAL_OFFSET, Constants.VERTICAL_OFFSET);  
669 - faceHelperIr.requestFaceLiveness(irData.clone(), faceInfo, previewSize.width, previewSize.height, FaceEngine.CP_PAF_NV21, facePreviewInfoList.get(i).getTrackId(), LivenessType.IR);  
670 - }  
671 - }  
672 - /**  
673 - * 对于每个人脸,若状态为空或者为失败,则请求特征提取(可根据需要添加其他判断以限制特征提取次数),  
674 - * 特征提取回传的人脸特征结果在{@link FaceListener#onFaceFeatureInfoGet(FaceFeature, Integer, Integer)}中回传  
675 - */  
676 - if (status == null  
677 - || status == RequestFeatureStatus.TO_RETRY) {  
678 - requestFeatureStatusMap.put(facePreviewInfoList.get(i).getTrackId(), RequestFeatureStatus.SEARCHING);  
679 - faceHelperIr.requestFaceFeature(cloneNv21Rgb, facePreviewInfoList.get(i).getFaceInfo(),  
680 - previewSize.width, previewSize.height, FaceEngine.CP_PAF_NV21,  
681 - facePreviewInfoList.get(i).getTrackId());  
682 - }  
683 - }  
684 - }  
685 - rgbData = null;  
686 - irData = null;  
687 - }  
688 -  
689 - }  
690 -  
691 - /**  
692 - * 绘制预览相关数据  
693 - *  
694 - * @param facePreviewInfoList {@link FaceHelper#onPreviewFrame(byte[])}回传的处理结果  
695 - */  
696 - private void drawPreviewInfo(List<FacePreviewInfo> facePreviewInfoList) {  
697 - List<DrawInfo> drawInfoList = new ArrayList<>();  
698 - List<DrawInfo> drawInfoListIr = new ArrayList<>();  
699 - for (int i = 0; i < facePreviewInfoList.size(); i++) {  
700 - int trackId = facePreviewInfoList.get(i).getTrackId();  
701 - String name = faceHelperIr.getName(trackId);  
702 - Integer liveness = livenessMap.get(trackId);  
703 - Rect ftRect = facePreviewInfoList.get(i).getFaceInfo().getRect();  
704 -  
705 -  
706 - Integer recognizeStatus = requestFeatureStatusMap.get(facePreviewInfoList.get(i).getTrackId());  
707 -  
708 - // 根据识别结果和活体结果设置颜色  
709 - int color = RecognizeColor.COLOR_UNKNOWN;  
710 - if (recognizeStatus != null) {  
711 - if (recognizeStatus == RequestFeatureStatus.FAILED) {  
712 - color = RecognizeColor.COLOR_FAILED;  
713 - }  
714 - if (recognizeStatus == RequestFeatureStatus.SUCCEED) {  
715 - color = RecognizeColor.COLOR_SUCCESS;  
716 - }  
717 - }  
718 - if (liveness != null && liveness == LivenessInfo.NOT_ALIVE) {  
719 - color = RecognizeColor.COLOR_FAILED;  
720 - }  
721 -  
722 -  
723 - drawInfoList.add(new DrawInfo(drawHelperRgb.adjustRect(ftRect),  
724 - GenderInfo.UNKNOWN, AgeInfo.UNKNOWN_AGE,  
725 - liveness != null ? liveness : LivenessInfo.UNKNOWN, color,  
726 - name == null ? String.valueOf(trackId) : name));  
727 -  
728 - Rect offsetFtRect = new Rect(ftRect);  
729 - offsetFtRect.offset(Constants.HORIZONTAL_OFFSET, Constants.VERTICAL_OFFSET);  
730 - drawInfoListIr.add(new DrawInfo(drawHelperIr.adjustRect(offsetFtRect),  
731 - GenderInfo.UNKNOWN, AgeInfo.UNKNOWN_AGE,  
732 - liveness != null ? liveness : LivenessInfo.UNKNOWN, color,  
733 - name == null ? String.valueOf(trackId) : name));  
734 - }  
735 - drawHelperRgb.draw(faceRectView, drawInfoList);  
736 - drawHelperIr.draw(faceRectViewIr, drawInfoListIr);  
737 - }  
738 -  
739 - /**  
740 - * 注册人脸  
741 - *  
742 - * @param nv21Rgb RGB摄像头的帧数据  
743 - * @param facePreviewInfoList {@link FaceHelper#onPreviewFrame(byte[])}回传的处理结果  
744 - */  
745 - private void registerFace(final byte[] nv21Rgb, final List<FacePreviewInfo> facePreviewInfoList) {  
746 - if (registerStatus == REGISTER_STATUS_READY && facePreviewInfoList != null && facePreviewInfoList.size() > 0) {  
747 - registerStatus = REGISTER_STATUS_PROCESSING;  
748 - Observable.create(new ObservableOnSubscribe<Boolean>() {  
749 - @Override  
750 - public void subscribe(ObservableEmitter<Boolean> emitter) {  
751 - boolean success = FaceServer.getInstance().registerNv21(  
752 - IrRegisterAndRecognizeActivity.this, nv21Rgb,  
753 - previewSize.width, previewSize.height, facePreviewInfoList.get(0).getFaceInfo(), "registered " + faceHelperIr.getTrackedFaceCount());  
754 - emitter.onNext(success);  
755 - }  
756 - })  
757 - .subscribeOn(Schedulers.computation())  
758 - .observeOn(AndroidSchedulers.mainThread())  
759 - .subscribe(new Observer<Boolean>() {  
760 - @Override  
761 - public void onSubscribe(Disposable d) {  
762 -  
763 - }  
764 -  
765 - @Override  
766 - public void onNext(Boolean success) {  
767 - String result = success ? "register success!" : "register failed!";  
768 - showToast(result);  
769 - registerStatus = REGISTER_STATUS_DONE;  
770 - }  
771 -  
772 - @Override  
773 - public void onError(Throwable e) {  
774 - e.printStackTrace();  
775 - showToast("register failed!");  
776 - registerStatus = REGISTER_STATUS_DONE;  
777 - }  
778 -  
779 - @Override  
780 - public void onComplete() {  
781 -  
782 - }  
783 - });  
784 - }  
785 - }  
786 -  
787 - @Override  
788 - void afterRequestPermission(int requestCode, boolean isAllGranted) {  
789 - if (requestCode == ACTION_REQUEST_PERMISSIONS) {  
790 - if (isAllGranted) {  
791 - initEngine();  
792 - initRgbCamera();  
793 - initIrCamera();  
794 - } else {  
795 - showToast(getString(R.string.permission_denied));  
796 - }  
797 - }  
798 - }  
799 -  
800 -  
801 - /**  
802 - * 删除已经离开的人脸  
803 - *  
804 - * @param facePreviewInfoList 人脸和trackId列表  
805 - */  
806 - private void clearLeftFace(List<FacePreviewInfo> facePreviewInfoList) {  
807 - if (compareResultList != null) {  
808 - for (int i = compareResultList.size() - 1; i >= 0; i--) {  
809 - if (!requestFeatureStatusMap.containsKey(compareResultList.get(i).getTrackId())) {  
810 - compareResultList.remove(i);  
811 - adapter.notifyItemRemoved(i);  
812 - }  
813 - }  
814 - }  
815 - if (facePreviewInfoList == null || facePreviewInfoList.size() == 0) {  
816 - requestFeatureStatusMap.clear();  
817 - livenessMap.clear();  
818 - livenessErrorRetryMap.clear();  
819 - extractErrorRetryMap.clear();  
820 - if (getFeatureDelayedDisposables != null) {  
821 - getFeatureDelayedDisposables.clear();  
822 - }  
823 - return;  
824 - }  
825 - Enumeration<Integer> keys = requestFeatureStatusMap.keys();  
826 - while (keys.hasMoreElements()) {  
827 - int key = keys.nextElement();  
828 - boolean contained = false;  
829 - for (FacePreviewInfo facePreviewInfo : facePreviewInfoList) {  
830 - if (facePreviewInfo.getTrackId() == key) {  
831 - contained = true;  
832 - break;  
833 - }  
834 - }  
835 - if (!contained) {  
836 - requestFeatureStatusMap.remove(key);  
837 - livenessMap.remove(key);  
838 - livenessErrorRetryMap.remove(key);  
839 - extractErrorRetryMap.remove(key);  
840 - }  
841 - }  
842 -  
843 -  
844 - }  
845 -  
846 - private void searchFace(final FaceFeature frFace, final Integer requestId) {  
847 - Observable  
848 - .create(new ObservableOnSubscribe<CompareResult>() {  
849 - @Override  
850 - public void subscribe(ObservableEmitter<CompareResult> emitter) {  
851 -// Log.i(TAG, "subscribe: fr search start = " + System.currentTimeMillis() + " trackId = " + requestId);  
852 - CompareResult compareResult = FaceServer.getInstance().getTopOfFaceLib(frFace);  
853 -// Log.i(TAG, "subscribe: fr search end = " + System.currentTimeMillis() + " trackId = " + requestId);  
854 - emitter.onNext(compareResult);  
855 -  
856 - }  
857 - })  
858 - .subscribeOn(Schedulers.computation())  
859 - .observeOn(AndroidSchedulers.mainThread())  
860 - .subscribe(new Observer<CompareResult>() {  
861 - @Override  
862 - public void onSubscribe(Disposable d) {  
863 -  
864 - }  
865 -  
866 - @Override  
867 - public void onNext(CompareResult compareResult) {  
868 - if (compareResult == null || compareResult.getUserName() == null) {  
869 - requestFeatureStatusMap.put(requestId, RequestFeatureStatus.FAILED);  
870 - faceHelperIr.setName(requestId, "VISITOR " + requestId);  
871 - return;  
872 - }  
873 -  
874 -// Log.i(TAG, "onNext: fr search get result = " + System.currentTimeMillis() + " trackId = " + requestId + " similar = " + compareResult.getSimilar());  
875 - if (compareResult.getSimilar() > SIMILAR_THRESHOLD) {  
876 - boolean isAdded = false;  
877 - if (compareResultList == null) {  
878 - requestFeatureStatusMap.put(requestId, RequestFeatureStatus.FAILED);  
879 - faceHelperIr.setName(requestId, "VISITOR " + requestId);  
880 - return;  
881 - }  
882 - for (CompareResult compareResult1 : compareResultList) {  
883 - if (compareResult1.getTrackId() == requestId) {  
884 - isAdded = true;  
885 - break;  
886 - }  
887 - }  
888 - if (!isAdded) {  
889 - //对于多人脸搜索,假如最大显示数量为 MAX_DETECT_NUM 且有新的人脸进入,则以队列的形式移除  
890 - if (compareResultList.size() >= MAX_DETECT_NUM) {  
891 - compareResultList.remove(0);  
892 - adapter.notifyItemRemoved(0);  
893 - }  
894 - //添加显示人员时,保存其trackId  
895 - compareResult.setTrackId(requestId);  
896 - compareResultList.add(compareResult);  
897 - adapter.notifyItemInserted(compareResultList.size() - 1);  
898 - }  
899 - requestFeatureStatusMap.put(requestId, RequestFeatureStatus.SUCCEED);  
900 - faceHelperIr.setName(requestId, getString(R.string.recognize_success_notice, compareResult.getUserName()));  
901 -  
902 - } else {  
903 - faceHelperIr.setName(requestId, getString(R.string.recognize_failed_notice, "NOT_REGISTERED"));  
904 - retryRecognizeDelayed(requestId);  
905 - }  
906 - }  
907 -  
908 - @Override  
909 - public void onError(Throwable e) {  
910 - faceHelperIr.setName(requestId, getString(R.string.recognize_failed_notice, "NOT_REGISTERED"));  
911 - retryRecognizeDelayed(requestId);  
912 - }  
913 -  
914 - @Override  
915 - public void onComplete() {  
916 -  
917 - }  
918 - });  
919 - }  
920 -  
921 -  
922 - /**  
923 - * 将准备注册的状态置为{@link #REGISTER_STATUS_READY}  
924 - *  
925 - * @param view 注册按钮  
926 - */  
927 - public void register(View view) {  
928 - if (registerStatus == REGISTER_STATUS_DONE) {  
929 - registerStatus = REGISTER_STATUS_READY;  
930 - }  
931 - }  
932 -  
933 - /**  
934 - * 在{@link #previewViewRgb}第一次布局完成后,去除该监听,并且进行引擎和相机的初始化  
935 - */  
936 - @Override  
937 - public void onGlobalLayout() {  
938 - previewViewRgb.getViewTreeObserver().removeOnGlobalLayoutListener(this);  
939 - if (!checkPermissions(NEEDED_PERMISSIONS)) {  
940 - ActivityCompat.requestPermissions(this, NEEDED_PERMISSIONS, ACTION_REQUEST_PERMISSIONS);  
941 - } else {  
942 - initEngine();  
943 - initRgbCamera();  
944 - initIrCamera();  
945 - }  
946 - }  
947 -  
948 - public void drawIrRectVerticalMirror(View view) {  
949 - if (drawHelperIr != null) {  
950 - drawHelperIr.setMirrorVertical(!drawHelperIr.isMirrorVertical());  
951 - }  
952 - }  
953 -  
954 - public void drawIrRectHorizontalMirror(View view) {  
955 - if (drawHelperIr != null) {  
956 - drawHelperIr.setMirrorHorizontal(!drawHelperIr.isMirrorHorizontal());  
957 - }  
958 - }  
959 -  
960 -  
961 - /**  
962 - * 将map中key对应的value增1回传  
963 - *  
964 - * @param countMap map  
965 - * @param key key  
966 - * @return 增1后的value  
967 - */  
968 - public int increaseAndGetValue(Map<Integer, Integer> countMap, int key) {  
969 - if (countMap == null) {  
970 - return 0;  
971 - }  
972 - Integer value = countMap.get(key);  
973 - if (value == null) {  
974 - value = 0;  
975 - }  
976 - countMap.put(key, ++value);  
977 - return value;  
978 - }  
979 -  
980 - /**  
981 - * 延迟 FAIL_RETRY_INTERVAL 重新进行活体检测  
982 - *  
983 - * @param requestId 人脸ID  
984 - */  
985 - private void retryLivenessDetectDelayed(final Integer requestId) {  
986 - Observable.timer(FAIL_RETRY_INTERVAL, TimeUnit.MILLISECONDS)  
987 - .subscribe(new Observer<Long>() {  
988 - Disposable disposable;  
989 -  
990 - @Override  
991 - public void onSubscribe(Disposable d) {  
992 - disposable = d;  
993 - delayFaceTaskCompositeDisposable.add(disposable);  
994 - }  
995 -  
996 - @Override  
997 - public void onNext(Long aLong) {  
998 -  
999 - }  
1000 -  
1001 - @Override  
1002 - public void onError(Throwable e) {  
1003 - e.printStackTrace();  
1004 - }  
1005 -  
1006 - @Override  
1007 - public void onComplete() {  
1008 - // 将该人脸状态置为UNKNOWN,帧回调处理时会重新进行活体检测  
1009 - if (livenessDetect) {  
1010 - faceHelperIr.setName(requestId, Integer.toString(requestId));  
1011 - }  
1012 - livenessMap.put(requestId, LivenessInfo.UNKNOWN);  
1013 - delayFaceTaskCompositeDisposable.remove(disposable);  
1014 - }  
1015 - });  
1016 - }  
1017 -  
1018 - /**  
1019 - * 延迟 FAIL_RETRY_INTERVAL 重新进行人脸识别  
1020 - *  
1021 - * @param requestId 人脸ID  
1022 - */  
1023 - private void retryRecognizeDelayed(final Integer requestId) {  
1024 - requestFeatureStatusMap.put(requestId, RequestFeatureStatus.FAILED);  
1025 - Observable.timer(FAIL_RETRY_INTERVAL, TimeUnit.MILLISECONDS)  
1026 - .subscribe(new Observer<Long>() {  
1027 - Disposable disposable;  
1028 -  
1029 - @Override  
1030 - public void onSubscribe(Disposable d) {  
1031 - disposable = d;  
1032 - delayFaceTaskCompositeDisposable.add(disposable);  
1033 - }  
1034 -  
1035 - @Override  
1036 - public void onNext(Long aLong) {  
1037 -  
1038 - }  
1039 -  
1040 - @Override  
1041 - public void onError(Throwable e) {  
1042 - e.printStackTrace();  
1043 - }  
1044 -  
1045 - @Override  
1046 - public void onComplete() {  
1047 - // 将该人脸特征提取状态置为FAILED,帧回调处理时会重新进行活体检测  
1048 - faceHelperIr.setName(requestId, Integer.toString(requestId));  
1049 - requestFeatureStatusMap.put(requestId, RequestFeatureStatus.TO_RETRY);  
1050 - delayFaceTaskCompositeDisposable.remove(disposable);  
1051 - }  
1052 - });  
1053 - }  
1054 -}  
1 -package com.arcsoft.arcfacedemo.activity;  
2 -  
3 -import android.Manifest;  
4 -import android.content.Intent;  
5 -import android.content.pm.PackageManager;  
6 -import android.graphics.Bitmap;  
7 -import android.graphics.Canvas;  
8 -import android.graphics.Color;  
9 -import android.graphics.Paint;  
10 -import android.graphics.Typeface;  
11 -import android.os.Build;  
12 -import android.os.Bundle;  
13 -import android.provider.MediaStore;  
14 -import android.support.annotation.NonNull;  
15 -import android.support.annotation.Nullable;  
16 -import android.support.v4.app.ActivityCompat;  
17 -import android.support.v4.content.ContextCompat;  
18 -import android.support.v7.app.AppCompatActivity;  
19 -import android.support.v7.widget.DividerItemDecoration;  
20 -import android.support.v7.widget.LinearLayoutManager;  
21 -import android.support.v7.widget.RecyclerView;  
22 -import android.text.style.StyleSpan;  
23 -import android.util.Log;  
24 -import android.view.View;  
25 -import android.view.WindowManager;  
26 -import android.widget.ImageView;  
27 -import android.widget.TextView;  
28 -  
29 -import com.arcsoft.arcfacedemo.R;  
30 -import com.arcsoft.arcfacedemo.model.ItemShowInfo;  
31 -import com.arcsoft.arcfacedemo.widget.MultiFaceInfoAdapter;  
32 -import com.arcsoft.face.AgeInfo;  
33 -import com.arcsoft.face.ErrorInfo;  
34 -import com.arcsoft.face.Face3DAngle;  
35 -import com.arcsoft.face.FaceEngine;  
36 -import com.arcsoft.face.FaceFeature;  
37 -import com.arcsoft.face.FaceInfo;  
38 -import com.arcsoft.face.FaceSimilar;  
39 -import com.arcsoft.face.GenderInfo;  
40 -import com.arcsoft.face.enums.DetectFaceOrientPriority;  
41 -import com.arcsoft.face.enums.DetectMode;  
42 -import com.arcsoft.face.util.ImageUtils;  
43 -import com.arcsoft.imageutil.ArcSoftImageFormat;  
44 -import com.arcsoft.imageutil.ArcSoftImageUtil;  
45 -import com.arcsoft.imageutil.ArcSoftImageUtilError;  
46 -import com.bumptech.glide.Glide;  
47 -  
48 -import java.io.IOException;  
49 -import java.util.ArrayList;  
50 -import java.util.Arrays;  
51 -import java.util.List;  
52 -  
53 -  
54 -public class MultiImageActivity extends BaseActivity {  
55 - private static final String TAG = "MultiImageActivity";  
56 -  
57 - private static final int ACTION_CHOOSE_MAIN_IMAGE = 0x201;  
58 - private static final int ACTION_ADD_RECYCLER_ITEM_IMAGE = 0x202;  
59 -  
60 - private static final int ACTION_REQUEST_PERMISSIONS = 0x001;  
61 -  
62 - private ImageView ivMainImage;  
63 - private TextView tvMainImageInfo;  
64 - /**  
65 - * 选择图片时的类型  
66 - */  
67 - private static final int TYPE_MAIN = 0;  
68 - private static final int TYPE_ITEM = 1;  
69 -  
70 - /**  
71 - * 主图的第0张人脸的特征数据  
72 - */  
73 - private FaceFeature mainFeature;  
74 -  
75 - private MultiFaceInfoAdapter multiFaceInfoAdapter;  
76 - private List<ItemShowInfo> showInfoList;  
77 -  
78 - private FaceEngine faceEngine;  
79 - private int faceEngineCode = -1;  
80 -  
81 - private Bitmap mainBitmap;  
82 -  
83 - private static String[] NEEDED_PERMISSIONS = new String[]{  
84 - Manifest.permission.READ_PHONE_STATE  
85 - };  
86 -  
87 - @Override  
88 - protected void onCreate(Bundle savedInstanceState) {  
89 - super.onCreate(savedInstanceState);  
90 - getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);  
91 - setContentView(R.layout.activity_multi_image);  
92 - /**  
93 - * 在选择图片的时候,在android 7.0及以上通过FileProvider获取Uri,不需要文件权限  
94 - */  
95 - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {  
96 - List<String> permissionList = new ArrayList<>(Arrays.asList(NEEDED_PERMISSIONS));  
97 - permissionList.add(Manifest.permission.READ_EXTERNAL_STORAGE);  
98 - NEEDED_PERMISSIONS = permissionList.toArray(new String[0]);  
99 - }  
100 -  
101 - if (!checkPermissions(NEEDED_PERMISSIONS)) {  
102 - ActivityCompat.requestPermissions(this, NEEDED_PERMISSIONS, ACTION_REQUEST_PERMISSIONS);  
103 - } else {  
104 - initEngine();  
105 - }  
106 - initView();  
107 - }  
108 -  
109 - private void initView() {  
110 - ivMainImage = findViewById(R.id.iv_main_image);  
111 - tvMainImageInfo = findViewById(R.id.tv_main_image_info);  
112 - RecyclerView recyclerFaces = findViewById(R.id.recycler_faces);  
113 - showInfoList = new ArrayList<>();  
114 - multiFaceInfoAdapter = new MultiFaceInfoAdapter(showInfoList, this);  
115 - recyclerFaces.setAdapter(multiFaceInfoAdapter);  
116 - recyclerFaces.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));  
117 - recyclerFaces.setLayoutManager(new LinearLayoutManager(this));  
118 - }  
119 -  
120 - private void initEngine() {  
121 -  
122 - faceEngine = new FaceEngine();  
123 - faceEngineCode = faceEngine.init(this, DetectMode.ASF_DETECT_MODE_IMAGE, DetectFaceOrientPriority.ASF_OP_0_ONLY,  
124 - 16, 6, FaceEngine.ASF_FACE_RECOGNITION | FaceEngine.ASF_AGE | FaceEngine.ASF_FACE_DETECT | FaceEngine.ASF_GENDER | FaceEngine.ASF_FACE3DANGLE);  
125 -  
126 - Log.i(TAG, "initEngine: init " + faceEngineCode);  
127 -  
128 - if (faceEngineCode != ErrorInfo.MOK) {  
129 - showToast(getString(R.string.init_failed, faceEngineCode));  
130 - }  
131 - }  
132 -  
133 - private void unInitEngine() {  
134 - if (faceEngine != null) {  
135 - faceEngineCode = faceEngine.unInit();  
136 - Log.i(TAG, "unInitEngine: " + faceEngineCode);  
137 - }  
138 - }  
139 -  
140 - @Override  
141 - protected void onDestroy() {  
142 - unInitEngine();  
143 - super.onDestroy();  
144 - }  
145 -  
146 - @Override  
147 - protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {  
148 - super.onActivityResult(requestCode, resultCode, data);  
149 -  
150 - if (data == null || data.getData() == null) {  
151 - showToast(getString(R.string.get_picture_failed));  
152 - return;  
153 - }  
154 - if (requestCode == ACTION_CHOOSE_MAIN_IMAGE) {  
155 - try {  
156 - mainBitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), data.getData());  
157 - } catch (IOException e) {  
158 - e.printStackTrace();  
159 - showToast(getString(R.string.get_picture_failed));  
160 - return;  
161 - }  
162 - if (mainBitmap == null) {  
163 - showToast(getString(R.string.get_picture_failed));  
164 - return;  
165 - }  
166 - processImage(mainBitmap, TYPE_MAIN);  
167 - } else if (requestCode == ACTION_ADD_RECYCLER_ITEM_IMAGE) {  
168 - Bitmap bitmap = null;  
169 - try {  
170 - bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), data.getData());  
171 - } catch (IOException e) {  
172 - e.printStackTrace();  
173 - showToast(getString(R.string.get_picture_failed));  
174 - return;  
175 - }  
176 - if (bitmap == null) {  
177 - showToast(getString(R.string.get_picture_failed));  
178 - return;  
179 - }  
180 - if (mainFeature == null) {  
181 - return;  
182 - }  
183 - processImage(bitmap, TYPE_ITEM);  
184 - }  
185 - }  
186 -  
187 -  
188 - public void processImage(Bitmap bitmap, int type) {  
189 - if (bitmap == null) {  
190 - return;  
191 - }  
192 -  
193 - if (faceEngine == null) {  
194 - return;  
195 - }  
196 -  
197 - // 接口需要的bgr24宽度必须为4的倍数  
198 - bitmap = ArcSoftImageUtil.getAlignedBitmap(bitmap, true);  
199 -  
200 - if (bitmap == null) {  
201 - return;  
202 - }  
203 - int width = bitmap.getWidth();  
204 - int height = bitmap.getHeight();  
205 - // bitmap转bgr24  
206 - long start = System.currentTimeMillis();  
207 - byte[] bgr24 = ArcSoftImageUtil.createImageData(bitmap.getWidth(), bitmap.getHeight(), ArcSoftImageFormat.BGR24);  
208 - int transformCode = ArcSoftImageUtil.bitmapToImageData(bitmap, bgr24, ArcSoftImageFormat.BGR24);  
209 - if (transformCode != ArcSoftImageUtilError.CODE_SUCCESS) {  
210 - showToast("failed to transform bitmap to imageData, code is " + transformCode);  
211 - return;  
212 - }  
213 -// Log.i(TAG, "processImage:bitmapToBgr24 cost = " + (System.currentTimeMillis() - start));  
214 -  
215 - if (bgr24 != null) {  
216 -  
217 - List<FaceInfo> faceInfoList = new ArrayList<>();  
218 - //人脸检测  
219 - int detectCode = faceEngine.detectFaces(bgr24, width, height, FaceEngine.CP_PAF_BGR24, faceInfoList);  
220 - if (detectCode != 0 || faceInfoList.size() == 0) {  
221 - showToast("face detection finished, code is " + detectCode + ", face num is " + faceInfoList.size());  
222 - return;  
223 - }  
224 - //绘制bitmap  
225 - bitmap = bitmap.copy(Bitmap.Config.RGB_565, true);  
226 - Canvas canvas = new Canvas(bitmap);  
227 - Paint paint = new Paint();  
228 - paint.setAntiAlias(true);  
229 - paint.setStrokeWidth(10);  
230 - paint.setColor(Color.YELLOW);  
231 -  
232 - if (faceInfoList.size() > 0) {  
233 -  
234 - for (int i = 0; i < faceInfoList.size(); i++) {  
235 - //绘制人脸框  
236 - paint.setStyle(Paint.Style.STROKE);  
237 - canvas.drawRect(faceInfoList.get(i).getRect(), paint);  
238 - //绘制人脸序号  
239 - paint.setStyle(Paint.Style.FILL_AND_STROKE);  
240 - paint.setTextSize(faceInfoList.get(i).getRect().width() / 2);  
241 - canvas.drawText("" + i, faceInfoList.get(i).getRect().left, faceInfoList.get(i).getRect().top, paint);  
242 -  
243 - }  
244 - }  
245 -  
246 - int faceProcessCode = faceEngine.process(bgr24, width, height, FaceEngine.CP_PAF_BGR24, faceInfoList, FaceEngine.ASF_AGE | FaceEngine.ASF_GENDER | FaceEngine.ASF_FACE3DANGLE);  
247 - Log.i(TAG, "processImage: " + faceProcessCode);  
248 - if (faceProcessCode != ErrorInfo.MOK) {  
249 - showToast("face process finished, code is " + faceProcessCode);  
250 - return;  
251 - }  
252 - //年龄信息结果  
253 - List<AgeInfo> ageInfoList = new ArrayList<>();  
254 - //性别信息结果  
255 - List<GenderInfo> genderInfoList = new ArrayList<>();  
256 - //三维角度结果  
257 - List<Face3DAngle> face3DAngleList = new ArrayList<>();  
258 - //获取年龄、性别、三维角度  
259 - int ageCode = faceEngine.getAge(ageInfoList);  
260 - int genderCode = faceEngine.getGender(genderInfoList);  
261 - int face3DAngleCode = faceEngine.getFace3DAngle(face3DAngleList);  
262 -  
263 - if ((ageCode | genderCode | face3DAngleCode) != ErrorInfo.MOK) {  
264 - showToast("at lease one of age、gender、face3DAngle detect failed! codes are: " + ageCode  
265 - + " ," + genderCode + " ," + face3DAngleCode);  
266 - return;  
267 - }  
268 -  
269 - //人脸比对数据显示  
270 - if (faceInfoList.size() > 0) {  
271 - if (type == TYPE_MAIN) {  
272 - int size = showInfoList.size();  
273 - showInfoList.clear();  
274 - multiFaceInfoAdapter.notifyItemRangeRemoved(0, size);  
275 - mainFeature = new FaceFeature();  
276 - int res = faceEngine.extractFaceFeature(bgr24, width, height, FaceEngine.CP_PAF_BGR24, faceInfoList.get(0), mainFeature);  
277 - if (res != ErrorInfo.MOK) {  
278 - mainFeature = null;  
279 - }  
280 - Glide.with(ivMainImage.getContext())  
281 - .load(bitmap)  
282 - .into(ivMainImage);  
283 - StringBuilder stringBuilder = new StringBuilder();  
284 - if (faceInfoList.size() > 0) {  
285 - stringBuilder.append("face info:\n\n");  
286 - }  
287 - for (int i = 0; i < faceInfoList.size(); i++) {  
288 - stringBuilder.append("face[")  
289 - .append(i)  
290 - .append("]:\n")  
291 - .append(faceInfoList.get(i))  
292 - .append("\nage:")  
293 - .append(ageInfoList.get(i).getAge())  
294 - .append("\ngender:")  
295 - .append(genderInfoList.get(i).getGender() == GenderInfo.MALE ? "MALE"  
296 - : (genderInfoList.get(i).getGender() == GenderInfo.FEMALE ? "FEMALE" : "UNKNOWN"))  
297 - .append("\nface3DAngle:")  
298 - .append(face3DAngleList.get(i))  
299 - .append("\n\n");  
300 - }  
301 - tvMainImageInfo.setText(stringBuilder);  
302 - } else if (type == TYPE_ITEM) {  
303 - FaceFeature faceFeature = new FaceFeature();  
304 - int res = faceEngine.extractFaceFeature(bgr24, width, height, FaceEngine.CP_PAF_BGR24, faceInfoList.get(0), faceFeature);  
305 - if (res == 0) {  
306 - FaceSimilar faceSimilar = new FaceSimilar();  
307 - int compareResult = faceEngine.compareFaceFeature(mainFeature, faceFeature, faceSimilar);  
308 - if (compareResult == ErrorInfo.MOK) {  
309 -  
310 - ItemShowInfo showInfo = new ItemShowInfo(bitmap, ageInfoList.get(0).getAge(), genderInfoList.get(0).getGender(), faceSimilar.getScore());  
311 - showInfoList.add(showInfo);  
312 - multiFaceInfoAdapter.notifyItemInserted(showInfoList.size() - 1);  
313 - } else {  
314 - showToast(getString(R.string.compare_failed, compareResult));  
315 - }  
316 - }  
317 - }  
318 - } else {  
319 - if (type == TYPE_MAIN) {  
320 - mainBitmap = null;  
321 - }  
322 - }  
323 -  
324 - } else {  
325 - showToast("can not get bgr24 from bitmap!");  
326 - }  
327 - }  
328 -  
329 - /**  
330 - * 从本地选择文件  
331 - *  
332 - * @param action 可为选择主图{@link #ACTION_CHOOSE_MAIN_IMAGE}或者选择item图{@link #ACTION_ADD_RECYCLER_ITEM_IMAGE}  
333 - */  
334 - public void chooseLocalImage(int action) {  
335 - Intent intent = new Intent(Intent.ACTION_PICK);  
336 - intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*");  
337 - startActivityForResult(intent, action);  
338 - }  
339 -  
340 - public void addItemFace(View view) {  
341 - if (faceEngineCode != ErrorInfo.MOK) {  
342 - showToast(getString(R.string.engine_not_initialized, faceEngineCode));  
343 - return;  
344 - }  
345 - if (mainBitmap == null) {  
346 - showToast(getString(R.string.notice_choose_main_img));  
347 - return;  
348 - }  
349 - chooseLocalImage(ACTION_ADD_RECYCLER_ITEM_IMAGE);  
350 - }  
351 -  
352 - public void chooseMainImage(View view) {  
353 -  
354 - if (faceEngineCode != ErrorInfo.MOK) {  
355 - showToast(getString(R.string.engine_not_initialized, faceEngineCode));  
356 - return;  
357 - }  
358 - chooseLocalImage(ACTION_CHOOSE_MAIN_IMAGE);  
359 - }  
360 -  
361 - @Override  
362 - void afterRequestPermission(int requestCode, boolean isAllGranted) {  
363 - if (requestCode == ACTION_REQUEST_PERMISSIONS) {  
364 - if (isAllGranted) {  
365 - initEngine();  
366 - } else {  
367 - showToast(getString(R.string.permission_denied));  
368 - }  
369 - }  
370 - }  
371 -}  
1 -package com.arcsoft.arcfacedemo.activity;  
2 -  
3 -import android.Manifest;  
4 -import android.content.pm.ActivityInfo;  
5 -import android.graphics.Point;  
6 -import android.hardware.Camera;  
7 -import android.os.Build;  
8 -import android.os.Bundle;  
9 -import android.support.annotation.Nullable;  
10 -import android.support.v4.app.ActivityCompat;  
11 -import android.support.v7.widget.DefaultItemAnimator;  
12 -import android.support.v7.widget.GridLayoutManager;  
13 -import android.support.v7.widget.RecyclerView;  
14 -import android.util.DisplayMetrics;  
15 -import android.util.Log;  
16 -import android.view.View;  
17 -import android.view.ViewTreeObserver;  
18 -import android.view.WindowManager;  
19 -import android.widget.CompoundButton;  
20 -import android.widget.Switch;  
21 -  
22 -import com.arcsoft.arcfacedemo.R;  
23 -import com.arcsoft.arcfacedemo.faceserver.CompareResult;  
24 -import com.arcsoft.arcfacedemo.faceserver.FaceServer;  
25 -import com.arcsoft.arcfacedemo.model.DrawInfo;  
26 -import com.arcsoft.arcfacedemo.model.FacePreviewInfo;  
27 -import com.arcsoft.arcfacedemo.util.ConfigUtil;  
28 -import com.arcsoft.arcfacedemo.util.DrawHelper;  
29 -import com.arcsoft.arcfacedemo.util.camera.CameraHelper;  
30 -import com.arcsoft.arcfacedemo.util.camera.CameraListener;  
31 -import com.arcsoft.arcfacedemo.util.face.FaceHelper;  
32 -import com.arcsoft.arcfacedemo.util.face.FaceListener;  
33 -import com.arcsoft.arcfacedemo.util.face.LivenessType;  
34 -import com.arcsoft.arcfacedemo.util.face.RecognizeColor;  
35 -import com.arcsoft.arcfacedemo.util.face.RequestFeatureStatus;  
36 -import com.arcsoft.arcfacedemo.util.face.RequestLivenessStatus;  
37 -import com.arcsoft.arcfacedemo.widget.FaceRectView;  
38 -import com.arcsoft.arcfacedemo.widget.FaceSearchResultAdapter;  
39 -import com.arcsoft.face.AgeInfo;  
40 -import com.arcsoft.face.ErrorInfo;  
41 -import com.arcsoft.face.FaceEngine;  
42 -import com.arcsoft.face.FaceFeature;  
43 -import com.arcsoft.face.GenderInfo;  
44 -import com.arcsoft.face.LivenessInfo;  
45 -import com.arcsoft.face.enums.DetectFaceOrientPriority;  
46 -import com.arcsoft.face.enums.DetectMode;  
47 -  
48 -import java.util.ArrayList;  
49 -import java.util.Enumeration;  
50 -import java.util.List;  
51 -import java.util.Map;  
52 -import java.util.concurrent.ConcurrentHashMap;  
53 -import java.util.concurrent.TimeUnit;  
54 -  
55 -import io.reactivex.Observable;  
56 -import io.reactivex.ObservableEmitter;  
57 -import io.reactivex.ObservableOnSubscribe;  
58 -import io.reactivex.Observer;  
59 -import io.reactivex.android.schedulers.AndroidSchedulers;  
60 -import io.reactivex.disposables.CompositeDisposable;  
61 -import io.reactivex.disposables.Disposable;  
62 -import io.reactivex.schedulers.Schedulers;  
63 -  
64 -public class RegisterAndRecognizeActivity extends BaseActivity implements ViewTreeObserver.OnGlobalLayoutListener {  
65 - private static final String TAG = "RegisterAndRecognize";  
66 - private static final int MAX_DETECT_NUM = 10;  
67 - /**  
68 - * 当FR成功,活体未成功时,FR等待活体的时间  
69 - */  
70 - private static final int WAIT_LIVENESS_INTERVAL = 100;  
71 - /**  
72 - * 失败重试间隔时间(ms)  
73 - */  
74 - private static final long FAIL_RETRY_INTERVAL = 1000;  
75 - /**  
76 - * 出错重试最大次数  
77 - */  
78 - private static final int MAX_RETRY_TIME = 3;  
79 -  
80 - private CameraHelper cameraHelper;  
81 - private DrawHelper drawHelper;  
82 - private Camera.Size previewSize;  
83 - /**  
84 - * 优先打开的摄像头,本界面主要用于单目RGB摄像头设备,因此默认打开前置  
85 - */  
86 - private Integer rgbCameraID = Camera.CameraInfo.CAMERA_FACING_FRONT;  
87 -  
88 - /**  
89 - * VIDEO模式人脸检测引擎,用于预览帧人脸追踪  
90 - */  
91 - private FaceEngine ftEngine;  
92 - /**  
93 - * 用于特征提取的引擎  
94 - */  
95 - private FaceEngine frEngine;  
96 - /**  
97 - * IMAGE模式活体检测引擎,用于预览帧人脸活体检测  
98 - */  
99 - private FaceEngine flEngine;  
100 -  
101 - private int ftInitCode = -1;  
102 - private int frInitCode = -1;  
103 - private int flInitCode = -1;  
104 - private FaceHelper faceHelper;  
105 - private List<CompareResult> compareResultList;  
106 - private FaceSearchResultAdapter adapter;  
107 - /**  
108 - * 活体检测的开关  
109 - */  
110 - private boolean livenessDetect = true;  
111 - /**  
112 - * 注册人脸状态码,准备注册  
113 - */  
114 - private static final int REGISTER_STATUS_READY = 0;  
115 - /**  
116 - * 注册人脸状态码,注册中  
117 - */  
118 - private static final int REGISTER_STATUS_PROCESSING = 1;  
119 - /**  
120 - * 注册人脸状态码,注册结束(无论成功失败)  
121 - */  
122 - private static final int REGISTER_STATUS_DONE = 2;  
123 -  
124 - private int registerStatus = REGISTER_STATUS_DONE;  
125 - /**  
126 - * 用于记录人脸识别相关状态  
127 - */  
128 - private ConcurrentHashMap<Integer, Integer> requestFeatureStatusMap = new ConcurrentHashMap<>();  
129 - /**  
130 - * 用于记录人脸特征提取出错重试次数  
131 - */  
132 - private ConcurrentHashMap<Integer, Integer> extractErrorRetryMap = new ConcurrentHashMap<>();  
133 - /**  
134 - * 用于存储活体值  
135 - */  
136 - private ConcurrentHashMap<Integer, Integer> livenessMap = new ConcurrentHashMap<>();  
137 - /**  
138 - * 用于存储活体检测出错重试次数  
139 - */  
140 - private ConcurrentHashMap<Integer, Integer> livenessErrorRetryMap = new ConcurrentHashMap<>();  
141 -  
142 - private CompositeDisposable getFeatureDelayedDisposables = new CompositeDisposable();  
143 - private CompositeDisposable delayFaceTaskCompositeDisposable = new CompositeDisposable();  
144 - /**  
145 - * 相机预览显示的控件,可为SurfaceView或TextureView  
146 - */  
147 - private View previewView;  
148 - /**  
149 - * 绘制人脸框的控件  
150 - */  
151 - private FaceRectView faceRectView;  
152 -  
153 - private Switch switchLivenessDetect;  
154 -  
155 - private static final int ACTION_REQUEST_PERMISSIONS = 0x001;  
156 - /**  
157 - * 识别阈值  
158 - */  
159 - private static final float SIMILAR_THRESHOLD = 0.8F;  
160 - /**  
161 - * 所需的所有权限信息  
162 - */  
163 - private static final String[] NEEDED_PERMISSIONS = new String[]{  
164 - Manifest.permission.CAMERA,  
165 - Manifest.permission.READ_PHONE_STATE  
166 -  
167 - };  
168 -  
169 - @Override  
170 - protected void onCreate(Bundle savedInstanceState) {  
171 - super.onCreate(savedInstanceState);  
172 - setContentView(R.layout.activity_register_and_recognize);  
173 - //保持亮屏  
174 - getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);  
175 -  
176 - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {  
177 - WindowManager.LayoutParams attributes = getWindow().getAttributes();  
178 - attributes.systemUiVisibility = View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;  
179 - getWindow().setAttributes(attributes);  
180 - }  
181 -  
182 - // Activity启动后就锁定为启动时的方向  
183 - setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED);  
184 - //本地人脸库初始化  
185 - FaceServer.getInstance().init(this);  
186 -  
187 - initView();  
188 - }  
189 -  
190 - private void initView() {  
191 - previewView = findViewById(R.id.single_camera_texture_preview);  
192 - //在布局结束后才做初始化操作  
193 - previewView.getViewTreeObserver().addOnGlobalLayoutListener(this);  
194 -  
195 - faceRectView = findViewById(R.id.single_camera_face_rect_view);  
196 - switchLivenessDetect = findViewById(R.id.single_camera_switch_liveness_detect);  
197 - switchLivenessDetect.setChecked(livenessDetect);  
198 - switchLivenessDetect.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {  
199 - @Override  
200 - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {  
201 - livenessDetect = isChecked;  
202 - }  
203 - });  
204 - RecyclerView recyclerShowFaceInfo = findViewById(R.id.single_camera_recycler_view_person);  
205 - compareResultList = new ArrayList<>();  
206 - adapter = new FaceSearchResultAdapter(compareResultList, this);  
207 - recyclerShowFaceInfo.setAdapter(adapter);  
208 - DisplayMetrics dm = getResources().getDisplayMetrics();  
209 - int spanCount = (int) (dm.widthPixels / (getResources().getDisplayMetrics().density * 100 + 0.5f));  
210 - recyclerShowFaceInfo.setLayoutManager(new GridLayoutManager(this, spanCount));  
211 - recyclerShowFaceInfo.setItemAnimator(new DefaultItemAnimator());  
212 - }  
213 -  
214 - /**  
215 - * 初始化引擎  
216 - */  
217 - private void initEngine() {  
218 - ftEngine = new FaceEngine();  
219 - ftInitCode = ftEngine.init(this, DetectMode.ASF_DETECT_MODE_VIDEO, ConfigUtil.getFtOrient(this),  
220 - 16, MAX_DETECT_NUM, FaceEngine.ASF_FACE_DETECT);  
221 -  
222 - frEngine = new FaceEngine();  
223 - frInitCode = frEngine.init(this, DetectMode.ASF_DETECT_MODE_IMAGE, DetectFaceOrientPriority.ASF_OP_0_ONLY,  
224 - 16, MAX_DETECT_NUM, FaceEngine.ASF_FACE_RECOGNITION);  
225 -  
226 - flEngine = new FaceEngine();  
227 - flInitCode = flEngine.init(this, DetectMode.ASF_DETECT_MODE_IMAGE, DetectFaceOrientPriority.ASF_OP_0_ONLY,  
228 - 16, MAX_DETECT_NUM, FaceEngine.ASF_LIVENESS);  
229 -  
230 - Log.i(TAG, "initEngine: init: " + ftInitCode);  
231 -  
232 - if (ftInitCode != ErrorInfo.MOK) {  
233 - String error = getString(R.string.specific_engine_init_failed, "ftEngine", ftInitCode);  
234 - Log.i(TAG, "initEngine: " + error);  
235 - showToast(error);  
236 - }  
237 - if (frInitCode != ErrorInfo.MOK) {  
238 - String error = getString(R.string.specific_engine_init_failed, "frEngine", frInitCode);  
239 - Log.i(TAG, "initEngine: " + error);  
240 - showToast(error);  
241 - }  
242 - if (flInitCode != ErrorInfo.MOK) {  
243 - String error = getString(R.string.specific_engine_init_failed, "flEngine", flInitCode);  
244 - Log.i(TAG, "initEngine: " + error);  
245 - showToast(error);  
246 - }  
247 - }  
248 -  
249 - /**  
250 - * 销毁引擎,faceHelper中可能会有特征提取耗时操作仍在执行,加锁防止crash  
251 - */  
252 - private void unInitEngine() {  
253 - if (ftInitCode == ErrorInfo.MOK && ftEngine != null) {  
254 - synchronized (ftEngine) {  
255 - int ftUnInitCode = ftEngine.unInit();  
256 - Log.i(TAG, "unInitEngine: " + ftUnInitCode);  
257 - }  
258 - }  
259 - if (frInitCode == ErrorInfo.MOK && frEngine != null) {  
260 - synchronized (frEngine) {  
261 - int frUnInitCode = frEngine.unInit();  
262 - Log.i(TAG, "unInitEngine: " + frUnInitCode);  
263 - }  
264 - }  
265 - if (flInitCode == ErrorInfo.MOK && flEngine != null) {  
266 - synchronized (flEngine) {  
267 - int flUnInitCode = flEngine.unInit();  
268 - Log.i(TAG, "unInitEngine: " + flUnInitCode);  
269 - }  
270 - }  
271 - }  
272 -  
273 -  
274 - @Override  
275 - protected void onDestroy() {  
276 -  
277 - if (cameraHelper != null) {  
278 - cameraHelper.release();  
279 - cameraHelper = null;  
280 - }  
281 -  
282 - unInitEngine();  
283 - if (getFeatureDelayedDisposables != null) {  
284 - getFeatureDelayedDisposables.clear();  
285 - }  
286 - if (delayFaceTaskCompositeDisposable != null) {  
287 - delayFaceTaskCompositeDisposable.clear();  
288 - }  
289 - if (faceHelper != null) {  
290 - ConfigUtil.setTrackedFaceCount(this, faceHelper.getTrackedFaceCount());  
291 - faceHelper.release();  
292 - faceHelper = null;  
293 - }  
294 -  
295 - FaceServer.getInstance().unInit();  
296 - super.onDestroy();  
297 - }  
298 -  
299 - private void initCamera() {  
300 - DisplayMetrics metrics = new DisplayMetrics();  
301 - getWindowManager().getDefaultDisplay().getMetrics(metrics);  
302 -  
303 - final FaceListener faceListener = new FaceListener() {  
304 - @Override  
305 - public void onFail(Exception e) {  
306 - Log.e(TAG, "onFail: " + e.getMessage());  
307 - }  
308 -  
309 - //请求FR的回调  
310 - @Override  
311 - public void onFaceFeatureInfoGet(@Nullable final FaceFeature faceFeature, final Integer requestId, final Integer errorCode) {  
312 - //FR成功  
313 - if (faceFeature != null) {  
314 -// Log.i(TAG, "onPreview: fr end = " + System.currentTimeMillis() + " trackId = " + requestId);  
315 - Integer liveness = livenessMap.get(requestId);  
316 - //不做活体检测的情况,直接搜索  
317 - if (!livenessDetect) {  
318 - searchFace(faceFeature, requestId);  
319 - }  
320 - //活体检测通过,搜索特征  
321 - else if (liveness != null && liveness == LivenessInfo.ALIVE) {  
322 - searchFace(faceFeature, requestId);  
323 - }  
324 - //活体检测未出结果,或者非活体,延迟执行该函数  
325 - else {  
326 - if (requestFeatureStatusMap.containsKey(requestId)) {  
327 - Observable.timer(WAIT_LIVENESS_INTERVAL, TimeUnit.MILLISECONDS)  
328 - .subscribe(new Observer<Long>() {  
329 - Disposable disposable;  
330 -  
331 - @Override  
332 - public void onSubscribe(Disposable d) {  
333 - disposable = d;  
334 - getFeatureDelayedDisposables.add(disposable);  
335 - }  
336 -  
337 - @Override  
338 - public void onNext(Long aLong) {  
339 - onFaceFeatureInfoGet(faceFeature, requestId, errorCode);  
340 - }  
341 -  
342 - @Override  
343 - public void onError(Throwable e) {  
344 -  
345 - }  
346 -  
347 - @Override  
348 - public void onComplete() {  
349 - getFeatureDelayedDisposables.remove(disposable);  
350 - }  
351 - });  
352 - }  
353 - }  
354 -  
355 - }  
356 - //特征提取失败  
357 - else {  
358 - if (increaseAndGetValue(extractErrorRetryMap, requestId) > MAX_RETRY_TIME) {  
359 - extractErrorRetryMap.put(requestId, 0);  
360 -  
361 - String msg;  
362 - // 传入的FaceInfo在指定的图像上无法解析人脸,此处使用的是RGB人脸数据,一般是人脸模糊  
363 - if (errorCode != null && errorCode == ErrorInfo.MERR_FSDK_FACEFEATURE_LOW_CONFIDENCE_LEVEL) {  
364 - msg = getString(R.string.low_confidence_level);  
365 - } else {  
366 - msg = "ExtractCode:" + errorCode;  
367 - }  
368 - faceHelper.setName(requestId, getString(R.string.recognize_failed_notice, msg));  
369 - // 在尝试最大次数后,特征提取仍然失败,则认为识别未通过  
370 - requestFeatureStatusMap.put(requestId, RequestFeatureStatus.FAILED);  
371 - retryRecognizeDelayed(requestId);  
372 - } else {  
373 - requestFeatureStatusMap.put(requestId, RequestFeatureStatus.TO_RETRY);  
374 - }  
375 - }  
376 - }  
377 -  
378 - @Override  
379 - public void onFaceLivenessInfoGet(@Nullable LivenessInfo livenessInfo, final Integer requestId, Integer errorCode) {  
380 - if (livenessInfo != null) {  
381 - int liveness = livenessInfo.getLiveness();  
382 - livenessMap.put(requestId, liveness);  
383 - // 非活体,重试  
384 - if (liveness == LivenessInfo.NOT_ALIVE) {  
385 - faceHelper.setName(requestId, getString(R.string.recognize_failed_notice, "NOT_ALIVE"));  
386 - // 延迟 FAIL_RETRY_INTERVAL 后,将该人脸状态置为UNKNOWN,帧回调处理时会重新进行活体检测  
387 - retryLivenessDetectDelayed(requestId);  
388 - }  
389 - } else {  
390 - if (increaseAndGetValue(livenessErrorRetryMap, requestId) > MAX_RETRY_TIME) {  
391 - livenessErrorRetryMap.put(requestId, 0);  
392 - String msg;  
393 - // 传入的FaceInfo在指定的图像上无法解析人脸,此处使用的是RGB人脸数据,一般是人脸模糊  
394 - if (errorCode != null && errorCode == ErrorInfo.MERR_FSDK_FACEFEATURE_LOW_CONFIDENCE_LEVEL) {  
395 - msg = getString(R.string.low_confidence_level);  
396 - } else {  
397 - msg = "ProcessCode:" + errorCode;  
398 - }  
399 - faceHelper.setName(requestId, getString(R.string.recognize_failed_notice, msg));  
400 - retryLivenessDetectDelayed(requestId);  
401 - } else {  
402 - livenessMap.put(requestId, LivenessInfo.UNKNOWN);  
403 - }  
404 - }  
405 - }  
406 -  
407 -  
408 - };  
409 -  
410 -  
411 - CameraListener cameraListener = new CameraListener() {  
412 - @Override  
413 - public void onCameraOpened(Camera camera, int cameraId, int displayOrientation, boolean isMirror) {  
414 - Camera.Size lastPreviewSize = previewSize;  
415 - previewSize = camera.getParameters().getPreviewSize();  
416 - drawHelper = new DrawHelper(previewSize.width, previewSize.height, previewView.getWidth(), previewView.getHeight(), displayOrientation  
417 - , cameraId, isMirror, false, true);  
418 - Log.i(TAG, "onCameraOpened: " + drawHelper.toString());  
419 - // 切换相机的时候可能会导致预览尺寸发生变化  
420 - if (faceHelper == null ||  
421 - lastPreviewSize == null ||  
422 - lastPreviewSize.width != previewSize.width || lastPreviewSize.height != previewSize.height) {  
423 - Integer trackedFaceCount = null;  
424 - // 记录切换时的人脸序号  
425 - if (faceHelper != null) {  
426 - trackedFaceCount = faceHelper.getTrackedFaceCount();  
427 - faceHelper.release();  
428 - }  
429 - faceHelper = new FaceHelper.Builder()  
430 - .ftEngine(ftEngine)  
431 - .frEngine(frEngine)  
432 - .flEngine(flEngine)  
433 - .frQueueSize(MAX_DETECT_NUM)  
434 - .flQueueSize(MAX_DETECT_NUM)  
435 - .previewSize(previewSize)  
436 - .faceListener(faceListener)  
437 - .trackedFaceCount(trackedFaceCount == null ? ConfigUtil.getTrackedFaceCount(RegisterAndRecognizeActivity.this.getApplicationContext()) : trackedFaceCount)  
438 - .build();  
439 - }  
440 - }  
441 -  
442 -  
443 - @Override  
444 - public void onPreview(final byte[] nv21, Camera camera) {  
445 - if (faceRectView != null) {  
446 - faceRectView.clearFaceInfo();  
447 - }  
448 - List<FacePreviewInfo> facePreviewInfoList = faceHelper.onPreviewFrame(nv21);  
449 - if (facePreviewInfoList != null && faceRectView != null && drawHelper != null) {  
450 - drawPreviewInfo(facePreviewInfoList);  
451 - }  
452 - registerFace(nv21, facePreviewInfoList);  
453 - clearLeftFace(facePreviewInfoList);  
454 -  
455 - if (facePreviewInfoList != null && facePreviewInfoList.size() > 0 && previewSize != null) {  
456 - for (int i = 0; i < facePreviewInfoList.size(); i++) {  
457 - Integer status = requestFeatureStatusMap.get(facePreviewInfoList.get(i).getTrackId());  
458 - /**  
459 - * 在活体检测开启,在人脸识别状态不为成功或人脸活体状态不为处理中(ANALYZING)且不为处理完成(ALIVE、NOT_ALIVE)时重新进行活体检测  
460 - */  
461 - if (livenessDetect && (status == null || status != RequestFeatureStatus.SUCCEED)) {  
462 - Integer liveness = livenessMap.get(facePreviewInfoList.get(i).getTrackId());  
463 - if (liveness == null  
464 - || (liveness != LivenessInfo.ALIVE && liveness != LivenessInfo.NOT_ALIVE && liveness != RequestLivenessStatus.ANALYZING)) {  
465 - livenessMap.put(facePreviewInfoList.get(i).getTrackId(), RequestLivenessStatus.ANALYZING);  
466 - faceHelper.requestFaceLiveness(nv21, facePreviewInfoList.get(i).getFaceInfo(), previewSize.width, previewSize.height, FaceEngine.CP_PAF_NV21, facePreviewInfoList.get(i).getTrackId(), LivenessType.RGB);  
467 - }  
468 - }  
469 - /**  
470 - * 对于每个人脸,若状态为空或者为失败,则请求特征提取(可根据需要添加其他判断以限制特征提取次数),  
471 - * 特征提取回传的人脸特征结果在{@link FaceListener#onFaceFeatureInfoGet(FaceFeature, Integer, Integer)}中回传  
472 - */  
473 - if (status == null  
474 - || status == RequestFeatureStatus.TO_RETRY) {  
475 - requestFeatureStatusMap.put(facePreviewInfoList.get(i).getTrackId(), RequestFeatureStatus.SEARCHING);  
476 - faceHelper.requestFaceFeature(nv21, facePreviewInfoList.get(i).getFaceInfo(), previewSize.width, previewSize.height, FaceEngine.CP_PAF_NV21, facePreviewInfoList.get(i).getTrackId());  
477 -// Log.i(TAG, "onPreview: fr start = " + System.currentTimeMillis() + " trackId = " + facePreviewInfoList.get(i).getTrackedFaceCount());  
478 - }  
479 - }  
480 - }  
481 - }  
482 -  
483 - @Override  
484 - public void onCameraClosed() {  
485 - Log.i(TAG, "onCameraClosed: ");  
486 - }  
487 -  
488 - @Override  
489 - public void onCameraError(Exception e) {  
490 - Log.i(TAG, "onCameraError: " + e.getMessage());  
491 - }  
492 -  
493 - @Override  
494 - public void onCameraConfigurationChanged(int cameraID, int displayOrientation) {  
495 - if (drawHelper != null) {  
496 - drawHelper.setCameraDisplayOrientation(displayOrientation);  
497 - }  
498 - Log.i(TAG, "onCameraConfigurationChanged: " + cameraID + " " + displayOrientation);  
499 - }  
500 - };  
501 -  
502 - cameraHelper = new CameraHelper.Builder()  
503 - .previewViewSize(new Point(previewView.getMeasuredWidth(), previewView.getMeasuredHeight()))  
504 - .rotation(getWindowManager().getDefaultDisplay().getRotation())  
505 - .specificCameraId(rgbCameraID != null ? rgbCameraID : Camera.CameraInfo.CAMERA_FACING_FRONT)  
506 - .isMirror(false)  
507 - .previewOn(previewView)  
508 - .cameraListener(cameraListener)  
509 - .build();  
510 - cameraHelper.init();  
511 - cameraHelper.start();  
512 - }  
513 -  
514 - private void registerFace(final byte[] nv21, final List<FacePreviewInfo> facePreviewInfoList) {  
515 - if (registerStatus == REGISTER_STATUS_READY && facePreviewInfoList != null && facePreviewInfoList.size() > 0) {  
516 - registerStatus = REGISTER_STATUS_PROCESSING;  
517 - Observable.create(new ObservableOnSubscribe<Boolean>() {  
518 - @Override  
519 - public void subscribe(ObservableEmitter<Boolean> emitter) {  
520 -  
521 - boolean success = FaceServer.getInstance().registerNv21(RegisterAndRecognizeActivity.this, nv21.clone(), previewSize.width, previewSize.height,  
522 - facePreviewInfoList.get(0).getFaceInfo(), "registered " + faceHelper.getTrackedFaceCount());  
523 - emitter.onNext(success);  
524 - }  
525 - })  
526 - .subscribeOn(Schedulers.computation())  
527 - .observeOn(AndroidSchedulers.mainThread())  
528 - .subscribe(new Observer<Boolean>() {  
529 - @Override  
530 - public void onSubscribe(Disposable d) {  
531 -  
532 - }  
533 -  
534 - @Override  
535 - public void onNext(Boolean success) {  
536 - String result = success ? "register success!" : "register failed!";  
537 - showToast(result);  
538 - registerStatus = REGISTER_STATUS_DONE;  
539 - }  
540 -  
541 - @Override  
542 - public void onError(Throwable e) {  
543 - e.printStackTrace();  
544 - showToast("register failed!");  
545 - registerStatus = REGISTER_STATUS_DONE;  
546 - }  
547 -  
548 - @Override  
549 - public void onComplete() {  
550 -  
551 - }  
552 - });  
553 - }  
554 - }  
555 -  
556 - private void drawPreviewInfo(List<FacePreviewInfo> facePreviewInfoList) {  
557 - List<DrawInfo> drawInfoList = new ArrayList<>();  
558 - for (int i = 0; i < facePreviewInfoList.size(); i++) {  
559 - String name = faceHelper.getName(facePreviewInfoList.get(i).getTrackId());  
560 - Integer liveness = livenessMap.get(facePreviewInfoList.get(i).getTrackId());  
561 - Integer recognizeStatus = requestFeatureStatusMap.get(facePreviewInfoList.get(i).getTrackId());  
562 -  
563 - // 根据识别结果和活体结果设置颜色  
564 - int color = RecognizeColor.COLOR_UNKNOWN;  
565 - if (recognizeStatus != null) {  
566 - if (recognizeStatus == RequestFeatureStatus.FAILED) {  
567 - color = RecognizeColor.COLOR_FAILED;  
568 - }  
569 - if (recognizeStatus == RequestFeatureStatus.SUCCEED) {  
570 - color = RecognizeColor.COLOR_SUCCESS;  
571 - }  
572 - }  
573 - if (liveness != null && liveness == LivenessInfo.NOT_ALIVE) {  
574 - color = RecognizeColor.COLOR_FAILED;  
575 - }  
576 -  
577 - drawInfoList.add(new DrawInfo(drawHelper.adjustRect(facePreviewInfoList.get(i).getFaceInfo().getRect()),  
578 - GenderInfo.UNKNOWN, AgeInfo.UNKNOWN_AGE, liveness == null ? LivenessInfo.UNKNOWN : liveness, color,  
579 - name == null ? String.valueOf(facePreviewInfoList.get(i).getTrackId()) : name));  
580 - }  
581 - drawHelper.draw(faceRectView, drawInfoList);  
582 - }  
583 -  
584 - @Override  
585 - void afterRequestPermission(int requestCode, boolean isAllGranted) {  
586 - if (requestCode == ACTION_REQUEST_PERMISSIONS) {  
587 - if (isAllGranted) {  
588 - initEngine();  
589 - initCamera();  
590 - } else {  
591 - showToast(getString(R.string.permission_denied));  
592 - }  
593 - }  
594 - }  
595 -  
596 - /**  
597 - * 删除已经离开的人脸  
598 - *  
599 - * @param facePreviewInfoList 人脸和trackId列表  
600 - */  
601 - private void clearLeftFace(List<FacePreviewInfo> facePreviewInfoList) {  
602 - if (compareResultList != null) {  
603 - for (int i = compareResultList.size() - 1; i >= 0; i--) {  
604 - if (!requestFeatureStatusMap.containsKey(compareResultList.get(i).getTrackId())) {  
605 - compareResultList.remove(i);  
606 - adapter.notifyItemRemoved(i);  
607 - }  
608 - }  
609 - }  
610 - if (facePreviewInfoList == null || facePreviewInfoList.size() == 0) {  
611 - requestFeatureStatusMap.clear();  
612 - livenessMap.clear();  
613 - livenessErrorRetryMap.clear();  
614 - extractErrorRetryMap.clear();  
615 - if (getFeatureDelayedDisposables != null) {  
616 - getFeatureDelayedDisposables.clear();  
617 - }  
618 - return;  
619 - }  
620 - Enumeration<Integer> keys = requestFeatureStatusMap.keys();  
621 - while (keys.hasMoreElements()) {  
622 - int key = keys.nextElement();  
623 - boolean contained = false;  
624 - for (FacePreviewInfo facePreviewInfo : facePreviewInfoList) {  
625 - if (facePreviewInfo.getTrackId() == key) {  
626 - contained = true;  
627 - break;  
628 - }  
629 - }  
630 - if (!contained) {  
631 - requestFeatureStatusMap.remove(key);  
632 - livenessMap.remove(key);  
633 - livenessErrorRetryMap.remove(key);  
634 - extractErrorRetryMap.remove(key);  
635 - }  
636 - }  
637 -  
638 -  
639 - }  
640 -  
641 - private void searchFace(final FaceFeature frFace, final Integer requestId) {  
642 - Observable  
643 - .create(new ObservableOnSubscribe<CompareResult>() {  
644 - @Override  
645 - public void subscribe(ObservableEmitter<CompareResult> emitter) {  
646 -// Log.i(TAG, "subscribe: fr search start = " + System.currentTimeMillis() + " trackId = " + requestId);  
647 - CompareResult compareResult = FaceServer.getInstance().getTopOfFaceLib(frFace);  
648 -// Log.i(TAG, "subscribe: fr search end = " + System.currentTimeMillis() + " trackId = " + requestId);  
649 - emitter.onNext(compareResult);  
650 -  
651 - }  
652 - })  
653 - .subscribeOn(Schedulers.computation())  
654 - .observeOn(AndroidSchedulers.mainThread())  
655 - .subscribe(new Observer<CompareResult>() {  
656 - @Override  
657 - public void onSubscribe(Disposable d) {  
658 -  
659 - }  
660 -  
661 - @Override  
662 - public void onNext(CompareResult compareResult) {  
663 - if (compareResult == null || compareResult.getUserName() == null) {  
664 - requestFeatureStatusMap.put(requestId, RequestFeatureStatus.FAILED);  
665 - faceHelper.setName(requestId, "VISITOR " + requestId);  
666 - return;  
667 - }  
668 -  
669 -// Log.i(TAG, "onNext: fr search get result = " + System.currentTimeMillis() + " trackId = " + requestId + " similar = " + compareResult.getSimilar());  
670 - if (compareResult.getSimilar() > SIMILAR_THRESHOLD) {  
671 - boolean isAdded = false;  
672 - if (compareResultList == null) {  
673 - requestFeatureStatusMap.put(requestId, RequestFeatureStatus.FAILED);  
674 - faceHelper.setName(requestId, "VISITOR " + requestId);  
675 - return;  
676 - }  
677 - for (CompareResult compareResult1 : compareResultList) {  
678 - if (compareResult1.getTrackId() == requestId) {  
679 - isAdded = true;  
680 - break;  
681 - }  
682 - }  
683 - if (!isAdded) {  
684 - //对于多人脸搜索,假如最大显示数量为 MAX_DETECT_NUM 且有新的人脸进入,则以队列的形式移除  
685 - if (compareResultList.size() >= MAX_DETECT_NUM) {  
686 - compareResultList.remove(0);  
687 - adapter.notifyItemRemoved(0);  
688 - }  
689 - //添加显示人员时,保存其trackId  
690 - compareResult.setTrackId(requestId);  
691 - compareResultList.add(compareResult);  
692 - adapter.notifyItemInserted(compareResultList.size() - 1);  
693 - }  
694 - requestFeatureStatusMap.put(requestId, RequestFeatureStatus.SUCCEED);  
695 - faceHelper.setName(requestId, getString(R.string.recognize_success_notice, compareResult.getUserName()));  
696 -  
697 - } else {  
698 - faceHelper.setName(requestId, getString(R.string.recognize_failed_notice, "NOT_REGISTERED"));  
699 - retryRecognizeDelayed(requestId);  
700 - }  
701 - }  
702 -  
703 - @Override  
704 - public void onError(Throwable e) {  
705 - faceHelper.setName(requestId, getString(R.string.recognize_failed_notice, "NOT_REGISTERED"));  
706 - retryRecognizeDelayed(requestId);  
707 - }  
708 -  
709 - @Override  
710 - public void onComplete() {  
711 -  
712 - }  
713 - });  
714 - }  
715 -  
716 -  
717 - /**  
718 - * 将准备注册的状态置为{@link #REGISTER_STATUS_READY}  
719 - *  
720 - * @param view 注册按钮  
721 - */  
722 - public void register(View view) {  
723 - if (registerStatus == REGISTER_STATUS_DONE) {  
724 - registerStatus = REGISTER_STATUS_READY;  
725 - }  
726 - }  
727 -  
728 - /**  
729 - * 切换相机。注意:若切换相机发现检测不到人脸,则极有可能是检测角度导致的,需要销毁引擎重新创建或者在设置界面修改配置的检测角度  
730 - *  
731 - * @param view  
732 - */  
733 - public void switchCamera(View view) {  
734 - if (cameraHelper != null) {  
735 - boolean success = cameraHelper.switchCamera();  
736 - if (!success) {  
737 - showToast(getString(R.string.switch_camera_failed));  
738 - } else {  
739 - showLongToast(getString(R.string.notice_change_detect_degree));  
740 - }  
741 - }  
742 - }  
743 -  
744 - /**  
745 - * 在{@link #previewView}第一次布局完成后,去除该监听,并且进行引擎和相机的初始化  
746 - */  
747 - @Override  
748 - public void onGlobalLayout() {  
749 - previewView.getViewTreeObserver().removeOnGlobalLayoutListener(this);  
750 - if (!checkPermissions(NEEDED_PERMISSIONS)) {  
751 - ActivityCompat.requestPermissions(this, NEEDED_PERMISSIONS, ACTION_REQUEST_PERMISSIONS);  
752 - } else {  
753 - initEngine();  
754 - initCamera();  
755 - }  
756 - }  
757 -  
758 - /**  
759 - * 将map中key对应的value增1回传  
760 - *  
761 - * @param countMap map  
762 - * @param key key  
763 - * @return 增1后的value  
764 - */  
765 - public int increaseAndGetValue(Map<Integer, Integer> countMap, int key) {  
766 - if (countMap == null) {  
767 - return 0;  
768 - }  
769 - Integer value = countMap.get(key);  
770 - if (value == null) {  
771 - value = 0;  
772 - }  
773 - countMap.put(key, ++value);  
774 - return value;  
775 - }  
776 -  
777 - /**  
778 - * 延迟 FAIL_RETRY_INTERVAL 重新进行活体检测  
779 - *  
780 - * @param requestId 人脸ID  
781 - */  
782 - private void retryLivenessDetectDelayed(final Integer requestId) {  
783 - Observable.timer(FAIL_RETRY_INTERVAL, TimeUnit.MILLISECONDS)  
784 - .subscribe(new Observer<Long>() {  
785 - Disposable disposable;  
786 -  
787 - @Override  
788 - public void onSubscribe(Disposable d) {  
789 - disposable = d;  
790 - delayFaceTaskCompositeDisposable.add(disposable);  
791 - }  
792 -  
793 - @Override  
794 - public void onNext(Long aLong) {  
795 -  
796 - }  
797 -  
798 - @Override  
799 - public void onError(Throwable e) {  
800 - e.printStackTrace();  
801 - }  
802 -  
803 - @Override  
804 - public void onComplete() {  
805 - // 将该人脸状态置为UNKNOWN,帧回调处理时会重新进行活体检测  
806 - if (livenessDetect) {  
807 - faceHelper.setName(requestId, Integer.toString(requestId));  
808 - }  
809 - livenessMap.put(requestId, LivenessInfo.UNKNOWN);  
810 - delayFaceTaskCompositeDisposable.remove(disposable);  
811 - }  
812 - });  
813 - }  
814 -  
815 - /**  
816 - * 延迟 FAIL_RETRY_INTERVAL 重新进行人脸识别  
817 - *  
818 - * @param requestId 人脸ID  
819 - */  
820 - private void retryRecognizeDelayed(final Integer requestId) {  
821 - requestFeatureStatusMap.put(requestId, RequestFeatureStatus.FAILED);  
822 - Observable.timer(FAIL_RETRY_INTERVAL, TimeUnit.MILLISECONDS)  
823 - .subscribe(new Observer<Long>() {  
824 - Disposable disposable;  
825 -  
826 - @Override  
827 - public void onSubscribe(Disposable d) {  
828 - disposable = d;  
829 - delayFaceTaskCompositeDisposable.add(disposable);  
830 - }  
831 -  
832 - @Override  
833 - public void onNext(Long aLong) {  
834 -  
835 - }  
836 -  
837 - @Override  
838 - public void onError(Throwable e) {  
839 - e.printStackTrace();  
840 - }  
841 -  
842 - @Override  
843 - public void onComplete() {  
844 - // 将该人脸特征提取状态置为FAILED,帧回调处理时会重新进行活体检测  
845 - faceHelper.setName(requestId, Integer.toString(requestId));  
846 - requestFeatureStatusMap.put(requestId, RequestFeatureStatus.TO_RETRY);  
847 - delayFaceTaskCompositeDisposable.remove(disposable);  
848 - }  
849 - });  
850 - }  
851 -}  
1 -package com.arcsoft.arcfacedemo.activity;  
2 -  
3 -import android.Manifest;  
4 -import android.content.Intent;  
5 -import android.graphics.Bitmap;  
6 -import android.graphics.BitmapFactory;  
7 -import android.graphics.Canvas;  
8 -import android.graphics.Color;  
9 -import android.graphics.Paint;  
10 -import android.graphics.Typeface;  
11 -import android.os.Build;  
12 -import android.os.Bundle;  
13 -import android.provider.MediaStore;  
14 -import android.support.annotation.Nullable;  
15 -import android.support.v4.app.ActivityCompat;  
16 -import android.support.v7.app.AlertDialog;  
17 -import android.text.ParcelableSpan;  
18 -import android.text.SpannableStringBuilder;  
19 -import android.text.Spanned;  
20 -import android.text.style.ForegroundColorSpan;  
21 -import android.text.style.StyleSpan;  
22 -import android.util.Log;  
23 -import android.view.View;  
24 -import android.widget.ImageView;  
25 -import android.widget.ProgressBar;  
26 -import android.widget.TextView;  
27 -  
28 -import com.arcsoft.arcfacedemo.R;  
29 -import com.arcsoft.face.AgeInfo;  
30 -import com.arcsoft.face.ErrorInfo;  
31 -import com.arcsoft.face.Face3DAngle;  
32 -import com.arcsoft.face.FaceEngine;  
33 -import com.arcsoft.face.FaceFeature;  
34 -import com.arcsoft.face.FaceInfo;  
35 -import com.arcsoft.face.FaceSimilar;  
36 -import com.arcsoft.face.GenderInfo;  
37 -import com.arcsoft.face.LivenessInfo;  
38 -import com.arcsoft.face.VersionInfo;  
39 -import com.arcsoft.face.enums.CompareModel;  
40 -import com.arcsoft.face.enums.DetectFaceOrientPriority;  
41 -import com.arcsoft.face.enums.DetectMode;  
42 -import com.arcsoft.face.enums.DetectModel;  
43 -import com.arcsoft.face.model.ArcSoftImageInfo;  
44 -import com.arcsoft.face.util.ImageUtils;  
45 -import com.arcsoft.imageutil.ArcSoftImageFormat;  
46 -import com.arcsoft.imageutil.ArcSoftImageUtil;  
47 -import com.arcsoft.imageutil.ArcSoftImageUtilError;  
48 -import com.bumptech.glide.Glide;  
49 -  
50 -import java.io.IOException;  
51 -import java.util.ArrayList;  
52 -import java.util.Arrays;  
53 -import java.util.List;  
54 -  
55 -import io.reactivex.Observable;  
56 -import io.reactivex.ObservableEmitter;  
57 -import io.reactivex.ObservableOnSubscribe;  
58 -import io.reactivex.Observer;  
59 -import io.reactivex.android.schedulers.AndroidSchedulers;  
60 -import io.reactivex.disposables.Disposable;  
61 -import io.reactivex.schedulers.Schedulers;  
62 -  
63 -  
64 -public class SingleImageActivity extends BaseActivity {  
65 - private static final String TAG = "SingleImageActivity";  
66 - private ImageView ivShow;  
67 - private TextView tvNotice;  
68 - private FaceEngine faceEngine;  
69 - private int faceEngineCode = -1;  
70 - /**  
71 - * 请求权限的请求码  
72 - */  
73 - private static final int ACTION_REQUEST_PERMISSIONS = 0x001;  
74 - /**  
75 - * 请求选择本地图片文件的请求码  
76 - */  
77 - private static final int ACTION_CHOOSE_IMAGE = 0x201;  
78 - /**  
79 - * 提示对话框  
80 - */  
81 - private AlertDialog progressDialog;  
82 - /**  
83 - * 被处理的图片  
84 - */  
85 - private Bitmap mBitmap = null;  
86 -  
87 - /**  
88 - * 所需的所有权限信息  
89 - */  
90 - private static String[] NEEDED_PERMISSIONS = new String[]{  
91 - Manifest.permission.READ_PHONE_STATE  
92 - };  
93 -  
94 - @Override  
95 - protected void onCreate(Bundle savedInstanceState) {  
96 - super.onCreate(savedInstanceState);  
97 - setContentView(R.layout.activity_image_process);  
98 - initView();  
99 - /**  
100 - * 在选择图片的时候,在android 7.0及以上通过FileProvider获取Uri,不需要文件权限  
101 - */  
102 - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {  
103 - List<String> permissionList = new ArrayList<>(Arrays.asList(NEEDED_PERMISSIONS));  
104 - permissionList.add(Manifest.permission.READ_EXTERNAL_STORAGE);  
105 - NEEDED_PERMISSIONS = permissionList.toArray(new String[0]);  
106 - }  
107 -  
108 - if (!checkPermissions(NEEDED_PERMISSIONS)) {  
109 - ActivityCompat.requestPermissions(this, NEEDED_PERMISSIONS, ACTION_REQUEST_PERMISSIONS);  
110 - } else {  
111 - initEngine();  
112 - }  
113 -  
114 - }  
115 -  
116 - private void initEngine() {  
117 - faceEngine = new FaceEngine();  
118 - faceEngineCode = faceEngine.init(this, DetectMode.ASF_DETECT_MODE_IMAGE, DetectFaceOrientPriority.ASF_OP_ALL_OUT,  
119 - 16, 10, FaceEngine.ASF_FACE_RECOGNITION | FaceEngine.ASF_FACE_DETECT | FaceEngine.ASF_AGE | FaceEngine.ASF_GENDER | FaceEngine.ASF_FACE3DANGLE | FaceEngine.ASF_LIVENESS);  
120 - Log.i(TAG, "initEngine: init: " + faceEngineCode);  
121 -  
122 - if (faceEngineCode != ErrorInfo.MOK) {  
123 - showToast(getString(R.string.init_failed, faceEngineCode));  
124 - }  
125 - }  
126 -  
127 - /**  
128 - * 销毁引擎  
129 - */  
130 - private void unInitEngine() {  
131 - if (faceEngine != null) {  
132 - faceEngineCode = faceEngine.unInit();  
133 - faceEngine = null;  
134 - Log.i(TAG, "unInitEngine: " + faceEngineCode);  
135 - }  
136 - }  
137 -  
138 - @Override  
139 - protected void onDestroy() {  
140 - if (mBitmap != null && !mBitmap.isRecycled()) {  
141 - mBitmap.recycle();  
142 - }  
143 - mBitmap = null;  
144 -  
145 - if (progressDialog != null && progressDialog.isShowing()) {  
146 - progressDialog.dismiss();  
147 - }  
148 - progressDialog = null;  
149 -  
150 - unInitEngine();  
151 - super.onDestroy();  
152 - }  
153 -  
154 - private void initView() {  
155 - tvNotice = findViewById(R.id.tv_notice);  
156 - ivShow = findViewById(R.id.iv_show);  
157 - ivShow.setImageResource(R.drawable.faces);  
158 - progressDialog = new AlertDialog.Builder(this)  
159 - .setTitle(R.string.processing)  
160 - .setView(new ProgressBar(this))  
161 - .create();  
162 - }  
163 -  
164 - /**  
165 - * 按钮点击响应事件  
166 - *  
167 - * @param view  
168 - */  
169 - public void process(final View view) {  
170 -  
171 - view.setClickable(false);  
172 - if (progressDialog == null || progressDialog.isShowing()) {  
173 - return;  
174 - }  
175 - progressDialog.show();  
176 - //图像转化操作和部分引擎调用比较耗时,建议放子线程操作  
177 - Observable.create(new ObservableOnSubscribe<Object>() {  
178 - @Override  
179 - public void subscribe(ObservableEmitter<Object> emitter) throws Exception {  
180 - processImage();  
181 - emitter.onComplete();  
182 - }  
183 - })  
184 - .subscribeOn(Schedulers.computation())  
185 - .observeOn(AndroidSchedulers.mainThread())  
186 - .subscribe(new Observer<Object>() {  
187 - @Override  
188 - public void onSubscribe(Disposable d) {  
189 -  
190 - }  
191 -  
192 - @Override  
193 - public void onNext(Object o) {  
194 -  
195 - }  
196 -  
197 - @Override  
198 - public void onError(Throwable e) {  
199 - e.printStackTrace();  
200 - }  
201 -  
202 - @Override  
203 - public void onComplete() {  
204 - view.setClickable(true);  
205 - }  
206 - });  
207 - }  
208 -  
209 -  
210 - /**  
211 - * 主要操作逻辑部分  
212 - */  
213 - public void processImage() {  
214 - /**  
215 - * 1.准备操作(校验,显示,获取BGR)  
216 - */  
217 - if (mBitmap == null) {  
218 - mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.faces);  
219 - }  
220 - // 图像对齐  
221 - Bitmap bitmap = ArcSoftImageUtil.getAlignedBitmap(mBitmap, true);  
222 -  
223 - final SpannableStringBuilder notificationSpannableStringBuilder = new SpannableStringBuilder();  
224 - if (faceEngineCode != ErrorInfo.MOK) {  
225 - addNotificationInfo(notificationSpannableStringBuilder, null, " face engine not initialized!");  
226 - showNotificationAndFinish(notificationSpannableStringBuilder);  
227 - return;  
228 - }  
229 - if (bitmap == null) {  
230 - addNotificationInfo(notificationSpannableStringBuilder, null, " bitmap is null!");  
231 - showNotificationAndFinish(notificationSpannableStringBuilder);  
232 - return;  
233 - }  
234 - if (faceEngine == null) {  
235 - addNotificationInfo(notificationSpannableStringBuilder, null, " faceEngine is null!");  
236 - showNotificationAndFinish(notificationSpannableStringBuilder);  
237 - return;  
238 - }  
239 -  
240 - int width = bitmap.getWidth();  
241 - int height = bitmap.getHeight();  
242 - final Bitmap finalBitmap = bitmap;  
243 - runOnUiThread(new Runnable() {  
244 - @Override  
245 - public void run() {  
246 - Glide.with(ivShow.getContext())  
247 - .load(finalBitmap)  
248 - .into(ivShow);  
249 - }  
250 - });  
251 -  
252 - // bitmap转bgr24  
253 - long start = System.currentTimeMillis();  
254 - byte[] bgr24 = ArcSoftImageUtil.createImageData(bitmap.getWidth(), bitmap.getHeight(), ArcSoftImageFormat.BGR24);  
255 - int transformCode = ArcSoftImageUtil.bitmapToImageData(bitmap, bgr24, ArcSoftImageFormat.BGR24);  
256 - if (transformCode != ArcSoftImageUtilError.CODE_SUCCESS) {  
257 - Log.e(TAG, "transform failed, code is " + transformCode);  
258 - addNotificationInfo(notificationSpannableStringBuilder, new StyleSpan(Typeface.BOLD), "transform bitmap To ImageData failed", "code is ", String.valueOf(transformCode), "\n");  
259 - return;  
260 - }  
261 -// Log.i(TAG, "processImage:bitmapToBgr24 cost = " + (System.currentTimeMillis() - start));  
262 - addNotificationInfo(notificationSpannableStringBuilder, new StyleSpan(Typeface.BOLD), "start face detection,imageWidth is ", String.valueOf(width), ", imageHeight is ", String.valueOf(height), "\n");  
263 -  
264 - List<FaceInfo> faceInfoList = new ArrayList<>();  
265 -  
266 - /**  
267 - * 2.成功获取到了BGR24 数据,开始人脸检测  
268 - */  
269 - long fdStartTime = System.currentTimeMillis();  
270 -// ArcSoftImageInfo arcSoftImageInfo = new ArcSoftImageInfo(width,height,FaceEngine.CP_PAF_BGR24,new byte[][]{bgr24},new int[]{width * 3});  
271 -// Log.i(TAG, "processImage: " + arcSoftImageInfo.getPlanes()[0].length);  
272 -// int detectCode = faceEngine.detectFaces(arcSoftImageInfo, faceInfoList);  
273 - int detectCode = faceEngine.detectFaces(bgr24, width, height, FaceEngine.CP_PAF_BGR24, DetectModel.RGB, faceInfoList);  
274 - if (detectCode == ErrorInfo.MOK) {  
275 -// Log.i(TAG, "processImage: fd costTime = " + (System.currentTimeMillis() - fdStartTime));  
276 - }  
277 -  
278 - //绘制bitmap  
279 - Bitmap bitmapForDraw = bitmap.copy(Bitmap.Config.RGB_565, true);  
280 - Canvas canvas = new Canvas(bitmapForDraw);  
281 - Paint paint = new Paint();  
282 - addNotificationInfo(notificationSpannableStringBuilder, null, "detect result:\nerrorCode is :", String.valueOf(detectCode), " face Number is ", String.valueOf(faceInfoList.size()), "\n");  
283 - /**  
284 - * 3.若检测结果人脸数量大于0,则在bitmap上绘制人脸框并且重新显示到ImageView,若人脸数量为0,则无法进行下一步操作,操作结束  
285 - */  
286 - if (faceInfoList.size() > 0) {  
287 - addNotificationInfo(notificationSpannableStringBuilder, null, "face list:\n");  
288 - paint.setAntiAlias(true);  
289 - paint.setStrokeWidth(5);  
290 - paint.setColor(Color.YELLOW);  
291 - for (int i = 0; i < faceInfoList.size(); i++) {  
292 - //绘制人脸框  
293 - paint.setStyle(Paint.Style.STROKE);  
294 - canvas.drawRect(faceInfoList.get(i).getRect(), paint);  
295 - //绘制人脸序号  
296 - paint.setStyle(Paint.Style.FILL_AND_STROKE);  
297 - int textSize = faceInfoList.get(i).getRect().width() / 2;  
298 - paint.setTextSize(textSize);  
299 -  
300 - canvas.drawText(String.valueOf(i), faceInfoList.get(i).getRect().left, faceInfoList.get(i).getRect().top, paint);  
301 - addNotificationInfo(notificationSpannableStringBuilder, null, "face[", String.valueOf(i), "]:", faceInfoList.get(i).toString(), "\n");  
302 - }  
303 - //显示  
304 - final Bitmap finalBitmapForDraw = bitmapForDraw;  
305 - runOnUiThread(new Runnable() {  
306 - @Override  
307 - public void run() {  
308 - Glide.with(ivShow.getContext())  
309 - .load(finalBitmapForDraw)  
310 - .into(ivShow);  
311 - }  
312 - });  
313 - } else {  
314 - addNotificationInfo(notificationSpannableStringBuilder, null, "can not do further action, exit!");  
315 - showNotificationAndFinish(notificationSpannableStringBuilder);  
316 - return;  
317 - }  
318 - addNotificationInfo(notificationSpannableStringBuilder, null, "\n");  
319 -  
320 -  
321 - /**  
322 - * 4.上一步已获取到人脸位置和角度信息,传入给process函数,进行年龄、性别、三维角度、活体检测  
323 - */  
324 -  
325 - long processStartTime = System.currentTimeMillis();  
326 - int faceProcessCode = faceEngine.process(bgr24, width, height, FaceEngine.CP_PAF_BGR24, faceInfoList, FaceEngine.ASF_AGE | FaceEngine.ASF_GENDER | FaceEngine.ASF_FACE3DANGLE | FaceEngine.ASF_LIVENESS);  
327 -  
328 - if (faceProcessCode != ErrorInfo.MOK) {  
329 - addNotificationInfo(notificationSpannableStringBuilder, new ForegroundColorSpan(Color.RED), "process failed! code is ", String.valueOf(faceProcessCode), "\n");  
330 - } else {  
331 -// Log.i(TAG, "processImage: process costTime = " + (System.currentTimeMillis() - processStartTime));  
332 - }  
333 - //年龄信息结果  
334 - List<AgeInfo> ageInfoList = new ArrayList<>();  
335 - //性别信息结果  
336 - List<GenderInfo> genderInfoList = new ArrayList<>();  
337 - //人脸三维角度结果  
338 - List<Face3DAngle> face3DAngleList = new ArrayList<>();  
339 - //活体检测结果  
340 - List<LivenessInfo> livenessInfoList = new ArrayList<>();  
341 - //获取年龄、性别、三维角度、活体结果  
342 - int ageCode = faceEngine.getAge(ageInfoList);  
343 - int genderCode = faceEngine.getGender(genderInfoList);  
344 - int face3DAngleCode = faceEngine.getFace3DAngle(face3DAngleList);  
345 - int livenessCode = faceEngine.getLiveness(livenessInfoList);  
346 -  
347 - if ((ageCode | genderCode | face3DAngleCode | livenessCode) != ErrorInfo.MOK) {  
348 - addNotificationInfo(notificationSpannableStringBuilder, null, "at least one of age,gender,face3DAngle detect failed!,codes are:",  
349 - String.valueOf(ageCode), " , ", String.valueOf(genderCode), " , ", String.valueOf(face3DAngleCode));  
350 - showNotificationAndFinish(notificationSpannableStringBuilder);  
351 - return;  
352 - }  
353 - /**  
354 - * 5.年龄、性别、三维角度已获取成功,添加信息到提示文字中  
355 - */  
356 - //年龄数据  
357 - if (ageInfoList.size() > 0) {  
358 - addNotificationInfo(notificationSpannableStringBuilder, new StyleSpan(Typeface.BOLD), "age of each face:\n");  
359 - }  
360 - for (int i = 0; i < ageInfoList.size(); i++) {  
361 - addNotificationInfo(notificationSpannableStringBuilder, null, "face[", String.valueOf(i), "]:", String.valueOf(ageInfoList.get(i).getAge()), "\n");  
362 - }  
363 - addNotificationInfo(notificationSpannableStringBuilder, null, "\n");  
364 -  
365 - //性别数据  
366 - if (genderInfoList.size() > 0) {  
367 - addNotificationInfo(notificationSpannableStringBuilder, new StyleSpan(Typeface.BOLD), "gender of each face:\n");  
368 - }  
369 - for (int i = 0; i < genderInfoList.size(); i++) {  
370 - addNotificationInfo(notificationSpannableStringBuilder, null, "face[", String.valueOf(i), "]:"  
371 - , genderInfoList.get(i).getGender() == GenderInfo.MALE ?  
372 - "MALE" : (genderInfoList.get(i).getGender() == GenderInfo.FEMALE ? "FEMALE" : "UNKNOWN"), "\n");  
373 - }  
374 - addNotificationInfo(notificationSpannableStringBuilder, null, "\n");  
375 -  
376 -  
377 - //人脸三维角度数据  
378 - if (face3DAngleList.size() > 0) {  
379 - addNotificationInfo(notificationSpannableStringBuilder, new StyleSpan(Typeface.BOLD), "face3DAngle of each face:\n");  
380 - for (int i = 0; i < face3DAngleList.size(); i++) {  
381 - addNotificationInfo(notificationSpannableStringBuilder, null, "face[", String.valueOf(i), "]:", face3DAngleList.get(i).toString(), "\n");  
382 - }  
383 - }  
384 - addNotificationInfo(notificationSpannableStringBuilder, null, "\n");  
385 -  
386 - //活体检测数据  
387 - if (livenessInfoList.size() > 0) {  
388 - addNotificationInfo(notificationSpannableStringBuilder, new StyleSpan(Typeface.BOLD), "liveness of each face:\n");  
389 - for (int i = 0; i < livenessInfoList.size(); i++) {  
390 - String liveness = null;  
391 - switch (livenessInfoList.get(i).getLiveness()) {  
392 - case LivenessInfo.ALIVE:  
393 - liveness = "ALIVE";  
394 - break;  
395 - case LivenessInfo.NOT_ALIVE:  
396 - liveness = "NOT_ALIVE";  
397 - break;  
398 - case LivenessInfo.UNKNOWN:  
399 - liveness = "UNKNOWN";  
400 - break;  
401 - case LivenessInfo.FACE_NUM_MORE_THAN_ONE:  
402 - liveness = "FACE_NUM_MORE_THAN_ONE";  
403 - break;  
404 - default:  
405 - liveness = "UNKNOWN";  
406 - break;  
407 - }  
408 - addNotificationInfo(notificationSpannableStringBuilder, null, "face[", String.valueOf(i), "]:", liveness, "\n");  
409 - }  
410 - }  
411 - addNotificationInfo(notificationSpannableStringBuilder, null, "\n");  
412 -  
413 - /**  
414 - * 6.最后将图片内的所有人脸进行一一比对并添加到提示文字中  
415 - */  
416 - if (faceInfoList.size() > 0) {  
417 -  
418 - FaceFeature[] faceFeatures = new FaceFeature[faceInfoList.size()];  
419 - int[] extractFaceFeatureCodes = new int[faceInfoList.size()];  
420 -  
421 - addNotificationInfo(notificationSpannableStringBuilder, new StyleSpan(Typeface.BOLD), "faceFeatureExtract:\n");  
422 - for (int i = 0; i < faceInfoList.size(); i++) {  
423 - faceFeatures[i] = new FaceFeature();  
424 - //从图片解析出人脸特征数据  
425 - long frStartTime = System.currentTimeMillis();  
426 - extractFaceFeatureCodes[i] = faceEngine.extractFaceFeature(bgr24, width, height, FaceEngine.CP_PAF_BGR24, faceInfoList.get(i), faceFeatures[i]);  
427 -  
428 - if (extractFaceFeatureCodes[i] != ErrorInfo.MOK) {  
429 - addNotificationInfo(notificationSpannableStringBuilder, null, "faceFeature of face[", String.valueOf(i), "]",  
430 - " extract failed, code is ", String.valueOf(extractFaceFeatureCodes[i]), "\n");  
431 - } else {  
432 -// Log.i(TAG, "processImage: fr costTime = " + (System.currentTimeMillis() - frStartTime));  
433 - addNotificationInfo(notificationSpannableStringBuilder, null, "faceFeature of face[", String.valueOf(i), "]",  
434 - " extract success\n");  
435 - }  
436 - }  
437 - addNotificationInfo(notificationSpannableStringBuilder, null, "\n");  
438 -  
439 - //人脸特征的数量大于2,将所有特征进行比较  
440 - if (faceFeatures.length >= 2) {  
441 -  
442 - addNotificationInfo(notificationSpannableStringBuilder, new StyleSpan(Typeface.BOLD), "similar of faces:\n");  
443 -  
444 - for (int i = 0; i < faceFeatures.length; i++) {  
445 - for (int j = i + 1; j < faceFeatures.length; j++) {  
446 - addNotificationInfo(notificationSpannableStringBuilder, new StyleSpan(Typeface.BOLD_ITALIC), "compare face[", String.valueOf(i), "] and face["  
447 - , String.valueOf(j), "]:\n");  
448 - //若其中一个特征提取失败,则不进行比对  
449 - boolean canCompare = true;  
450 - if (extractFaceFeatureCodes[i] != 0) {  
451 - addNotificationInfo(notificationSpannableStringBuilder, null, "faceFeature of face[", String.valueOf(i), "] extract failed, can not compare!\n");  
452 - canCompare = false;  
453 - }  
454 - if (extractFaceFeatureCodes[j] != 0) {  
455 - addNotificationInfo(notificationSpannableStringBuilder, null, "faceFeature of face[", String.valueOf(j), "] extract failed, can not compare!\n");  
456 - canCompare = false;  
457 - }  
458 - if (!canCompare) {  
459 - continue;  
460 - }  
461 -  
462 - FaceSimilar matching = new FaceSimilar();  
463 - //比对两个人脸特征获取相似度信息  
464 - faceEngine.compareFaceFeature(faceFeatures[i], faceFeatures[j], CompareModel.LIFE_PHOTO, matching);  
465 - //新增相似度比对结果信息  
466 - addNotificationInfo(notificationSpannableStringBuilder, null, "similar of face[", String.valueOf(i), "] and face[",  
467 - String.valueOf(j), "] is:", String.valueOf(matching.getScore()), "\n");  
468 - }  
469 - }  
470 - }  
471 - }  
472 -  
473 - showNotificationAndFinish(notificationSpannableStringBuilder);  
474 -  
475 - }  
476 -  
477 - /**  
478 - * 展示提示信息并且关闭提示框  
479 - *  
480 - * @param stringBuilder 带格式的提示文字  
481 - */  
482 - private void showNotificationAndFinish(final SpannableStringBuilder stringBuilder) {  
483 - runOnUiThread(new Runnable() {  
484 - @Override  
485 - public void run() {  
486 - if (tvNotice != null) {  
487 - tvNotice.setText(stringBuilder);  
488 - }  
489 - if (progressDialog != null && progressDialog.isShowing()) {  
490 - progressDialog.dismiss();  
491 - }  
492 - }  
493 - });  
494 - }  
495 -  
496 - /**  
497 - * 追加提示信息  
498 - *  
499 - * @param stringBuilder 提示的字符串的存放对象  
500 - * @param styleSpan 添加的字符串的格式  
501 - * @param strings 字符串数组  
502 - */  
503 - private void addNotificationInfo(SpannableStringBuilder stringBuilder, ParcelableSpan styleSpan, String... strings) {  
504 - if (stringBuilder == null || strings == null || strings.length == 0) {  
505 - return;  
506 - }  
507 - int startLength = stringBuilder.length();  
508 - for (String string : strings) {  
509 - stringBuilder.append(string);  
510 - }  
511 - int endLength = stringBuilder.length();  
512 - if (styleSpan != null) {  
513 - stringBuilder.setSpan(styleSpan, startLength, endLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);  
514 - }  
515 - }  
516 -  
517 - /**  
518 - * 从本地选择文件  
519 - *  
520 - * @param view  
521 - */  
522 - public void chooseLocalImage(View view) {  
523 - Intent intent = new Intent(Intent.ACTION_PICK);  
524 - intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*");  
525 - startActivityForResult(intent, ACTION_CHOOSE_IMAGE);  
526 - }  
527 -  
528 - @Override  
529 - protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {  
530 - super.onActivityResult(requestCode, resultCode, data);  
531 - if (requestCode == ACTION_CHOOSE_IMAGE) {  
532 - if (data == null || data.getData() == null) {  
533 - showToast(getString(R.string.get_picture_failed));  
534 - return;  
535 - }  
536 - try {  
537 - mBitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), data.getData());  
538 - } catch (IOException e) {  
539 - e.printStackTrace();  
540 - return;  
541 - }  
542 - if (mBitmap == null) {  
543 - showToast(getString(R.string.get_picture_failed));  
544 - return;  
545 - }  
546 - Glide.with(ivShow.getContext())  
547 - .load(mBitmap)  
548 - .into(ivShow);  
549 - }  
550 - }  
551 -  
552 - @Override  
553 - void afterRequestPermission(int requestCode, boolean isAllGranted) {  
554 - if (requestCode == ACTION_REQUEST_PERMISSIONS) {  
555 - if (isAllGranted) {  
556 - initEngine();  
557 - } else {  
558 - showToast(getString(R.string.permission_denied));  
559 - }  
560 - }  
561 - }  
562 -}  
1 -<resources>  
2 - <!--app名称-->  
3 - <string name="app_name">ArcFaceDemo</string>  
4 -  
5 - <!--配置、功能选择界面-->  
6 - <string name="page_preview">人脸属性检测(视频)</string>  
7 - <string name="page_ir_preview">红外活体检测(视频)</string>  
8 - <string name="page_face_recognize">人脸比对1:n(视频vs人脸库,RGB活体)</string>  
9 - <string name="page_ir_face_recognize">人脸比对1:n(视频vs人脸库,IR活体)</string>  
10 - <string name="page_single_image">人脸属性检测(图片)</string>  
11 - <string name="page_multi_image">人脸比对1:n(图片vs图片)</string>  
12 - <string name="page_face_manage">人脸批量注册管理</string>  
13 - <string name="get_device_finger_failed">获取设备指纹失败,错误码:%d</string>  
14 - <string name="choose_detect_degree">选择视频模式检测角度</string>  
15 - <string name="copy_device_fingerprint">复制设备指纹信息</string>  
16 - <string name="device_fingerprint_copied">设备指纹信息:\n%s\n已复制</string>  
17 - <string name="active_engine">激活引擎</string>  
18 - <string name="active_success">激活引擎成功</string>  
19 - <string name="already_activated">引擎已激活,无需再次激活</string>  
20 - <string name="active_failed">引擎激活失败,错误码为 %d</string>  
21 - <string name="library_not_found">未找到库文件,请检查是否有将.so文件放至工程的 app\\src\\main\\jniLibs 目录下</string>  
22 - <string name="ft_op_0">视频模式仅检测0度</string>  
23 - <string name="ft_op_90">视频模式仅检测90度</string>  
24 - <string name="ft_op_180">视频模式仅检测180度</string>  
25 - <string name="ft_op_270">视频模式仅检测270度</string>  
26 - <string name="ft_op_all">视频模式全方向人脸检测</string>  
27 -  
28 - <!--人脸属性检测(图片) 界面-->  
29 - <string name="start_process">开始处理</string>  
30 - <string name="choose_local_image">选择本地图片</string>  
31 - <string name="processing">处理中</string>  
32 -  
33 - <!--人脸比对1:n(图片vs图片) 界面-->  
34 - <string name="choose_main_image">选择主图</string>  
35 - <string name="add_item_image">添加比对图</string>  
36 - <string name="notice_choose_main_img">请先选择主图</string>  
37 - <string name="compare_failed">比对失败,错误码为 %d</string>  
38 -  
39 - <!--各个界面获取本地图片失败提示-->  
40 - <string name="get_picture_failed">获取图片失败</string>  
41 -  
42 - <!--各个界面获取权限失败时的提示-->  
43 - <string name="permission_denied">权限被拒绝!</string>  
44 -  
45 - <!--各个界面引擎初始化失败的提示-->  
46 - <string name="init_failed">引擎初始化失败,错误码为 %d</string>  
47 - <string name="engine_not_initialized">引擎未初始化,错误码为 %d</string>  
48 -  
49 - <!--单目、双目识别注册界面-->  
50 - <string name="register">注册</string>  
51 - <string name="switch_camera">切换相机</string>  
52 - <string name="switch_camera_failed">切换相机失败</string>  
53 - <string name="recognize_failed_notice">未通过:%s</string>  
54 - <string name="recognize_success_notice">通过:%s</string>  
55 - <string name="low_confidence_level">人脸置信度低</string>  
56 - <string name="specific_engine_init_failed">%s 初始化失败,错误码为:%d</string>  
57 - <string name="notice_change_detect_degree">相机已切换,若无法检测到人脸,需要在首页修改视频模式人脸检测角度</string>  
58 - <string name="liveness_detect">活体检测</string>  
59 - <string name="camera_rgb">RGB CAMERA</string>  
60 - <string name="camera_ir">IR CAMERA</string>  
61 - <string name="camera_rgb_preview_size">RGB CAMERA\n%dx%d</string>  
62 - <string name="camera_ir_preview_size">IR CAMERA\n%dx%d</string>  
63 - <string name="camera_error_notice">\n可能的原因:该设备不支持同时打开两个摄像头</string>  
64 - <string name="draw_ir_rect_mirror_horizontal">IR人脸框水平镜像绘制</string>  
65 - <string name="draw_ir_rect_mirror_vertical">IR人脸框垂直镜像绘制</string>  
66 -  
67 - <!--人脸批量注册进度框-->  
68 - <string name="progress_dialog_batch_register">进度: %d / %d</string>  
69 - <string name="progress_dialog_registering_please_wait">注册中,请稍等</string>  
70 -  
71 - <!--批量处理-->  
72 - <string name="ok">确认</string>  
73 - <string name="cancel">取消</string>  
74 - <string name="batch_process_notification_register">请将需要注册的图片放在\nsdcard/arcfacedemo/register\n目录下</string>  
75 - <string name="batch_process_no_face_need_to_delete">无人脸需要删除</string>  
76 - <string name="batch_process_confirm_delete">确认删除这%d张图片及人脸特征?</string>  
77 - <string name="batch_process_batch_register">批量注册</string>  
78 - <string name="batch_process_notification">提示</string>  
79 - <string name="batch_process_clear_faces">清空人脸库</string>  
80 - <string name="batch_process_path_is_not_exists">路径 \n%s\n 不存在</string>  
81 - <string name="batch_process_path_is_not_dir">路径 \n%s\n 不是文件夹</string>  
82 - <string name="batch_process_processing_please_wait">处理中,请稍等</string>  
83 - <string name="batch_process_finished_info">处理完成!\n处理总数 = %d \n成功数 = %d \n失败数 = %d \n处理失败的图片已保存在文件夹 \' %s \'</string>  
84 -  
85 -  
86 -  
87 -</resources>  
1 <resources> 1 <resources>
2 - <!--app name--> 2 + <!--app名称-->
3 <string name="app_name">ArcFaceDemo</string> 3 <string name="app_name">ArcFaceDemo</string>
4 4
5 - <!--settings、choose function page-->  
6 - <string name="page_preview">face attributes detect(video)</string>  
7 - <string name="page_ir_preview">ir liveness detect(video)</string>  
8 - <string name="page_face_recognize">face compare 1:n(video vs database,rgb liveness detect)</string>  
9 - <string name="page_ir_face_recognize">face compare 1:n(video vs database,ir liveness detect)</string>  
10 - <string name="page_single_image">face attributes detect(image)</string>  
11 - <string name="page_multi_image">face compare 1:n(image vs image)</string>  
12 - <string name="page_face_manage">face manage</string>  
13 - <string name="get_device_finger_failed">get device finger failed, code is: %d</string>  
14 - <string name="choose_detect_degree">choose video-mode detect degree</string>  
15 - <string name="copy_device_fingerprint">copy device fingerprint</string>  
16 - <string name="device_fingerprint_copied">device fingerprint::\n%s\n copied</string>  
17 - <string name="active_engine">active engine</string>  
18 - <string name="active_success">active success</string>  
19 - <string name="already_activated">already activated</string>  
20 - <string name="active_failed">active failed,code is %d</string>  
21 - <string name="library_not_found">library not found, please check if you put .so files into the project directory app\\src\\main\\jniLibs</string>  
22 - <string name="ft_op_0">only detect 0 degree in video mode</string>  
23 - <string name="ft_op_90">only detect 90 degree in video mode</string>  
24 - <string name="ft_op_180">only detect 180 degree in video mode</string>  
25 - <string name="ft_op_270">only detect 270 degree in video mode</string>  
26 - <string name="ft_op_all">detect all degrees in video mode</string> 5 + <!--配置、功能选择界面-->
  6 + <string name="page_preview">人脸属性检测(视频)</string>
  7 + <string name="page_ir_preview">红外活体检测(视频)</string>
  8 + <string name="page_face_recognize">人脸比对1:n(视频vs人脸库,RGB活体)</string>
  9 + <string name="page_ir_face_recognize">人脸比对1:n(视频vs人脸库,IR活体)</string>
  10 + <string name="page_single_image">人脸属性检测(图片)</string>
  11 + <string name="page_multi_image">人脸比对1:n(图片vs图片)</string>
  12 + <string name="page_face_manage">人脸批量注册管理</string>
  13 + <string name="get_device_finger_failed">获取设备指纹失败,错误码:%d</string>
  14 + <string name="choose_detect_degree">选择视频模式检测角度</string>
  15 + <string name="copy_device_fingerprint">复制设备指纹信息</string>
  16 + <string name="device_fingerprint_copied">设备指纹信息:\n%s\n已复制</string>
  17 + <string name="active_engine">激活引擎</string>
  18 + <string name="active_success">激活引擎成功</string>
  19 + <string name="already_activated">引擎已激活,无需再次激活</string>
  20 + <string name="active_failed">引擎激活失败,错误码为 %d</string>
  21 + <string name="library_not_found">未找到库文件,请检查是否有将.so文件放至工程的 app\\src\\main\\jniLibs 目录下</string>
  22 + <string name="ft_op_0">视频模式仅检测0度</string>
  23 + <string name="ft_op_90">视频模式仅检测90度</string>
  24 + <string name="ft_op_180">视频模式仅检测180度</string>
  25 + <string name="ft_op_270">视频模式仅检测270度</string>
  26 + <string name="ft_op_all">视频模式全方向人脸检测</string>
27 27
28 - <!--face attributes detect(image) page-->  
29 - <string name="start_process">process</string>  
30 - <string name="choose_local_image">choose local image</string>  
31 - <string name="processing">processing</string> 28 + <!--人脸属性检测(图片) 界面-->
  29 + <string name="start_process">开始处理</string>
  30 + <string name="choose_local_image">选择本地图片</string>
  31 + <string name="processing">处理中</string>
32 32
33 - <!--face compare 1:n(image vs image) page-->  
34 - <string name="choose_main_image">choose main image</string>  
35 - <string name="add_item_image">add item image</string>  
36 - <string name="notice_choose_main_img">please choose main image first</string>  
37 - <string name="compare_failed">compare failed!code is %d</string> 33 + <!--人脸比对1:n(图片vs图片) 界面-->
  34 + <string name="choose_main_image">选择主图</string>
  35 + <string name="add_item_image">添加比对图</string>
  36 + <string name="notice_choose_main_img">请先选择主图</string>
  37 + <string name="compare_failed">比对失败,错误码为 %d</string>
38 38
39 - <!--global get picture failed notification-->  
40 - <string name="get_picture_failed">failed to get picture!</string> 39 + <!--各个界面获取本地图片失败提示-->
  40 + <string name="get_picture_failed">获取图片失败</string>
41 41
42 - <!--global permission denied notification-->  
43 - <string name="permission_denied">permission denied!</string> 42 + <!--各个界面获取权限失败时的提示-->
  43 + <string name="permission_denied">权限被拒绝!</string>
44 44
45 - <!--global init failed notification-->  
46 - <string name="init_failed">init failed,code is %d</string>  
47 - <string name="engine_not_initialized">engine not initialized! code is %d</string> 45 + <!--各个界面引擎初始化失败的提示-->
  46 + <string name="init_failed">引擎初始化失败,错误码为 %d</string>
  47 + <string name="engine_not_initialized">引擎未初始化,错误码为 %d</string>
48 48
49 - <!--single/dual camera register&recognize page-->  
50 - <string name="register">register</string>  
51 - <string name="switch_camera">switch camera</string>  
52 - <string name="switch_camera_failed">switch camera failed</string>  
53 - <string name="notice_change_detect_degree">camera switched, if no face detected, please change face detect degree in homepage</string>  
54 - <string name="recognize_failed_notice">DENIED:%s</string>  
55 - <string name="recognize_success_notice">PASS:%s</string>  
56 - <string name="low_confidence_level">face low confidence level</string>  
57 - <string name="specific_engine_init_failed">%s init failed, code is:%d</string>  
58 - <string name="liveness_detect">liveness detect</string> 49 + <!--单目、双目识别注册界面-->
  50 + <string name="register">注册</string>
  51 + <string name="switch_camera">切换相机</string>
  52 + <string name="switch_camera_failed">切换相机失败</string>
  53 + <string name="recognize_failed_notice">未通过:%s</string>
  54 + <string name="recognize_success_notice">通过:%s</string>
  55 + <string name="low_confidence_level">人脸置信度低</string>
  56 + <string name="specific_engine_init_failed">%s 初始化失败,错误码为:%d</string>
  57 + <string name="notice_change_detect_degree">相机已切换,若无法检测到人脸,需要在首页修改视频模式人脸检测角度</string>
  58 + <string name="liveness_detect">活体检测</string>
59 <string name="camera_rgb">RGB CAMERA</string> 59 <string name="camera_rgb">RGB CAMERA</string>
60 <string name="camera_ir">IR CAMERA</string> 60 <string name="camera_ir">IR CAMERA</string>
61 <string name="camera_rgb_preview_size">RGB CAMERA\n%dx%d</string> 61 <string name="camera_rgb_preview_size">RGB CAMERA\n%dx%d</string>
62 <string name="camera_ir_preview_size">IR CAMERA\n%dx%d</string> 62 <string name="camera_ir_preview_size">IR CAMERA\n%dx%d</string>
63 - <string name="camera_error_notice">\npossible reason:open two cameras at the same time is not allowed on this device</string>  
64 - <string name="draw_ir_rect_mirror_horizontal">draw IR face rect mirror horizontal</string>  
65 - <string name="draw_ir_rect_mirror_vertical">draw IR face rect mirror vertical</string> 63 + <string name="camera_error_notice">\n可能的原因:该设备不支持同时打开两个摄像头</string>
  64 + <string name="draw_ir_rect_mirror_horizontal">IR人脸框水平镜像绘制</string>
  65 + <string name="draw_ir_rect_mirror_vertical">IR人脸框垂直镜像绘制</string>
66 66
67 - <!--batch process dialog-->  
68 - <string name="progress_dialog_batch_register">progress: %d / %d</string>  
69 - <string name="progress_dialog_registering_please_wait">registering,please wait</string> 67 + <!--人脸批量注册进度框-->
  68 + <string name="progress_dialog_batch_register">进度: %d / %d</string>
  69 + <string name="progress_dialog_registering_please_wait">注册中,请稍等</string>
70 70
71 - <!--batch process-->  
72 - <string name="ok">OK</string>  
73 - <string name="cancel">Cancel</string>  
74 - <string name="batch_process_notification_register">please put the images that need to register into directory \nsdcard/arcfacedemo/register</string>  
75 - <string name="batch_process_no_face_need_to_delete">no face need to delete</string>  
76 - <string name="batch_process_confirm_delete">delete those %d images and face features?</string>  
77 - <string name="batch_process_batch_register">batch register</string>  
78 - <string name="batch_process_notification">notification</string>  
79 - <string name="batch_process_clear_faces">clear faces</string>  
80 - <string name="batch_process_path_is_not_exists">path \n%s\n is not exists</string>  
81 - <string name="batch_process_path_is_not_dir">path \n%s\n is not a directory</string>  
82 - <string name="batch_process_processing_please_wait">process start,please wait</string>  
83 - <string name="batch_process_finished_info">process finished!\ntotal count = %d \nsuccess count = %d \nfailed count = %d \nfailed images are in directory \' %s \'</string> 71 + <!--批量处理-->
  72 + <string name="ok">确认</string>
  73 + <string name="cancel">取消</string>
  74 + <string name="batch_process_notification_register">请将需要注册的图片放在\nsdcard/arcfacedemo/register\n目录下</string>
  75 + <string name="batch_process_no_face_need_to_delete">无人脸需要删除</string>
  76 + <string name="batch_process_confirm_delete">确认删除这%d张图片及人脸特征?</string>
  77 + <string name="batch_process_batch_register">批量注册</string>
  78 + <string name="batch_process_notification">提示</string>
  79 + <string name="batch_process_clear_faces">清空人脸库</string>
  80 + <string name="batch_process_path_is_not_exists">路径 \n%s\n 不存在</string>
  81 + <string name="batch_process_path_is_not_dir">路径 \n%s\n 不是文件夹</string>
  82 + <string name="batch_process_processing_please_wait">处理中,请稍等</string>
  83 + <string name="batch_process_finished_info">处理完成!\n处理总数 = %d \n成功数 = %d \n失败数 = %d \n处理失败的图片已保存在文件夹 \' %s \'</string>
84 84
85 85
86 86
1 -package com.arcsoft.arcfacedemo;  
2 -  
3 -import org.junit.Test;  
4 -  
5 -import static org.junit.Assert.*;  
6 -  
7 -/**  
8 - * Example local unit test, which will execute on the development machine (host).  
9 - *  
10 - * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>  
11 - */  
12 -public class ExampleUnitTest {  
13 - @Test  
14 - public void addition_isCorrect() {  
15 - assertEquals(4, 2 + 2);  
16 - }  
17 -}  
@@ -43,5 +43,5 @@ android { @@ -43,5 +43,5 @@ android {
43 43
44 dependencies { 44 dependencies {
45 api fileTree(include: ['*.jar'], dir: 'libs') 45 api fileTree(include: ['*.jar'], dir: 'libs')
46 - implementation 'org.jetbrains:annotations-java5:15.0' 46 + implementation 'org.jetbrains:annotations:13.0'
47 } 47 }
1 <manifest xmlns:android="http://schemas.android.com/apk/res/android" 1 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
2 package="com.baidu.idl.main.facesdk"> 2 package="com.baidu.idl.main.facesdk">
3 3
  4 + <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
4 <application android:allowBackup="false" 5 <application android:allowBackup="false"
5 android:label="@string/app_name" 6 android:label="@string/app_name"
6 android:supportsRtl="true"> 7 android:supportsRtl="true">
  1 +/*
  2 + * Copyright (C) 2018 Baidu, Inc. All Rights Reserved.
  3 + */
  4 +package com.baidu.idl.main.facesdk.api;
  5 +
  6 +import android.graphics.Bitmap;
  7 +import android.text.TextUtils;
  8 +
  9 +import com.baidu.idl.main.facesdk.FaceInfo;
  10 +import com.baidu.idl.main.facesdk.db.DBManager;
  11 +import com.baidu.idl.main.facesdk.manager.FaceSDKManager;
  12 +import com.baidu.idl.main.facesdk.model.BDFaceImageInstance;
  13 +import com.baidu.idl.main.facesdk.model.BDFaceSDKCommon;
  14 +import com.baidu.idl.main.facesdk.model.Feature;
  15 +import com.baidu.idl.main.facesdk.model.Group;
  16 +import com.baidu.idl.main.facesdk.model.ResponseGetRecords;
  17 +import com.baidu.idl.main.facesdk.model.User;
  18 +
  19 +import java.util.ArrayList;
  20 +import java.util.List;
  21 +import java.util.UUID;
  22 +import java.util.concurrent.ExecutorService;
  23 +import java.util.concurrent.Executors;
  24 +import java.util.concurrent.Future;
  25 +import java.util.regex.Matcher;
  26 +import java.util.regex.Pattern;
  27 +
  28 +public class FaceApi {
  29 + private static FaceApi instance;
  30 + private ExecutorService es = Executors.newSingleThreadExecutor();
  31 + private Future future;
  32 +
  33 + private int mUserNum;
  34 + private boolean isinitSuccess = false;
  35 +
  36 +
  37 + private FaceApi() {
  38 +
  39 + }
  40 +
  41 + public static synchronized FaceApi getInstance() {
  42 + if (instance == null) {
  43 + instance = new FaceApi();
  44 + }
  45 + return instance;
  46 + }
  47 +
  48 + /**
  49 + * 添加用户组
  50 + */
  51 + public boolean groupAdd(Group group) {
  52 + if (group == null || TextUtils.isEmpty(group.getGroupId())) {
  53 + return false;
  54 + }
  55 + Pattern pattern = Pattern.compile("^[0-9a-zA-Z_-]{1,}$");
  56 + Matcher matcher = pattern.matcher(group.getGroupId());
  57 + if (!matcher.matches()) {
  58 + return false;
  59 + }
  60 + boolean ret = DBManager.getInstance().addGroup(group);
  61 +
  62 + return ret;
  63 + }
  64 +
  65 + /**
  66 + * 查询用户组(默认最多取1000个组)
  67 + */
  68 + public List<Group> getGroupList(int start, int length) {
  69 + if (start < 0 || length < 0) {
  70 + return null;
  71 + }
  72 + if (length > 1000) {
  73 + length = 1000;
  74 + }
  75 + List<Group> groupList = DBManager.getInstance().queryGroups(start, length);
  76 + return groupList;
  77 + }
  78 +
  79 + /**
  80 + * 根据groupId查询用户组
  81 + */
  82 + public List<Group> getGroupListByGroupId(String groupId) {
  83 + if (TextUtils.isEmpty(groupId)) {
  84 + return null;
  85 + }
  86 + return DBManager.getInstance().queryGroupsByGroupId(groupId);
  87 + }
  88 +
  89 + /**
  90 + * 根据groupId删除用户组
  91 + */
  92 + public boolean groupDelete(String groupId) {
  93 + if (TextUtils.isEmpty(groupId)) {
  94 + return false;
  95 + }
  96 + boolean ret = DBManager.getInstance().deleteGroup(groupId);
  97 + return ret;
  98 + }
  99 +
  100 + /**
  101 + * 添加用户
  102 + */
  103 + public boolean userAdd(User user) {
  104 + if (user == null || TextUtils.isEmpty(user.getGroupId())) {
  105 + return false;
  106 + }
  107 + Pattern pattern = Pattern.compile("^[0-9a-zA-Z_-]{1,}$");
  108 + Matcher matcher = pattern.matcher(user.getUserId());
  109 + if (!matcher.matches()) {
  110 + return false;
  111 + }
  112 + boolean ret = DBManager.getInstance().addUser(user);
  113 +
  114 + return ret;
  115 + }
  116 +
  117 + /**
  118 + * 根据groupId查找用户
  119 + */
  120 + public List<User> getUserList(String groupId) {
  121 + if (TextUtils.isEmpty(groupId)) {
  122 + return null;
  123 + }
  124 + List<User> userList = DBManager.getInstance().queryUserByGroupId(groupId);
  125 + return userList;
  126 + }
  127 +
  128 + /**
  129 + * 根据groupId、userName查找用户
  130 + */
  131 + public List<User> getUserListByUserName(String groupId, String userName) {
  132 + if (TextUtils.isEmpty(groupId) || TextUtils.isEmpty(userName)) {
  133 + return null;
  134 + }
  135 + List<User> userList = DBManager.getInstance().queryUserByUserName(groupId, userName);
  136 + return userList;
  137 + }
  138 +
  139 + /**
  140 + * 根据_id查找用户
  141 + */
  142 + public User getUserListById(int _id) {
  143 + if (_id < 0) {
  144 + return null;
  145 + }
  146 + List<User> userList = DBManager.getInstance().queryUserById(_id);
  147 + if (userList != null && userList.size() > 0) {
  148 + return userList.get(0);
  149 + }
  150 + return null;
  151 + }
  152 +
  153 + /**
  154 + * 更新用户
  155 + */
  156 + public boolean userUpdate(User user) {
  157 + if (user == null) {
  158 + return false;
  159 + }
  160 +
  161 + boolean ret = DBManager.getInstance().updateUser(user);
  162 + return ret;
  163 + }
  164 +
  165 + /**
  166 + * 更新用户
  167 + */
  168 + public boolean userUpdate(String groupId, String userName, String imageName, byte[] feature) {
  169 + if (groupId == null || userName == null || imageName == null || feature == null) {
  170 + return false;
  171 + }
  172 +
  173 + boolean ret = DBManager.getInstance().updateUser(groupId, userName, imageName, feature);
  174 + return ret;
  175 + }
  176 +
  177 + /**
  178 + * 删除用户
  179 + */
  180 + public boolean userDelete(String userId, String groupId) {
  181 + if (TextUtils.isEmpty(userId) || TextUtils.isEmpty(groupId)) {
  182 + return false;
  183 + }
  184 +
  185 + boolean ret = DBManager.getInstance().deleteUser(userId, groupId);
  186 + return ret;
  187 + }
  188 +
  189 + /**
  190 + * 远程删除用户
  191 + */
  192 + public boolean userDeleteByName(String userName, String groupId) {
  193 + if (TextUtils.isEmpty(userName) || TextUtils.isEmpty(groupId)) {
  194 + return false;
  195 + }
  196 +
  197 + boolean ret = DBManager.getInstance().userDeleteByName(userName, groupId);
  198 + return ret;
  199 + }
  200 +
  201 + /**
  202 + * 是否是有效姓名
  203 + *
  204 + * @param username 用户名
  205 + * @return 有效或无效信息
  206 + */
  207 + public String isValidName(String username) {
  208 + if (username == null || "".equals(username.trim())) {
  209 + return "姓名为空";
  210 + }
  211 +
  212 + // 姓名过长
  213 + if (username.length() > 10) {
  214 + return "姓名过长";
  215 + }
  216 +
  217 + // 含有特殊符号
  218 + String regex = "[ _`~!@#$%^&*()+=|{}':;',\\[\\].<>/?~!@#¥%……&*()—"
  219 + + "—+|{}【】‘;:”“’。,、?]|\n|\r|\t";
  220 + Pattern p = Pattern.compile(regex);
  221 + Matcher m = p.matcher(username);
  222 + if (m.find()) {
  223 + return "姓名中含有特殊符号";
  224 + }
  225 + return "0";
  226 + }
  227 +
  228 + /**
  229 + * 提取特征值
  230 + */
  231 + public float getFeature(Bitmap bitmap, byte[] feature, BDFaceSDKCommon.FeatureType featureType) {
  232 + if (bitmap == null) {
  233 + return -1;
  234 + }
  235 +
  236 + BDFaceImageInstance imageInstance = new BDFaceImageInstance(bitmap);
  237 + // 最大检测人脸,获取人脸信息
  238 + FaceInfo[] faceInfos = FaceSDKManager.getInstance().getFaceDetect()
  239 + .detect(BDFaceSDKCommon.DetectType.DETECT_VIS, imageInstance);
  240 + float ret = -1;
  241 + if (faceInfos != null && faceInfos.length > 0) {
  242 + FaceInfo faceInfo = faceInfos[0];
  243 + // 人脸识别,提取人脸特征值
  244 + ret = FaceSDKManager.getInstance().getFaceFeature().feature(
  245 + featureType, imageInstance,
  246 + faceInfo.landmarks, feature);
  247 + }
  248 + imageInstance.destory();
  249 + return ret;
  250 + }
  251 +
  252 +
  253 + public boolean registerUserIntoDBmanager(String groupName, String userName, String picName,
  254 + String userInfo, byte[] faceFeature) {
  255 + boolean isSuccess = false;
  256 +
  257 + Group group = new Group();
  258 + group.setGroupId(groupName);
  259 +
  260 + User user = new User();
  261 + user.setGroupId(groupName);
  262 + /*
  263 + * 用户id(由数字、字母、下划线组成),长度限制128B
  264 + * uid为用户的id,百度对uid不做限制和处理,应该与您的帐号系统中的用户id对应。
  265 + */
  266 + final String uid = UUID.randomUUID().toString();
  267 + user.setUserId(uid);
  268 + user.setUserName(userName);
  269 + user.setFeature(faceFeature);
  270 + user.setImageName(picName);
  271 + if (userInfo != null) {
  272 + user.setUserInfo(userInfo);
  273 + }
  274 + // 添加用户信息到数据库
  275 + boolean importUserSuccess = FaceApi.getInstance().userAdd(user);
  276 + if (importUserSuccess) {
  277 + // 如果添加到数据库成功,则添加用户组信息到数据库
  278 + // 如果当前图片组名和上一张图片组名相同,则不添加数据库到组表
  279 + if (FaceApi.getInstance().groupAdd(group)) {
  280 + isSuccess = true;
  281 + } else {
  282 + isSuccess = false;
  283 + }
  284 + } else {
  285 + isSuccess = false;
  286 + }
  287 +
  288 + return isSuccess;
  289 + }
  290 +
  291 + /**
  292 + * 获取底库数量
  293 + *
  294 + * @return
  295 + */
  296 + public int getmUserNum() {
  297 + return mUserNum;
  298 + }
  299 +
  300 + public boolean isinitSuccess() {
  301 + return isinitSuccess;
  302 + }
  303 +
  304 + /**
  305 + * 数据库发现变化时候,重新把数据库中的人脸信息添加到内存中,id+feature
  306 + */
  307 + public void initDatabases(final boolean isFeaturePush) {
  308 +
  309 + if (future != null && !future.isDone()) {
  310 + future.cancel(true);
  311 + }
  312 +
  313 + isinitSuccess = false;
  314 + future = es.submit(new Runnable() {
  315 + @Override
  316 + public void run() {
  317 + List<Group> listGroup = FaceApi.getInstance().getGroupList(0, 100);
  318 + if (listGroup != null && listGroup.size() > 0) {
  319 + ArrayList<Feature> features = new ArrayList<>();
  320 + for (int i = 0; i < listGroup.size(); i++) {
  321 + List<User> listUser = FaceApi.getInstance().getUserList(listGroup.get(i).getGroupId());
  322 + for (int j = 0; j < listUser.size(); j++) {
  323 + Feature feature = new Feature();
  324 + feature.setId(listUser.get(j).getId());
  325 + feature.setFeature(listUser.get(j).getFeature());
  326 + features.add(feature);
  327 + }
  328 + }
  329 + if (isFeaturePush) {
  330 + FaceSDKManager.getInstance().getFaceFeature().featurePush(features);
  331 + }
  332 + mUserNum = features.size();
  333 + }
  334 + isinitSuccess = true;
  335 + }
  336 + });
  337 + }
  338 +
  339 +
  340 + // 查询识别记录
  341 + public List<ResponseGetRecords> getRecords(String startTime, String endTime) {
  342 +// if (TextUtils.isEmpty(startTime) || TextUtils.isEmpty(endTime)) {
  343 +// return null;
  344 +// }
  345 +
  346 + List<ResponseGetRecords> responseGetRecords = DBManager.getInstance().queryRecords(startTime, endTime);
  347 + if (responseGetRecords != null && responseGetRecords.size() > 0) {
  348 + return responseGetRecords;
  349 + }
  350 +
  351 + return null;
  352 + }
  353 +
  354 + // 添加识别记录
  355 + public boolean addRecords(ResponseGetRecords responseGetRecords) {
  356 + boolean ret = false;
  357 + if (responseGetRecords == null) {
  358 + return ret;
  359 + }
  360 + ret = DBManager.getInstance().addResponseGetRecords(responseGetRecords);
  361 + return ret;
  362 + }
  363 +
  364 + // 删除识别记录
  365 + public boolean deleteRecords(String userName) {
  366 + boolean ret = false;
  367 + if (TextUtils.isEmpty(userName)) {
  368 + return ret;
  369 + }
  370 + ret = DBManager.getInstance().deleteRecords(userName);
  371 + return ret;
  372 + }
  373 +
  374 + // 删除识别记录
  375 + public boolean deleteRecords(String startTime, String endTime) {
  376 + boolean ret = false;
  377 + if (TextUtils.isEmpty(startTime) && TextUtils.isEmpty(endTime)) {
  378 + return ret;
  379 + }
  380 + ret = DBManager.getInstance().deleteRecords(startTime, endTime);
  381 + return ret;
  382 + }
  383 +
  384 + // 清除识别记录
  385 + public int cleanRecords() {
  386 + boolean ret = false;
  387 + int num = DBManager.getInstance().cleanRecords();
  388 + return num;
  389 + }
  390 +
  391 +
  392 +}
  1 +package com.baidu.idl.main.facesdk.callback;
  2 +
  3 +import android.hardware.Camera;
  4 +
  5 +/**
  6 + * Time: 2019/1/25
  7 + * Author: v_chaixiaogang
  8 + * Description: camera1数据结果回调
  9 + */
  10 +public interface CameraDataCallback {
  11 + /**
  12 + * @param data 预览数据
  13 + * @param camera 相机设备
  14 + * @param width 预览宽
  15 + * @param height 预览高
  16 + */
  17 + void onGetCameraData(byte[] data, Camera camera, int width, int height);
  18 +}
  1 +/*
  2 + * Copyright (C) 2018 Baidu, Inc. All Rights Reserved.
  3 + */
  4 +package com.baidu.idl.main.facesdk.callback;
  5 +
  6 +
  7 +import com.baidu.idl.main.facesdk.model.LivenessModel;
  8 +
  9 +/**
  10 + * 人脸检测回调接口。
  11 + *
  12 + * @Time: 2019/1/25
  13 + * @Author: v_chaixiaogang
  14 + */
  15 +public interface FaceDetectCallBack {
  16 + void onFaceDetectCallback(LivenessModel livenessModel);
  17 +
  18 + void onTip(int code, String msg);
  19 +
  20 + void onFaceDetectDarwCallback(LivenessModel livenessModel);
  21 +}
  1 +/*
  2 + * Copyright (C) 2019 Baidu, Inc. All Rights Reserved.
  3 + */
  4 +package com.baidu.idl.main.facesdk.callback;
  5 +
  6 +
  7 +/**
  8 + * 人脸特征抽取回调接口。
  9 + *
  10 + * @Time: 2019/5/30
  11 + * @Author: v_zhangxiaoqing01
  12 + */
  13 +public interface FaceFeatureCallBack {
  14 +
  15 + public void onFaceFeatureCallBack(float featureSize, byte[] feature);
  16 +
  17 +}
  1 +/*
  2 + * Copyright (C) 2017 Baidu, Inc. All Rights Reserved.
  3 + */
  4 +package com.baidu.idl.main.facesdk.camera;
  5 +
  6 +import android.content.Context;
  7 +import android.graphics.Canvas;
  8 +import android.graphics.Path;
  9 +import android.graphics.Region;
  10 +import android.os.Handler;
  11 +import android.os.Looper;
  12 +import android.util.AttributeSet;
  13 +import android.view.TextureView;
  14 +import android.widget.FrameLayout;
  15 +
  16 +import com.baidu.idl.main.facesdk.model.SingleBaseConfig;
  17 +
  18 +
  19 +/**
  20 + * 基于 系统TextureView实现的预览View。
  21 + *
  22 + * @Time: 2019/1/28
  23 + * @Author: v_chaixiaogang
  24 + */
  25 +public class AutoTexturePreviewView extends FrameLayout {
  26 +
  27 + public TextureView textureView;
  28 +
  29 + private int videoWidth = 0;
  30 + private int videoHeight = 0;
  31 +
  32 +
  33 + private int previewWidth = 0;
  34 + private int previewHeight = 0;
  35 + private static int scale = 2;
  36 +
  37 + public static float circleRadius;
  38 + public static float circleX;
  39 + public static float circleY;
  40 +
  41 + private float[] pointXY = new float[3];
  42 +
  43 +
  44 + public AutoTexturePreviewView(Context context) {
  45 + super(context);
  46 + init();
  47 + }
  48 +
  49 + public AutoTexturePreviewView(Context context, AttributeSet attrs) {
  50 + super(context, attrs);
  51 + init();
  52 + }
  53 +
  54 + public AutoTexturePreviewView(Context context, AttributeSet attrs,
  55 + int defStyleAttr) {
  56 + super(context, attrs, defStyleAttr);
  57 + init();
  58 + }
  59 +
  60 +
  61 + private Handler handler = new Handler(Looper.getMainLooper());
  62 +
  63 + private void init() {
  64 + setWillNotDraw(false);
  65 + textureView = new TextureView(getContext());
  66 + addView(textureView);
  67 + }
  68 +
  69 + @Override
  70 + protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
  71 + super.onLayout(changed, left, top, right, bottom);
  72 +
  73 + previewWidth = getWidth();
  74 + previewHeight = getHeight();
  75 +
  76 + if (videoWidth == 0 || videoHeight == 0 || previewWidth == 0 || previewHeight == 0) {
  77 + return;
  78 + }
  79 +
  80 + if (previewWidth * videoHeight > previewHeight * videoWidth) {
  81 + int scaledChildHeight = videoHeight * previewWidth / videoWidth;
  82 + textureView.layout(0, (previewHeight - scaledChildHeight) / scale,
  83 + previewWidth, (previewHeight + scaledChildHeight) / scale);
  84 + } else {
  85 + int scaledChildWidth = videoWidth * previewHeight / videoHeight;
  86 + textureView.layout((previewWidth - scaledChildWidth) / scale, 0,
  87 + (previewWidth + scaledChildWidth) / scale, previewHeight);
  88 +
  89 + }
  90 +
  91 +
  92 + }
  93 +
  94 + public TextureView getTextureView() {
  95 + return textureView;
  96 + }
  97 +
  98 + public int getPreviewWidth() {
  99 + return previewWidth;
  100 + }
  101 +
  102 + public int getPreviewHeight() {
  103 + return previewHeight;
  104 + }
  105 +
  106 + public void setPreviewSize(int width, int height) {
  107 + if (this.videoWidth == width && this.videoHeight == height) {
  108 + return;
  109 + }
  110 + this.videoWidth = width;
  111 + this.videoHeight = height;
  112 + handler.post(new Runnable() {
  113 + @Override
  114 + public void run() {
  115 + requestLayout();
  116 + }
  117 + });
  118 +
  119 + }
  120 +
  121 +
  122 + @Override
  123 + protected void onDraw(Canvas canvas) {
  124 + String displayType = SingleBaseConfig.getBaseConfig().getDetectFrame();
  125 + if (displayType.equals("fixedarea")) {
  126 + Path path = new Path();
  127 + // 设置裁剪的圆心坐标,半径
  128 + path.addCircle(getWidth() / 2, getHeight() / 2, getWidth() / 3, Path.Direction.CCW);
  129 + // 裁剪画布,并设置其填充方式
  130 + canvas.clipPath(path, Region.Op.REPLACE);
  131 +
  132 + circleRadius = getWidth() / 3;
  133 + circleX = (getRight() - getLeft()) / 2;
  134 + circleY = (getBottom() - getTop()) / 2;
  135 + }
  136 + super.onDraw(canvas);
  137 + }
  138 +}
  1 +package com.baidu.idl.main.facesdk.camera;
  2 +
  3 +import android.content.Context;
  4 +import android.graphics.SurfaceTexture;
  5 +import android.hardware.Camera;
  6 +import android.util.Log;
  7 +import android.util.SparseIntArray;
  8 +import android.view.Surface;
  9 +import android.view.TextureView;
  10 +
  11 +import com.baidu.idl.main.facesdk.callback.CameraDataCallback;
  12 +import com.baidu.idl.main.facesdk.model.SingleBaseConfig;
  13 +
  14 +import java.util.List;
  15 +
  16 +/**
  17 + * Time: 2019/1/24
  18 + * Author: v_chaixiaogang
  19 + * Description:
  20 + */
  21 +public class CameraPreviewManager implements TextureView.SurfaceTextureListener {
  22 +
  23 + private static final String TAG = "camera_preview";
  24 +
  25 +
  26 + AutoTexturePreviewView mTextureView;
  27 + boolean mPreviewed = false;
  28 + private boolean mSurfaceCreated = false;
  29 + private SurfaceTexture mSurfaceTexture;
  30 +
  31 + public static final int CAMERA_FACING_BACK = 0;
  32 +
  33 + public static final int CAMERA_FACING_FRONT = 1;
  34 +
  35 + public static final int CAMERA_USB = 2;
  36 +
  37 + public static final int CAMERA_ORBBEC = 3;
  38 +
  39 + /**
  40 + * 垂直方向
  41 + */
  42 + public static final int ORIENTATION_PORTRAIT = 0;
  43 + /**
  44 + * 水平方向
  45 + */
  46 + public static final int ORIENTATION_HORIZONTAL = 1;
  47 +
  48 + /**
  49 + * 当前相机的ID。
  50 + */
  51 + private int cameraFacing = CAMERA_FACING_FRONT;
  52 +
  53 + private int previewWidth;
  54 + private int previewHeight;
  55 +
  56 + private int videoWidth;
  57 + private int videoHeight;
  58 +
  59 + private int tempWidth;
  60 + private int tempHeight;
  61 +
  62 + private int textureWidth;
  63 + private int textureHeight;
  64 +
  65 + private Camera mCamera;
  66 + private int mCameraNum;
  67 +
  68 + private int displayOrientation = 0;
  69 + private int cameraId = 0;
  70 + private int mirror = 1; // 镜像处理
  71 + private CameraDataCallback mCameraDataCallback;
  72 + private static volatile CameraPreviewManager instance = null;
  73 +
  74 + private static final SparseIntArray ORIENTATIONS = new SparseIntArray();
  75 +
  76 + static {
  77 +
  78 + ORIENTATIONS.append(Surface.ROTATION_0, 0);
  79 + ORIENTATIONS.append(Surface.ROTATION_90, 90);
  80 + ORIENTATIONS.append(Surface.ROTATION_180, 180);
  81 + ORIENTATIONS.append(Surface.ROTATION_270, 270);
  82 + }
  83 +
  84 + public static CameraPreviewManager getInstance() {
  85 + synchronized (CameraPreviewManager.class) {
  86 + if (instance == null) {
  87 + instance = new CameraPreviewManager();
  88 + }
  89 + }
  90 + return instance;
  91 + }
  92 +
  93 + public int getCameraFacing() {
  94 + return cameraFacing;
  95 + }
  96 +
  97 + public void setCameraFacing(int cameraFacing) {
  98 + this.cameraFacing = cameraFacing;
  99 + }
  100 +
  101 + public int getDisplayOrientation() {
  102 + return displayOrientation;
  103 + }
  104 +
  105 + public void setDisplayOrientation(int displayOrientation) {
  106 + this.displayOrientation = displayOrientation;
  107 + }
  108 +
  109 + /**
  110 + * 开启预览
  111 + *
  112 + * @param context
  113 + * @param textureView
  114 + */
  115 + public void startPreview(Context context, AutoTexturePreviewView textureView, int width,
  116 + int height, CameraDataCallback cameraDataCallback) {
  117 + Log.e(TAG, "开启预览模式");
  118 + Context mContext = context;
  119 + this.mCameraDataCallback = cameraDataCallback;
  120 + mTextureView = textureView;
  121 + this.previewWidth = width;
  122 + this.previewHeight = height;
  123 + mSurfaceTexture = mTextureView.getTextureView().getSurfaceTexture();
  124 + mTextureView.getTextureView().setSurfaceTextureListener(this);
  125 + }
  126 +
  127 + @Override
  128 + public void onSurfaceTextureAvailable(SurfaceTexture texture, int i, int i1) {
  129 + Log.e(TAG, "--surfaceTexture--SurfaceTextureAvailable");
  130 + mSurfaceTexture = texture;
  131 + mSurfaceCreated = true;
  132 + textureWidth = i;
  133 + textureHeight = i1;
  134 + openCamera();
  135 +
  136 + }
  137 +
  138 + @Override
  139 + public void onSurfaceTextureSizeChanged(SurfaceTexture texture, int i, int i1) {
  140 + Log.e(TAG, "--surfaceTexture--TextureSizeChanged");
  141 + }
  142 +
  143 + @Override
  144 + public boolean onSurfaceTextureDestroyed(SurfaceTexture texture) {
  145 + Log.e(TAG, "--surfaceTexture--destroyed");
  146 + mSurfaceCreated = false;
  147 + if (mCamera != null) {
  148 + mCamera.setPreviewCallback(null);
  149 + mCamera.stopPreview();
  150 + mCamera.release();
  151 + mCamera = null;
  152 + }
  153 + return true;
  154 + }
  155 +
  156 + @Override
  157 + public void onSurfaceTextureUpdated(SurfaceTexture texture) {
  158 + // Log.e(TAG, "--surfaceTexture--Updated");
  159 + }
  160 +
  161 +
  162 + /**
  163 + * 关闭预览
  164 + */
  165 + public void stopPreview() {
  166 + if (mCamera != null) {
  167 + try {
  168 + mCamera.setPreviewTexture(null);
  169 + mSurfaceCreated = false;
  170 + mTextureView = null;
  171 + mCamera.setPreviewCallback(null);
  172 + mCamera.stopPreview();
  173 + mCamera.release();
  174 + mCamera = null;
  175 + } catch (Exception e) {
  176 + Log.e("qing", "camera destory error");
  177 + e.printStackTrace();
  178 +
  179 + }
  180 + }
  181 + }
  182 +
  183 +
  184 + /**
  185 + * 开启摄像头
  186 + */
  187 +
  188 + private void openCamera() {
  189 +
  190 + try {
  191 + if (mCamera == null) {
  192 + Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
  193 + for (int i = 0; i < Camera.getNumberOfCameras(); i++) {
  194 + Camera.getCameraInfo(i, cameraInfo);
  195 + if (cameraInfo.facing == cameraFacing) {
  196 + cameraId = i;
  197 + }
  198 + }
  199 + mCamera = Camera.open(cameraId);
  200 + Log.e(TAG, "initCamera---open camera");
  201 + }
  202 +
  203 + // 摄像头图像预览角度
  204 + int cameraRotation = SingleBaseConfig.getBaseConfig().getVideoDirection();
  205 + switch (cameraFacing) {
  206 + case CAMERA_FACING_FRONT: {
  207 +// cameraRotation = ORIENTATIONS.get(displayOrientation);
  208 +// cameraRotation = getCameraDisplayOrientation(cameraRotation, cameraId);
  209 + mCamera.setDisplayOrientation(cameraRotation);
  210 + break;
  211 + }
  212 +
  213 + case CAMERA_FACING_BACK: {
  214 +// cameraRotation = ORIENTATIONS.get(displayOrientation);
  215 +// cameraRotation = getCameraDisplayOrientation(cameraRotation, cameraId);
  216 + mCamera.setDisplayOrientation(cameraRotation);
  217 + break;
  218 + }
  219 +
  220 + case CAMERA_USB: {
  221 + mCamera.setDisplayOrientation(cameraRotation);
  222 + break;
  223 + }
  224 +
  225 + default:
  226 + break;
  227 +
  228 + }
  229 +
  230 +
  231 + if (cameraRotation == 90 || cameraRotation == 270) {
  232 + boolean isRgbRevert = SingleBaseConfig.getBaseConfig().getRgbRevert();
  233 + if (isRgbRevert) {
  234 + mTextureView.setRotationY(180);
  235 + } else {
  236 + mTextureView.setRotationY(0);
  237 + }
  238 + // 旋转90度或者270,需要调整宽高
  239 + mTextureView.setPreviewSize(previewHeight, previewWidth);
  240 + } else {
  241 + boolean isRgbRevert = SingleBaseConfig.getBaseConfig().getRgbRevert();
  242 + if (isRgbRevert) {
  243 + mTextureView.setRotationY(180);
  244 + } else {
  245 + mTextureView.setRotationY(0);
  246 + }
  247 + mTextureView.setPreviewSize(previewWidth, previewHeight);
  248 + }
  249 + Camera.Parameters params = mCamera.getParameters();
  250 + List<Camera.Size> sizeList = params.getSupportedPreviewSizes(); // 获取所有支持的camera尺寸
  251 + final Camera.Size optionSize = getOptimalPreviewSize(sizeList, previewWidth,
  252 + previewHeight); // 获取一个最为适配的camera.size
  253 + if (optionSize.width == previewWidth && optionSize.height == previewHeight) {
  254 + videoWidth = previewWidth;
  255 + videoHeight = previewHeight;
  256 + } else {
  257 + videoWidth = optionSize.width;
  258 + videoHeight = optionSize.height;
  259 + }
  260 + params.setPreviewSize(videoWidth, videoHeight);
  261 +
  262 + mCamera.setParameters(params);
  263 + try {
  264 + mCamera.setPreviewTexture(mSurfaceTexture);
  265 + mCamera.setPreviewCallback(new Camera.PreviewCallback() {
  266 + @Override
  267 + public void onPreviewFrame(byte[] bytes, Camera camera) {
  268 + if (mCameraDataCallback != null) {
  269 + mCameraDataCallback.onGetCameraData(bytes, camera,
  270 + videoWidth, videoHeight);
  271 + }
  272 + }
  273 + });
  274 + mCamera.startPreview();
  275 +
  276 + } catch (Exception e) {
  277 + e.printStackTrace();
  278 + Log.e(TAG, e.getMessage());
  279 + }
  280 + } catch (RuntimeException e) {
  281 + Log.e(TAG, e.getMessage());
  282 + }
  283 + }
  284 +
  285 +
  286 + private int getCameraDisplayOrientation(int degrees, int cameraId) {
  287 + Camera.CameraInfo info = new Camera.CameraInfo();
  288 + Camera.getCameraInfo(cameraId, info);
  289 + int rotation = 0;
  290 + if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
  291 + rotation = (info.orientation + degrees) % 360;
  292 + rotation = (360 - rotation) % 360; // compensate the mirror
  293 + } else { // back-facing
  294 + rotation = (info.orientation - degrees + 360) % 360;
  295 + }
  296 + return rotation;
  297 + }
  298 +
  299 +
  300 + /**
  301 + * 解决预览变形问题
  302 + *
  303 + * @param sizes
  304 + * @param w
  305 + * @param h
  306 + * @return
  307 + */
  308 + private Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int w, int h) {
  309 + final double aspectTolerance = 0.1;
  310 + double targetRatio = (double) w / h;
  311 + if (sizes == null) {
  312 + return null;
  313 + }
  314 + Camera.Size optimalSize = null;
  315 + double minDiff = Double.MAX_VALUE;
  316 +
  317 + int targetHeight = h;
  318 +
  319 + // Try to find an size match aspect ratio and size
  320 + for (Camera.Size size : sizes) {
  321 + double ratio = (double) size.width / size.height;
  322 + if (Math.abs(ratio - targetRatio) > aspectTolerance) {
  323 + continue;
  324 + }
  325 + if (Math.abs(size.height - targetHeight) < minDiff) {
  326 + optimalSize = size;
  327 + minDiff = Math.abs(size.height - targetHeight);
  328 + }
  329 + }
  330 +
  331 + // Cannot find the one match the aspect ratio, ignore the requirement
  332 + if (optimalSize == null) {
  333 + minDiff = Double.MAX_VALUE;
  334 + for (Camera.Size size : sizes) {
  335 + if (Math.abs(size.height - targetHeight) < minDiff) {
  336 + optimalSize = size;
  337 + minDiff = Math.abs(size.height - targetHeight);
  338 + }
  339 + }
  340 + }
  341 + return optimalSize;
  342 + }
  343 +}