作者 张卫卫

sdk架构修改

正在显示 93 个修改的文件 包含 7191 行增加4005 行删除
@@ -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 +}
  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.Bitmap;
  8 +import android.graphics.Canvas;
  9 +import android.graphics.Color;
  10 +import android.graphics.Paint;
  11 +import android.graphics.PorterDuff;
  12 +import android.graphics.Rect;
  13 +import android.os.Handler;
  14 +import android.os.Looper;
  15 +import android.util.AttributeSet;
  16 +import android.view.TextureView;
  17 +import android.widget.FrameLayout;
  18 +
  19 +public class PicoAutoTexturePreviewView extends FrameLayout {
  20 +
  21 + public TextureView textureView;
  22 +
  23 + private int videoWidth = 0;
  24 + private int videoHeight = 0;
  25 +
  26 +
  27 + private int previewWidth = 0;
  28 + private int previewHeight = 0;
  29 + private static int scale = 2;
  30 +
  31 + private Paint mPaint;
  32 + private Rect mSrcRect;
  33 + private Rect mDstRect;
  34 +
  35 +
  36 + public PicoAutoTexturePreviewView(Context context) {
  37 + super(context);
  38 + init();
  39 + }
  40 +
  41 + public PicoAutoTexturePreviewView(Context context, AttributeSet attrs) {
  42 + super(context, attrs);
  43 + init();
  44 + }
  45 +
  46 + public PicoAutoTexturePreviewView(Context context, AttributeSet attrs,
  47 + int defStyleAttr) {
  48 + super(context, attrs, defStyleAttr);
  49 + init();
  50 + }
  51 +
  52 +
  53 + private Handler handler = new Handler(Looper.getMainLooper());
  54 +
  55 + private void init() {
  56 + textureView = new TextureView(getContext());
  57 + addView(textureView);
  58 +
  59 + mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
  60 + mSrcRect = new Rect();
  61 + mDstRect = new Rect();
  62 + }
  63 +
  64 + @Override
  65 + protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
  66 + super.onLayout(changed, left, top, right, bottom);
  67 +
  68 + previewWidth = getWidth();
  69 + previewHeight = getHeight();
  70 +
  71 + if (videoWidth == 0 || videoHeight == 0 || previewWidth == 0 || previewHeight == 0) {
  72 + return;
  73 + }
  74 +
  75 +
  76 + if (previewWidth * videoHeight > previewHeight * videoWidth) {
  77 + int scaledChildHeight = videoHeight * previewWidth / videoWidth;
  78 + textureView.layout(0, (previewHeight - scaledChildHeight) / scale,
  79 + previewWidth, (previewHeight + scaledChildHeight) / scale);
  80 + } else {
  81 + int scaledChildWidth = videoWidth * previewHeight / videoHeight;
  82 + textureView.layout((previewWidth - scaledChildWidth) / scale, 0,
  83 + (previewWidth + scaledChildWidth) / scale, previewHeight);
  84 +
  85 + }
  86 +
  87 +
  88 + }
  89 +
  90 + public TextureView getTextureView() {
  91 + return textureView;
  92 + }
  93 +
  94 + public int getPreviewWidth() {
  95 + return previewWidth;
  96 + }
  97 +
  98 + public int getPreviewHeight() {
  99 + return previewHeight;
  100 + }
  101 +
  102 + public void setPreviewSize(int width, int height) {
  103 + if (this.videoWidth == width && this.videoHeight == height) {
  104 + return;
  105 + }
  106 + this.videoWidth = width;
  107 + this.videoHeight = height;
  108 + handler.post(new Runnable() {
  109 + @Override
  110 + public void run() {
  111 + requestLayout();
  112 + }
  113 + });
  114 +
  115 + }
  116 +
  117 + public void draw(Bitmap bm) {
  118 + Canvas canvas = textureView.lockCanvas();
  119 + if (canvas != null) {
  120 + canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
  121 + mSrcRect.set(0, 0, bm.getWidth(), bm.getHeight());
  122 + mDstRect.set(0, 0, textureView.getWidth(), bm.getHeight() * textureView.getWidth() / bm.getWidth());
  123 + canvas.drawBitmap(bm, mSrcRect, mDstRect, mPaint);
  124 + }
  125 + textureView.unlockCanvasAndPost(canvas);
  126 + }
  127 +
  128 +
  129 +}
  1 +/*
  2 + * Copyright (C) 2018 Baidu, Inc. All Rights Reserved.
  3 + */
  4 +package com.baidu.idl.main.facesdk.db;
  5 +
  6 +import android.content.Context;
  7 +import android.database.sqlite.SQLiteDatabase;
  8 +import android.database.sqlite.SQLiteOpenHelper;
  9 +
  10 +/**
  11 + * 数据库创建工具
  12 + */
  13 +public class DBHelper extends SQLiteOpenHelper {
  14 + private static final String CREATE_TABLE_START_SQL = "CREATE TABLE IF NOT EXISTS ";
  15 + private static final String CREATE_TABLE_PRIMIRY_SQL = " integer primary key autoincrement,";
  16 +
  17 + /**
  18 + * 数据库名称
  19 + */
  20 + private static final String DB_NAME = "face.db";
  21 + /**
  22 + * 数据库版本
  23 + */
  24 + private static final int VERSION = 1;
  25 + /** 人脸特征表 */
  26 + // public static final String TABLE_FEATURE = "feature";
  27 + /**
  28 + * 用户组表
  29 + */
  30 + public static final String TABLE_USER_GROUP = "user_group";
  31 + /**
  32 + * 用户表
  33 + */
  34 + public static final String TABLE_USER = "user";
  35 +
  36 + /**
  37 + * 识别记录表
  38 + */
  39 + public static final String TABLE_Records = "records";
  40 +
  41 + public DBHelper(Context context) {
  42 + super(context, DB_NAME, null, VERSION);
  43 + }
  44 +
  45 + @Override
  46 + public void onCreate(SQLiteDatabase db) {
  47 + createTables(db);
  48 + }
  49 +
  50 + @Override
  51 + public synchronized void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
  52 + if (newVersion > oldVersion) {
  53 + // db.execSQL("DROP TABLE IF EXISTS " + TABLE_FEATURE);
  54 + db.execSQL("DROP TABLE IF EXISTS " + TABLE_USER_GROUP);
  55 + db.execSQL("DROP TABLE IF EXISTS " + TABLE_USER);
  56 + db.execSQL("DROP TABLE IF EXISTS " + TABLE_Records);
  57 + onCreate(db);
  58 + }
  59 + }
  60 +
  61 + public synchronized void createTables(SQLiteDatabase db) {
  62 + if (db == null || db.isReadOnly()) {
  63 + db = getWritableDatabase();
  64 + }
  65 +
  66 + // 创建人脸特征表的SQL语句
  67 +// StringBuffer featureSql = new StringBuffer();
  68 +// featureSql.append(CREATE_TABLE_START_SQL).append(TABLE_FEATURE).append(" ( ");
  69 +// featureSql.append(" _id").append(CREATE_TABLE_PRIMIRY_SQL);
  70 +// featureSql.append(" face_token").append(" varchar(128) default \"\" ,");
  71 +// featureSql.append(" group_id").append(" varchar(32) default \"\" ,");
  72 +// featureSql.append(" user_id").append(" varchar(32) default \"\" ,");
  73 +// featureSql.append(" feature").append(" blob ,");
  74 +// featureSql.append(" image_name").append(" varchar(64) default \"\" ,");
  75 +// featureSql.append(" ctime").append(" long ,");
  76 +// featureSql.append(" update_time").append(" long )");
  77 +
  78 + // 创建用户组表的SQL语句
  79 + StringBuffer groupSql = new StringBuffer();
  80 + groupSql.append(CREATE_TABLE_START_SQL).append(TABLE_USER_GROUP).append(" ( ");
  81 + groupSql.append(" _id").append(CREATE_TABLE_PRIMIRY_SQL);
  82 + groupSql.append(" group_id").append(" varchar(32) default \"\" ,");
  83 + groupSql.append(" desc").append(" varchar(32) default \"\" ,");
  84 + groupSql.append(" ctime").append(" long ,");
  85 + groupSql.append(" update_time").append(" long )");
  86 +
  87 + // 创建用户表的SQL语句
  88 + StringBuffer userSql = new StringBuffer();
  89 + userSql.append(CREATE_TABLE_START_SQL).append(TABLE_USER).append(" ( ");
  90 + userSql.append(" _id").append(CREATE_TABLE_PRIMIRY_SQL);
  91 + userSql.append(" user_id").append(" varchar(32) default \"\" ,");
  92 + userSql.append(" user_name").append(" varchar(32) default \"\" ,");
  93 + userSql.append(" user_info").append(" varchar(32) default \"\" ,");
  94 + userSql.append(" group_id").append(" varchar(32) default \"\" ,");
  95 + userSql.append(" face_token").append(" varchar(128) default \"\" ,");
  96 + userSql.append(" feature").append(" blob ,");
  97 + userSql.append(" image_name").append(" varchar(64) default \"\" ,");
  98 + userSql.append(" ctime").append(" long ,");
  99 + userSql.append(" update_time").append(" long )");
  100 +
  101 + // 创建识别记录的SQL语句
  102 + StringBuffer recordSql = new StringBuffer();
  103 + recordSql.append(CREATE_TABLE_START_SQL).append(TABLE_Records).append(" ( ");
  104 + recordSql.append(" _id").append(CREATE_TABLE_PRIMIRY_SQL);
  105 + recordSql.append(" deviceid").append(" varchar(32) default \"\" ,");
  106 + recordSql.append(" user_id").append(" varchar(32) default \"\" ,");
  107 + recordSql.append(" user_name").append(" varchar(32) default \"\" ,");
  108 + recordSql.append(" group_id").append(" varchar(32) default \"\" ,");
  109 + recordSql.append(" face_token").append(" varchar(128) default \"\" ,");
  110 + recordSql.append(" time").append(" datetime ,");
  111 + recordSql.append(" records").append(" varchar(32) default \"\" ,");
  112 + recordSql.append(" longId").append(" varchar(32) default \"\" ,");
  113 + recordSql.append(" score").append(" varchar(32) default \"\" )");
  114 +
  115 + try {
  116 + db.execSQL(groupSql.toString());
  117 + db.execSQL(userSql.toString());
  118 + db.execSQL(recordSql.toString());
  119 + // db.execSQL(featureSql.toString());
  120 + } catch (Exception e) {
  121 + e.printStackTrace();
  122 + }
  123 + }
  124 +}
  1 +package com.baidu.idl.main.facesdk.db;
  2 +
  3 +
  4 +import android.content.ContentValues;
  5 +import android.content.Context;
  6 +import android.database.Cursor;
  7 +import android.database.sqlite.SQLiteDatabase;
  8 +import android.database.sqlite.SQLiteOpenHelper;
  9 +import android.text.TextUtils;
  10 +import android.util.Log;
  11 +
  12 +import com.baidu.idl.main.facesdk.model.Group;
  13 +import com.baidu.idl.main.facesdk.model.ResponseGetRecords;
  14 +import com.baidu.idl.main.facesdk.model.User;
  15 +
  16 +import java.util.ArrayList;
  17 +import java.util.List;
  18 +import java.util.concurrent.atomic.AtomicInteger;
  19 +import java.util.concurrent.locks.Lock;
  20 +import java.util.concurrent.locks.ReentrantLock;
  21 +
  22 +/**
  23 + * 数据库管理类
  24 + */
  25 +public class DBManager {
  26 + /**
  27 + * The constant TAG
  28 + */
  29 + private static final String TAG = "DBManager";
  30 +
  31 + private AtomicInteger mOpenCounter = new AtomicInteger();
  32 + private static DBManager instance;
  33 + private static SQLiteOpenHelper mDBHelper;
  34 + private SQLiteDatabase mDatabase;
  35 + private boolean allowTransaction = true;
  36 + private Lock writeLock = new ReentrantLock();
  37 + private volatile boolean writeLocked = false;
  38 +
  39 + /**
  40 + * 单例模式,初始化DBManager
  41 + *
  42 + * @return DBManager实例
  43 + */
  44 + public static synchronized DBManager getInstance() {
  45 + if (instance == null) {
  46 + instance = new DBManager();
  47 + }
  48 + return instance;
  49 + }
  50 +
  51 + /**
  52 + * 数据库初始化
  53 + *
  54 + * @param context 当前上下文
  55 + */
  56 + public void init(Context context) {
  57 + if (context == null) {
  58 + return;
  59 + }
  60 +
  61 + if (mDBHelper == null) {
  62 + mDBHelper = new DBHelper(context.getApplicationContext());
  63 + }
  64 + }
  65 +
  66 + /**
  67 + * 释放数据库
  68 + */
  69 + public void release() {
  70 + if (mDBHelper != null) {
  71 + mDBHelper.close();
  72 + mDBHelper = null;
  73 + }
  74 + instance = null;
  75 + }
  76 +
  77 + /**
  78 + * 打开数据库
  79 + */
  80 + public synchronized SQLiteDatabase openDatabase() {
  81 + if (mOpenCounter.incrementAndGet() == 1) {
  82 + // Opening new database
  83 + try {
  84 + mDatabase = mDBHelper.getWritableDatabase();
  85 + } catch (Exception e) {
  86 + Log.e(TAG, "openDatabase e = " + e.getMessage());
  87 + mDatabase = mDBHelper.getReadableDatabase();
  88 + }
  89 + }
  90 + return mDatabase;
  91 + }
  92 +
  93 + /**
  94 + * 关闭数据库
  95 + */
  96 + public synchronized void closeDatabase() {
  97 + if (mOpenCounter.decrementAndGet() == 0) {
  98 + // Closing database
  99 + mDatabase.close();
  100 + }
  101 + }
  102 +
  103 + // ---------------------------------------用户组相关 start--------------------------------------
  104 +
  105 + /**
  106 + * 添加用户组
  107 + */
  108 + public boolean addGroup(Group group) {
  109 + if (mDBHelper == null) {
  110 + return false;
  111 + }
  112 + Cursor cursor = null;
  113 +
  114 + SQLiteDatabase db = mDBHelper.getReadableDatabase();
  115 + String where = "group_id = ? ";
  116 + String[] whereValue = {group.getGroupId()};
  117 + // 查询该groupId是否在数据库中,如果在,则不添加
  118 + cursor = db.query(DBHelper.TABLE_USER_GROUP, null, where, whereValue,
  119 + null, null, null);
  120 + if (cursor == null) {
  121 + return false;
  122 + }
  123 +
  124 + if (cursor.getCount() > 0) {
  125 + return true;
  126 + }
  127 +
  128 + mDatabase = mDBHelper.getWritableDatabase();
  129 + ContentValues cv = new ContentValues();
  130 + cv.put("group_id", group.getGroupId());
  131 + cv.put("desc", group.getDesc() == null ? "" : group.getDesc());
  132 + cv.put("update_time", System.currentTimeMillis());
  133 + cv.put("ctime", System.currentTimeMillis());
  134 +
  135 + long rowId = -1;
  136 + try {
  137 + rowId = mDatabase.insert(DBHelper.TABLE_USER_GROUP, null, cv);
  138 + } catch (Exception e) {
  139 + Log.e(TAG, "addGroup e = " + e.getMessage());
  140 + e.printStackTrace();
  141 + }
  142 + if (rowId < 0) {
  143 + return false;
  144 + }
  145 + Log.i(TAG, "insert group success:" + rowId);
  146 + closeCursor(cursor);
  147 + return true;
  148 + }
  149 +
  150 + /**
  151 + * 查询用户组
  152 + */
  153 + public List<Group> queryGroups(int start, int offset) {
  154 + Cursor cursor = null;
  155 + List<Group> groupList = new ArrayList<>();
  156 + try {
  157 + if (mDBHelper == null) {
  158 + return null;
  159 + }
  160 + SQLiteDatabase db = mDBHelper.getReadableDatabase();
  161 + String limit = start + " , " + offset;
  162 + cursor = db.query(DBHelper.TABLE_USER_GROUP, null, null, null, null, null, null, limit);
  163 + while (cursor != null && cursor.getCount() > 0 && cursor.moveToNext()) {
  164 + int dbId = cursor.getInt(cursor.getColumnIndex("_id"));
  165 + String groupId = cursor.getString(cursor.getColumnIndex("group_id"));
  166 + String desc = cursor.getString(cursor.getColumnIndex("desc"));
  167 + long updateTime = cursor.getLong(cursor.getColumnIndex("update_time"));
  168 + long ctime = cursor.getLong(cursor.getColumnIndex("ctime"));
  169 +
  170 + Group group = new Group();
  171 + group.setGroupId(groupId);
  172 + group.setDesc(desc);
  173 + group.setCtime(ctime);
  174 + groupList.add(group);
  175 + }
  176 + } finally {
  177 + closeCursor(cursor);
  178 + }
  179 + return groupList;
  180 + }
  181 +
  182 + /**
  183 + * 查询用户组(根据groupId)
  184 + */
  185 + public List<Group> queryGroupsByGroupId(String groupId) {
  186 + ArrayList<Group> groupList = new ArrayList<>();
  187 + Cursor cursor = null;
  188 +
  189 + try {
  190 + if (mDBHelper == null) {
  191 + return groupList;
  192 + }
  193 + SQLiteDatabase db = mDBHelper.getReadableDatabase();
  194 + String where = "group_id = ? ";
  195 + String[] whereValue = {groupId};
  196 + cursor = db.query(DBHelper.TABLE_USER_GROUP, null, where, whereValue, null, null, null);
  197 + while (cursor != null && cursor.getCount() > 0 && cursor.moveToNext()) {
  198 + int dbId = cursor.getInt(cursor.getColumnIndex("_id"));
  199 + String desc = cursor.getString(cursor.getColumnIndex("desc"));
  200 + long updateTime = cursor.getLong(cursor.getColumnIndex("update_time"));
  201 + long ctime = cursor.getLong(cursor.getColumnIndex("ctime"));
  202 +
  203 + Group group = new Group();
  204 + group.setGroupId(groupId);
  205 + group.setDesc(desc);
  206 + group.setCtime(ctime);
  207 + groupList.add(group);
  208 + }
  209 + } catch (Exception e) {
  210 + Log.e(TAG, "queryGroupsByGroupId e = " + e.getMessage());
  211 + } finally {
  212 + closeCursor(cursor);
  213 + }
  214 + return groupList;
  215 + }
  216 +
  217 + /**
  218 + * 删除用户组
  219 + */
  220 + public boolean deleteGroup(String groupId) {
  221 + boolean success = false;
  222 + try {
  223 + mDatabase = mDBHelper.getWritableDatabase();
  224 + beginTransaction(mDatabase);
  225 +
  226 + if (!TextUtils.isEmpty(groupId)) {
  227 + String where = "group_id = ?";
  228 + String[] whereValue = {groupId};
  229 +
  230 + if (mDatabase.delete(DBHelper.TABLE_USER, where, whereValue) < 0) {
  231 + return false;
  232 + }
  233 + if (mDatabase.delete(DBHelper.TABLE_USER_GROUP, where, whereValue) < 0) {
  234 + return false;
  235 + }
  236 +
  237 + setTransactionSuccessful(mDatabase);
  238 + success = true;
  239 + }
  240 +
  241 + } finally {
  242 + endTransaction(mDatabase);
  243 + }
  244 + return success;
  245 + }
  246 +
  247 + // ---------------------------------------用户组相关 end----------------------------------------
  248 +
  249 + // ---------------------------------------用户相关 start----------------------------------------
  250 +
  251 + /**
  252 + * 添加用户
  253 + */
  254 + public boolean addUser(User user) {
  255 + if (mDBHelper == null) {
  256 + return false;
  257 + }
  258 + try {
  259 + mDatabase = mDBHelper.getWritableDatabase();
  260 + beginTransaction(mDatabase);
  261 +
  262 + ContentValues cv = new ContentValues();
  263 + cv.put("user_id", user.getUserId());
  264 + cv.put("user_name", user.getUserName());
  265 + cv.put("user_info", user.getUserInfo());
  266 + cv.put("group_id", user.getGroupId());
  267 + cv.put("face_token", user.getFaceToken());
  268 + cv.put("feature", user.getFeature());
  269 + cv.put("image_name", user.getImageName());
  270 + cv.put("ctime", System.currentTimeMillis());
  271 + cv.put("update_time", System.currentTimeMillis());
  272 +
  273 + long rowId = mDatabase.insert(DBHelper.TABLE_USER, null, cv);
  274 + if (rowId < 0) {
  275 + return false;
  276 + }
  277 +
  278 + setTransactionSuccessful(mDatabase);
  279 + Log.i(TAG, "insert user success:" + rowId);
  280 + } catch (Exception e) {
  281 + Log.e(TAG, "addUser e = " + e.getMessage());
  282 + return false;
  283 + } finally {
  284 + endTransaction(mDatabase);
  285 + }
  286 + return true;
  287 + }
  288 +
  289 + /**
  290 + * 查询用户(根据groupId、userId)
  291 + */
  292 + public User queryUser(String groupId, String userId) {
  293 + Cursor cursor = null;
  294 +
  295 + try {
  296 + if (mDBHelper == null) {
  297 + return null;
  298 + }
  299 + SQLiteDatabase db = mDBHelper.getReadableDatabase();
  300 + String where = "user_id = ? and group_id = ? ";
  301 + String[] whereValue = {userId, groupId};
  302 + cursor = db.query(DBHelper.TABLE_USER, null, where, whereValue, null, null, null);
  303 + if (cursor != null && cursor.getCount() > 0 && cursor.moveToNext()) {
  304 + int dbId = cursor.getInt(cursor.getColumnIndex("_id"));
  305 + String userName = cursor.getString(cursor.getColumnIndex("user_name"));
  306 + long updateTime = cursor.getLong(cursor.getColumnIndex("update_time"));
  307 + long ctime = cursor.getLong(cursor.getColumnIndex("ctime"));
  308 +
  309 + User user = new User();
  310 + user.setId(dbId);
  311 + user.setUserId(userId);
  312 + user.setGroupId(groupId);
  313 + user.setUserName(userName);
  314 + user.setCtime(ctime);
  315 + user.setUpdateTime(updateTime);
  316 + return user;
  317 + }
  318 + } finally {
  319 + closeCursor(cursor);
  320 + }
  321 + return null;
  322 + }
  323 +
  324 + /**
  325 + * 查询用户(根据groupId)
  326 + */
  327 + public List<User> queryUserByGroupId(String groupId) {
  328 + Cursor cursor = null;
  329 + List<User> users = new ArrayList<>();
  330 + try {
  331 + if (mDBHelper == null) {
  332 + return null;
  333 + }
  334 + SQLiteDatabase db = mDBHelper.getReadableDatabase();
  335 + String where = "group_id = ? order by ctime desc";
  336 + String[] whereValue = {groupId};
  337 + cursor = db.query(DBHelper.TABLE_USER, null, where, whereValue, null, null, null);
  338 + while (cursor != null && cursor.getCount() > 0 && cursor.moveToNext()) {
  339 + int dbId = cursor.getInt(cursor.getColumnIndex("_id"));
  340 + String userId = cursor.getString(cursor.getColumnIndex("user_id"));
  341 + String userName = cursor.getString(cursor.getColumnIndex("user_name"));
  342 + String userInfo = cursor.getString(cursor.getColumnIndex("user_info"));
  343 + String faceToken = cursor.getString(cursor.getColumnIndex("face_token"));
  344 + byte[] feature = cursor.getBlob(cursor.getColumnIndex("feature"));
  345 + String imageName = cursor.getString(cursor.getColumnIndex("image_name"));
  346 + long updateTime = cursor.getLong(cursor.getColumnIndex("update_time"));
  347 + long ctime = cursor.getLong(cursor.getColumnIndex("ctime"));
  348 +
  349 + User user = new User();
  350 + user.setId(dbId);
  351 + user.setUserId(userId);
  352 + user.setGroupId(groupId);
  353 + user.setUserName(userName);
  354 + user.setCtime(ctime);
  355 + user.setUpdateTime(updateTime);
  356 + user.setUserInfo(userInfo);
  357 + user.setFaceToken(faceToken);
  358 + user.setFeature(feature);
  359 + user.setImageName(imageName);
  360 + users.add(user);
  361 + }
  362 + } finally {
  363 + closeCursor(cursor);
  364 + }
  365 + return users;
  366 + }
  367 +
  368 + /**
  369 + * 查询用户(根据groupId、userName)
  370 + */
  371 + public List<User> queryUserByUserName(String groupId, String userName) {
  372 + Cursor cursor = null;
  373 + List<User> users = new ArrayList<>();
  374 + try {
  375 + if (mDBHelper == null) {
  376 + return null;
  377 + }
  378 + SQLiteDatabase db = mDBHelper.getReadableDatabase();
  379 + String where = "user_name = ? and group_id = ? ";
  380 + String[] whereValue = {userName, groupId};
  381 + cursor = db.query(DBHelper.TABLE_USER, null, where, whereValue, null, null, null);
  382 + if (cursor != null && cursor.getCount() > 0 && cursor.moveToNext()) {
  383 + int dbId = cursor.getInt(cursor.getColumnIndex("_id"));
  384 + String userId = cursor.getString(cursor.getColumnIndex("user_id"));
  385 + String userInfo = cursor.getString(cursor.getColumnIndex("user_info"));
  386 + String faceToken = cursor.getString(cursor.getColumnIndex("face_token"));
  387 + byte[] feature = cursor.getBlob(cursor.getColumnIndex("feature"));
  388 + String imageName = cursor.getString(cursor.getColumnIndex("image_name"));
  389 + long updateTime = cursor.getLong(cursor.getColumnIndex("update_time"));
  390 + long ctime = cursor.getLong(cursor.getColumnIndex("ctime"));
  391 +
  392 + User user = new User();
  393 + user.setId(dbId);
  394 + user.setUserId(userId);
  395 + user.setGroupId(groupId);
  396 + user.setUserName(userName);
  397 + user.setCtime(ctime);
  398 + user.setUpdateTime(updateTime);
  399 + user.setUserInfo(userInfo);
  400 + user.setFeature(feature);
  401 + user.setImageName(imageName);
  402 + user.setFaceToken(faceToken);
  403 + users.add(user);
  404 + }
  405 + } finally {
  406 + closeCursor(cursor);
  407 + }
  408 + return users;
  409 + }
  410 +
  411 + /**
  412 + * 查询用户(根据dbId)
  413 + */
  414 + public List<User> queryUserById(int _id) {
  415 + List<User> users = new ArrayList<>();
  416 + Cursor cursor = null;
  417 + try {
  418 + if (mDBHelper == null) {
  419 + return null;
  420 + }
  421 + SQLiteDatabase db = mDBHelper.getReadableDatabase();
  422 + String where = "_id = ? ";
  423 + String[] whereValue = {String.valueOf(_id)};
  424 + cursor = db.query(DBHelper.TABLE_USER, null, where, whereValue, null, null, null);
  425 + if (cursor != null && cursor.getCount() > 0 && cursor.moveToNext()) {
  426 + String groupId = cursor.getString(cursor.getColumnIndex("group_id"));
  427 + String userId = cursor.getString(cursor.getColumnIndex("user_id"));
  428 + String userName = cursor.getString(cursor.getColumnIndex("user_name"));
  429 + String userInfo = cursor.getString(cursor.getColumnIndex("user_info"));
  430 + String faceToken = cursor.getString(cursor.getColumnIndex("face_token"));
  431 + byte[] feature = cursor.getBlob(cursor.getColumnIndex("feature"));
  432 + String imageName = cursor.getString(cursor.getColumnIndex("image_name"));
  433 + long updateTime = cursor.getLong(cursor.getColumnIndex("update_time"));
  434 + long ctime = cursor.getLong(cursor.getColumnIndex("ctime"));
  435 +
  436 + User user = new User();
  437 + user.setId(_id);
  438 + user.setUserId(userId);
  439 + user.setGroupId(groupId);
  440 + user.setUserName(userName);
  441 + user.setCtime(ctime);
  442 + user.setUpdateTime(updateTime);
  443 + user.setUserInfo(userInfo);
  444 + user.setFeature(feature);
  445 + user.setImageName(imageName);
  446 + user.setFaceToken(faceToken);
  447 + users.add(user);
  448 + }
  449 + } finally {
  450 + closeCursor(cursor);
  451 + }
  452 + return users;
  453 + }
  454 +
  455 + /**
  456 + * 更新用户
  457 + */
  458 + public boolean updateUser(User user) {
  459 + boolean success = false;
  460 + if (mDBHelper == null) {
  461 + return success;
  462 + }
  463 +
  464 + try {
  465 + mDatabase = mDBHelper.getWritableDatabase();
  466 + beginTransaction(mDatabase);
  467 +
  468 + if (user != null) {
  469 + mDatabase.beginTransaction();
  470 + String where = "user_id = ? and group_id = ?";
  471 + String[] whereValue = {user.getUserId(), user.getGroupId()};
  472 + ContentValues cv = new ContentValues();
  473 +
  474 + cv.put("user_id", user.getUserId());
  475 + cv.put("user_name", user.getUserName());
  476 + cv.put("group_id", user.getGroupId());
  477 + cv.put("image_name", user.getImageName());
  478 + cv.put("update_time", System.currentTimeMillis());
  479 +
  480 + if (mDatabase.update(DBHelper.TABLE_USER, cv, where, whereValue) < 0) {
  481 + return false;
  482 + }
  483 + }
  484 + setTransactionSuccessful(mDatabase);
  485 + success = true;
  486 + } finally {
  487 + endTransaction(mDatabase);
  488 + }
  489 + return success;
  490 + }
  491 +
  492 + /**
  493 + * 更新用户
  494 + */
  495 + public boolean updateUser(String groupId, String userName, String imageName, byte[] feature) {
  496 +
  497 + if (mDBHelper == null) {
  498 + return false;
  499 + }
  500 + try {
  501 + mDatabase = mDBHelper.getWritableDatabase();
  502 + beginTransaction(mDatabase);
  503 +
  504 + String where = "user_name = ? and group_id = ?";
  505 + String[] whereValue = {userName, groupId};
  506 + ContentValues cv = new ContentValues();
  507 +
  508 + cv.put("user_name", userName);
  509 + cv.put("group_id", groupId);
  510 + cv.put("image_name", imageName);
  511 + cv.put("update_time", System.currentTimeMillis());
  512 + cv.put("feature", feature);
  513 +
  514 + if (mDatabase.update(DBHelper.TABLE_USER, cv, where, whereValue) < 0) {
  515 + return false;
  516 + }
  517 + setTransactionSuccessful(mDatabase);
  518 + } finally {
  519 + endTransaction(mDatabase);
  520 + }
  521 + return true;
  522 + }
  523 +
  524 + /**
  525 + * 删除用户
  526 + */
  527 + public boolean deleteUser(String userId, String groupId) {
  528 + boolean success = false;
  529 + try {
  530 + mDatabase = mDBHelper.getWritableDatabase();
  531 + beginTransaction(mDatabase);
  532 +
  533 + if (!TextUtils.isEmpty(userId) && !TextUtils.isEmpty(groupId)) {
  534 + String where = "user_id = ? and group_id = ?";
  535 + String[] whereValue = {userId, groupId};
  536 +
  537 + if (mDatabase.delete(DBHelper.TABLE_USER, where, whereValue) < 0) {
  538 + return false;
  539 + }
  540 +
  541 + setTransactionSuccessful(mDatabase);
  542 + success = true;
  543 + }
  544 +
  545 + } finally {
  546 + endTransaction(mDatabase);
  547 + }
  548 + return success;
  549 + }
  550 +
  551 + // ---------------------------------------用户相关 end------------------------------------------
  552 +
  553 + private void beginTransaction(SQLiteDatabase mDatabase) {
  554 + if (allowTransaction) {
  555 + mDatabase.beginTransaction();
  556 + } else {
  557 + writeLock.lock();
  558 + writeLocked = true;
  559 + }
  560 + }
  561 +
  562 + private void setTransactionSuccessful(SQLiteDatabase mDatabase) {
  563 + if (allowTransaction) {
  564 + mDatabase.setTransactionSuccessful();
  565 + }
  566 + }
  567 +
  568 + private void endTransaction(SQLiteDatabase mDatabase) {
  569 + if (allowTransaction) {
  570 + mDatabase.endTransaction();
  571 + }
  572 + if (writeLocked) {
  573 + writeLock.unlock();
  574 + writeLocked = false;
  575 + }
  576 + }
  577 +
  578 + private void closeCursor(Cursor cursor) {
  579 + if (cursor != null) {
  580 + try {
  581 + cursor.close();
  582 + } catch (Throwable e) {
  583 + Log.e(TAG, "closeCursor e = " + e.getMessage());
  584 + }
  585 + }
  586 + }
  587 +
  588 +
  589 + // 获取识别记录
  590 + public List<ResponseGetRecords> queryRecords(String startTime, String endTime) {
  591 + List<ResponseGetRecords> responseGetRecords = new ArrayList<>();
  592 + Cursor cursor = null;
  593 + try {
  594 + if (mDBHelper == null) {
  595 + return null;
  596 + }
  597 + SQLiteDatabase db = mDBHelper.getReadableDatabase();
  598 + if (!TextUtils.isEmpty(startTime) && !TextUtils.isEmpty(endTime)) {
  599 + String where = "time > ? and time < ? ";
  600 + String[] whereValue = {startTime, endTime};
  601 + cursor = db.query(DBHelper.TABLE_Records, null, where, whereValue, null, null, null);
  602 + } else {
  603 + String sql = "select * from " + DBHelper.TABLE_Records;
  604 + cursor = db.rawQuery(sql, null);
  605 + }
  606 +
  607 + if (cursor != null && cursor.getCount() > 0) {
  608 + while (cursor.moveToNext()) {
  609 + int dbId = cursor.getInt(cursor.getColumnIndex("_id"));
  610 + String userId = cursor.getString(cursor.getColumnIndex("user_id"));
  611 + String records = cursor.getString(cursor.getColumnIndex("records"));
  612 + String longId = cursor.getString(cursor.getColumnIndex("longId"));
  613 + String score = cursor.getString(cursor.getColumnIndex("score"));
  614 + String deviceId = cursor.getString(cursor.getColumnIndex("deviceid"));
  615 + String time = cursor.getString(cursor.getColumnIndex("time"));
  616 + String faceToken = cursor.getString(cursor.getColumnIndex("face_token"));
  617 + String groupId = cursor.getString(cursor.getColumnIndex("group_id"));
  618 + String userName = cursor.getString(cursor.getColumnIndex("user_name"));
  619 +
  620 + ResponseGetRecords responseGetRecord = new ResponseGetRecords();
  621 + responseGetRecord.setIndex(String.valueOf(dbId));
  622 + responseGetRecord.setUserId(userId);
  623 + responseGetRecord.setDeviceId(deviceId);
  624 + responseGetRecord.setRecords(records);
  625 + responseGetRecord.setLogId(longId);
  626 + responseGetRecord.setScore(score);
  627 + responseGetRecord.setTime(time);
  628 + responseGetRecord.setFaceToken("");
  629 + responseGetRecord.setGroupId(groupId);
  630 + responseGetRecord.setUserName(userName);
  631 +
  632 + responseGetRecords.add(responseGetRecord);
  633 + }
  634 + }
  635 + } catch (Exception e) {
  636 + Log.e("shangtest", e.getMessage());
  637 + } finally {
  638 + closeCursor(cursor);
  639 + }
  640 + return responseGetRecords;
  641 + }
  642 +
  643 +
  644 + // 添加识别记录
  645 + public boolean addResponseGetRecords(ResponseGetRecords responseGetRecords) {
  646 + if (mDBHelper == null) {
  647 + return false;
  648 + }
  649 + try {
  650 + mDatabase = mDBHelper.getWritableDatabase();
  651 + beginTransaction(mDatabase);
  652 +
  653 + ContentValues cv = new ContentValues();
  654 + cv.put("user_id", responseGetRecords.getUserId());
  655 + cv.put("records", responseGetRecords.getRecords());
  656 + cv.put("longId", responseGetRecords.getLogId());
  657 + cv.put("score", responseGetRecords.getScore());
  658 + cv.put("deviceid", responseGetRecords.getDeviceId());
  659 + cv.put("time", responseGetRecords.getTime());
  660 + cv.put("user_name", responseGetRecords.getUserName());
  661 + cv.put("group_id", responseGetRecords.getGroupId());
  662 + cv.put("face_token", responseGetRecords.getFaceToken());
  663 +
  664 + long rowId = mDatabase.insert(DBHelper.TABLE_Records, null, cv);
  665 + if (rowId < 0) {
  666 + return false;
  667 + }
  668 +
  669 + setTransactionSuccessful(mDatabase);
  670 + } catch (Exception e) {
  671 + return false;
  672 + } finally {
  673 + endTransaction(mDatabase);
  674 + }
  675 + return true;
  676 + }
  677 +
  678 +
  679 + // 根据时间删除识别记录
  680 + public boolean deleteRecords(String startTime, String endTime) {
  681 + boolean success = false;
  682 + try {
  683 + mDatabase = mDBHelper.getWritableDatabase();
  684 + beginTransaction(mDatabase);
  685 +
  686 + if (!TextUtils.isEmpty(startTime) && !TextUtils.isEmpty(endTime)) {
  687 + String where = " time > ? and time < ?";
  688 + String[] whereValue = {startTime, endTime};
  689 +// String[] whereValue = {"2019-09-05 10:50:09", "2019-09-05 10:50:13"};
  690 +
  691 + if (mDatabase.delete(DBHelper.TABLE_Records, where, whereValue) < 0) {
  692 + return false;
  693 + }
  694 +
  695 + setTransactionSuccessful(mDatabase);
  696 + success = true;
  697 + }
  698 +
  699 + } finally {
  700 + endTransaction(mDatabase);
  701 + }
  702 + return success;
  703 + }
  704 +
  705 + // 根据userId删除识别记录
  706 + public boolean deleteRecords(String userName) {
  707 + boolean success = false;
  708 + try {
  709 + mDatabase = mDBHelper.getWritableDatabase();
  710 + beginTransaction(mDatabase);
  711 +
  712 + if (!TextUtils.isEmpty(userName)) {
  713 + String where = "user_name = ?";
  714 + String[] whereValue = {userName};
  715 +
  716 + if (mDatabase.delete(DBHelper.TABLE_Records, where, whereValue) < 0) {
  717 + return false;
  718 + }
  719 +
  720 + setTransactionSuccessful(mDatabase);
  721 + success = true;
  722 + }
  723 +
  724 + } finally {
  725 + endTransaction(mDatabase);
  726 + }
  727 + return success;
  728 + }
  729 +
  730 + // 根据userId删除识别记录
  731 + public int cleanRecords() {
  732 + int number = getRecordsNum();
  733 + boolean success = false;
  734 + try {
  735 + mDatabase = mDBHelper.getWritableDatabase();
  736 + beginTransaction(mDatabase);
  737 +
  738 + String where = "1 = 1";
  739 + String[] whereValue = {};
  740 +
  741 + if (mDatabase.delete(DBHelper.TABLE_Records, where, whereValue) < 0) {
  742 + return 0;
  743 + }
  744 +
  745 + // 自增恢复为0
  746 + String resoreIdSql = "update sqlite_sequence set seq=0 where name='" + DBHelper.TABLE_Records + "'";
  747 + mDatabase.execSQL(resoreIdSql);
  748 +
  749 + setTransactionSuccessful(mDatabase);
  750 + success = true;
  751 +
  752 + } finally {
  753 + endTransaction(mDatabase);
  754 + }
  755 + return number;
  756 + }
  757 +
  758 +
  759 + public int getRecordsNum() {
  760 + int number = 0;
  761 + {
  762 + Cursor cursor = null;
  763 + try {
  764 + if (mDBHelper == null) {
  765 + return 0;
  766 + }
  767 + SQLiteDatabase db = mDBHelper.getReadableDatabase();
  768 + cursor = db.rawQuery("select count(*) from " + DBHelper.TABLE_Records, null);
  769 + if (!cursor.moveToFirst()) {
  770 + number = 0;
  771 + } else {
  772 + number = (int) cursor.getLong(0);
  773 + }
  774 +
  775 + } finally {
  776 + closeCursor(cursor);
  777 + }
  778 + return number;
  779 + }
  780 + }
  781 +
  782 + /**
  783 + * 远程删除用户
  784 + */
  785 + public boolean userDeleteByName(String userNmae, String groupId) {
  786 + boolean success = false;
  787 + try {
  788 + mDatabase = mDBHelper.getWritableDatabase();
  789 + beginTransaction(mDatabase);
  790 +
  791 + if (!TextUtils.isEmpty(userNmae) && !TextUtils.isEmpty(groupId)) {
  792 + String where = "user_name = ? and group_id = ?";
  793 + String[] whereValue = {userNmae, groupId};
  794 +
  795 + if (mDatabase.delete(DBHelper.TABLE_USER, where, whereValue) < 0) {
  796 + return false;
  797 + }
  798 +
  799 + setTransactionSuccessful(mDatabase);
  800 + success = true;
  801 + }
  802 +
  803 + } finally {
  804 + endTransaction(mDatabase);
  805 + }
  806 + return success;
  807 + }
  808 +
  809 +}
  1 +package com.baidu.idl.main.facesdk.listener;
  2 +
  3 +public interface SdkInitListener {
  4 + public void initStart();
  5 +
  6 + public void initLicenseSuccess();
  7 +
  8 + public void initLicenseFail(int errorCode, String msg);
  9 +
  10 + public void initModelSuccess();
  11 +
  12 + public void initModelFail(int errorCode, String msg);
  13 +}
  1 +package com.baidu.idl.main.facesdk.manager;
  2 +
  3 +import android.content.Context;
  4 +import android.text.TextUtils;
  5 +import android.util.Log;
  6 +
  7 +import com.baidu.idl.main.facesdk.FaceAuth;
  8 +import com.baidu.idl.main.facesdk.FaceDetect;
  9 +import com.baidu.idl.main.facesdk.FaceFeature;
  10 +import com.baidu.idl.main.facesdk.FaceInfo;
  11 +import com.baidu.idl.main.facesdk.FaceLive;
  12 +import com.baidu.idl.main.facesdk.FaceMouthMask;
  13 +import com.baidu.idl.main.facesdk.api.FaceApi;
  14 +import com.baidu.idl.main.facesdk.callback.Callback;
  15 +import com.baidu.idl.main.facesdk.callback.FaceDetectCallBack;
  16 +import com.baidu.idl.main.facesdk.callback.FaceFeatureCallBack;
  17 +import com.baidu.idl.main.facesdk.db.DBManager;
  18 +import com.baidu.idl.main.facesdk.listener.SdkInitListener;
  19 +import com.baidu.idl.main.facesdk.model.BDFaceDetectListConf;
  20 +import com.baidu.idl.main.facesdk.model.BDFaceImageInstance;
  21 +import com.baidu.idl.main.facesdk.model.BDFaceInstance;
  22 +import com.baidu.idl.main.facesdk.model.BDFaceOcclusion;
  23 +import com.baidu.idl.main.facesdk.model.BDFaceSDKCommon;
  24 +import com.baidu.idl.main.facesdk.model.BDFaceSDKConfig;
  25 +import com.baidu.idl.main.facesdk.model.Feature;
  26 +import com.baidu.idl.main.facesdk.model.GlobalSet;
  27 +import com.baidu.idl.main.facesdk.model.LivenessModel;
  28 +import com.baidu.idl.main.facesdk.model.SingleBaseConfig;
  29 +import com.baidu.idl.main.facesdk.model.User;
  30 +import com.baidu.idl.main.facesdk.utils.LogUtils;
  31 +import com.baidu.idl.main.facesdk.utils.PreferencesUtil;
  32 +import com.baidu.idl.main.facesdk.utils.ToastUtils;
  33 +
  34 +import java.util.ArrayList;
  35 +import java.util.concurrent.ExecutorService;
  36 +import java.util.concurrent.Executors;
  37 +import java.util.concurrent.Future;
  38 +
  39 +import static com.baidu.idl.main.facesdk.model.BDFaceSDKCommon.BDFaceLogInfo.BDFACE_LOG_ALL_MESSAGE;
  40 +import static com.baidu.idl.main.facesdk.model.GlobalSet.FEATURE_SIZE;
  41 +import static com.baidu.idl.main.facesdk.model.GlobalSet.TIME_TAG;
  42 +
  43 +public class FaceSDKManager {
  44 +
  45 + public static final int SDK_MODEL_LOAD_SUCCESS = 0;
  46 + public static final int SDK_UNACTIVATION = 1;
  47 + public static final int SDK_UNINIT = 2;
  48 + public static final int SDK_INITING = 3;
  49 + public static final int SDK_INITED = 4;
  50 + public static final int SDK_INIT_FAIL = 5;
  51 + public static final int SDK_INIT_SUCCESS = 6;
  52 +
  53 +
  54 + public static volatile int initStatus = SDK_UNACTIVATION;
  55 + private FaceAuth faceAuth;
  56 + private FaceDetect faceDetect;
  57 + private FaceFeature faceFeature;
  58 + private FaceLive faceLiveness;
  59 +
  60 + private ExecutorService es = Executors.newSingleThreadExecutor();
  61 + private Future future;
  62 + private ExecutorService es2 = Executors.newSingleThreadExecutor();
  63 + private Future future2;
  64 +
  65 + private BDFaceDetectListConf bdFaceDetectListConfig;
  66 + private FaceDetect faceDetectNir;
  67 + private FaceMouthMask faceMouthMask;
  68 + private float[] scores;
  69 +
  70 + private FaceSDKManager() {
  71 + faceAuth = new FaceAuth();
  72 + faceAuth.setActiveLog(BDFACE_LOG_ALL_MESSAGE);
  73 + faceAuth.setCoreConfigure(BDFaceSDKCommon.BDFaceCoreRunMode.BDFACE_LITE_POWER_LOW, 2);
  74 +
  75 +// faceDetect = new FaceDetect();
  76 +// faceFeature = new FaceFeature();
  77 +// faceLiveness = new FaceLive();
  78 +
  79 + }
  80 +
  81 + private static class HolderClass {
  82 + private static final FaceSDKManager instance = new FaceSDKManager();
  83 + }
  84 +
  85 + public static FaceSDKManager getInstance() {
  86 + return HolderClass.instance;
  87 + }
  88 +
  89 + public FaceDetect getFaceDetect() {
  90 + return faceDetect;
  91 + }
  92 +
  93 + public FaceFeature getFaceFeature() {
  94 + return faceFeature;
  95 + }
  96 +
  97 + public FaceLive getFaceLiveness() {
  98 + return faceLiveness;
  99 + }
  100 +
  101 + /**
  102 + * 初始化鉴权,如果鉴权通过,直接初始化模型
  103 + *
  104 + * @param context
  105 + * @param listener
  106 + */
  107 + public void init(final Context context, final SdkInitListener listener) {
  108 +
  109 + PreferencesUtil.initPrefs(context.getApplicationContext());
  110 + final String licenseOfflineKey = PreferencesUtil.getString("activate_offline_key", "");
  111 + final String licenseOnlineKey = PreferencesUtil.getString("activate_online_key", "HDDJ-PARX-TMWX-HUYX");
  112 +
  113 + // 如果licenseKey 不存在提示授权码为空,并跳转授权页面授权
  114 + if (TextUtils.isEmpty(licenseOfflineKey) && TextUtils.isEmpty(licenseOnlineKey)) {
  115 + ToastUtils.toast(context, "未授权设备,请完成授权激活");
  116 + if (listener != null) {
  117 + listener.initLicenseFail(-1, "授权码不存在,请重新输入!");
  118 + }
  119 + return;
  120 + }
  121 + // todo 增加判空处理
  122 + if (listener != null) {
  123 + listener.initStart();
  124 + }
  125 +
  126 + if (!TextUtils.isEmpty(licenseOfflineKey)) {
  127 + // 离线激活
  128 + faceAuth.initLicenseOffLine(context, new Callback() {
  129 + @Override
  130 + public void onResponse(int code, String response) {
  131 + if (code == 0) {
  132 + initStatus = SDK_INIT_SUCCESS;
  133 + if (listener != null) {
  134 + listener.initLicenseSuccess();
  135 + }
  136 + initModel(context, listener);
  137 + return;
  138 + }
  139 + }
  140 + });
  141 + } else if (!TextUtils.isEmpty(licenseOnlineKey)) {
  142 +
  143 + // 在线激活
  144 + faceAuth.initLicenseOnLine(context, licenseOnlineKey, new Callback() {
  145 + @Override
  146 + public void onResponse(int code, String response) {
  147 + if (code == 0) {
  148 + initStatus = SDK_INIT_SUCCESS;
  149 + if (listener != null) {
  150 + listener.initLicenseSuccess();
  151 + }
  152 + initModel(context, listener);
  153 + return;
  154 + }
  155 + }
  156 + });
  157 +
  158 + } else {
  159 + if (listener != null) {
  160 + listener.initLicenseFail(-1, "授权码不存在,请重新输入!");
  161 + }
  162 + }
  163 +
  164 + }
  165 +
  166 + /**
  167 + * 初始化模型,目前包含检查,活体,识别模型;因为初始化是顺序执行,可以在最好初始化回掉中返回状态结果
  168 + *
  169 + * @param context
  170 + * @param listener
  171 + */
  172 + public void initModel(final Context context, final SdkInitListener listener) {
  173 + ToastUtils.toast(context, "模型初始化中,请稍后片刻");
  174 +
  175 + faceDetect = new FaceDetect();
  176 + BDFaceInstance bdFaceInstance = new BDFaceInstance();
  177 + bdFaceInstance.creatInstance();
  178 + faceDetectNir = new FaceDetect(bdFaceInstance);
  179 + faceFeature = new FaceFeature();
  180 + faceLiveness = new FaceLive();
  181 + faceMouthMask = new FaceMouthMask();
  182 +
  183 + initConfig();
  184 +
  185 + final long startInitModelTime = System.currentTimeMillis();
  186 +
  187 + faceDetect.initModel(context,
  188 + GlobalSet.DETECT_VIS_MODEL,
  189 + GlobalSet.ALIGN_TRACK_MODEL,
  190 + BDFaceSDKCommon.DetectType.DETECT_VIS,
  191 + BDFaceSDKCommon.AlignType.BDFACE_ALIGN_TYPE_RGB_FAST,
  192 + new Callback() {
  193 + @Override
  194 + public void onResponse(int code, String response) {
  195 + if (code != 0 && listener != null) {
  196 + listener.initModelFail(code, response);
  197 + }
  198 + }
  199 + });
  200 +
  201 + faceDetect.initModel(context,
  202 + GlobalSet.DETECT_VIS_MODEL,
  203 + GlobalSet.ALIGN_RGB_MODEL, BDFaceSDKCommon.DetectType.DETECT_VIS,
  204 + BDFaceSDKCommon.AlignType.BDFACE_ALIGN_TYPE_RGB_ACCURATE,
  205 + new Callback() {
  206 + @Override
  207 + public void onResponse(int code, String response) {
  208 + // ToastUtils.toast(context, code + " " + response);
  209 + if (code != 0 && listener != null) {
  210 + listener.initModelFail(code, response);
  211 + }
  212 + }
  213 + });
  214 +
  215 +// faceDetect.initModel(context,
  216 +// GlobalSet.DETECT_VIS_MODEL,
  217 +// "",
  218 +// GlobalSet.ALIGN_MODEL, BDFaceSDKCommon.DetectType.DETECT_VIS,
  219 +// BDFaceSDKCommon.AlignType.BDFACE_FULL_ALIGN_TYPE_GENERAL_RGB,
  220 +// new Callback() {
  221 +// @Override
  222 +// public void onResponse(int code, String response) {
  223 +// // ToastUtils.toast(context, code + " " + response);
  224 +// if (code != 0 && listener != null) {
  225 +// listener.initModelFail(code, response);
  226 +// }
  227 +// }
  228 +// });
  229 +
  230 + faceDetectNir.initModel(context,
  231 + GlobalSet.DETECT_NIR_MODE,
  232 + GlobalSet.ALIGN_NIR_MODEL, BDFaceSDKCommon.DetectType.DETECT_NIR,
  233 + BDFaceSDKCommon.AlignType.BDFACE_ALIGN_TYPE_NIR_ACCURATE,
  234 + new Callback() {
  235 + @Override
  236 + public void onResponse(int code, String response) {
  237 + // ToastUtils.toast(context, code + " " + response);
  238 + if (code != 0 && listener != null) {
  239 + listener.initModelFail(code, response);
  240 + }
  241 + }
  242 + });
  243 +
  244 + faceDetect.initQuality(context,
  245 + GlobalSet.BLUR_MODEL,
  246 + GlobalSet.OCCLUSION_MODEL, new Callback() {
  247 + @Override
  248 + public void onResponse(int code, String response) {
  249 + if (code != 0 && listener != null) {
  250 + listener.initModelFail(code, response);
  251 + }
  252 + }
  253 + });
  254 +
  255 + faceDetect.initAttrEmo(context, GlobalSet.ATTRIBUTE_MODEL, GlobalSet.EMOTION_MODEL, new Callback() {
  256 + @Override
  257 + public void onResponse(int code, String response) {
  258 + if (code != 0 && listener != null) {
  259 + listener.initModelFail(code, response);
  260 + }
  261 + }
  262 + });
  263 +
  264 + faceLiveness.initModel(context,
  265 + GlobalSet.LIVE_VIS_MODEL,
  266 + GlobalSet.LIVE_NIR_MODEL,
  267 + GlobalSet.LIVE_DEPTH_MODEL,
  268 + new Callback() {
  269 + @Override
  270 + public void onResponse(int code, String response) {
  271 + // ToastUtils.toast(context, code + " " + response);
  272 + if (code != 0 && listener != null) {
  273 + listener.initModelFail(code, response);
  274 + }
  275 + }
  276 + });
  277 +
  278 + faceMouthMask.initModel(context, GlobalSet.MOUTH_MASK, new Callback() {
  279 + @Override
  280 + public void onResponse(int code, String response) {
  281 + if (code != 0 && listener != null) {
  282 + listener.initModelFail(code, response);
  283 + }
  284 + }
  285 + });
  286 + faceFeature.initModel(context,
  287 + GlobalSet.RECOGNIZE_IDPHOTO_MODEL,
  288 + GlobalSet.RECOGNIZE_VIS_MODEL,
  289 + "",
  290 + new Callback() {
  291 + @Override
  292 + public void onResponse(int code, String response) {
  293 + long endInitModelTime = System.currentTimeMillis();
  294 + LogUtils.e(TIME_TAG, "init model time = " + (endInitModelTime - startInitModelTime));
  295 + if (code != 0) {
  296 + ToastUtils.toast(context, "模型加载失败");
  297 + if (listener != null) {
  298 + listener.initModelFail(code, response);
  299 + }
  300 + } else {
  301 + initStatus = SDK_MODEL_LOAD_SUCCESS;
  302 + // 模型初始化成功,加载人脸数据
  303 + initDataBases(context);
  304 + ToastUtils.toast(context, "模型加载完毕,欢迎使用");
  305 + if (listener != null) {
  306 + listener.initModelSuccess();
  307 + }
  308 + }
  309 + }
  310 + });
  311 + }
  312 +
  313 +
  314 + /**
  315 + * 初始化配置
  316 + *
  317 + * @return
  318 + */
  319 + public boolean initConfig() {
  320 + if (faceDetect != null) {
  321 + BDFaceSDKConfig config = new BDFaceSDKConfig();
  322 + // TODO: 最小人脸个数检查,默认设置为1,用户根据自己需求调整
  323 + config.maxDetectNum = 1;
  324 +
  325 + // TODO: 默认为80px。可传入大于30px的数值,小于此大小的人脸不予检测,生效时间第一次加载模型
  326 + config.minFaceSize = SingleBaseConfig.getBaseConfig().getMinimumFace();
  327 +
  328 + // 是否进行属性检测,默认关闭
  329 + config.isAttribute = SingleBaseConfig.getBaseConfig().isAttribute();
  330 +//
  331 +// // TODO: 模糊,遮挡,光照三个质量检测和姿态角查默认关闭,如果要开启,设置页启动
  332 +// config.isCheckBlur = config.isOcclusion
  333 +// = config.isIllumination = config.isHeadPose
  334 +// = SingleBaseConfig.getBaseConfig().isQualityControl();
  335 +
  336 + bdFaceDetectListConfig = new BDFaceDetectListConf();
  337 + bdFaceDetectListConfig.usingQuality = bdFaceDetectListConfig.usingHeadPose
  338 + = SingleBaseConfig.getBaseConfig().isQualityControl();
  339 + bdFaceDetectListConfig.usingAttribute = SingleBaseConfig.getBaseConfig().isAttribute();
  340 +
  341 + bdFaceDetectListConfig.usingEyeClose = false;
  342 + bdFaceDetectListConfig.usingMouthClose = false;
  343 +
  344 + faceDetect.loadConfig(config);
  345 + return true;
  346 + }
  347 + return false;
  348 + }
  349 +
  350 + public void initDataBases(Context context) {
  351 + // 初始化数据库
  352 + DBManager.getInstance().init(context);
  353 + // 数据变化,更新内存
  354 + FaceApi.getInstance().initDatabases(true);
  355 + }
  356 +
  357 +
  358 + /**
  359 + * 检测-活体-特征-人脸检索流程
  360 + *
  361 + * @param rgbData 可见光YUV 数据流
  362 + * @param nirData 红外YUV 数据流
  363 + * @param depthData 深度depth 数据流
  364 + * @param srcHeight 可见光YUV 数据流-高度
  365 + * @param srcWidth 可见光YUV 数据流-宽度
  366 + * @param liveCheckMode 活体检测类型【不使用活体:1】;【RGB活体:2】;【RGB+NIR活体:3】;【RGB+Depth活体:4】
  367 + * @param faceDetectCallBack
  368 + */
  369 + public void onDetectCheck(final byte[] rgbData,
  370 + final byte[] nirData,
  371 + final byte[] depthData,
  372 + final int srcHeight,
  373 + final int srcWidth,
  374 + final int liveCheckMode,
  375 + final FaceDetectCallBack faceDetectCallBack) {
  376 +
  377 + // 【【提取特征+1:N检索:3】
  378 + onDetectCheck(rgbData, nirData, depthData, srcHeight, srcWidth, liveCheckMode, 3, faceDetectCallBack);
  379 + }
  380 +
  381 +
  382 + /**
  383 + * 检测-活体-特征- 全流程
  384 + *
  385 + * @param rgbData 可见光YUV 数据流
  386 + * @param nirData 红外YUV 数据流
  387 + * @param depthData 深度depth 数据流
  388 + * @param srcHeight 可见光YUV 数据流-高度
  389 + * @param srcWidth 可见光YUV 数据流-宽度
  390 + * @param liveCheckMode 活体检测模式【不使用活体:1】;【RGB活体:2】;【RGB+NIR活体:3】;【RGB+Depth活体:4】
  391 + * @param featureCheckMode 特征抽取模式【不提取特征:1】;【提取特征:2】;【提取特征+1:N检索:3】;
  392 + * @param faceDetectCallBack
  393 + */
  394 + public void onDetectCheck(final byte[] rgbData,
  395 + final byte[] nirData,
  396 + final byte[] depthData,
  397 + final int srcHeight,
  398 + final int srcWidth,
  399 + final int liveCheckMode,
  400 + final int featureCheckMode,
  401 + final FaceDetectCallBack faceDetectCallBack) {
  402 +
  403 + if (future != null && !future.isDone()) {
  404 + return;
  405 + }
  406 +
  407 + future = es.submit(new Runnable() {
  408 + @Override
  409 + public void run() {
  410 + long startTime = System.currentTimeMillis();
  411 + // 创建检测结果存储数据
  412 + LivenessModel livenessModel = new LivenessModel();
  413 + // 创建检测对象,如果原始数据YUV,转为算法检测的图片BGR
  414 + // TODO: 用户调整旋转角度和是否镜像,手机和开发版需要动态适配
  415 + BDFaceImageInstance rgbInstance;
  416 + if (SingleBaseConfig.getBaseConfig().getType() == 4
  417 + && SingleBaseConfig.getBaseConfig().getCameraType() == 6) {
  418 + rgbInstance = new BDFaceImageInstance(rgbData, srcHeight,
  419 + srcWidth, BDFaceSDKCommon.BDFaceImageType.BDFACE_IMAGE_TYPE_RGB,
  420 + SingleBaseConfig.getBaseConfig().getDetectDirection(),
  421 + SingleBaseConfig.getBaseConfig().getMirrorRGB());
  422 + } else {
  423 + rgbInstance = new BDFaceImageInstance(rgbData, srcHeight,
  424 + srcWidth, BDFaceSDKCommon.BDFaceImageType.BDFACE_IMAGE_TYPE_YUV_NV21,
  425 + SingleBaseConfig.getBaseConfig().getDetectDirection(),
  426 + SingleBaseConfig.getBaseConfig().getMirrorRGB());
  427 + }
  428 +
  429 + // TODO: getImage() 获取送检图片,如果检测数据有问题,可以通过image view 展示送检图片
  430 + livenessModel.setBdFaceImageInstance(rgbInstance.getImage());
  431 +
  432 + // 检查函数调用,返回检测结果
  433 + long startDetectTime = System.currentTimeMillis();
  434 +
  435 + // 快速检测获取人脸信息,仅用于绘制人脸框,详细人脸数据后续获取
  436 + FaceInfo[] faceInfos = FaceSDKManager.getInstance().getFaceDetect()
  437 + .track(BDFaceSDKCommon.DetectType.DETECT_VIS,
  438 + BDFaceSDKCommon.AlignType.BDFACE_ALIGN_TYPE_RGB_FAST, rgbInstance);
  439 + livenessModel.setRgbDetectDuration(System.currentTimeMillis() - startDetectTime);
  440 + LogUtils.e(TIME_TAG, "detect vis time = " + livenessModel.getRgbDetectDuration());
  441 +
  442 + // 检测结果判断
  443 + if (faceInfos != null && faceInfos.length > 0) {
  444 + livenessModel.setTrackFaceInfo(faceInfos);
  445 + livenessModel.setFaceInfo(faceInfos[0]);
  446 + livenessModel.setLandmarks(faceInfos[0].landmarks);
  447 + if (faceDetectCallBack != null) {
  448 + faceDetectCallBack.onFaceDetectDarwCallback(livenessModel);
  449 + }
  450 +
  451 + onLivenessCheck(rgbInstance, nirData, depthData, srcHeight,
  452 + srcWidth, livenessModel.getLandmarks(),
  453 + livenessModel, startTime, liveCheckMode, featureCheckMode,
  454 + faceDetectCallBack, faceInfos);
  455 +
  456 + } else {
  457 + // 流程结束销毁图片,开始下一帧图片检测,否着内存泄露
  458 + rgbInstance.destory();
  459 + if (faceDetectCallBack != null) {
  460 + faceDetectCallBack.onFaceDetectCallback(null);
  461 + faceDetectCallBack.onFaceDetectDarwCallback(null);
  462 + faceDetectCallBack.onTip(0, "未检测到人脸");
  463 + }
  464 + }
  465 + }
  466 + });
  467 + }
  468 +
  469 +
  470 + /**
  471 + * 质量检测结果过滤,如果需要质量检测,
  472 + * 需要调用 SingleBaseConfig.getBaseConfig().setQualityControl(true);设置为true,
  473 + * 再调用 FaceSDKManager.getInstance().initConfig() 加载到底层配置项中
  474 + *
  475 + * @param livenessModel
  476 + * @param faceDetectCallBack
  477 + * @return
  478 + */
  479 + public boolean onQualityCheck(final LivenessModel livenessModel,
  480 + final FaceDetectCallBack faceDetectCallBack) {
  481 +
  482 + if (!SingleBaseConfig.getBaseConfig().isQualityControl()) {
  483 + return true;
  484 + }
  485 +
  486 + if (livenessModel != null && livenessModel.getFaceInfo() != null) {
  487 +
  488 + // 角度过滤
  489 + if (Math.abs(livenessModel.getFaceInfo().yaw) > SingleBaseConfig.getBaseConfig().getYaw()) {
  490 + faceDetectCallBack.onTip(-1, "人脸左右偏转角超出限制");
  491 + return false;
  492 + } else if (Math.abs(livenessModel.getFaceInfo().roll) > SingleBaseConfig.getBaseConfig().getRoll()) {
  493 + faceDetectCallBack.onTip(-1, "人脸平行平面内的头部旋转角超出限制");
  494 + return false;
  495 + } else if (Math.abs(livenessModel.getFaceInfo().pitch) > SingleBaseConfig.getBaseConfig().getPitch()) {
  496 + faceDetectCallBack.onTip(-1, "人脸上下偏转角超出限制");
  497 + return false;
  498 + }
  499 +
  500 + // 模糊结果过滤
  501 + float blur = livenessModel.getFaceInfo().bluriness;
  502 + if (blur > SingleBaseConfig.getBaseConfig().getBlur()) {
  503 + faceDetectCallBack.onTip(-1, "图片模糊");
  504 + return false;
  505 + }
  506 +
  507 + // 光照结果过滤
  508 + float illum = livenessModel.getFaceInfo().illum;
  509 + if (illum < SingleBaseConfig.getBaseConfig().getIllumination()) {
  510 + faceDetectCallBack.onTip(-1, "图片光照不通过");
  511 + return false;
  512 + }
  513 +
  514 +
  515 + // 遮挡结果过滤
  516 + if (livenessModel.getFaceInfo().occlusion != null) {
  517 + BDFaceOcclusion occlusion = livenessModel.getFaceInfo().occlusion;
  518 +
  519 + if (occlusion.leftEye > SingleBaseConfig.getBaseConfig().getLeftEye()) {
  520 + // 左眼遮挡置信度
  521 + faceDetectCallBack.onTip(-1, "左眼遮挡");
  522 + } else if (occlusion.rightEye > SingleBaseConfig.getBaseConfig().getRightEye()) {
  523 + // 右眼遮挡置信度
  524 + faceDetectCallBack.onTip(-1, "右眼遮挡");
  525 + } else if (occlusion.nose > SingleBaseConfig.getBaseConfig().getNose()) {
  526 + // 鼻子遮挡置信度
  527 + faceDetectCallBack.onTip(-1, "鼻子遮挡");
  528 + } else if (occlusion.mouth > SingleBaseConfig.getBaseConfig().getMouth()) {
  529 + // 嘴巴遮挡置信度
  530 + faceDetectCallBack.onTip(-1, "嘴巴遮挡");
  531 + } else if (occlusion.leftCheek > SingleBaseConfig.getBaseConfig().getLeftCheek()) {
  532 + // 左脸遮挡置信度
  533 + faceDetectCallBack.onTip(-1, "左脸遮挡");
  534 + } else if (occlusion.rightCheek > SingleBaseConfig.getBaseConfig().getRightCheek()) {
  535 + // 右脸遮挡置信度
  536 + faceDetectCallBack.onTip(-1, "右脸遮挡");
  537 + } else if (occlusion.chin > SingleBaseConfig.getBaseConfig().getChinContour()) {
  538 + // 下巴遮挡置信度
  539 + faceDetectCallBack.onTip(-1, "下巴遮挡");
  540 + } else {
  541 + return true;
  542 + }
  543 + }
  544 + }
  545 + return false;
  546 + }
  547 +
  548 +
  549 + /**
  550 + * 活体-特征-人脸检索全流程
  551 + *
  552 + * @param rgbInstance 可见光底层送检对象
  553 + * @param nirData 红外YUV 数据流
  554 + * @param depthData 深度depth 数据流
  555 + * @param srcHeight 可见光YUV 数据流-高度
  556 + * @param srcWidth 可见光YUV 数据流-宽度
  557 + * @param landmark 检测眼睛,嘴巴,鼻子,72个关键点
  558 + * @param livenessModel 检测结果数据集合
  559 + * @param startTime 开始检测时间
  560 + * @param liveCheckMode 活体检测模式【不使用活体:1】;【RGB活体:2】;【RGB+NIR活体:3】;【RGB+Depth活体:4】
  561 + * @param featureCheckMode 特征抽取模式【不提取特征:1】;【提取特征:2】;【提取特征+1:N检索:3】;
  562 + * @param faceDetectCallBack
  563 + */
  564 + public void onLivenessCheck(final BDFaceImageInstance rgbInstance,
  565 + final byte[] nirData,
  566 + final byte[] depthData,
  567 + final int srcHeight,
  568 + final int srcWidth,
  569 + final float[] landmark,
  570 + final LivenessModel livenessModel,
  571 + final long startTime,
  572 + final int liveCheckMode,
  573 + final int featureCheckMode,
  574 + final FaceDetectCallBack faceDetectCallBack,
  575 + final FaceInfo[] fastFaceInfos) {
  576 +
  577 + if (future2 != null && !future2.isDone()) {
  578 + // 流程结束销毁图片,开始下一帧图片检测,否着内存泄露
  579 + rgbInstance.destory();
  580 + return;
  581 + }
  582 +
  583 + future2 = es2.submit(new Runnable() {
  584 + @Override
  585 + public void run() {
  586 + FaceInfo[] faceInfos = FaceSDKManager.getInstance()
  587 + .getFaceDetect()
  588 + .detect(BDFaceSDKCommon.DetectType.DETECT_VIS,
  589 + BDFaceSDKCommon.AlignType.BDFACE_ALIGN_TYPE_RGB_ACCURATE,
  590 + rgbInstance,
  591 + fastFaceInfos, bdFaceDetectListConfig);
  592 +
  593 + // 重新赋予详细人脸信息
  594 + if (faceInfos != null && faceInfos.length > 0) {
  595 + livenessModel.setTrackFaceInfo(faceInfos);
  596 + livenessModel.setFaceInfo(faceInfos[0]);
  597 + livenessModel.setLandmarks(faceInfos[0].landmarks);
  598 +
  599 + scores = faceMouthMask.checkMask(rgbInstance, faceInfos);
  600 + if (scores != null) {
  601 + Log.e("FaceMouthMask", scores[0] + "");
  602 + }
  603 +
  604 + } else {
  605 + rgbInstance.destory();
  606 + return;
  607 + }
  608 +
  609 + // 质量检测未通过,销毁BDFaceImageInstance,结束函数
  610 + if (!onQualityCheck(livenessModel, faceDetectCallBack)) {
  611 + rgbInstance.destory();
  612 + return;
  613 + }
  614 +
  615 + // 获取LivenessConfig liveCheckMode 配置选项:【不使用活体:1】;【RGB活体:2】;【RGB+NIR活体:3】;【RGB+Depth活体:4】
  616 + // TODO 活体检测
  617 + float rgbScore = -1;
  618 + if (liveCheckMode != 1) {
  619 + long startRgbTime = System.currentTimeMillis();
  620 + rgbScore = FaceSDKManager.getInstance().getFaceLiveness().silentLive(
  621 + BDFaceSDKCommon.LiveType.BDFACE_SILENT_LIVE_TYPE_RGB,
  622 + rgbInstance, faceInfos[0].landmarks);
  623 + livenessModel.setRgbLivenessScore(rgbScore);
  624 + livenessModel.setRgbLivenessDuration(System.currentTimeMillis() - startRgbTime);
  625 + LogUtils.e(TIME_TAG, "live rgb time = " + livenessModel.getRgbLivenessDuration());
  626 + }
  627 +
  628 + float nirScore = -1;
  629 + if (liveCheckMode == 3 && nirData != null) {
  630 + // 创建检测对象,如果原始数据YUV-IR,转为算法检测的图片BGR
  631 + // TODO: 用户调整旋转角度和是否镜像,手机和开发版需要动态适配
  632 + BDFaceImageInstance nirInstance = new BDFaceImageInstance(nirData, srcHeight,
  633 + srcWidth, BDFaceSDKCommon.BDFaceImageType.BDFACE_IMAGE_TYPE_YUV_NV21,
  634 + SingleBaseConfig.getBaseConfig().getDetectDirection(),
  635 + SingleBaseConfig.getBaseConfig().getMirrorNIR());
  636 +
  637 + // 避免RGB检测关键点在IR对齐活体稳定,增加红外检测
  638 + long startIrDetectTime = System.currentTimeMillis();
  639 +// FaceInfo[] faceInfosIr = faceDetectNir.detect(BDFaceSDKCommon.DetectType.DETECT_NIR,
  640 +// nirInstance);
  641 + BDFaceDetectListConf bdFaceDetectListConf = new BDFaceDetectListConf();
  642 + bdFaceDetectListConf.usingDetect = true;
  643 + bdFaceDetectListConf.usingAlign = true;
  644 + FaceInfo[] faceInfosIr = faceDetectNir.detect(BDFaceSDKCommon.DetectType.DETECT_NIR,
  645 + BDFaceSDKCommon.AlignType.BDFACE_ALIGN_TYPE_NIR_ACCURATE,
  646 + nirInstance,
  647 + null, bdFaceDetectListConf);
  648 + bdFaceDetectListConf.usingDetect = false;
  649 +
  650 + livenessModel.setIrLivenessDuration(System.currentTimeMillis() - startIrDetectTime);
  651 + LogUtils.e(TIME_TAG, "detect ir time = " + livenessModel.getIrLivenessDuration());
  652 +
  653 + if (faceInfosIr != null && faceInfosIr.length > 0) {
  654 + FaceInfo faceInfoIr = faceInfosIr[0];
  655 + long startNirTime = System.currentTimeMillis();
  656 + nirScore = FaceSDKManager.getInstance().getFaceLiveness().silentLive(
  657 + BDFaceSDKCommon.LiveType.BDFACE_SILENT_LIVE_TYPE_NIR,
  658 + nirInstance, faceInfoIr.landmarks);
  659 + livenessModel.setIrLivenessScore(nirScore);
  660 + livenessModel.setIrLivenessDuration(System.currentTimeMillis() - startNirTime);
  661 + LogUtils.e(TIME_TAG, "live ir time = " + livenessModel.getIrLivenessDuration());
  662 + }
  663 +
  664 + nirInstance.destory();
  665 + }
  666 +
  667 + float depthScore = -1;
  668 + if (liveCheckMode == 4 && depthData != null) {
  669 + // TODO: 用户调整旋转角度和是否镜像,适配Atlas 镜头,目前宽和高400*640,其他摄像头需要动态调整,人脸72 个关键点x 坐标向左移动80个像素点
  670 + float[] depthLandmark = new float[faceInfos[0].landmarks.length];
  671 + BDFaceImageInstance depthInstance;
  672 + if (SingleBaseConfig.getBaseConfig().getCameraType() == 2) {
  673 + System.arraycopy(faceInfos[0].landmarks, 0, depthLandmark, 0, faceInfos[0].landmarks.length);
  674 + if (SingleBaseConfig.getBaseConfig().getCameraType() == 2) {
  675 + for (int i = 0; i < 144; i = i + 2) {
  676 + depthLandmark[i] -= 80;
  677 + }
  678 + }
  679 + depthInstance = new BDFaceImageInstance(depthData,
  680 + SingleBaseConfig.getBaseConfig().getDepthWidth(),
  681 + SingleBaseConfig.getBaseConfig().getDepthHeight(),
  682 + BDFaceSDKCommon.BDFaceImageType.BDFACE_IMAGE_TYPE_DEPTH,
  683 + 0, 0);
  684 + } else {
  685 + depthInstance = new BDFaceImageInstance(depthData,
  686 + SingleBaseConfig.getBaseConfig().getDepthHeight(),
  687 + SingleBaseConfig.getBaseConfig().getDepthWidth(),
  688 + BDFaceSDKCommon.BDFaceImageType.BDFACE_IMAGE_TYPE_DEPTH,
  689 + 0, 0);
  690 + }
  691 +
  692 + // 创建检测对象,如果原始数据Depth
  693 + long startDepthTime = System.currentTimeMillis();
  694 + if (SingleBaseConfig.getBaseConfig().getCameraType() == 2) {
  695 + depthScore = FaceSDKManager.getInstance().getFaceLiveness().silentLive(
  696 + BDFaceSDKCommon.LiveType.BDFACE_SILENT_LIVE_TYPE_DEPTH,
  697 + depthInstance, depthLandmark);
  698 + } else {
  699 + depthScore = FaceSDKManager.getInstance().getFaceLiveness().silentLive(
  700 + BDFaceSDKCommon.LiveType.BDFACE_SILENT_LIVE_TYPE_DEPTH,
  701 + depthInstance, faceInfos[0].landmarks);
  702 + }
  703 + livenessModel.setDepthLivenessScore(depthScore);
  704 + livenessModel.setDepthtLivenessDuration(System.currentTimeMillis() - startDepthTime);
  705 + LogUtils.e(TIME_TAG, "live depth time = " + livenessModel.getDepthtLivenessDuration());
  706 + depthInstance.destory();
  707 + }
  708 +
  709 + // TODO 特征提取+人脸检索
  710 + if (liveCheckMode == 1) {
  711 + onFeatureCheck(rgbInstance, faceInfos[0].landmarks, livenessModel, featureCheckMode);
  712 + } else {
  713 + if (liveCheckMode == 2 && rgbScore > SingleBaseConfig.getBaseConfig().getRgbLiveScore()) {
  714 + onFeatureCheck(rgbInstance, faceInfos[0].landmarks, livenessModel, featureCheckMode);
  715 + } else if (liveCheckMode == 3 && rgbScore > SingleBaseConfig.getBaseConfig().getRgbLiveScore()
  716 + && nirScore > SingleBaseConfig.getBaseConfig().getNirLiveScore()) {
  717 + onFeatureCheck(rgbInstance, faceInfos[0].landmarks, livenessModel, featureCheckMode);
  718 + } else if (liveCheckMode == 4 && rgbScore > SingleBaseConfig.getBaseConfig().getRgbLiveScore()
  719 + && depthScore > SingleBaseConfig.getBaseConfig().getDepthLiveScore()) {
  720 + onFeatureCheck(rgbInstance, faceInfos[0].landmarks, livenessModel, featureCheckMode);
  721 + }
  722 + }
  723 +
  724 + // 流程结束,记录最终时间
  725 + livenessModel.setAllDetectDuration(System.currentTimeMillis() - startTime);
  726 + LogUtils.e(TIME_TAG, "all process time = " + livenessModel.getAllDetectDuration());
  727 + // 流程结束销毁图片,开始下一帧图片检测,否着内存泄露
  728 + rgbInstance.destory();
  729 + // 显示最终结果提示
  730 + if (faceDetectCallBack != null) {
  731 + faceDetectCallBack.onFaceDetectCallback(livenessModel);
  732 + }
  733 + }
  734 + });
  735 + }
  736 +
  737 + /**
  738 + * 特征提取-人脸识别比对
  739 + *
  740 + * @param rgbInstance 可见光底层送检对象
  741 + * @param landmark 检测眼睛,嘴巴,鼻子,72个关键点
  742 + * @param livenessModel 检测结果数据集合
  743 + * @param featureCheckMode 特征抽取模式【不提取特征:1】;【提取特征:2】;【提取特征+1:N检索:3】;
  744 + */
  745 + public void onFeatureCheck(BDFaceImageInstance rgbInstance,
  746 + float[] landmark,
  747 + LivenessModel livenessModel,
  748 + final int featureCheckMode) {
  749 +
  750 + // 如果不抽取特征,直接返回
  751 + if (featureCheckMode == 1) {
  752 + return;
  753 + }
  754 +
  755 + byte[] feature = new byte[512];
  756 + long startFeatureTime = System.currentTimeMillis();
  757 + float featureSize = FaceSDKManager.getInstance().getFaceFeature().feature(
  758 + BDFaceSDKCommon.FeatureType.BDFACE_FEATURE_TYPE_LIVE_PHOTO, rgbInstance, landmark, feature);
  759 + livenessModel.setFeatureDuration(System.currentTimeMillis() - startFeatureTime);
  760 + LogUtils.e(TIME_TAG, "feature live time = " + livenessModel.getFeatureDuration());
  761 + livenessModel.setFeature(feature);
  762 +
  763 + // 如果只提去特征,不做检索,此处返回
  764 + if (featureCheckMode == 2) {
  765 + livenessModel.setFeatureCode(featureSize);
  766 + return;
  767 + }
  768 +
  769 + // 如果提取特征+检索,调用search 方法
  770 + if (featureSize == FEATURE_SIZE / 4) {
  771 + // 特征提取成功
  772 + // TODO 阈值可以根据不同模型调整
  773 + long startFeature = System.currentTimeMillis();
  774 + ArrayList<Feature> featureResult = FaceSDKManager.getInstance().getFaceFeature().featureSearch(feature,
  775 + BDFaceSDKCommon.FeatureType.BDFACE_FEATURE_TYPE_LIVE_PHOTO,
  776 + 1, true);
  777 + // TODO 返回top num = 1 个数据集合,此处可以任意设置,会返回比对从大到小排序的num 个数据集合
  778 + if (featureResult != null && featureResult.size() > 0) {
  779 + // 获取第一个数据
  780 + Feature topFeature = featureResult.get(0);
  781 + // 判断第一个阈值是否大于设定阈值,如果大于,检索成功
  782 + if (topFeature != null && topFeature.getScore() >
  783 + SingleBaseConfig.getBaseConfig().getThreshold()) {
  784 + // 当前featureEntity 只有id+feature 索引,在数据库中查到完整信息
  785 + User user = FaceApi.getInstance().getUserListById(topFeature.getId());
  786 + if (user != null) {
  787 + livenessModel.setUser(user);
  788 + livenessModel.setFeatureScore(topFeature.getScore());
  789 + }
  790 + }
  791 + }
  792 + livenessModel.setCheckDuration(System.currentTimeMillis() - startFeature);
  793 + LogUtils.e(TIME_TAG, "feature search time = " + livenessModel.getCheckDuration());
  794 + }
  795 + }
  796 +
  797 + /**
  798 + * 单独调用 特征提取
  799 + *
  800 + * @param imageInstance 可见光底层送检对象
  801 + * @param landmark 检测眼睛,嘴巴,鼻子,72个关键点
  802 + * @param featureCheckMode 特征提取模式
  803 + * @param faceFeatureCallBack 回掉方法
  804 + */
  805 + public void onFeatureCheck(BDFaceImageInstance imageInstance, float[] landmark,
  806 + BDFaceSDKCommon.FeatureType featureCheckMode,
  807 + final FaceFeatureCallBack faceFeatureCallBack) {
  808 +
  809 + BDFaceImageInstance rgbInstance = new BDFaceImageInstance(imageInstance.data,
  810 + imageInstance.height, imageInstance.width,
  811 + imageInstance.imageType, 0, 0);
  812 +
  813 + byte[] feature = new byte[512];
  814 + float featureSize = FaceSDKManager.getInstance().getFaceFeature().feature(
  815 + featureCheckMode, rgbInstance, landmark, feature);
  816 + if (featureSize == FEATURE_SIZE / 4) {
  817 + // 特征提取成功
  818 + if (faceFeatureCallBack != null) {
  819 + faceFeatureCallBack.onFaceFeatureCallBack(featureSize, feature);
  820 + }
  821 +
  822 + }
  823 + // 流程结束销毁图片
  824 + rgbInstance.destory();
  825 + }
  826 +}
  1 +package com.baidu.idl.main.facesdk.manager;
  2 +
  3 +
  4 +import com.baidu.idl.main.facesdk.FaceInfo;
  5 +import com.baidu.idl.main.facesdk.callback.FaceDetectCallBack;
  6 +import com.baidu.idl.main.facesdk.model.BDFaceImageInstance;
  7 +import com.baidu.idl.main.facesdk.model.BDFaceOcclusion;
  8 +import com.baidu.idl.main.facesdk.model.BDFaceSDKCommon;
  9 +import com.baidu.idl.main.facesdk.model.LivenessModel;
  10 +import com.baidu.idl.main.facesdk.model.SingleBaseConfig;
  11 +
  12 +import java.util.concurrent.ExecutorService;
  13 +import java.util.concurrent.Executors;
  14 +import java.util.concurrent.Future;
  15 +
  16 +/**
  17 + * Time: 2019/1/25
  18 + * Author: v_chaixiaogang
  19 + * rgb单目检测管理类
  20 + */
  21 +public class FaceTrackManager {
  22 +
  23 + private static volatile FaceTrackManager instance = null;
  24 +
  25 + private ExecutorService es;
  26 + private Future future;
  27 + private boolean isAliving;
  28 + private boolean isQualityControl = false;
  29 + private boolean isDetect = true;
  30 +
  31 + private FaceDetectCallBack mFaceDetectCallBack;
  32 +
  33 + public FaceDetectCallBack getFaceDetectCallBack() {
  34 + return mFaceDetectCallBack;
  35 + }
  36 +
  37 + public void setFaceDetectCallBack(FaceDetectCallBack faceDetectCallBack) {
  38 + mFaceDetectCallBack = faceDetectCallBack;
  39 + }
  40 +
  41 + public boolean isQualityControl() {
  42 + return isQualityControl;
  43 + }
  44 +
  45 + public void setQualityControl(boolean qualityControl) {
  46 + isQualityControl = qualityControl;
  47 + }
  48 +
  49 + public boolean isAliving() {
  50 + return isAliving;
  51 + }
  52 +
  53 + public void setAliving(boolean aliving) {
  54 + isAliving = aliving;
  55 + }
  56 +
  57 + public boolean isDetect() {
  58 + return isDetect;
  59 + }
  60 +
  61 + public void setDetect(boolean detect) {
  62 + isDetect = detect;
  63 + }
  64 +
  65 + public FaceTrackManager() {
  66 + es = Executors.newSingleThreadExecutor();
  67 + }
  68 +
  69 + public static FaceTrackManager getInstance() {
  70 + synchronized (FaceTrackManager.class) {
  71 + if (instance == null) {
  72 + instance = new FaceTrackManager();
  73 + }
  74 + }
  75 + return instance;
  76 + }
  77 +
  78 + public void faceTrack(final byte[] argb, final int width, final int height,
  79 + final FaceDetectCallBack faceDetectCallBack) {
  80 +
  81 + if (future != null && !future.isDone()) {
  82 + return;
  83 + }
  84 + future = es.submit(new Runnable() {
  85 + @Override
  86 + public void run() {
  87 + faceDataDetect(argb, width, height, faceDetectCallBack);
  88 + }
  89 + });
  90 + }
  91 +
  92 + /**
  93 + * 人脸检测
  94 + *
  95 + * @param argb
  96 + * @param width
  97 + * @param height
  98 + * @param faceDetectCallBack
  99 + */
  100 + private void faceDataDetect(final byte[] argb, int width, int height, FaceDetectCallBack faceDetectCallBack) {
  101 + LivenessModel livenessModel = new LivenessModel();
  102 +
  103 + BDFaceImageInstance rgbInstance;
  104 + if (SingleBaseConfig.getBaseConfig().getType() == 4 && SingleBaseConfig.getBaseConfig().getCameraType() == 6) {
  105 + rgbInstance = new BDFaceImageInstance(argb, height, width,
  106 + BDFaceSDKCommon.BDFaceImageType.BDFACE_IMAGE_TYPE_RGB,
  107 + SingleBaseConfig.getBaseConfig().getDetectDirection(),
  108 + SingleBaseConfig.getBaseConfig().getMirrorRGB());
  109 + } else {
  110 + rgbInstance = new BDFaceImageInstance(argb, height, width,
  111 + BDFaceSDKCommon.BDFaceImageType.BDFACE_IMAGE_TYPE_YUV_NV21,
  112 + SingleBaseConfig.getBaseConfig().getDetectDirection(),
  113 + SingleBaseConfig.getBaseConfig().getMirrorRGB());
  114 + }
  115 + livenessModel.setBdFaceImageInstance(rgbInstance);
  116 + long startTime = System.currentTimeMillis();
  117 + FaceInfo[] faceInfos = FaceSDKManager.getInstance().getFaceDetect()
  118 + .track(BDFaceSDKCommon.DetectType.DETECT_VIS, rgbInstance);
  119 + livenessModel.setRgbDetectDuration(System.currentTimeMillis() - startTime);
  120 + // getImage() 获取送检图片
  121 + livenessModel.setBdFaceImageInstance(rgbInstance.getImage());
  122 +
  123 + if (faceInfos != null && faceInfos.length > 0) {
  124 + livenessModel.setTrackFaceInfo(faceInfos);
  125 + FaceInfo faceInfo = faceInfos[0];
  126 + livenessModel.setFaceInfo(faceInfo);
  127 + livenessModel.setLandmarks(faceInfo.landmarks);
  128 + // 质量检测,针对模糊度、遮挡、角度
  129 + if (onQualityCheck(livenessModel, faceDetectCallBack)) {
  130 + // 活体检测
  131 + if (isAliving) {
  132 + startTime = System.currentTimeMillis();
  133 + float rgbScore = FaceSDKManager.getInstance().getFaceLiveness().silentLive(
  134 + BDFaceSDKCommon.LiveType.BDFACE_SILENT_LIVE_TYPE_RGB,
  135 + rgbInstance, faceInfo.landmarks);
  136 + livenessModel.setRgbLivenessScore(rgbScore);
  137 + livenessModel.setRgbLivenessDuration(System.currentTimeMillis() - startTime);
  138 + }
  139 + // 流程结束销毁图片,开始下一帧图片检测,否着内存泄露
  140 + rgbInstance.destory();
  141 + if (faceDetectCallBack != null) {
  142 + faceDetectCallBack.onFaceDetectCallback(livenessModel);
  143 + }
  144 + } else {
  145 + // 流程结束销毁图片,开始下一帧图片检测,否着内存泄露
  146 + rgbInstance.destory();
  147 + }
  148 +
  149 + } else {
  150 + // 流程结束销毁图片,开始下一帧图片检测,否着内存泄露
  151 + rgbInstance.destory();
  152 + if (faceDetectCallBack != null) {
  153 + faceDetectCallBack.onTip(0, "未检测到人脸");
  154 + faceDetectCallBack.onFaceDetectCallback(null);
  155 + }
  156 + }
  157 + }
  158 +
  159 +
  160 + /**
  161 + * 质量检测
  162 + *
  163 + * @param livenessModel
  164 + * @param faceDetectCallBack
  165 + * @return
  166 + */
  167 + public boolean onQualityCheck(final LivenessModel livenessModel,
  168 + final FaceDetectCallBack faceDetectCallBack) {
  169 +
  170 + if (!SingleBaseConfig.getBaseConfig().isQualityControl()) {
  171 + return true;
  172 + }
  173 +
  174 + if (livenessModel != null && livenessModel.getFaceInfo() != null) {
  175 +
  176 + // 角度过滤
  177 + if (Math.abs(livenessModel.getFaceInfo().yaw) > SingleBaseConfig.getBaseConfig().getYaw()) {
  178 + faceDetectCallBack.onTip(-1, "人脸左右偏转角超出限制");
  179 + return false;
  180 + } else if (Math.abs(livenessModel.getFaceInfo().roll) > SingleBaseConfig.getBaseConfig().getRoll()) {
  181 + faceDetectCallBack.onTip(-1, "人脸平行平面内的头部旋转角超出限制");
  182 + return false;
  183 + } else if (Math.abs(livenessModel.getFaceInfo().pitch) > SingleBaseConfig.getBaseConfig().getPitch()) {
  184 + faceDetectCallBack.onTip(-1, "人脸上下偏转角超出限制");
  185 + return false;
  186 + }
  187 +
  188 +
  189 + // 模糊结果过滤
  190 + float blur = livenessModel.getFaceInfo().bluriness;
  191 + if (blur > SingleBaseConfig.getBaseConfig().getBlur()) {
  192 + faceDetectCallBack.onTip(-1, "图片模糊");
  193 + return false;
  194 + }
  195 +
  196 + // 光照结果过滤
  197 + float illum = livenessModel.getFaceInfo().illum;
  198 + if (illum < SingleBaseConfig.getBaseConfig().getIllumination()) {
  199 + faceDetectCallBack.onTip(-1, "图片光照不通过");
  200 + return false;
  201 + }
  202 +
  203 + // 遮挡结果过滤
  204 + if (livenessModel.getFaceInfo().occlusion != null) {
  205 + BDFaceOcclusion occlusion = livenessModel.getFaceInfo().occlusion;
  206 +
  207 + if (occlusion.leftEye > SingleBaseConfig.getBaseConfig().getLeftEye()) {
  208 + // 左眼遮挡置信度
  209 + faceDetectCallBack.onTip(-1, "左眼遮挡");
  210 + } else if (occlusion.rightEye > SingleBaseConfig.getBaseConfig().getRightEye()) {
  211 + // 右眼遮挡置信度
  212 + faceDetectCallBack.onTip(-1, "右眼遮挡");
  213 + } else if (occlusion.nose > SingleBaseConfig.getBaseConfig().getNose()) {
  214 + // 鼻子遮挡置信度
  215 + faceDetectCallBack.onTip(-1, "鼻子遮挡");
  216 + } else if (occlusion.mouth > SingleBaseConfig.getBaseConfig().getMouth()) {
  217 + // 嘴巴遮挡置信度
  218 + faceDetectCallBack.onTip(-1, "嘴巴遮挡");
  219 + } else if (occlusion.leftCheek > SingleBaseConfig.getBaseConfig().getLeftCheek()) {
  220 + // 左脸遮挡置信度
  221 + faceDetectCallBack.onTip(-1, "左脸遮挡");
  222 + } else if (occlusion.rightCheek > SingleBaseConfig.getBaseConfig().getRightCheek()) {
  223 + // 右脸遮挡置信度
  224 + faceDetectCallBack.onTip(-1, "右脸遮挡");
  225 + } else if (occlusion.chin > SingleBaseConfig.getBaseConfig().getChinContour()) {
  226 + // 下巴遮挡置信度
  227 + faceDetectCallBack.onTip(-1, "下巴遮挡");
  228 + } else {
  229 + return true;
  230 + }
  231 + }
  232 + }
  233 + return false;
  234 + }
  235 +}
  1 +package com.baidu.idl.main.facesdk.model;
  2 +
  3 +/**
  4 + * author : shangrong
  5 + * date : 2019/5/22 9:10 PM
  6 + * description :配置文件
  7 + */
  8 +public class BaseConfig {
  9 + // 设备通信密码
  10 + private String dPass = "";
  11 + // RGB检测帧回显
  12 + private Boolean display = true;
  13 + // RGB预览Y轴转向falese为0,true为180
  14 + private Boolean rgbRevert = false;
  15 + // NIR或depth实时视频预览
  16 + private Boolean isNirOrDepth = true;
  17 + // 默认为false。可选项为"true"、"false",是否开启调试显示,将会作用到所有视频流识别页面,包含1:N、1:1采集人脸图片环节。
  18 + private boolean debug = false;
  19 + // 默认为0。可传入0、90、180、270四个选项。
  20 + private int videoDirection = 270;
  21 + // 默认为wireframe。可选项为"wireframe"、"fixedarea",如选择fixed_area,需要传入半径,px像素为单位
  22 + private String detectFrame = "wireframe";
  23 + // 当选择fixed_area,需要传入半径信息,以px为单位,如50px
  24 +// private int radius = 50;
  25 + // 默认为0。可传入0、90、180、270四个选项
  26 + private int detectDirection = 90;
  27 + // 默认为max。分为"max" 、"none"三个方式,分别是最大人脸 ,和不检测人脸
  28 + private String trackType = "max";
  29 + // 默认为80px。可传入大于50px的数值,小于此大小的人脸不予检测
  30 + private int minimumFace = 60;
  31 + // 模糊度设置,默认0.5。取值范围[0~1],0是最清晰,1是最模糊
  32 + private float blur = 0.5f;
  33 + // 光照设置,默认40.取值范围[0~255], 数值越大,光线越强
  34 + private int illumination = 40;
  35 + // 姿态阈值
  36 + private float gesture = 15;
  37 + // 三维旋转之俯仰角度[-90(上), 90(下)],默认20
  38 + private float pitch = 20;
  39 + // 平面内旋转角[-180(逆时针), 180(顺时针)],默认20
  40 + private float roll = 20;
  41 + // 三维旋转之左右旋转角[-90(左), 90(右)],默认20
  42 + private float yaw = 20;
  43 + // 遮挡阈值
  44 + private float occlusion = 0.6f;
  45 + // 左眼被遮挡的阈值,默认0.6
  46 + private float leftEye = 0.6f;
  47 + // 右眼被遮挡的阈值,默认0.6
  48 + private float rightEye = 0.6f;
  49 + // 鼻子被遮挡的阈值,默认0.7
  50 + private float nose = 0.7f;
  51 + // 嘴巴被遮挡的阈值,默认0.7
  52 + private float mouth = 0.7f;
  53 + // 左脸颊被遮挡的阈值,默认0.8
  54 + private float leftCheek = 0.8f;
  55 + // 右脸颊被遮挡的阈值,默认0.8
  56 + private float rightCheek = 0.8f;
  57 + // 下巴被遮挡阈值,默认为0.6
  58 + private float chinContour = 0.6f;
  59 + // 人脸完整度,默认为1。0为人脸溢出图像边界,1为人脸都在图像边界内
  60 + private float completeness = 1f;
  61 + // 识别阈值,0-100,默认为80分,需要选择具体模型的阈值。live:80、idcard:80
  62 + private int threshold = 80;
  63 + // 使用的特征抽取模型默认为生活照:1;证件照:2
  64 + private int activeModel = 1;
  65 + // 识别结果出来后的演示展示,默认为0ms
  66 + private int timeLapse = 0;
  67 + // 活体选择模式,默认为不使用活体,"1"
  68 + //不使用活体:1
  69 + //RGB活体:2
  70 + //RGB+NIR活体:3
  71 + //RGB+Depth活体:4
  72 + private int type = 1;
  73 + // 是否开启质量检测开关
  74 + private boolean qualityControl = false;
  75 + // RGB活体阀值
  76 + private float rgbLiveScore = 0.90f;
  77 + // NIR活体阀值
  78 + private float nirLiveScore = 0.90f;
  79 +
  80 + // Depth活体阀值
  81 + private float depthLiveScore = 0.90f;
  82 +
  83 + // 0:奥比中光Astra Mini、Mini S系列(结构光)
  84 + // 1:奥比中光 Astra Pro 、Pro S 、蝴蝶(结构光)
  85 + // 2:奥比中光Atlas(结构光)
  86 + // 3:奥比中光大白、海燕(结构光)
  87 + // 4:奥比中光Deeyea(结构光)
  88 + // 5:华捷艾米A100S、A200(结构光)
  89 + // 6:Pico DCAM710(ToF)
  90 + private int cameraType = 1;
  91 +
  92 + // 0:RGB无镜像,1:有镜像
  93 + private int mirrorRGB = 1;
  94 + // 0:NIR无镜像,1:有镜像
  95 + private int mirrorNIR = 0;
  96 +
  97 + // 是否开启属性检测
  98 + private boolean attribute = false;
  99 +
  100 + // rgb和nir摄像头宽
  101 + private int rgbAndNirWidth = 640;
  102 + // rgb和nir摄像头高
  103 + private int rgbAndNirHeight = 480;
  104 + // depth摄像头宽
  105 + private int depthWidth = 640;
  106 + // depth摄像头高
  107 + private int depthHeight = 480;
  108 +
  109 + public int getRgbAndNirWidth() {
  110 + return rgbAndNirWidth;
  111 + }
  112 +
  113 + public void setRgbAndNirWidth(int rgbAndNirWidth) {
  114 + this.rgbAndNirWidth = rgbAndNirWidth;
  115 + }
  116 +
  117 + public int getRgbAndNirHeight() {
  118 + return rgbAndNirHeight;
  119 + }
  120 +
  121 + public void setRgbAndNirHeight(int rgbAndNirHeight) {
  122 + this.rgbAndNirHeight = rgbAndNirHeight;
  123 + }
  124 +
  125 + public int getDepthWidth() {
  126 + return depthWidth;
  127 + }
  128 +
  129 + public void setDepthWidth(int depthWidth) {
  130 + this.depthWidth = depthWidth;
  131 + }
  132 +
  133 + public int getDepthHeight() {
  134 + return depthHeight;
  135 + }
  136 +
  137 + public void setDepthHeight(int depthHeight) {
  138 + this.depthHeight = depthHeight;
  139 + }
  140 +
  141 + public int getCameraType() {
  142 + return cameraType;
  143 + }
  144 +
  145 + public void setCameraType(int cameraType) {
  146 + this.cameraType = cameraType;
  147 + }
  148 +
  149 +
  150 + public float getRgbLiveScore() {
  151 + return rgbLiveScore;
  152 + }
  153 +
  154 + public void setRgbLiveScore(float rgbLiveScore) {
  155 + this.rgbLiveScore = rgbLiveScore;
  156 + }
  157 +
  158 + public float getNirLiveScore() {
  159 + return nirLiveScore;
  160 + }
  161 +
  162 + public void setNirLiveScore(float nirLiveScore) {
  163 + this.nirLiveScore = nirLiveScore;
  164 + }
  165 +
  166 + public float getDepthLiveScore() {
  167 + return depthLiveScore;
  168 + }
  169 +
  170 + public void setDepthLiveScore(float depthLiveScore) {
  171 + this.depthLiveScore = depthLiveScore;
  172 + }
  173 +
  174 + public String getdPass() {
  175 + return dPass;
  176 + }
  177 +
  178 + public void setdPass(String dPass) {
  179 + this.dPass = dPass;
  180 + }
  181 +
  182 + public boolean isDebug() {
  183 + return debug;
  184 + }
  185 +
  186 + public void setDebug(boolean debug) {
  187 + this.debug = debug;
  188 + }
  189 +
  190 + public int getVideoDirection() {
  191 + return videoDirection;
  192 + }
  193 +
  194 + public void setVideoDirection(int videoDirection) {
  195 + this.videoDirection = videoDirection;
  196 + }
  197 +
  198 + public String getDetectFrame() {
  199 + return detectFrame;
  200 + }
  201 +
  202 + public void setDetectFrame(String detectFrame) {
  203 + this.detectFrame = detectFrame;
  204 + }
  205 +
  206 +// public int getRadius() {
  207 +// return radius;
  208 +// }
  209 +//
  210 +// public void setRadius(int radius) {
  211 +// this.radius = radius;
  212 +// }
  213 +
  214 + public int getDetectDirection() {
  215 + return detectDirection;
  216 + }
  217 +
  218 + public void setDetectDirection(int detectDirection) {
  219 + this.detectDirection = detectDirection;
  220 + }
  221 +
  222 + public String getTrackType() {
  223 + return trackType;
  224 + }
  225 +
  226 + public void setTrackType(String trackType) {
  227 + this.trackType = trackType;
  228 + }
  229 +
  230 + public int getMinimumFace() {
  231 + return minimumFace;
  232 + }
  233 +
  234 + public void setMinimumFace(int minimumFace) {
  235 + this.minimumFace = minimumFace;
  236 + }
  237 +
  238 + public float getBlur() {
  239 + return blur;
  240 + }
  241 +
  242 + public void setBlur(float blur) {
  243 + this.blur = blur;
  244 + }
  245 +
  246 + public int getIllumination() {
  247 + return illumination;
  248 + }
  249 +
  250 + public void setIllumination(int illumination) {
  251 + this.illumination = illumination;
  252 + }
  253 +
  254 + public float getGesture() {
  255 + return gesture;
  256 + }
  257 +
  258 + public void setGesture(float gesture) {
  259 + this.gesture = gesture;
  260 + }
  261 +
  262 + public float getPitch() {
  263 + return pitch;
  264 + }
  265 +
  266 + public void setPitch(float pitch) {
  267 + this.pitch = pitch;
  268 + }
  269 +
  270 + public float getRoll() {
  271 + return roll;
  272 + }
  273 +
  274 + public void setRoll(float roll) {
  275 + this.roll = roll;
  276 + }
  277 +
  278 + public float getYaw() {
  279 + return yaw;
  280 + }
  281 +
  282 + public void setYaw(float yaw) {
  283 + this.yaw = yaw;
  284 + }
  285 +
  286 + public float getOcclusion() {
  287 + return occlusion;
  288 + }
  289 +
  290 + public void setOcclusion(float occlusion) {
  291 + this.occlusion = occlusion;
  292 + }
  293 +
  294 + public float getLeftEye() {
  295 + return leftEye;
  296 + }
  297 +
  298 + public void setLeftEye(float leftEye) {
  299 + this.leftEye = leftEye;
  300 + }
  301 +
  302 + public float getRightEye() {
  303 + return rightEye;
  304 + }
  305 +
  306 + public void setRightEye(float rightEye) {
  307 + this.rightEye = rightEye;
  308 + }
  309 +
  310 + public float getNose() {
  311 + return nose;
  312 + }
  313 +
  314 + public void setNose(float nose) {
  315 + this.nose = nose;
  316 + }
  317 +
  318 + public float getMouth() {
  319 + return mouth;
  320 + }
  321 +
  322 + public void setMouth(float mouth) {
  323 + this.mouth = mouth;
  324 + }
  325 +
  326 + public float getLeftCheek() {
  327 + return leftCheek;
  328 + }
  329 +
  330 + public void setLeftCheek(float leftCheek) {
  331 + this.leftCheek = leftCheek;
  332 + }
  333 +
  334 + public float getRightCheek() {
  335 + return rightCheek;
  336 + }
  337 +
  338 + public void setRightCheek(float rightCheek) {
  339 + this.rightCheek = rightCheek;
  340 + }
  341 +
  342 + public float getChinContour() {
  343 + return chinContour;
  344 + }
  345 +
  346 + public void setChinContour(float chinContour) {
  347 + this.chinContour = chinContour;
  348 + }
  349 +
  350 + public float getCompleteness() {
  351 + return completeness;
  352 + }
  353 +
  354 + public void setCompleteness(float completeness) {
  355 + this.completeness = completeness;
  356 + }
  357 +
  358 + public int getThreshold() {
  359 + return threshold;
  360 + }
  361 +
  362 + public void setThreshold(int threshold) {
  363 + this.threshold = threshold;
  364 + }
  365 +
  366 + public int getActiveModel() {
  367 + return activeModel;
  368 + }
  369 +
  370 + public void setActiveModel(int activeModel) {
  371 + this.activeModel = activeModel;
  372 + }
  373 +
  374 + public int getTimeLapse() {
  375 + return timeLapse;
  376 + }
  377 +
  378 + public void setTimeLapse(int timeLapse) {
  379 + this.timeLapse = timeLapse;
  380 + }
  381 +
  382 + public int getType() {
  383 + return type;
  384 + }
  385 +
  386 + public void setType(int type) {
  387 + this.type = type;
  388 + }
  389 +
  390 + public boolean isQualityControl() {
  391 + return qualityControl;
  392 + }
  393 +
  394 + public void setQualityControl(boolean qualityControl) {
  395 + this.qualityControl = qualityControl;
  396 + }
  397 +
  398 + public Boolean getDisplay() {
  399 + return display;
  400 + }
  401 +
  402 + public void setDisplay(Boolean display) {
  403 + this.display = display;
  404 + }
  405 +
  406 + public Boolean getNirOrDepth() {
  407 + return isNirOrDepth;
  408 + }
  409 +
  410 + public void setNirOrDepth(Boolean nirOrDepth) {
  411 + isNirOrDepth = nirOrDepth;
  412 + }
  413 +
  414 + public int getMirrorRGB() {
  415 + return mirrorRGB;
  416 + }
  417 +
  418 + public void setMirrorRGB(int mirrorRGB) {
  419 + this.mirrorRGB = mirrorRGB;
  420 + }
  421 +
  422 + public int getMirrorNIR() {
  423 + return mirrorNIR;
  424 + }
  425 +
  426 + public void setMirrorNIR(int mirrorNIR) {
  427 + this.mirrorNIR = mirrorNIR;
  428 + }
  429 +
  430 + public Boolean getRgbRevert() {
  431 + return rgbRevert;
  432 + }
  433 +
  434 + public void setRgbRevert(Boolean rgbRevert) {
  435 + this.rgbRevert = rgbRevert;
  436 + }
  437 +
  438 +
  439 + public boolean isAttribute() {
  440 + return attribute;
  441 + }
  442 +
  443 + public void setAttribute(boolean attribute) {
  444 + this.attribute = attribute;
  445 + }
  446 +}
  1 +package com.baidu.idl.main.facesdk.model;
  2 +
  3 +/**
  4 + * @Time: 2019/5/24
  5 + * @Author: v_zhangxiaoqing01
  6 + */
  7 +
  8 +public class GlobalSet {
  9 +
  10 + // 模型在asset 下path 为空
  11 + public static final String PATH = "";
  12 + // 模型在SD 卡下写对应的绝对路径
  13 + // public static final String PATH = "/storage/emulated/0/baidu_face/model/";
  14 +
  15 + public static final int FEATURE_SIZE = 512;
  16 +
  17 + public static final String TIME_TAG = "face_time";
  18 +
  19 + // 遮罩比例
  20 + public static final float SURFACE_RATIO = 0.6f;
  21 +
  22 + public static final String ACTIVATE_KEY = "faceexample-face-android-1";
  23 + public static final String LICENSE_FILE_NAME = "idl-license.face-android";
  24 +
  25 + public static final String DETECT_VIS_MODEL = PATH
  26 + + "detect/detect_rgb-customized-pa-faceid4_0.model.int8-0.0.6.1";
  27 + public static final String DETECT_NIR_MODE = PATH
  28 + + "detect/detect_nir-faceboxes-pa-faceid4_0.model.int8-0.0.4.2";
  29 + public static final String ALIGN_RGB_MODEL = PATH
  30 + + "align/align_rgb-customized-ca-paddle_6_4_0_1.model.float32-6.4.0.2";
  31 + public static final String ALIGN_NIR_MODEL = PATH
  32 + + "align/align-mobilenet-pa-nir_faceid4_0.model.int8-0.7.4.2";
  33 + public static final String ALIGN_TRACK_MODEL = PATH
  34 + + "align/align-customized-pa-mobile.model.float32-0.7.5.2";
  35 + public static final String LIVE_VIS_MODEL = PATH
  36 + + "silent_live/liveness_rgb-customized-pa-mobile.model.float32-4.1.10.1";
  37 + public static final String LIVE_NIR_MODEL = PATH
  38 + + "silent_live/liveness_nir-customized-pa-paddle_lite.model.int8-4.1.5.1";
  39 + public static final String LIVE_DEPTH_MODEL = PATH
  40 + + "silent_live/liveness_depth-customized-pa-autodl_mobile.model.int8-4.1.7.1.lite";
  41 + public static final String RECOGNIZE_VIS_MODEL = PATH
  42 + + "feature/feature_live-mnasnet-pa-mnasv4_int8.model.int8-1.0.27.1";
  43 + public static final String RECOGNIZE_IDPHOTO_MODEL = PATH
  44 + + "feature/feature_id-mnasnet-pa-dynamic_norm.model.float32-1.0.16.1";
  45 + public static final String RECOGNIZE_NIR_MODEL = PATH
  46 + + "feature/feature_nir-customized-pa-nir_91p5.model.float32-1.0.11.2";
  47 + public static final String RECOGNIZE_RGBD_MODEL = PATH
  48 + + "feature/feature_rgbd-mnasnet-pa-RGBD98p54.model.float32-1.0.15.2";
  49 + public static final String OCCLUSION_MODEL = PATH
  50 + + "occlusion/occlusion-customized-pa-mobile.model.float32-2.0.4.1";
  51 + public static final String BLUR_MODEL = PATH
  52 + + "blur/blur-customized-pa-server.model.float32-3.0.2.1.lite";
  53 +
  54 + public static final String ATTRIBUTE_MODEL = PATH
  55 + + "attribute/attribute-customized-pa-mobile.model.float32-1.0.9.2";
  56 + public static final String EMOTION_MODEL = PATH
  57 + + "";
  58 + public static final String GAZE_MODEL = PATH
  59 + + "gaze/gaze-customized-pa-mobile.model.float32.lite";
  60 + public static final String MOUTH_MASK = PATH
  61 + + "mouth_mask/mouth_mask-customized-pa-paddlie_lite.model.float32-1.0.0.1";
  62 + // 图片尺寸限制大小
  63 + public static final int PICTURE_SIZE = 1000000;
  64 +
  65 + // 摄像头类型
  66 + public static final String TYPE_CAMERA = "TYPE_CAMERA";
  67 + public static final int ORBBEC = 1;
  68 + public static final int IMIMECT = 2;
  69 + public static final int ORBBECPRO = 3;
  70 + public static final int ORBBECPROS1 = 4;
  71 + public static final int ORBBECPRODABAI = 5;
  72 + public static final int ORBBECPRODEEYEA = 6;
  73 + public static final int ORBBECATLAS = 7;
  74 +
  75 +}
  1 +/*
  2 + * Copyright (C) 2018 Baidu, Inc. All Rights Reserved.
  3 + */
  4 +package com.baidu.idl.main.facesdk.model;
  5 +
  6 +/**
  7 + * 组实体类
  8 + */
  9 +public class Group {
  10 + private String groupId = "";
  11 + private String desc = "";
  12 + private long ctime;
  13 +
  14 + private boolean isChecked; // 用于多选框
  15 +
  16 + public Group() {
  17 + }
  18 +
  19 + public String getGroupId() {
  20 + return groupId;
  21 + }
  22 +
  23 + public void setGroupId(String groupId) {
  24 + this.groupId = groupId;
  25 + }
  26 +
  27 + public String getDesc() {
  28 + return desc;
  29 + }
  30 +
  31 + public void setDesc(String desc) {
  32 + this.desc = desc;
  33 + }
  34 +
  35 + public long getCtime() {
  36 + return ctime;
  37 + }
  38 +
  39 + public void setCtime(long ctime) {
  40 + this.ctime = ctime;
  41 + }
  42 +
  43 + public boolean isChecked() {
  44 + return isChecked;
  45 + }
  46 +
  47 + public void setChecked(boolean checked) {
  48 + isChecked = checked;
  49 + }
  50 +}
  1 +/*
  2 + * Copyright (C) 2018 Baidu, Inc. All Rights Reserved.
  3 + */
  4 +package com.baidu.idl.main.facesdk.model;
  5 +
  6 +
  7 +import com.baidu.idl.main.facesdk.FaceInfo;
  8 +
  9 +public class LivenessModel {
  10 +
  11 + private int faceDetectCode;
  12 + private FaceInfo faceInfo;
  13 + private long rgbDetectDuration;
  14 + private long irDetectDuration;
  15 + private long rgbLivenessDuration;
  16 + private float irLivenessScore;
  17 + private long irLivenessDuration;
  18 + private long depthtLivenessDuration;
  19 + private float rgbLivenessScore;
  20 + private float depthLivenessScore;
  21 + private int liveType;
  22 + private float[] landmarks;
  23 + private byte[] feature;
  24 +
  25 + private float featureScore;
  26 + private float featureCode;
  27 + private long featureDuration;
  28 + private long checkDuration;
  29 + private BDFaceImageInstance bdFaceImageInstance;
  30 +
  31 + private User user;
  32 +
  33 + private int[] shape;
  34 +
  35 + private FaceInfo[] trackFaceInfo;
  36 + private long allDetectDuration;
  37 +
  38 + public BDFaceImageInstance getBdFaceImageInstance() {
  39 + return bdFaceImageInstance;
  40 + }
  41 +
  42 + public void setBdFaceImageInstance(BDFaceImageInstance bdFaceImageInstance) {
  43 + this.bdFaceImageInstance = bdFaceImageInstance;
  44 + }
  45 +
  46 + public long getAllDetectDuration() {
  47 + return allDetectDuration;
  48 + }
  49 +
  50 + public void setAllDetectDuration(long allDetectDuration) {
  51 + this.allDetectDuration = allDetectDuration;
  52 + }
  53 +
  54 + public byte[] getFeature() {
  55 + return feature;
  56 + }
  57 +
  58 + public void setFeature(byte[] feature) {
  59 + this.feature = feature;
  60 + }
  61 +
  62 + public float[] getLandmarks() {
  63 + return landmarks;
  64 + }
  65 +
  66 + public void setLandmarks(float[] landmarks) {
  67 + this.landmarks = landmarks;
  68 + }
  69 +
  70 + public int[] getShape() {
  71 + return shape;
  72 + }
  73 +
  74 + public void setShape(int[] shape) {
  75 + this.shape = shape;
  76 + }
  77 +
  78 +
  79 + public FaceInfo[] getTrackFaceInfo() {
  80 + return trackFaceInfo;
  81 + }
  82 +
  83 + public void setTrackFaceInfo(FaceInfo[] trackFaceInfo) {
  84 + this.trackFaceInfo = trackFaceInfo;
  85 + }
  86 +
  87 +
  88 + public int getFaceDetectCode() {
  89 + return faceDetectCode;
  90 + }
  91 +
  92 + public void setFaceDetectCode(int faceDetectCode) {
  93 + this.faceDetectCode = faceDetectCode;
  94 + }
  95 +
  96 + public FaceInfo getFaceInfo() {
  97 + return faceInfo;
  98 + }
  99 +
  100 + public void setFaceInfo(FaceInfo faceInfo) {
  101 + this.faceInfo = faceInfo;
  102 + }
  103 +
  104 + public long getRgbDetectDuration() {
  105 + return rgbDetectDuration;
  106 + }
  107 +
  108 + public void setRgbDetectDuration(long rgbDetectDuration) {
  109 + this.rgbDetectDuration = rgbDetectDuration;
  110 + }
  111 +
  112 + public long getIrDetectDuration() {
  113 + return irDetectDuration;
  114 + }
  115 +
  116 + public void setIrDetectDuration(long irDetectDuration) {
  117 + this.irDetectDuration = irDetectDuration;
  118 + }
  119 +
  120 + public float getRgbLivenessDuration() {
  121 + return rgbLivenessDuration;
  122 + }
  123 +
  124 + public void setRgbLivenessDuration(long rgbLivenessDuration) {
  125 + this.rgbLivenessDuration = rgbLivenessDuration;
  126 + }
  127 +
  128 + public float getIrLivenessScore() {
  129 + return irLivenessScore;
  130 + }
  131 +
  132 + public void setIrLivenessScore(float irLivenessScore) {
  133 + this.irLivenessScore = irLivenessScore;
  134 + }
  135 +
  136 + public long getIrLivenessDuration() {
  137 + return irLivenessDuration;
  138 + }
  139 +
  140 + public void setIrLivenessDuration(long irLivenessDuration) {
  141 + this.irLivenessDuration = irLivenessDuration;
  142 + }
  143 +
  144 + public long getDepthtLivenessDuration() {
  145 + return depthtLivenessDuration;
  146 + }
  147 +
  148 + public void setDepthtLivenessDuration(long depthtLivenessDuration) {
  149 + this.depthtLivenessDuration = depthtLivenessDuration;
  150 + }
  151 +
  152 + public float getRgbLivenessScore() {
  153 + return rgbLivenessScore;
  154 + }
  155 +
  156 + public void setRgbLivenessScore(float rgbLivenessScore) {
  157 + this.rgbLivenessScore = rgbLivenessScore;
  158 + }
  159 +
  160 + public float getDepthLivenessScore() {
  161 + return depthLivenessScore;
  162 + }
  163 +
  164 + public void setDepthLivenessScore(float depthLivenessScore) {
  165 + this.depthLivenessScore = depthLivenessScore;
  166 + }
  167 +
  168 + public int getLiveType() {
  169 + return liveType;
  170 + }
  171 +
  172 + public void setLiveType(int liveType) {
  173 + this.liveType = liveType;
  174 + }
  175 +
  176 + public float getFeatureScore() {
  177 + return featureScore;
  178 + }
  179 +
  180 + public void setFeatureScore(float featureScore) {
  181 + this.featureScore = featureScore;
  182 + }
  183 +
  184 + public long getFeatureDuration() {
  185 + return featureDuration;
  186 + }
  187 +
  188 + public void setFeatureDuration(long featureDuration) {
  189 + this.featureDuration = featureDuration;
  190 + }
  191 +
  192 + public long getCheckDuration() {
  193 + return checkDuration;
  194 + }
  195 +
  196 + public void setCheckDuration(long checkDuration) {
  197 + this.checkDuration = checkDuration;
  198 + }
  199 +
  200 + public User getUser() {
  201 + return user;
  202 + }
  203 +
  204 + public void setUser(User user) {
  205 + this.user = user;
  206 + }
  207 +
  208 + public float getFeatureCode() {
  209 + return featureCode;
  210 + }
  211 +
  212 + public void setFeatureCode(float featureCode) {
  213 + this.featureCode = featureCode;
  214 + }
  215 +}
  216 +
  217 +
  1 +package com.baidu.idl.main.facesdk.model;
  2 +
  3 +public class ResponseGetRecords {
  4 + // 设备指纹
  5 + private String deviceId;
  6 + // 记录详细内容
  7 + private String records;
  8 + // 记录的唯一记录
  9 + private String logId;
  10 + // 用户id
  11 + private String userId;
  12 + // 识别分数
  13 + private String score;
  14 + // 具体识别结果,时间戳形式,如:1529485386691
  15 + private String time;
  16 + // 用户名
  17 + private String userName;
  18 + // 用户所在组
  19 + private String groupId;
  20 +
  21 + private String faceToken;
  22 + // 所在序列
  23 + private String index;
  24 +
  25 + public String getIndex() {
  26 + return index;
  27 + }
  28 +
  29 + public void setIndex(String index) {
  30 + this.index = index;
  31 + }
  32 +
  33 + public String getUserName() {
  34 + return userName;
  35 + }
  36 +
  37 + public void setUserName(String userName) {
  38 + this.userName = userName;
  39 + }
  40 +
  41 + public String getGroupId() {
  42 + return groupId;
  43 + }
  44 +
  45 + public void setGroupId(String groupId) {
  46 + this.groupId = groupId;
  47 + }
  48 +
  49 + public String getFaceToken() {
  50 + return faceToken;
  51 + }
  52 +
  53 + public void setFaceToken(String faceToken) {
  54 + this.faceToken = faceToken;
  55 + }
  56 +
  57 + public String getDeviceId() {
  58 + return deviceId;
  59 + }
  60 +
  61 + public void setDeviceId(String deviceId) {
  62 + this.deviceId = deviceId;
  63 + }
  64 +
  65 + public String getRecords() {
  66 + return records;
  67 + }
  68 +
  69 + public void setRecords(String records) {
  70 + this.records = records;
  71 + }
  72 +
  73 + public String getLogId() {
  74 + return logId;
  75 + }
  76 +
  77 + public void setLogId(String logId) {
  78 + this.logId = logId;
  79 + }
  80 +
  81 + public String getUserId() {
  82 + return userId;
  83 + }
  84 +
  85 + public void setUserId(String userId) {
  86 + this.userId = userId;
  87 + }
  88 +
  89 + public String getScore() {
  90 + return score;
  91 + }
  92 +
  93 + public void setScore(String score) {
  94 + this.score = score;
  95 + }
  96 +
  97 +
  98 + public String getTime() {
  99 + return time;
  100 + }
  101 +
  102 + public void setTime(String time) {
  103 + this.time = time;
  104 + }
  105 +}
  1 +package com.baidu.idl.main.facesdk.model;
  2 +
  3 +/**
  4 + * author : shangrong
  5 + * date : 2019/5/23 11:23 AM
  6 + * description :配置BaseConfig单例
  7 + */
  8 +public class SingleBaseConfig {
  9 + private static BaseConfig baseConfig;
  10 +
  11 + private SingleBaseConfig() {
  12 +
  13 + }
  14 +
  15 + public static BaseConfig getBaseConfig() {
  16 + if (baseConfig == null) {
  17 + baseConfig = new BaseConfig();
  18 + }
  19 + return baseConfig;
  20 + }
  21 +
  22 + public static void copyInstance(BaseConfig result) {
  23 + baseConfig = result;
  24 + }
  25 +}
  1 +/*
  2 + * Copyright (C) 2018 Baidu, Inc. All Rights Reserved.
  3 + */
  4 +package com.baidu.idl.main.facesdk.model;
  5 +
  6 +import android.util.Base64;
  7 +
  8 +/**
  9 + * 用户实体类
  10 + */
  11 +public class User {
  12 + private int id;
  13 + private String userId = "";
  14 + private String userName = "";
  15 + private String groupId = "";
  16 + private long ctime;
  17 + private long updateTime;
  18 +
  19 + private String userInfo = "";
  20 + private String faceToken = "";
  21 + private String imageName = "";
  22 + private byte[] feature;
  23 +
  24 + private boolean isChecked;
  25 +
  26 + public User() {
  27 + }
  28 +
  29 + public int getId() {
  30 + return id;
  31 + }
  32 +
  33 + public void setId(int id) {
  34 + this.id = id;
  35 + }
  36 +
  37 + public String getUserId() {
  38 + return userId;
  39 + }
  40 +
  41 + public void setUserId(String userId) {
  42 + this.userId = userId;
  43 + }
  44 +
  45 + public String getUserName() {
  46 + return userName;
  47 + }
  48 +
  49 + public void setUserName(String userName) {
  50 + this.userName = userName;
  51 + }
  52 +
  53 + public String getGroupId() {
  54 + return groupId;
  55 + }
  56 +
  57 + public void setGroupId(String groupId) {
  58 + this.groupId = groupId;
  59 + }
  60 +
  61 + public long getCtime() {
  62 + return ctime;
  63 + }
  64 +
  65 + public void setCtime(long ctime) {
  66 + this.ctime = ctime;
  67 + }
  68 +
  69 + public long getUpdateTime() {
  70 + return updateTime;
  71 + }
  72 +
  73 + public void setUpdateTime(long updateTime) {
  74 + this.updateTime = updateTime;
  75 + }
  76 +
  77 + public String getUserInfo() {
  78 + return userInfo;
  79 + }
  80 +
  81 + public void setUserInfo(String userInfo) {
  82 + this.userInfo = userInfo;
  83 + }
  84 +
  85 + public String getFaceToken() {
  86 + if (feature != null) {
  87 + byte[] base = Base64.encode(feature, Base64.NO_WRAP);
  88 + faceToken = new String(base);
  89 + }
  90 + return faceToken;
  91 + }
  92 +
  93 + public void setFaceToken(String faceToken) {
  94 + this.faceToken = faceToken;
  95 + }
  96 +
  97 + public String getImageName() {
  98 + return imageName;
  99 + }
  100 +
  101 + public void setImageName(String imageName) {
  102 + this.imageName = imageName;
  103 + }
  104 +
  105 + public byte[] getFeature() {
  106 + return feature;
  107 + }
  108 +
  109 + public void setFeature(byte[] feature) {
  110 + this.feature = feature;
  111 + }
  112 +
  113 + public boolean isChecked() {
  114 + return isChecked;
  115 + }
  116 +
  117 + public void setChecked(boolean checked) {
  118 + isChecked = checked;
  119 + }
  120 +}
  1 +/*
  2 + * Copyright (C) 2011 Baidu Inc. All rights reserved.
  3 + */
  4 +
  5 +package com.baidu.idl.main.facesdk.utils;
  6 +
  7 +import android.content.Context;
  8 +import android.graphics.Bitmap;
  9 +import android.graphics.BitmapFactory;
  10 +import android.graphics.Matrix;
  11 +import android.media.ExifInterface;
  12 +import android.net.Uri;
  13 +import android.util.Base64;
  14 +import android.util.Log;
  15 +
  16 +import com.baidu.idl.main.facesdk.model.BDFaceImageInstance;
  17 +import com.baidu.idl.main.facesdk.model.BDFaceSDKCommon;
  18 +
  19 +import java.io.BufferedInputStream;
  20 +import java.io.File;
  21 +import java.io.FileInputStream;
  22 +import java.io.FileNotFoundException;
  23 +import java.io.FileOutputStream;
  24 +import java.io.IOException;
  25 +import java.nio.ByteBuffer;
  26 +
  27 +/**
  28 + * 这个类提供一些操作Bitmap的方法
  29 + */
  30 +public final class BitmapUtils {
  31 + /**
  32 + * 图像的旋转方向是0
  33 + */
  34 + public static final int ROTATE0 = 0;
  35 + /**
  36 + * 图像的旋转方向是90
  37 + */
  38 + public static final int ROTATE90 = 90;
  39 + /**
  40 + * 图像的旋转方向是180
  41 + */
  42 + public static final int ROTATE180 = 180;
  43 + /**
  44 + * 图像的旋转方向是270
  45 + */
  46 + public static final int ROTATE270 = 270;
  47 + /**
  48 + * 图像的旋转方向是360
  49 + */
  50 + public static final int ROTATE360 = 360;
  51 + /**
  52 + * 图片太大内存溢出后压缩的比例
  53 + */
  54 + public static final int PIC_COMPRESS_SIZE = 4;
  55 + /**
  56 + * 图像压缩边界
  57 + */
  58 + public static final int IMAGEBOUND = 128;
  59 + /**
  60 + * 图片显示最大边的像素
  61 + */
  62 + public static final int MAXLENTH = 1024;
  63 + /**
  64 + * Log TAG
  65 + */
  66 + private static final String TAG = "ImageUtils";
  67 + /**
  68 + * Log switch
  69 + */
  70 + private static final boolean DEBUG = false;
  71 + /**
  72 + * 保存图片的质量:100
  73 + */
  74 + private static final int QUALITY = 100;
  75 + /**
  76 + * 默认的最大尺寸
  77 + */
  78 + private static final int DEFAULT_MAX_SIZE_CELL_NETWORK = 600;
  79 + /**
  80 + * 题编辑wifi环境下压缩的最大尺寸
  81 + */
  82 + private static final int QUESTION_MAX_SIZE_CELL_NETWORK = 1024;
  83 + /**
  84 + * 图片压缩的质量
  85 + */
  86 + private static final int QUESTION_IMAGE_JPG_QUALITY = 75;
  87 + /**
  88 + * 默认的图片压缩的质量
  89 + */
  90 + private static final int DEFAULT_IMAGE_JPG_QUALITY = 50;
  91 + /**
  92 + * 网络请求超时时间
  93 + */
  94 + private static final int CONNECTTIMEOUT = 3000;
  95 +
  96 + /**
  97 + * Private constructor to prohibit nonsense instance creation.
  98 + */
  99 + private BitmapUtils() {
  100 + }
  101 +
  102 + /**
  103 + * 得到要显示的图片数据
  104 + */
  105 + public static Bitmap createBitmap(byte[] data, float orientation) {
  106 + Bitmap bitmap = null;
  107 + Bitmap transformed = null;
  108 + BitmapFactory.Options opts = new BitmapFactory.Options();
  109 + try {
  110 +
  111 + int width = 2000;
  112 + int hight = 2000;
  113 + int min = Math.min(width, hight);
  114 + opts.inJustDecodeBounds = true;
  115 + BitmapFactory.decodeByteArray(data, 0, data.length, opts);
  116 + opts.inSampleSize =
  117 + BitmapUtils.computeSampleSize(opts, min, BitmapUtils.MAXLENTH * BitmapUtils.MAXLENTH);
  118 + opts.inJustDecodeBounds = false;
  119 + bitmap = BitmapFactory.decodeByteArray(data, 0, data.length, opts);
  120 + transformed = BitmapUtils.rotateBitmap(orientation, bitmap);
  121 + } catch (OutOfMemoryError e) {
  122 + e.printStackTrace();
  123 + if (bitmap != null && !bitmap.isRecycled()) {
  124 + bitmap.recycle();
  125 + bitmap = null;
  126 + }
  127 + if (transformed != null && !transformed.isRecycled()) {
  128 + transformed.recycle();
  129 + transformed = null;
  130 + }
  131 + opts.inJustDecodeBounds = true;
  132 + BitmapFactory.decodeByteArray(data, 0, data.length, opts);
  133 + opts.inSampleSize =
  134 + BitmapUtils.computeSampleSize(opts, -1, opts.outWidth * opts.outHeight
  135 + / BitmapUtils.PIC_COMPRESS_SIZE);
  136 + opts.inJustDecodeBounds = false;
  137 + bitmap = BitmapFactory.decodeByteArray(data, 0, data.length, opts);
  138 + transformed = BitmapUtils.rotateBitmap(orientation, bitmap);
  139 + }
  140 + if (transformed != bitmap && bitmap != null) {
  141 + bitmap.recycle();
  142 + bitmap = null;
  143 + }
  144 + return transformed;
  145 +
  146 + }
  147 +
  148 + /**
  149 + * 根据从数据中读到的方向旋转图片
  150 + *
  151 + * @param orientation 图片方向
  152 + * @param bitmap 要旋转的bitmap
  153 + * @return 旋转后的图片
  154 + */
  155 + public static Bitmap rotateBitmap(float orientation, Bitmap bitmap) {
  156 + Bitmap transformed;
  157 + Matrix m = new Matrix();
  158 + if (orientation == 0) {
  159 + transformed = bitmap;
  160 + } else {
  161 + m.setRotate(orientation);
  162 + transformed = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), m, true);
  163 + }
  164 + return transformed;
  165 + }
  166 +
  167 + /**
  168 + * 获取无损压缩图片合适的压缩比例
  169 + *
  170 + * @param options 图片的一些设置项
  171 + * @param minSideLength 最小边长
  172 + * @param maxNumOfPixels 最大的像素数目
  173 + * @return 返回合适的压缩值
  174 + */
  175 + public static int computeSampleSize(BitmapFactory.Options options, int minSideLength, int maxNumOfPixels) {
  176 + int initialSize = BitmapUtils.computeInitialSampleSize(options, minSideLength, maxNumOfPixels);
  177 + int roundedSize;
  178 + if (initialSize <= 8) { // SUPPRESS CHECKSTYLE
  179 + roundedSize = 1;
  180 + while (roundedSize < initialSize) {
  181 + roundedSize <<= 1;
  182 + }
  183 + } else {
  184 + roundedSize = (initialSize + 7) / 8 * 8; // SUPPRESS CHECKSTYLE
  185 + }
  186 + return roundedSize;
  187 + }
  188 +
  189 + /**
  190 + * 获取无损压缩图片的压缩比
  191 + *
  192 + * @param options 图片的一些设置项
  193 + * @param minSideLength 最小边长
  194 + * @param maxNumOfPixels 最大的像素数目
  195 + * @return 返回合适的压缩值
  196 + */
  197 + public static int computeInitialSampleSize(BitmapFactory.Options options, int minSideLength,
  198 + int maxNumOfPixels) {
  199 + double w = options.outWidth;
  200 + double h = options.outHeight;
  201 + int lowerBound = (maxNumOfPixels == -1) ? 1 : (int) Math.ceil(Math.sqrt(w * h / maxNumOfPixels));
  202 + int upperBound =
  203 + (minSideLength == -1) ? BitmapUtils.IMAGEBOUND : (int) Math.min(
  204 + Math.floor(w / minSideLength), Math.floor(h / minSideLength));
  205 + if (upperBound < lowerBound) {
  206 + return lowerBound;
  207 + }
  208 + if ((maxNumOfPixels == -1) && (minSideLength == -1)) {
  209 + return 1;
  210 + } else if (minSideLength == -1) {
  211 + return lowerBound;
  212 + } else {
  213 + return upperBound;
  214 + }
  215 + }
  216 +
  217 + /**
  218 + * 解析图片的旋转方向
  219 + *
  220 + * @param path 图片的路径
  221 + * @return 旋转角度
  222 + */
  223 + public static int decodeImageDegree(String path) {
  224 + int degree = ROTATE0;
  225 + try {
  226 + ExifInterface exifInterface = new ExifInterface(path);
  227 + int orientation =
  228 + exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION,
  229 + ExifInterface.ORIENTATION_NORMAL);
  230 + switch (orientation) {
  231 + case ExifInterface.ORIENTATION_ROTATE_90:
  232 + degree = ROTATE90;
  233 + break;
  234 + case ExifInterface.ORIENTATION_ROTATE_180:
  235 + degree = ROTATE180;
  236 + break;
  237 + case ExifInterface.ORIENTATION_ROTATE_270:
  238 + degree = ROTATE270;
  239 + break;
  240 + default:
  241 + degree = ROTATE0;
  242 + break;
  243 + }
  244 + } catch (Exception e) {
  245 + e.printStackTrace();
  246 + degree = ROTATE0;
  247 + }
  248 + return degree;
  249 + }
  250 +
  251 + /**
  252 + * 等比压缩图片
  253 + *
  254 + * @param bitmap 原图
  255 + * @param scale 压缩因子
  256 + * @return 压缩后的图片
  257 + */
  258 + private static Bitmap scale(Bitmap bitmap, float scale) {
  259 + Matrix matrix = new Matrix();
  260 + matrix.postScale(scale, scale);
  261 + return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
  262 + }
  263 +
  264 + /**
  265 + * 尺寸缩放
  266 + *
  267 + * @param bitmap bitmap
  268 + * @param w width
  269 + * @param h height
  270 + * @return scaleBitmap
  271 + */
  272 + public static Bitmap scale(Bitmap bitmap, int w, int h) {
  273 + if (bitmap == null) {
  274 + return null;
  275 + }
  276 + int width = bitmap.getWidth();
  277 + int height = bitmap.getHeight();
  278 + Matrix matrix = new Matrix();
  279 + float scaleWidth = ((float) w / width);
  280 + float scaleHeight = ((float) h / height);
  281 + matrix.postScale(scaleWidth, scaleHeight);
  282 + return Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true);
  283 +
  284 + }
  285 +
  286 + /**
  287 + * 等比压缩图片
  288 + *
  289 + * @param resBitmap 原图
  290 + * @param desWidth 压缩后图片的宽度
  291 + * @param desHeight 压缩后图片的高度
  292 + * @return 压缩后的图片
  293 + */
  294 + public static Bitmap calculateInSampleSize(Bitmap resBitmap, int desWidth, int desHeight) {
  295 + int resWidth = resBitmap.getWidth();
  296 + int resHeight = resBitmap.getHeight();
  297 + if (resHeight > desHeight || resWidth > desWidth) {
  298 + // 计算出实际宽高和目标宽高的比率
  299 + final float heightRatio = (float) desHeight / (float) resHeight;
  300 + final float widthRatio = (float) desWidth / (float) resWidth;
  301 + float scale = heightRatio < widthRatio ? heightRatio : widthRatio;
  302 + return scale(resBitmap, scale);
  303 + }
  304 + return resBitmap;
  305 + }
  306 +
  307 +
  308 + public static Uri saveTakePictureImage(Context context, byte[] data) {
  309 + File file = context.getExternalFilesDir("file1");
  310 + file = new File(file.getAbsolutePath() + File.separator + System.currentTimeMillis() + ".jpg");
  311 + FileOutputStream fout = null;
  312 + try {
  313 + fout = new FileOutputStream(file);
  314 + fout.write(data);
  315 + fout.flush();
  316 + } catch (Exception e) {
  317 + e.printStackTrace();
  318 +
  319 + // 异常时删除保存失败的文件
  320 + try {
  321 + if (file != null && file.exists() && file.isFile()) {
  322 + file.delete();
  323 + }
  324 + } catch (Exception ex) {
  325 + ex.printStackTrace();
  326 + }
  327 +
  328 + return null;
  329 + } finally {
  330 + if (fout != null) {
  331 + try {
  332 + fout.close();
  333 + } catch (IOException e) {
  334 + e.printStackTrace();
  335 + }
  336 + }
  337 + }
  338 + Log.e(TAG, file.getAbsolutePath());
  339 + return Uri.fromFile(file);
  340 + }
  341 +
  342 + public static Bitmap yuv2Bitmap(byte[] data, int width, int height) {
  343 + int frameSize = width * height;
  344 + int[] rgba = new int[frameSize];
  345 +
  346 + for (int i = 0; i < height; i++) {
  347 + for (int j = 0; j < width; j++) {
  348 + int y = (0xff & ((int) data[i * width + j]));
  349 + int u = (0xff & ((int) data[frameSize + (i >> 1) * width + (j & ~1) + 0]));
  350 + int v = (0xff & ((int) data[frameSize + (i >> 1) * width + (j & ~1) + 1]));
  351 + y = y < 16 ? 16 : y;
  352 +
  353 + int r = Math.round(1.164f * (y - 16) + 1.596f * (v - 128));
  354 + int g = Math.round(1.164f * (y - 16) - 0.813f * (v - 128) - 0.391f * (u - 128));
  355 + int b = Math.round(1.164f * (y - 16) + 2.018f * (u - 128));
  356 +
  357 + r = r < 0 ? 0 : (r > 255 ? 255 : r);
  358 + g = g < 0 ? 0 : (g > 255 ? 255 : g);
  359 + b = b < 0 ? 0 : (b > 255 ? 255 : b);
  360 +
  361 + rgba[i * width + j] = 0xff000000 + (b << 16) + (g << 8) + r;
  362 + }
  363 + }
  364 +
  365 + Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
  366 + bmp.setPixels(rgba, 0, width, 0, 0, width, height);
  367 + return bmp;
  368 + }
  369 +
  370 + public static Bitmap Depth2Bitmap(byte[] depthBytes, int width, int height) {
  371 + Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
  372 + int[] argbData = new int[width * height];
  373 + for (int i = 0; i < width * height; i++) {
  374 + argbData[i] = (((int) depthBytes[i * 2] + depthBytes[i * 2 + 1] * 256) / 10 & 0x000000ff)
  375 + | ((((int) depthBytes[i * 2] + depthBytes[i * 2 + 1] * 256) / 10) & 0x000000ff) << 8
  376 + | ((((int) depthBytes[i * 2] + depthBytes[i * 2 + 1] * 256) / 10) & 0x000000ff) << 16
  377 + | 0xff000000;
  378 +
  379 + }
  380 + bitmap.setPixels(argbData, 0, width, 0, 0, width, height);
  381 + return bitmap;
  382 + }
  383 +
  384 + public static Bitmap RGB2Bitmap(byte[] bytes, int width, int height) {
  385 + // use Bitmap.Config.ARGB_8888 instead of type is OK
  386 + Bitmap stitchBmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
  387 + byte[] rgba = new byte[width * height * 4];
  388 + for (int i = 0; i < width * height; i++) {
  389 + byte b1 = bytes[i * 3 + 0];
  390 + byte b2 = bytes[i * 3 + 1];
  391 + byte b3 = bytes[i * 3 + 2];
  392 + // set value
  393 + rgba[i * 4 + 0] = b1;
  394 + rgba[i * 4 + 1] = b2;
  395 + rgba[i * 4 + 2] = b3;
  396 + rgba[i * 4 + 3] = (byte) 255;
  397 + }
  398 + stitchBmp.copyPixelsFromBuffer(ByteBuffer.wrap(rgba));
  399 + return stitchBmp;
  400 + }
  401 +
  402 + public static Bitmap BGR2Bitmap(byte[] bytes, int width, int height) {
  403 + // use Bitmap.Config.ARGB_8888 instead of type is OK
  404 + Bitmap stitchBmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
  405 + byte[] rgba = new byte[width * height * 4];
  406 + for (int i = 0; i < width * height; i++) {
  407 + byte b1 = bytes[i * 3 + 0];
  408 + byte b2 = bytes[i * 3 + 1];
  409 + byte b3 = bytes[i * 3 + 2];
  410 + // set value
  411 + rgba[i * 4 + 0] = b3;
  412 + rgba[i * 4 + 1] = b2;
  413 + rgba[i * 4 + 2] = b1;
  414 + rgba[i * 4 + 3] = (byte) 255;
  415 + }
  416 + stitchBmp.copyPixelsFromBuffer(ByteBuffer.wrap(rgba));
  417 + return stitchBmp;
  418 + }
  419 +
  420 + public static byte[] ARGB2R(byte[] bytes, int width, int height) {
  421 + byte[] IR = new byte[width * height];
  422 + for (int i = 0; i < width * height; i++) {
  423 + IR[i] = bytes[i * 4];
  424 + }
  425 + return IR;
  426 + }
  427 +
  428 + public static Bitmap getInstaceBmp(BDFaceImageInstance newInstance) {
  429 + Bitmap transBmp = null;
  430 + if (newInstance.imageType == BDFaceSDKCommon.BDFaceImageType.BDFACE_IMAGE_TYPE_RGBA) {
  431 + transBmp = Bitmap.createBitmap(newInstance.width, newInstance.height, Bitmap.Config.ARGB_8888);
  432 + transBmp.copyPixelsFromBuffer(ByteBuffer.wrap(newInstance.data));
  433 + } else if (newInstance.imageType == BDFaceSDKCommon.BDFaceImageType.BDFACE_IMAGE_TYPE_BGR) {
  434 + transBmp = BitmapUtils.BGR2Bitmap(newInstance.data, newInstance.width, newInstance.height);
  435 + } else if (newInstance.imageType == BDFaceSDKCommon.BDFaceImageType.BDFACE_IMAGE_TYPE_YUV_NV21) {
  436 + transBmp = BitmapUtils.yuv2Bitmap(newInstance.data, newInstance.width, newInstance.height);
  437 + } else if (newInstance.imageType == BDFaceSDKCommon.BDFaceImageType.BDFACE_IMAGE_TYPE_GRAY) {
  438 + transBmp = Depth2Bitmap(newInstance.data, newInstance.width, newInstance.height);
  439 + }
  440 + return transBmp;
  441 + }
  442 +
  443 + /**
  444 + * 获取图片数据
  445 + * @param path
  446 + * @return
  447 + */
  448 + public static Bitmap getBitmap(String path) {
  449 + FileInputStream fis;
  450 + Bitmap bm = null;
  451 + try {
  452 + fis = new FileInputStream(path);
  453 + BitmapFactory.Options options = new BitmapFactory.Options();
  454 + options.inSampleSize = 8;//图片的长宽都是原来的1/8
  455 + BufferedInputStream bis = new BufferedInputStream(fis);
  456 + bm = BitmapFactory.decodeStream(bis, null, options);
  457 + fis.close();
  458 + } catch (FileNotFoundException e) {
  459 + e.printStackTrace();
  460 + } catch (IOException e) {
  461 + e.printStackTrace();
  462 + }
  463 + return bm;
  464 + }
  465 +
  466 + public static Bitmap base64ToBitmap(String base64String) {
  467 + base64String = Uri.decode(base64String);
  468 + byte[] decode = Base64.decode(base64String, Base64.DEFAULT);
  469 + Bitmap bitmap = BitmapFactory.decodeByteArray(decode, 0, decode.length);
  470 + return bitmap;
  471 + }
  472 +}
  1 +/**
  2 + * Copyright (C) 2017 Baidu Inc. All rights reserved.
  3 + */
  4 +package com.baidu.idl.main.facesdk.utils;
  5 +
  6 +import android.content.Context;
  7 +import android.os.Build;
  8 +import android.text.TextUtils;
  9 +
  10 +/**
  11 + * 显示设备信息工具类
  12 + */
  13 +public final class DensityUtils {
  14 +
  15 + /**
  16 + * 四舍五入
  17 + */
  18 + private static final float DOT_FIVE = 0.5f;
  19 + /**
  20 + * portrait degree:90
  21 + */
  22 + private static final int PORTRAIT_DEGREE_90 = 90;
  23 +
  24 + /**
  25 + * portrait degree:270
  26 + */
  27 + private static final int PORTRAIT_DEGREE_270 = 270;
  28 +
  29 +
  30 + /**
  31 + * 需要比较的设置型号
  32 + */
  33 + private static final String[] BUILD_MODELS = {
  34 + "i700v", // 斐讯i700v
  35 + "A862W", // 夏新A862W
  36 + "V8526" // 桑菲V8526
  37 + };
  38 +
  39 + /**
  40 + * Private constructor to prohibit nonsense instance creation.
  41 + */
  42 + private DensityUtils() {
  43 + }
  44 +
  45 + /**
  46 + * sp转px.
  47 + *
  48 + * @param context context
  49 + * @param spValue spValue
  50 + * @return 换算后的px值
  51 + */
  52 + public static int sp2px(Context context, float spValue) {
  53 + final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
  54 + return (int) (spValue * fontScale + 0.5f);
  55 + }
  56 +
  57 + /**
  58 + * dip转换成px
  59 + *
  60 + * @param context Context
  61 + * @param dip dip Value
  62 + * @return 换算后的px值
  63 + */
  64 + public static int dip2px(Context context, float dip) {
  65 + float density = getDensity(context);
  66 + return (int) (dip * density + DensityUtils.DOT_FIVE);
  67 + }
  68 +
  69 + /**
  70 + * px转换成dip
  71 + *
  72 + * @param context Context
  73 + * @param px px Value
  74 + * @return 换算后的dip值
  75 + */
  76 + public static int px2dip(Context context, float px) {
  77 + float density = getDensity(context);
  78 + return (int) (px / density + DOT_FIVE);
  79 + }
  80 +
  81 + /**
  82 + * 得到显示宽度
  83 + *
  84 + * @param context Context
  85 + * @return 宽度
  86 + */
  87 + public static int getDisplayWidth(Context context) {
  88 + return context.getResources().getDisplayMetrics().widthPixels;
  89 + }
  90 +
  91 + /**
  92 + * 得到显示高度
  93 + *
  94 + * @param context Context
  95 + * @return 高度
  96 + */
  97 + public static int getDisplayHeight(Context context) {
  98 + return context.getResources().getDisplayMetrics().heightPixels;
  99 + }
  100 +
  101 + /**
  102 + * 得到显示密度
  103 + *
  104 + * @param context Context
  105 + * @return 密度
  106 + */
  107 + public static float getDensity(Context context) {
  108 + return context.getResources().getDisplayMetrics().density;
  109 + }
  110 +
  111 + /**
  112 + * 得到DPI
  113 + *
  114 + * @param context Context
  115 + * @return DPI
  116 + */
  117 + public static int getDensityDpi(Context context) {
  118 + return context.getResources().getDisplayMetrics().densityDpi;
  119 + }
  120 +
  121 +
  122 + /**
  123 + * 判断当前Android系统摄像头旋转多少度
  124 + *
  125 + * @return 当前Android系统能竖着屏幕,
  126 + * 正常应该旋转90度,
  127 + * 但是斐讯i700v、夏新A862W、桑菲V8526需要旋转270度
  128 + */
  129 + public static int getPortraitDegree() {
  130 + int degree = PORTRAIT_DEGREE_90;
  131 + // 为了更好的扩展更多的特殊设置型号,将要比较的设备型号提成一个数组,遍历这个数据。
  132 + for (String model : BUILD_MODELS) {
  133 + if (TextUtils.equals(model, Build.MODEL)) {
  134 + degree = PORTRAIT_DEGREE_270;
  135 + break;
  136 + }
  137 + }
  138 + return degree;
  139 + }
  140 +
  141 +
  142 +}
  1 +package com.baidu.idl.main.facesdk.utils;
  2 +
  3 +import android.graphics.Matrix;
  4 +import android.graphics.Rect;
  5 +import android.graphics.RectF;
  6 +import android.view.TextureView;
  7 +
  8 +import com.baidu.idl.main.facesdk.FaceInfo;
  9 +import com.baidu.idl.main.facesdk.camera.AutoTexturePreviewView;
  10 +import com.baidu.idl.main.facesdk.camera.PicoAutoTexturePreviewView;
  11 +import com.baidu.idl.main.facesdk.model.BDFaceImageInstance;
  12 +
  13 +/**
  14 + * Created by ShiShuaiFeng on 2019/6/5.
  15 + */
  16 +
  17 +public class FaceOnDrawTexturViewUtil {
  18 +
  19 +
  20 + private FaceOnDrawTexturViewUtil() {
  21 + }
  22 +
  23 + /**
  24 + * 通过中心点坐标(x,y) 和 width ,绘制Rect
  25 + *
  26 + * @param faceInfo
  27 + * @return
  28 + */
  29 + public static Rect getFaceRectTwo(FaceInfo faceInfo) {
  30 + Rect rect = new Rect();
  31 + rect.top = (int) ((faceInfo.centerY - faceInfo.width / 2));
  32 + rect.left = (int) ((faceInfo.centerX - faceInfo.width / 2));
  33 + rect.right = (int) ((faceInfo.centerX + faceInfo.width / 2));
  34 + rect.bottom = (int) ((faceInfo.centerY + faceInfo.width / 2));
  35 + return rect;
  36 + }
  37 +
  38 + public static void mapFromOriginalRect(RectF rectF,
  39 + AutoTexturePreviewView autoTexturePreviewView,
  40 + BDFaceImageInstance imageFrame) {
  41 + // 获取屏幕的宽
  42 + int selfWidth = autoTexturePreviewView.getPreviewWidth();
  43 + // 获取屏幕的高
  44 + int selfHeight = autoTexturePreviewView.getPreviewHeight();
  45 + // 新建矩阵对象
  46 + Matrix matrix = new Matrix();
  47 + // 当屏幕宽度/图像宽度>屏幕高度/图像高度时
  48 + if (selfWidth * imageFrame.height > selfHeight * imageFrame.width) {
  49 + // 将高度按照宽度比进行缩放
  50 + int targetHeight = imageFrame.height * selfWidth / imageFrame.width;
  51 + // 计算平移距离
  52 + int delta = (targetHeight - selfHeight) / 2;
  53 + // 计算宽度比
  54 + float ratio = 1.0f * selfWidth / imageFrame.width;
  55 + // 设置矩阵变换缩放比
  56 + matrix.postScale(ratio, ratio);
  57 + // 设置变换矩阵的平移距离
  58 + matrix.postTranslate(0, -delta);
  59 + } else {
  60 + // 将宽度按照高度比进行缩放
  61 + int targetWith = imageFrame.width * selfHeight / imageFrame.height;
  62 + // 计算平移距离
  63 + int delta = (targetWith - selfWidth) / 2;
  64 + // 计算宽度比
  65 + float ratio = 1.0f * selfHeight / imageFrame.height;
  66 + // 设置矩阵变换缩放比
  67 + matrix.postScale(ratio, ratio);
  68 + // 设置变换矩阵的平移距离
  69 + matrix.postTranslate(-delta, 0);
  70 + }
  71 + // 对人脸框数据进行矩阵变换
  72 + matrix.mapRect(rectF);
  73 +
  74 + }
  75 +
  76 + public static void mapFromOriginalRect(RectF rectF,
  77 + TextureView textureView,
  78 + BDFaceImageInstance imageFrame) {
  79 + int selfWidth = textureView.getWidth();
  80 + int selfHeight = textureView.getHeight();
  81 + Matrix matrix = new Matrix();
  82 + if (selfWidth * imageFrame.height > selfHeight * imageFrame.width) {
  83 + int targetHeight = imageFrame.height * selfWidth / imageFrame.width;
  84 + int delta = (targetHeight - selfHeight) / 2;
  85 + float ratio = 1.0f * selfWidth / imageFrame.width;
  86 + matrix.postScale(ratio, ratio);
  87 + matrix.postTranslate(0, -delta);
  88 + } else {
  89 + int targetWith = imageFrame.width * selfHeight / imageFrame.height;
  90 + int delta = (targetWith - selfWidth) / 2;
  91 + float ratio = 1.0f * selfHeight / imageFrame.height;
  92 + matrix.postScale(ratio, ratio);
  93 + matrix.postTranslate(-delta, 0);
  94 + }
  95 + matrix.mapRect(rectF);
  96 +
  97 + }
  98 +
  99 + public static void mapFromOriginalRect(RectF rectF,
  100 + PicoAutoTexturePreviewView textureView,
  101 + BDFaceImageInstance imageFrame) {
  102 + int selfWidth = textureView.getWidth();
  103 + int selfHeight = textureView.getHeight();
  104 + Matrix matrix = new Matrix();
  105 + if (selfWidth * imageFrame.height > selfHeight * imageFrame.width) {
  106 + int targetHeight = imageFrame.height * selfWidth / imageFrame.width;
  107 + int delta = (targetHeight - selfHeight) / 2;
  108 + float ratio = 1.0f * selfWidth / imageFrame.width;
  109 + matrix.postScale(ratio, ratio);
  110 + matrix.postTranslate(0, -delta);
  111 + } else {
  112 + int targetWith = imageFrame.width * selfHeight / imageFrame.height;
  113 + int delta = (targetWith - selfWidth) / 2;
  114 + float ratio = 1.0f * selfHeight / imageFrame.height;
  115 + matrix.postScale(ratio, ratio);
  116 + matrix.postTranslate(-delta, 0);
  117 + }
  118 + matrix.mapRect(rectF);
  119 +
  120 + }
  121 +
  122 +
  123 + public static void converttPointXY(float[] pointXY, AutoTexturePreviewView textureView,
  124 + BDFaceImageInstance imageFrame, float width) {
  125 + int selfWidth = textureView.getWidth();
  126 + int selfHeight = textureView.getHeight();
  127 + if (selfWidth * imageFrame.height > selfHeight * imageFrame.width) {
  128 + int targetHeight = imageFrame.height * selfWidth / imageFrame.width;
  129 + int delta = (targetHeight - selfHeight) / 2;
  130 + float ratio = 1.0f * selfWidth / imageFrame.width;
  131 + pointXY[0] = pointXY[0] * ratio;
  132 + pointXY[1] = pointXY[1] * ratio;
  133 + pointXY[1] = pointXY[1] - delta;
  134 + pointXY[2] = width * ratio;
  135 + } else {
  136 + int targetWith = imageFrame.width * selfHeight / imageFrame.height;
  137 + int delta = (targetWith - selfWidth) / 2;
  138 + float ratio = 1.0f * selfHeight / imageFrame.height;
  139 + pointXY[0] = pointXY[0] * ratio;
  140 + pointXY[1] = pointXY[1] * ratio;
  141 + pointXY[0] = pointXY[0] - delta;
  142 + pointXY[2] = width * ratio;
  143 + }
  144 + }
  145 +}
  1 +package com.baidu.idl.main.facesdk.utils;
  2 +
  3 +import android.graphics.Bitmap;
  4 +import android.os.Environment;
  5 +
  6 +import java.io.BufferedReader;
  7 +import java.io.File;
  8 +import java.io.FileInputStream;
  9 +import java.io.FileOutputStream;
  10 +import java.io.FileReader;
  11 +import java.io.IOException;
  12 +import java.io.InputStream;
  13 +import java.io.RandomAccessFile;
  14 +
  15 +/**
  16 + * author : shangrong
  17 + * date : 2019/5/23 2:05 PM
  18 + * description :文件工具类
  19 + */
  20 +public class FileUtils {
  21 + /**
  22 + * 读取txt文件的内容
  23 + *
  24 + * @param filePath 想要读取的文件对象
  25 + * @return 返回文件内容
  26 + */
  27 + public static String txt2String(String filePath) {
  28 + File file = new File(filePath);
  29 + if (!file.exists()) {
  30 + return "";
  31 + }
  32 +
  33 + StringBuilder result = new StringBuilder();
  34 + try {
  35 + BufferedReader br = new BufferedReader(new FileReader(file));//构造一个BufferedReader类来读取文件
  36 + String s = null;
  37 + while ((s = br.readLine()) != null) {//使用readLine方法,一次读一行
  38 + result.append(System.lineSeparator() + s);
  39 + }
  40 + br.close();
  41 + } catch (Exception e) {
  42 + e.printStackTrace();
  43 + }
  44 + return result.toString();
  45 + }
  46 +
  47 +
  48 + /**
  49 + * 写入TXT文件
  50 + */
  51 + public static boolean writeTxtFile(String content, String filePath) {
  52 + File file = new File(filePath);
  53 + if (!file.exists()) {
  54 + return false;
  55 + }
  56 +
  57 + RandomAccessFile mm = null;
  58 + boolean flag = false;
  59 + FileOutputStream fileOutputStream = null;
  60 + try {
  61 + fileOutputStream = new FileOutputStream(file);
  62 + fileOutputStream.write(content.getBytes("utf-8"));
  63 + fileOutputStream.close();
  64 + flag = true;
  65 + } catch (Exception e) {
  66 + e.printStackTrace();
  67 + }
  68 + return flag;
  69 + }
  70 +
  71 + /**
  72 + * Checks if is sd card available.检查SD卡是否可用
  73 + */
  74 + public static boolean isSdCardAvailable() {
  75 + return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);
  76 + }
  77 +
  78 + /**
  79 + * Gets the SD root file.获取SD卡根目录
  80 + */
  81 + public static File getSDRootFile() {
  82 + if (isSdCardAvailable()) {
  83 + return Environment.getExternalStorageDirectory();
  84 + } else {
  85 + return null;
  86 + }
  87 + }
  88 +
  89 + /**
  90 + * 获取导入图片文件的目录信息
  91 + */
  92 + public static File getBatchImportDirectory() {
  93 + File sdRootFile = getSDRootFile();
  94 + File file = null;
  95 + if (sdRootFile != null && sdRootFile.exists()) {
  96 + file = new File(sdRootFile, "Face-Import");
  97 + if (!file.exists()) {
  98 + file.mkdirs();
  99 + }
  100 + }
  101 + return file;
  102 + }
  103 +
  104 + /**
  105 + * 获取导入图片成功的目录信息
  106 + */
  107 + public static File getBatchImportSuccessDirectory() {
  108 + File sdRootFile = getSDRootFile();
  109 + File file = null;
  110 + if (sdRootFile != null && sdRootFile.exists()) {
  111 + file = new File(sdRootFile, "Success-Import");
  112 + if (!file.exists()) {
  113 + file.mkdirs();
  114 + }
  115 + }
  116 + return file;
  117 + }
  118 +
  119 + /**
  120 + * 判断文件是否存在
  121 + */
  122 + public static File isFileExist(String fileDirectory, String fileName) {
  123 + File file = new File(fileDirectory + "/" + fileName);
  124 + try {
  125 + if (!file.exists()) {
  126 + return null;
  127 + }
  128 + } catch (Exception e) {
  129 + return null;
  130 + }
  131 + return file;
  132 + }
  133 +
  134 + /**
  135 + * 删除文件
  136 + */
  137 + public static void deleteFile(String filePath) {
  138 + try {
  139 + // 找到文件所在的路径并删除该文件
  140 + File file = new File(filePath);
  141 + file.delete();
  142 + } catch (Exception e) {
  143 + e.printStackTrace();
  144 + }
  145 + }
  146 +
  147 + /*
  148 + * 获取不带扩展名的文件名
  149 + * */
  150 + public static String getFileNameNoEx(String filename) {
  151 + if ((filename != null) && (filename.length() > 0)) {
  152 + int dot = filename.lastIndexOf('.');
  153 + if ((dot > -1) && (dot < (filename.length()))) {
  154 + return filename.substring(0, dot);
  155 + }
  156 + }
  157 + return filename;
  158 + }
  159 +
  160 + /**
  161 + * 保存图片
  162 + */
  163 + public static boolean saveBitmap(File file, Bitmap bitmap) {
  164 + FileOutputStream out = null;
  165 + try {
  166 + out = new FileOutputStream(file);
  167 + bitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);
  168 + return true;
  169 + } catch (Exception e) {
  170 + e.printStackTrace();
  171 + } finally {
  172 + try {
  173 + if (out != null) {
  174 + out.close();
  175 + }
  176 + } catch (Exception e) {
  177 + e.printStackTrace();
  178 + }
  179 + }
  180 + return false;
  181 + }
  182 +
  183 + public static boolean copyFile(String oldPath, String newPath) {
  184 + InputStream inStream = null;
  185 + FileOutputStream fs = null;
  186 + boolean result = false;
  187 + try {
  188 + int bytesum = 0;
  189 + int byteread = 0;
  190 + File oldfile = new File(oldPath);
  191 + // 判断目录是否存在
  192 + File newfile = new File(newPath);
  193 + File newFileDir = new File(newfile.getPath().replace(newfile.getName(), ""));
  194 + if (!newFileDir.exists()) {
  195 + newFileDir.mkdirs();
  196 + }
  197 + if (oldfile.exists()) { // 文件存在时
  198 + inStream = new FileInputStream(oldPath); // 读入原文件
  199 + fs = new FileOutputStream(newPath);
  200 + byte[] buffer = new byte[1444];
  201 + int length;
  202 + while ((byteread = inStream.read(buffer)) != -1) {
  203 + bytesum += byteread; // 字节数 文件大小
  204 + System.out.println(bytesum);
  205 + fs.write(buffer, 0, byteread);
  206 + }
  207 + result = true;
  208 + } else {
  209 + result = false;
  210 + }
  211 + } catch (Exception e) {
  212 + System.out.println("复制单个文件操作出错");
  213 + e.printStackTrace();
  214 + } finally {
  215 + if (inStream != null) {
  216 + try {
  217 + inStream.close();
  218 + } catch (IOException e) {
  219 + e.printStackTrace();
  220 + }
  221 + }
  222 + if (fs != null) {
  223 + try {
  224 + fs.close();
  225 + } catch (IOException e) {
  226 + e.printStackTrace();
  227 + }
  228 + }
  229 + }
  230 + return result;
  231 + }
  232 +
  233 +}
  1 +package com.baidu.idl.main.facesdk.utils;
  2 +
  3 +import android.util.Log;
  4 +
  5 +import com.baidu.idl.main.facesdk.model.SingleBaseConfig;
  6 +
  7 +
  8 +public class LogUtils {
  9 +
  10 + private static boolean isDebug = SingleBaseConfig.getBaseConfig().isDebug();
  11 +
  12 + public static void setIsDebug(boolean isDebug) {
  13 + LogUtils.isDebug = isDebug;
  14 + }
  15 +
  16 + public static int v(String tag, String msg) {
  17 + if (isDebug) {
  18 + return Log.v(tag, msg);
  19 + } else {
  20 + return -1;
  21 + }
  22 + }
  23 +
  24 + public static int d(String tag, String msg) {
  25 + if (isDebug) {
  26 + return Log.d(tag, msg);
  27 + } else {
  28 + return -1;
  29 + }
  30 + }
  31 +
  32 + public static int i(String tag, String msg) {
  33 + if (isDebug) {
  34 + return Log.i(tag, msg);
  35 + } else {
  36 + return -1;
  37 + }
  38 + }
  39 +
  40 + public static int w(String tag, String msg) {
  41 + if (isDebug) {
  42 + return Log.w(tag, msg);
  43 + } else {
  44 + return -1;
  45 + }
  46 + }
  47 +
  48 + public static int e(String tag, String msg) {
  49 + if (isDebug) {
  50 + return Log.e(tag, msg);
  51 + } else {
  52 + return -1;
  53 + }
  54 + }
  55 +}
  1 +package com.baidu.idl.main.facesdk.utils;
  2 +
  3 +import android.content.Context;
  4 +import android.os.Handler;
  5 +import android.os.Looper;
  6 +import android.widget.Toast;
  7 +
  8 +public class ToastUtils {
  9 +
  10 + private static Handler handler = new Handler(Looper.getMainLooper());
  11 +
  12 + public static void toast(final Context context, final String text) {
  13 + handler.post(new Runnable() {
  14 + @Override
  15 + public void run() {
  16 + Toast.makeText(context, text, Toast.LENGTH_SHORT).show();
  17 + }
  18 + });
  19 + }
  20 +
  21 + public static void toast(final Context context, final int resId) {
  22 + handler.post(new Runnable() {
  23 + @Override
  24 + public void run() {
  25 + Toast.makeText(context, resId, Toast.LENGTH_SHORT).show();
  26 + }
  27 + });
  28 + }
  29 +}
  1 +package com.baidu.idl.main.facesdk.view;
  2 +
  3 +import android.annotation.SuppressLint;
  4 +import android.content.Context;
  5 +import android.content.res.TypedArray;
  6 +import android.graphics.Bitmap;
  7 +import android.graphics.BitmapShader;
  8 +import android.graphics.Canvas;
  9 +import android.graphics.Color;
  10 +import android.graphics.Matrix;
  11 +import android.graphics.Paint;
  12 +import android.graphics.RectF;
  13 +import android.graphics.Shader;
  14 +import android.graphics.drawable.BitmapDrawable;
  15 +import android.graphics.drawable.ColorDrawable;
  16 +import android.graphics.drawable.Drawable;
  17 +import android.net.Uri;
  18 +import android.util.AttributeSet;
  19 +import android.widget.ImageView;
  20 +
  21 +import com.baidu.idl.main.facesdk.R;
  22 +import com.baidu.idl.main.facesdk.utils.LogUtils;
  23 +
  24 +
  25 +/**
  26 + * Created by v_liujialu01 on 2018/12/3.
  27 + */
  28 +
  29 +@SuppressLint("AppCompatCustomView")
  30 +public class CircleImageView extends ImageView {
  31 + private static final String TAG = CircleImageView.class.getSimpleName();
  32 + private static final ScaleType SCALE_TYPE = ScaleType.CENTER_CROP;
  33 + private static final Bitmap.Config BITMAP_CONFIG = Bitmap.Config.ARGB_8888;
  34 + private static final int COLORDRAWABLE_DIMENSION = 2;
  35 + private static final int DEFAULT_BORDER_WIDTH = 0;
  36 + private static final int DEFAULT_BORDER_COLOR = Color.BLACK;
  37 + private final RectF mDrawableRect = new RectF();
  38 + private final RectF mBorderRect = new RectF();
  39 + private final Matrix mShaderMatrix = new Matrix();
  40 + private final Paint mBitmapPaint = new Paint();
  41 + private final Paint mBorderPaint = new Paint();
  42 + private int mBorderColor = DEFAULT_BORDER_COLOR;
  43 + private int mBorderWidth = DEFAULT_BORDER_WIDTH;
  44 + private Bitmap mBitmap;
  45 + private BitmapShader mBitmapShader;
  46 + private int mBitmapWidth;
  47 + private int mBitmapHeight;
  48 + private float mDrawableRadius;
  49 + private float mBorderRadius;
  50 + private boolean mReady;
  51 + private boolean mSetupPending;
  52 +
  53 + public CircleImageView(Context context) {
  54 + super(context);
  55 + init();
  56 + }
  57 +
  58 + public CircleImageView(Context context, AttributeSet attrs) {
  59 + this(context, attrs, 0);
  60 + }
  61 +
  62 + public CircleImageView(Context context, AttributeSet attrs, int defStyle) {
  63 + super(context, attrs, defStyle);
  64 + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircleImageView,
  65 + defStyle, 0);
  66 + mBorderWidth = a.getDimensionPixelSize(R.styleable.CircleImageView_border_width,
  67 + DEFAULT_BORDER_WIDTH);
  68 + mBorderColor = a.getColor(R.styleable.CircleImageView_border_color, DEFAULT_BORDER_COLOR);
  69 + a.recycle();
  70 + init();
  71 + }
  72 +
  73 + private void init() {
  74 + super.setScaleType(SCALE_TYPE);
  75 + mReady = true;
  76 + if (mSetupPending) {
  77 + setup();
  78 + mSetupPending = false;
  79 + }
  80 + }
  81 +
  82 + @Override
  83 + public ScaleType getScaleType() {
  84 + return SCALE_TYPE;
  85 + }
  86 +
  87 + @Override
  88 + public void setScaleType(ScaleType scaleType) {
  89 + LogUtils.i(TAG, "scaleType = " + scaleType);
  90 + }
  91 +
  92 + @Override
  93 + public void setAdjustViewBounds(boolean adjustViewBounds) {
  94 + if (adjustViewBounds) {
  95 + throw new IllegalArgumentException("adjustViewBounds not supported.");
  96 + }
  97 + }
  98 +
  99 + @Override
  100 + protected void onDraw(Canvas canvas) {
  101 + if (getDrawable() == null) {
  102 + return;
  103 + }
  104 + canvas.drawCircle(getWidth() / 2, getHeight() / 2, mDrawableRadius, mBitmapPaint);
  105 + if (mBorderWidth != 0) {
  106 + canvas.drawCircle(getWidth() / 2, getHeight() / 2, mBorderRadius, mBorderPaint);
  107 + }
  108 + }
  109 +
  110 + @Override
  111 + protected void onSizeChanged(int w, int h, int oldw, int oldh) {
  112 + super.onSizeChanged(w, h, oldw, oldh);
  113 + setup();
  114 + }
  115 +
  116 + public int getBorderColor() {
  117 + return mBorderColor;
  118 + }
  119 +
  120 + public void setBorderColor(int borderColor) {
  121 + if (borderColor == mBorderColor) {
  122 + return;
  123 + }
  124 + mBorderColor = borderColor;
  125 + mBorderPaint.setColor(mBorderColor);
  126 + invalidate();
  127 + }
  128 +
  129 + public int getBorderWidth() {
  130 + return mBorderWidth;
  131 + }
  132 +
  133 + public void setBorderWidth(int borderWidth) {
  134 + if (borderWidth == mBorderWidth) {
  135 + return;
  136 + }
  137 + mBorderWidth = borderWidth;
  138 + setup();
  139 + }
  140 +
  141 + @Override
  142 + public void setImageBitmap(Bitmap bm) {
  143 + super.setImageBitmap(bm);
  144 + mBitmap = bm;
  145 + setup();
  146 + }
  147 +
  148 + @Override
  149 + public void setImageDrawable(Drawable drawable) {
  150 + super.setImageDrawable(drawable);
  151 + mBitmap = getBitmapFromDrawable(drawable);
  152 + setup();
  153 + }
  154 +
  155 + @Override
  156 + public void setImageResource(int resId) {
  157 + super.setImageResource(resId);
  158 + mBitmap = getBitmapFromDrawable(getDrawable());
  159 + setup();
  160 + }
  161 +
  162 + @Override
  163 + public void setImageURI(Uri uri) {
  164 + super.setImageURI(uri);
  165 + mBitmap = getBitmapFromDrawable(getDrawable());
  166 + setup();
  167 + }
  168 +
  169 + private Bitmap getBitmapFromDrawable(Drawable drawable) {
  170 + try {
  171 +
  172 + if (drawable instanceof BitmapDrawable) {
  173 + return ((BitmapDrawable) drawable).getBitmap();
  174 + }
  175 +
  176 + Bitmap bitmap;
  177 + if (drawable instanceof ColorDrawable) {
  178 + bitmap = Bitmap.createBitmap(COLORDRAWABLE_DIMENSION, COLORDRAWABLE_DIMENSION,
  179 + BITMAP_CONFIG);
  180 + } else {
  181 + bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(),
  182 + BITMAP_CONFIG);
  183 + }
  184 + Canvas canvas = new Canvas(bitmap);
  185 + drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
  186 + drawable.draw(canvas);
  187 + return bitmap;
  188 + } catch (Exception e) {
  189 + LogUtils.e(TAG, "e = " + e.getMessage());
  190 + return null;
  191 + }
  192 + }
  193 +
  194 + private void setup() {
  195 + if (!mReady) {
  196 + mSetupPending = true;
  197 + return;
  198 + }
  199 + if (mBitmap == null) {
  200 + return;
  201 + }
  202 + int power = 2;
  203 + mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
  204 + mBitmapPaint.setAntiAlias(true);
  205 + mBitmapPaint.setShader(mBitmapShader);
  206 + mBorderPaint.setStyle(Paint.Style.STROKE);
  207 + mBorderPaint.setAntiAlias(true);
  208 + mBorderPaint.setColor(mBorderColor);
  209 + mBorderPaint.setStrokeWidth(mBorderWidth);
  210 + mBitmapHeight = mBitmap.getHeight();
  211 + mBitmapWidth = mBitmap.getWidth();
  212 + mBorderRect.set(0, 0, getWidth(), getHeight());
  213 + mBorderRadius = Math.min((mBorderRect.height() - mBorderWidth) / power,
  214 + (mBorderRect.width() - mBorderWidth) / power);
  215 + mDrawableRect.set(mBorderWidth, mBorderWidth,
  216 + mBorderRect.width() - mBorderWidth,
  217 + mBorderRect.height() - mBorderWidth);
  218 + mDrawableRadius = Math.min(mDrawableRect.height() / power, mDrawableRect.width() / power);
  219 + updateShaderMatrix();
  220 + invalidate();
  221 + }
  222 +
  223 + private void updateShaderMatrix() {
  224 + float scale;
  225 + float dx = 0;
  226 + float dy = 0;
  227 + float power = 0.5f;
  228 + mShaderMatrix.set(null);
  229 + if (mBitmapWidth * mDrawableRect.height() > mDrawableRect.width() * mBitmapHeight) {
  230 + scale = mDrawableRect.height() / (float) mBitmapHeight;
  231 + dx = (mDrawableRect.width() - mBitmapWidth * scale) * power;
  232 + } else {
  233 + scale = mDrawableRect.width() / (float) mBitmapWidth;
  234 + dy = (mDrawableRect.height() - mBitmapHeight * scale) * power;
  235 + }
  236 + mShaderMatrix.setScale(scale, scale);
  237 + mShaderMatrix.postTranslate((int) (dx + power) + mBorderWidth,
  238 + (int) (dy + power) + mBorderWidth);
  239 + mBitmapShader.setLocalMatrix(mShaderMatrix);
  240 + }
  241 +}
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<resources>
  3 + <declare-styleable name="CircleImageView">
  4 + <attr name="border_width" format="dimension" />
  5 + <attr name="border_color" format="color" />
  6 + </declare-styleable>
  7 +</resources>
@@ -32,11 +32,7 @@ android { @@ -32,11 +32,7 @@ android {
32 dependencies { 32 dependencies {
33 implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 33 implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
34 implementation 'com.android.support:appcompat-v7:28.0.0' 34 implementation 'com.android.support:appcompat-v7:28.0.0'
35 - testImplementation 'junit:junit:4.12'  
36 - androidTestImplementation 'com.android.support.test:runner:1.0.2'  
37 - androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'  
38 - implementation 'com.apkfuns.logutils:library:1.4.0'  
39 - implementation project(path: ':arcface')  
40 - implementation project(path: ':bdface') 35 +// api project(path: ':arcface')
  36 + api project(path: ':bdface')
41 37
42 } 38 }
1 -package com.yhkj.rebotsdk.engine  
2 -  
3 -  
4 -/**  
5 - * 状态码  
6 - */  
7 -internal class EngineStatusCode {  
8 -  
9 - companion object {  
10 - const val SUCCEED = 1000 //操作成功  
11 - const val FAILED = -1000 //操作失败  
12 - }  
13 -  
14 -}  
15 -  
16 -/**  
17 - * 状态  
18 - * @param code 状态码  
19 - * @param msg 状态消息  
20 - */  
21 -internal data class EngineStatusData(val code: Int, val msg: String)  
22 -  
23 -  
24 -/**  
25 - * 人脸识别结果  
26 - * @param faceId 人脸记录Id  
27 - * @param score 比对分数  
28 - */  
29 -internal data class IdentifyData(val faceId: String, val score: Float)  
@@ -2,25 +2,22 @@ package com.yhkj.rebotsdk.engine @@ -2,25 +2,22 @@ package com.yhkj.rebotsdk.engine
2 2
3 import android.content.Context 3 import android.content.Context
4 import android.graphics.Bitmap 4 import android.graphics.Bitmap
  5 +import com.baidu.idl.main.facesdk.callback.Callback
  6 +import com.baidu.idl.main.facesdk.model.BaseConfig
  7 +import com.yhkj.rebotsdk.face.ResultCallBack
5 8
6 9
7 internal interface EngineInterface { 10 internal interface EngineInterface {
8 11
9 12
10 /** 13 /**
11 - * 激活人脸license 14 + * 激活人脸license和初始化model
12 */ 15 */
13 - fun initLicense( 16 + fun init(
14 context: Context, active_key: String, appId: String, 17 context: Context, active_key: String, appId: String,
15 - sdkKey: String  
16 - ): Int 18 + sdkKey: String,callback: ResultCallBack
  19 + )
17 20
18 - /**  
19 - * 初始化  
20 - * @param context 上下文对象  
21 - * @return 是否初始化成功  
22 - */  
23 - fun init(context: Context?): EngineStatusData  
24 21
25 /** 22 /**
26 * 销毁 23 * 销毁
@@ -41,4 +38,5 @@ internal interface EngineInterface { @@ -41,4 +38,5 @@ internal interface EngineInterface {
41 * 获取视频流中符合条件的人脸 38 * 获取视频流中符合条件的人脸
42 */ 39 */
43 fun getBitmap(byteArray: ByteArray,width: Int,height: Int): Bitmap 40 fun getBitmap(byteArray: ByteArray,width: Int,height: Int): Bitmap
  41 +
44 } 42 }
1 package com.yhkj.rebotsdk.engine 1 package com.yhkj.rebotsdk.engine
2 2
3 import com.yhkj.rebotsdk.engine.arcface.ArcFaceEngine 3 import com.yhkj.rebotsdk.engine.arcface.ArcFaceEngine
  4 +import com.yhkj.rebotsdk.engine.baidu.BDFaceEngine
4 5
5 6
6 internal class FaceEngineFactory { 7 internal class FaceEngineFactory {
7 8
8 - fun createEngine() : EngineInterface = ArcFaceEngine() 9 + fun createEngine() : EngineInterface = BDFaceEngine()
  10 +
  11 + fun createArcEngine() : EngineInterface = ArcFaceEngine()
9 12
10 } 13 }
1 package com.yhkj.rebotsdk.engine.arcface 1 package com.yhkj.rebotsdk.engine.arcface
2 2
  3 +//import com.arcsoft.face.FaceEngine
3 import android.content.Context 4 import android.content.Context
4 import android.graphics.Bitmap 5 import android.graphics.Bitmap
5 -import com.arcsoft.face.FaceEngine  
6 import com.yhkj.rebotsdk.engine.EngineInterface 6 import com.yhkj.rebotsdk.engine.EngineInterface
7 -import com.yhkj.rebotsdk.engine.EngineStatusCode  
8 -import com.yhkj.rebotsdk.engine.EngineStatusData  
9 -import java.io.File  
10 -import java.io.FileInputStream  
11 -import java.io.IOException 7 +import com.yhkj.rebotsdk.face.ResultCallBack
12 8
13 /** 9 /**
14 * Created by zhangweiwei on 2020/3/16. 10 * Created by zhangweiwei on 2020/3/16.
@@ -17,18 +13,19 @@ internal class ArcFaceEngine : EngineInterface { @@ -17,18 +13,19 @@ internal class ArcFaceEngine : EngineInterface {
17 13
18 14
19 15
20 - override fun initLicense( 16 + override fun init(
21 context: Context, 17 context: Context,
22 active_key: String, 18 active_key: String,
23 appId: String, 19 appId: String,
24 - sdkKey: String  
25 - ): Int {  
26 - return FaceEngine.activeOnline(context, appId, sdkKey) 20 + sdkKey: String, callback: ResultCallBack
  21 + ) {
  22 +// val code=FaceEngine.activeOnline(context, appId, sdkKey)
  23 +// callback.onLicenseResult(code,"")
  24 +// if(code==0){
  25 +// //todo 初始化Model
  26 +// }
27 } 27 }
28 28
29 - override fun init(context: Context?): EngineStatusData {  
30 - TODO("Not yet implemented")  
31 - }  
32 29
33 override fun unInit() { 30 override fun unInit() {
34 TODO("Not yet implemented") 31 TODO("Not yet implemented")
@@ -46,5 +43,4 @@ internal class ArcFaceEngine : EngineInterface { @@ -46,5 +43,4 @@ internal class ArcFaceEngine : EngineInterface {
46 TODO("Not yet implemented") 43 TODO("Not yet implemented")
47 } 44 }
48 45
49 -  
50 } 46 }
@@ -4,37 +4,60 @@ import android.content.Context @@ -4,37 +4,60 @@ import android.content.Context
4 import android.graphics.Bitmap 4 import android.graphics.Bitmap
5 import com.baidu.idl.main.facesdk.FaceAuth 5 import com.baidu.idl.main.facesdk.FaceAuth
6 import com.baidu.idl.main.facesdk.callback.Callback 6 import com.baidu.idl.main.facesdk.callback.Callback
  7 +import com.baidu.idl.main.facesdk.listener.SdkInitListener
  8 +import com.baidu.idl.main.facesdk.manager.FaceSDKManager
  9 +import com.baidu.idl.main.facesdk.model.BaseConfig
  10 +import com.baidu.idl.main.facesdk.model.SingleBaseConfig
7 import com.yhkj.rebotsdk.engine.EngineInterface 11 import com.yhkj.rebotsdk.engine.EngineInterface
8 -import com.yhkj.rebotsdk.engine.EngineStatusData 12 +import com.yhkj.rebotsdk.face.ResultCallBack
9 13
10 /** 14 /**
11 * Created by zhangweiwei on 2020/3/16. 15 * Created by zhangweiwei on 2020/3/16.
12 */ 16 */
13 internal class BDFaceEngine : EngineInterface { 17 internal class BDFaceEngine : EngineInterface {
14 18
15 - private var initCode = -1  
16 19
17 - override fun initLicense(  
18 - context: Context,  
19 - active_key: String,  
20 - appId: String,  
21 - sdkKey: String  
22 - ): Int { 20 + override fun init(
  21 + context: Context, active_key: String, appId: String,
  22 + sdkKey: String, callback: ResultCallBack
  23 + ) {
23 val faceAuth = FaceAuth() 24 val faceAuth = FaceAuth()
24 // 在线激活 25 // 在线激活
25 faceAuth.initLicenseOnLine( 26 faceAuth.initLicenseOnLine(
26 context, 27 context,
27 - active_key,  
28 - Callback { code, response ->  
29 - initCode = code  
30 - return@Callback 28 + active_key
  29 + ) { code, response ->
  30 + callback.onLicenseResult(code,response)
  31 + if(code==0){
  32 + FaceSDKManager.getInstance().initModel(context,object :SdkInitListener{
  33 + override fun initStart() {
  34 +
  35 + }
  36 +
  37 + override fun initLicenseSuccess() {
  38 +
  39 + }
  40 +
  41 + override fun initLicenseFail(errorCode: Int, msg: String?) {
  42 +
  43 + }
  44 +
  45 + override fun initModelSuccess() {
  46 + callback.onModelResult(0,"")
  47 + }
  48 +
  49 +
  50 + override fun initModelFail(errorCode: Int, msg: String?) {
  51 + callback.onModelResult(errorCode,msg)
  52 + }
  53 +
31 }) 54 })
32 - return initCode  
33 } 55 }
34 56
35 - override fun init(context: Context?): EngineStatusData {  
36 - TODO("Not yet implemented")  
37 } 57 }
  58 + }
  59 +
  60 +
38 61
39 override fun unInit() { 62 override fun unInit() {
40 TODO("Not yet implemented") 63 TODO("Not yet implemented")
@@ -53,4 +76,5 @@ internal class BDFaceEngine : EngineInterface { @@ -53,4 +76,5 @@ internal class BDFaceEngine : EngineInterface {
53 } 76 }
54 77
55 78
  79 +
56 } 80 }
@@ -3,15 +3,12 @@ package com.yhkj.rebotsdk.face @@ -3,15 +3,12 @@ package com.yhkj.rebotsdk.face
3 import android.content.Context 3 import android.content.Context
4 import android.graphics.Bitmap 4 import android.graphics.Bitmap
5 import com.yhkj.rebotsdk.engine.EngineInterface 5 import com.yhkj.rebotsdk.engine.EngineInterface
6 -import com.yhkj.rebotsdk.engine.EngineStatusCode  
7 import com.yhkj.rebotsdk.engine.FaceEngineFactory 6 import com.yhkj.rebotsdk.engine.FaceEngineFactory
8 7
9 8
10 -internal class FaceApi private constructor() : FaceInterface { 9 +class FaceApi private constructor() : FaceInterface {
11 10
12 private lateinit var mFaceEngine: EngineInterface 11 private lateinit var mFaceEngine: EngineInterface
13 - private var mOnEnrollListener: FaceInterface.OnEnrollListener? = null  
14 - private var mOnVerifyListener: FaceInterface.OnVerifyListener? = null  
15 12
16 companion object { 13 companion object {
17 14
@@ -21,47 +18,28 @@ internal class FaceApi private constructor() : FaceInterface { @@ -21,47 +18,28 @@ internal class FaceApi private constructor() : FaceInterface {
21 18
22 } 19 }
23 20
24 - override fun initLicense(  
25 - context: Context,  
26 - active_key: String,  
27 - appId: String,  
28 - sdkKey: String  
29 - ): Int { 21 + override fun init(
  22 + context: Context, active_key: String, appId: String,
  23 + sdkKey: String,callBack: ResultCallBack
  24 + ) {
30 mFaceEngine = FaceEngineFactory().createEngine() 25 mFaceEngine = FaceEngineFactory().createEngine()
31 - return mFaceEngine.initLicense(context, active_key, appId, sdkKey) 26 + mFaceEngine.init(context, active_key, appId, sdkKey,callBack)
32 } 27 }
33 28
34 - override fun initFace(context: Context): StatusData {  
35 - return mFaceEngine.init(context).let {  
36 - when (it.code) {  
37 - EngineStatusCode.SUCCEED -> {  
38 - StatusData(StatusCode.SUCCEED, it.msg)  
39 - }  
40 - else -> {  
41 - StatusData(StatusCode.FAILED, it.msg)  
42 - }  
43 - }  
44 - }  
45 - }  
46 29
47 override fun initConfig(context: Context): Boolean { 30 override fun initConfig(context: Context): Boolean {
48 TODO("Not yet implemented") 31 TODO("Not yet implemented")
49 } 32 }
50 33
51 - override fun setEnrollListener(listener: FaceInterface.OnEnrollListener) {  
52 - mOnEnrollListener=listener  
53 - }  
54 -  
55 - override fun setVerifyListener(listener: FaceInterface.OnVerifyListener) {  
56 - mOnVerifyListener=listener  
57 - }  
58 34
59 override fun release() { 35 override fun release() {
60 mFaceEngine.unInit() 36 mFaceEngine.unInit()
61 } 37 }
62 38
63 - override fun registerFace(context: Context?, nv21: ByteArray?, width: Int, height: Int,  
64 - faceData: ByteArray,faceId: String): Boolean { 39 + override fun registerFace(
  40 + context: Context?, nv21: ByteArray?, width: Int, height: Int,
  41 + faceData: ByteArray, faceId: String
  42 + ): Boolean {
65 TODO("Not yet implemented") 43 TODO("Not yet implemented")
66 } 44 }
67 45
@@ -2,77 +2,23 @@ package com.yhkj.rebotsdk.face @@ -2,77 +2,23 @@ package com.yhkj.rebotsdk.face
2 2
3 import android.content.Context 3 import android.content.Context
4 import android.graphics.Bitmap 4 import android.graphics.Bitmap
  5 +import com.baidu.idl.main.facesdk.model.BaseConfig
5 6
6 7
7 interface FaceInterface { 8 interface FaceInterface {
8 9
9 - /**  
10 - * 录入模式监听  
11 - */  
12 - interface OnEnrollListener {  
13 -  
14 -  
15 - /**  
16 - * 录入结束(并未注册到人脸引擎中,仅获取到符合录入的人脸数据)  
17 - */  
18 - fun onEnrollFinished(fingerData : ByteArray)  
19 -  
20 - /**  
21 - * 录入异常  
22 - * @param statusData 提示信息  
23 - */  
24 - fun onEnrollException(statusData: StatusData)  
25 -  
26 - }  
27 10
28 /** 11 /**
29 - * 识别模式监听 12 + * 激活人脸license并初始化模块
30 */ 13 */
31 - interface OnVerifyListener { 14 + fun init(context:Context,active_key:String,appId:String,
  15 + sdkKey:String,callBack: ResultCallBack)
32 16
33 - /**  
34 - * 人脸识别成功  
35 - * @param faceId 人脸Id  
36 - */  
37 - fun onVerifySucceed(faceId: String)  
38 -  
39 - /**  
40 - * 人脸识别失败  
41 - * @param msg 提示消息  
42 - */  
43 - fun onVerifyFailed(msg: String)  
44 -  
45 - }  
46 -  
47 -  
48 - /**  
49 - * 激活人脸license  
50 - */  
51 - fun initLicense(context:Context,active_key:String,appId:String,  
52 - sdkKey:String): Int  
53 -  
54 - /**  
55 - * 初始化人脸模块  
56 - * @param appId  
57 - * @param appKey  
58 - */  
59 - fun initFace(context:Context): StatusData  
60 17
61 /** 18 /**
62 * 初始化配置模块 19 * 初始化配置模块
63 */ 20 */
64 fun initConfig(context: Context):Boolean 21 fun initConfig(context: Context):Boolean
65 - /**  
66 - * 设置录入模式监听  
67 - * @param listener 录入模式监听  
68 - */  
69 - fun setEnrollListener(listener: OnEnrollListener)  
70 -  
71 - /**  
72 - * 设置识别模式监听  
73 - * @param listener 识别模式监听  
74 - */  
75 - fun setVerifyListener(listener: OnVerifyListener)  
76 22
77 23
78 /** 24 /**
@@ -106,4 +52,6 @@ interface FaceInterface { @@ -106,4 +52,6 @@ interface FaceInterface {
106 */ 52 */
107 fun getRecognizeBitmap(byteArray: ByteArray,width: Int,height: Int):Bitmap 53 fun getRecognizeBitmap(byteArray: ByteArray,width: Int,height: Int):Bitmap
108 54
  55 +
  56 +
109 } 57 }
  1 +package com.yhkj.rebotsdk.face
  2 +
  3 +/**
  4 + * Created by zhangweiwei on 2020/3/18.
  5 + */
  6 +interface ResultCallBack {
  7 + /**
  8 + * 回调函数 code 0 : 成功;code 1 加载失败
  9 + * @param code
  10 + * @param response
  11 + */
  12 + fun onLicenseResult(code: Int, response: String?)
  13 +
  14 +
  15 + fun onModelResult(code: Int,response: String?)
  16 +}
1 rootProject.name='rebot_sdk' 1 rootProject.name='rebot_sdk'
2 include ':sdk', ':arcface', ':bdface' 2 include ':sdk', ':arcface', ':bdface'
  3 +include ':test'
  1 +apply plugin: 'com.android.application'
  2 +apply plugin: 'kotlin-android'
  3 +apply plugin: 'kotlin-android-extensions'
  4 +
  5 +android {
  6 + compileSdkVersion 28
  7 + buildToolsVersion "29.0.3"
  8 +
  9 + defaultConfig {
  10 + applicationId "com.yhkj.test"
  11 + minSdkVersion 23
  12 + targetSdkVersion 28
  13 + versionCode 1
  14 + versionName "1.0"
  15 + multiDexEnabled true
  16 +
  17 + ndk {
  18 + moduleName "facesdk"
  19 + ldLibs "log"
  20 + abiFilters "armeabi-v7a" // "armeabi", "x86", "arm64-v8a"
  21 + }
  22 +
  23 + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
  24 + }
  25 +
  26 +
  27 + buildTypes {
  28 + release {
  29 + minifyEnabled false
  30 + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
  31 + }
  32 + }
  33 +
  34 +}
  35 +
  36 +dependencies {
  37 + implementation fileTree(dir: 'libs', include: ['*.jar'])
  38 + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
  39 + implementation 'com.android.support:appcompat-v7:28.0.0'
  40 + implementation 'com.android.support.constraint:constraint-layout:1.1.3'
  41 + testImplementation 'junit:junit:4.12'
  42 + androidTestImplementation 'com.android.support.test:runner:1.0.2'
  43 + androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
  44 + implementation 'com.apkfuns.logutils:library:1.4.0'
  45 + api project(path: ':sdk')
  46 +}
  1 +# Add project specific ProGuard rules here.
  2 +# You can control the set of applied configuration files using the
  3 +# proguardFiles setting in build.gradle.
  4 +#
  5 +# For more details, see
  6 +# http://developer.android.com/guide/developing/tools/proguard.html
  7 +
  8 +# If your project uses WebView with JS, uncomment the following
  9 +# and specify the fully qualified class name to the JavaScript interface
  10 +# class:
  11 +#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
  12 +# public *;
  13 +#}
  14 +
  15 +# Uncomment this to preserve the line number information for
  16 +# debugging stack traces.
  17 +#-keepattributes SourceFile,LineNumberTable
  18 +
  19 +# If you keep the line number information, uncomment this to
  20 +# hide the original source file name.
  21 +#-renamesourcefileattribute SourceFile
1 -package com.yhkj.rebotsdk 1 +package com.yhkj.test
2 2
3 import android.support.test.InstrumentationRegistry 3 import android.support.test.InstrumentationRegistry
4 import android.support.test.runner.AndroidJUnit4 4 import android.support.test.runner.AndroidJUnit4
@@ -19,6 +19,6 @@ class ExampleInstrumentedTest { @@ -19,6 +19,6 @@ class ExampleInstrumentedTest {
19 fun useAppContext() { 19 fun useAppContext() {
20 // Context of the app under test. 20 // Context of the app under test.
21 val appContext = InstrumentationRegistry.getInstrumentation().targetContext 21 val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22 - assertEquals("com.yhkj.rebotsdk", appContext.packageName) 22 + assertEquals("com.yhkj.test", appContext.packageName)
23 } 23 }
24 } 24 }
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  3 + xmlns:tools="http://schemas.android.com/tools"
  4 + package="com.yhkj.test">
  5 +
  6 + <uses-permission android:name="android.permission.INTERNET"/>
  7 + <uses-permission android:name="android.permission.CAMERA" />
  8 + <uses-permission android:name="android.permission.READ_PHONE_STATE" />
  9 + <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
  10 + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
  11 +
  12 + <application
  13 + android:allowBackup="true"
  14 + android:icon="@mipmap/ic_launcher"
  15 + android:label="@string/app_name"
  16 + android:roundIcon="@mipmap/ic_launcher_round"
  17 + android:supportsRtl="true"
  18 + android:theme="@style/AppTheme"
  19 + tools:replace="android:allowBackup">
  20 + <activity android:name=".MainActivity">
  21 + <intent-filter>
  22 + <action android:name="android.intent.action.MAIN" />
  23 +
  24 + <category android:name="android.intent.category.LAUNCHER" />
  25 + </intent-filter>
  26 + </activity>
  27 + <activity android:name=".FaceAttributeRGBActivity" />
  28 + <activity android:name=".FaceRGBCloseDebugSearchActivity" />
  29 + </application>
  30 +
  31 +</manifest>
  1 +package com.yhkj.test
  2 +
  3 +import android.Manifest
  4 +import android.app.Activity
  5 +import android.content.pm.PackageManager
  6 +import android.os.Build
  7 +import android.os.Bundle
  8 +import android.view.KeyEvent
  9 +import android.view.Window
  10 +import android.view.WindowManager
  11 +import java.util.*
  12 +
  13 +/**
  14 + * Created by zhangweiwei on 2020/3/18.
  15 + */
  16 +open class BaseActivity: Activity() {
  17 + override fun onCreate(savedInstanceState: Bundle?) {
  18 + super.onCreate(savedInstanceState)
  19 + // 将activity设置为全屏显示
  20 + requestWindowFeature(Window.FEATURE_NO_TITLE)
  21 + window.setFlags(
  22 + WindowManager.LayoutParams.FLAG_FULLSCREEN,
  23 + WindowManager.LayoutParams.FLAG_FULLSCREEN
  24 + )
  25 + window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
  26 + requestPermissions(99)
  27 + }
  28 +
  29 + // 请求权限
  30 + fun requestPermissions(requestCode: Int) {
  31 + try {
  32 + if (Build.VERSION.SDK_INT >= 23) {
  33 + val requestPerssionArr =
  34 + ArrayList<String>()
  35 + val hasCamera =
  36 + checkSelfPermission(Manifest.permission.CAMERA)
  37 + if (hasCamera != PackageManager.PERMISSION_GRANTED) {
  38 + requestPerssionArr.add(Manifest.permission.CAMERA)
  39 + }
  40 + val hasPhoneStateRead =
  41 + checkSelfPermission(Manifest.permission.READ_PHONE_STATE)
  42 + if (hasPhoneStateRead != PackageManager.PERMISSION_GRANTED) {
  43 + requestPerssionArr.add(Manifest.permission.READ_PHONE_STATE)
  44 + }
  45 + val hasSdcardRead =
  46 + checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE)
  47 + if (hasSdcardRead != PackageManager.PERMISSION_GRANTED) {
  48 + requestPerssionArr.add(Manifest.permission.READ_EXTERNAL_STORAGE)
  49 + }
  50 + val hasSdcardWrite =
  51 + checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
  52 + if (hasSdcardWrite != PackageManager.PERMISSION_GRANTED) {
  53 + requestPerssionArr.add(Manifest.permission.WRITE_EXTERNAL_STORAGE)
  54 + }
  55 + // 是否应该显示权限请求
  56 + if (requestPerssionArr.size >= 1) {
  57 + val requestArray =
  58 + arrayOfNulls<String>(requestPerssionArr.size)
  59 + for (i in requestArray.indices) {
  60 + requestArray[i] = requestPerssionArr[i]
  61 + }
  62 + requestPermissions(requestArray, requestCode)
  63 + }
  64 + }
  65 + } catch (e: Exception) {
  66 + e.printStackTrace()
  67 + }
  68 + }
  69 +
  70 + override fun onRequestPermissionsResult(
  71 + requestCode: Int, permissions: Array<String?>,
  72 + grantResults: IntArray
  73 + ) {
  74 + var flag = false
  75 + for (i in permissions.indices) {
  76 + if (PackageManager.PERMISSION_GRANTED == grantResults[i]) {
  77 + flag = true
  78 + }
  79 + }
  80 + }
  81 +
  82 + override fun dispatchKeyEvent(event: KeyEvent): Boolean {
  83 + return if (event.keyCode == KeyEvent.KEYCODE_BACK) {
  84 + true
  85 + } else {
  86 + super.dispatchKeyEvent(event)
  87 + }
  88 + }
  89 +}
  1 +package com.yhkj.test;
  2 +
  3 +import android.graphics.Bitmap;
  4 +import android.graphics.Canvas;
  5 +import android.graphics.Color;
  6 +import android.graphics.Paint;
  7 +import android.graphics.PorterDuff;
  8 +import android.graphics.RectF;
  9 +import android.hardware.Camera;
  10 +import android.os.Bundle;
  11 +import android.util.Log;
  12 +import android.view.TextureView;
  13 +import android.view.View;
  14 +import android.widget.Button;
  15 +import android.widget.ImageView;
  16 +import android.widget.TextView;
  17 +
  18 +import com.baidu.idl.main.facesdk.FaceInfo;
  19 +import com.baidu.idl.main.facesdk.callback.CameraDataCallback;
  20 +import com.baidu.idl.main.facesdk.callback.FaceDetectCallBack;
  21 +import com.baidu.idl.main.facesdk.camera.AutoTexturePreviewView;
  22 +import com.baidu.idl.main.facesdk.camera.CameraPreviewManager;
  23 +import com.baidu.idl.main.facesdk.manager.FaceSDKManager;
  24 +import com.baidu.idl.main.facesdk.manager.FaceTrackManager;
  25 +import com.baidu.idl.main.facesdk.model.BDFaceImageInstance;
  26 +import com.baidu.idl.main.facesdk.model.BDFaceSDKCommon;
  27 +import com.baidu.idl.main.facesdk.model.LivenessModel;
  28 +import com.baidu.idl.main.facesdk.model.SingleBaseConfig;
  29 +import com.baidu.idl.main.facesdk.utils.BitmapUtils;
  30 +import com.baidu.idl.main.facesdk.utils.FaceOnDrawTexturViewUtil;
  31 +
  32 +
  33 +public class FaceAttributeRGBActivity extends BaseActivity {
  34 +
  35 +
  36 + private AutoTexturePreviewView mPreviewView;
  37 + private TextView textAttr;
  38 + private TextView rgbLivenessScoreTv;
  39 + private TextView rgbLivenssDurationTv;
  40 + private TextView tipTv;
  41 + private TextView tvDetectDuration;
  42 + private ImageView imagePreview;
  43 +
  44 + // 图片越大,性能消耗越大,也可以选择640*480, 1280*720
  45 + private static final int mWidth = SingleBaseConfig.getBaseConfig().getRgbAndNirWidth();
  46 + private static final int mHeight = SingleBaseConfig.getBaseConfig().getRgbAndNirHeight();
  47 +
  48 + // textureView用于绘制人脸框等。
  49 + private TextureView textureView;
  50 +
  51 + private RectF rectF;
  52 + private Paint paint;
  53 +
  54 + // 包含适配屏幕后后的人脸的x坐标,y坐标,和width
  55 + private float[] pointXY = new float[3];
  56 + private boolean requestToInner = false;
  57 + private TextView detectText;
  58 + private Button btnBack;
  59 +
  60 + @Override
  61 + protected void onCreate(Bundle savedInstanceState) {
  62 + super.onCreate(savedInstanceState);
  63 + setContentView(R.layout.activity_attribute_track);
  64 +
  65 + // 属性开启属性检测
  66 + SingleBaseConfig.getBaseConfig().setAttribute(true);
  67 + FaceSDKManager.getInstance().initConfig();
  68 +
  69 + findView();
  70 +
  71 + }
  72 +
  73 + private void findView() {
  74 + btnBack = findViewById(R.id.btn_back);
  75 + mPreviewView = findViewById(R.id.preview_view);
  76 + textureView = findViewById(R.id.texture_view);
  77 + textureView.setOpaque(false);
  78 + // 不需要屏幕自动变黑。
  79 + textureView.setKeepScreenOn(true);
  80 + textAttr = findViewById(R.id.text_attr);
  81 + rgbLivenessScoreTv = findViewById(R.id.text_rgb_liveness_score);
  82 + rgbLivenssDurationTv = findViewById(R.id.text_rgb_livenss_duration);
  83 + tipTv = findViewById(R.id.text_tip);
  84 + tvDetectDuration = findViewById(R.id.text_face_detect_duration);
  85 +
  86 + imagePreview = findViewById(R.id.image_preview);
  87 +
  88 + // 画人脸框
  89 + rectF = new RectF();
  90 + paint = new Paint();
  91 + detectText = findViewById(R.id.detect_text);
  92 +
  93 + btnBack.setOnClickListener(new View.OnClickListener() {
  94 + @Override
  95 + public void onClick(View view) {
  96 + finish();
  97 + }
  98 + });
  99 + }
  100 +
  101 + @Override
  102 + protected void onResume() {
  103 + super.onResume();
  104 + // 摄像头图像预览
  105 + startCameraPreview();
  106 + Log.e("qing", "start camera");
  107 + }
  108 +
  109 + /**
  110 + * 摄像头图像预览
  111 + */
  112 + private void startCameraPreview() {
  113 + // 设置前置摄像头
  114 + // CameraPreviewManager.getInstance().setCameraFacing(CameraPreviewManager.CAMERA_FACING_FRONT);
  115 + // 设置后置摄像头
  116 +// CameraPreviewManager.getInstance().setCameraFacing(CameraPreviewManager.CAMERA_FACING_BACK);
  117 + // 设置USB摄像头
  118 + CameraPreviewManager.getInstance().setCameraFacing(CameraPreviewManager.CAMERA_USB);
  119 +
  120 + CameraPreviewManager.getInstance().startPreview(this, mPreviewView, mWidth, mHeight, new CameraDataCallback() {
  121 + @Override
  122 + public void onGetCameraData(byte[] data, Camera camera, int width, int height) {
  123 + // 拿到相机帧数据
  124 + faceDetect(data, width, height);
  125 +
  126 + // 调试模式打开 显示实际送检图片的方向,SDK只检测人脸朝上的图
  127 + boolean isRGBDisplay = SingleBaseConfig.getBaseConfig().getDisplay();
  128 + if (isRGBDisplay) {
  129 + showDetectImage(data);
  130 + }
  131 + }
  132 + });
  133 + }
  134 +
  135 + /**
  136 + * 摄像头数据处理
  137 + *
  138 + * @param data
  139 + * @param width
  140 + * @param height
  141 + */
  142 + private void faceDetect(byte[] data, int width, int height) {
  143 +
  144 + // 无活体检测
  145 + FaceTrackManager.getInstance().setAliving(true);
  146 + FaceTrackManager.getInstance().faceTrack(data, width, height, new FaceDetectCallBack() {
  147 + @Override
  148 + public void onFaceDetectCallback(LivenessModel livenessModel) {
  149 +// ToastUtils.toast(getApplicationContext(), String.valueOf(livenessModel.getRgbLivenessScore()));
  150 + // 输出结果
  151 + if (SingleBaseConfig.getBaseConfig().getDetectFrame().equals("fixedarea")) {
  152 + isInserLimit(livenessModel);
  153 + // 输出结果
  154 + checkResult(livenessModel);
  155 + }
  156 +
  157 + if (SingleBaseConfig.getBaseConfig().getDetectFrame().equals("wireframe")) {
  158 + showFrame(livenessModel);
  159 + checkResult(livenessModel);
  160 + }
  161 + }
  162 +
  163 + @Override
  164 + public void onTip(int code, final String msg) {
  165 + displayTip(msg);
  166 + }
  167 +
  168 + @Override
  169 + public void onFaceDetectDarwCallback(LivenessModel livenessModel) {
  170 +
  171 + }
  172 + });
  173 +
  174 +
  175 + }
  176 +
  177 +
  178 + /**
  179 + * 显示检测的图片。用于调试,如果人脸sdk检测的人脸需要朝上,可以通过该图片判断。实际应用中可注释掉
  180 + *
  181 + * @param rgb
  182 + */
  183 + private void showDetectImage(byte[] rgb) {
  184 + if (rgb == null) {
  185 + return;
  186 + }
  187 + BDFaceImageInstance rgbInstance = new BDFaceImageInstance(rgb, mHeight,
  188 + mWidth, BDFaceSDKCommon.BDFaceImageType.BDFACE_IMAGE_TYPE_YUV_NV21,
  189 + SingleBaseConfig.getBaseConfig().getDetectDirection(),
  190 + SingleBaseConfig.getBaseConfig().getMirrorRGB());
  191 + BDFaceImageInstance imageInstance = rgbInstance.getImage();
  192 + final Bitmap bitmap = BitmapUtils.getInstaceBmp(imageInstance);
  193 + runOnUiThread(new Runnable() {
  194 + @Override
  195 + public void run() {
  196 + imagePreview.setVisibility(View.VISIBLE);
  197 + imagePreview.setImageBitmap(bitmap);
  198 + }
  199 + });
  200 + // 流程结束销毁图片,开始下一帧图片检测,否则内存泄露
  201 + rgbInstance.destory();
  202 + }
  203 +
  204 +
  205 + @Override
  206 + protected void onDestroy() {
  207 + super.onDestroy();
  208 + CameraPreviewManager.getInstance().stopPreview();
  209 + // 关闭属性检测
  210 + SingleBaseConfig.getBaseConfig().setAttribute(false);
  211 + FaceSDKManager.getInstance().initConfig();
  212 + }
  213 +
  214 +
  215 + // 检测结果输出
  216 + private void checkResult(LivenessModel model) {
  217 +
  218 + if (model == null) {
  219 + clearTip();
  220 + return;
  221 + } else {
  222 + displayTip("");
  223 + }
  224 +
  225 + if (requestToInner) {
  226 + runOnUiThread(new Runnable() {
  227 + @Override
  228 + public void run() {
  229 + clearTip();
  230 + detectText.setText("预览区域内人脸不全");
  231 + }
  232 + });
  233 + return;
  234 + } else {
  235 + runOnUiThread(new Runnable() {
  236 + @Override
  237 + public void run() {
  238 + detectText.setText("");
  239 + }
  240 + });
  241 + }
  242 +
  243 + displayResult(model, null);
  244 + attrCheck(model);
  245 + }
  246 +
  247 + /**
  248 + * 人脸属性检测
  249 + *
  250 + * @param model
  251 + */
  252 + private void attrCheck(LivenessModel model) {
  253 + final FaceInfo faceInfo = model.getFaceInfo();
  254 + // todo 人脸属性数据获取
  255 + runOnUiThread(new Runnable() {
  256 + @Override
  257 + public void run() {
  258 + textAttr.setText("人脸属性:" + getMsg(faceInfo));
  259 + }
  260 + });
  261 + }
  262 +
  263 + public String getMsg(FaceInfo faceInfo) {
  264 + StringBuilder msg = new StringBuilder();
  265 + if (faceInfo != null) {
  266 + msg.append(faceInfo.age);
  267 + msg.append(",").append(faceInfo.emotionThree == BDFaceSDKCommon.BDFaceEmotion.BDFACE_EMOTION_CALM ?
  268 + "平静"
  269 + : faceInfo.emotionThree == BDFaceSDKCommon.BDFaceEmotion.BDFACE_EMOTION_SMILE ? "笑"
  270 + : faceInfo.emotionThree == BDFaceSDKCommon.BDFaceEmotion.BDFACE_EMOTION_FROWN ? "皱眉" : "没有表情");
  271 + msg.append(",").append(faceInfo.gender == BDFaceSDKCommon.BDFaceGender.BDFACE_GENDER_FEMALE ? "女性" :
  272 + faceInfo.gender == BDFaceSDKCommon.BDFaceGender.BDFACE_GENDER_MALE ? "男性" : "婴儿");
  273 + msg.append(",").append(faceInfo.glasses == BDFaceSDKCommon.BDFaceGlasses.BDFACE_NO_GLASSES ? "无眼镜"
  274 + : faceInfo.glasses == BDFaceSDKCommon.BDFaceGlasses.BDFACE_GLASSES ? "有眼镜"
  275 + : faceInfo.glasses == BDFaceSDKCommon.BDFaceGlasses.BDFACE_SUN_GLASSES ? "墨镜" : "太阳镜");
  276 + msg.append(",").append(faceInfo.race == BDFaceSDKCommon.BDFaceRace.BDFACE_RACE_YELLOW ? "黄种人"
  277 + : faceInfo.race == BDFaceSDKCommon.BDFaceRace.BDFACE_RACE_WHITE ? "白种人"
  278 + : faceInfo.race == BDFaceSDKCommon.BDFaceRace.BDFACE_RACE_BLACK ? "黑种人"
  279 + : faceInfo.race == BDFaceSDKCommon.BDFaceRace.BDFACE_RACE_INDIAN ? "印度人"
  280 + : "地球人");
  281 + }
  282 + return msg.toString();
  283 + }
  284 +
  285 + private void displayResult(final LivenessModel livenessModel, final String livess) {
  286 + runOnUiThread(new Runnable() {
  287 + @Override
  288 + public void run() {
  289 + if (livess != null && livess.equals("livess")) {
  290 + rgbLivenessScoreTv.setVisibility(View.VISIBLE);
  291 + rgbLivenssDurationTv.setVisibility(View.VISIBLE);
  292 + rgbLivenessScoreTv.setText("RGB活体得分:" + livenessModel.getRgbLivenessScore());
  293 + rgbLivenssDurationTv.setText("RGB活体耗时:" + livenessModel.getRgbLivenessDuration());
  294 + } else {
  295 + rgbLivenessScoreTv.setVisibility(View.GONE);
  296 + rgbLivenssDurationTv.setVisibility(View.GONE);
  297 + }
  298 + tvDetectDuration.setText("人脸检测耗时:" + livenessModel.getRgbDetectDuration());
  299 + }
  300 + });
  301 + }
  302 +
  303 + private void displayTip(final String tip) {
  304 + runOnUiThread(new Runnable() {
  305 + @Override
  306 + public void run() {
  307 + tipTv.setText(tip);
  308 + }
  309 + });
  310 + }
  311 +
  312 + private void clearTip() {
  313 + runOnUiThread(new Runnable() {
  314 + @Override
  315 + public void run() {
  316 + tvDetectDuration.setText("");
  317 + rgbLivenessScoreTv.setText("");
  318 + rgbLivenssDurationTv.setText("");
  319 + textAttr.setText("");
  320 + if (SingleBaseConfig.getBaseConfig().getDetectFrame().equals("wireframe")) {
  321 + detectText.setText("");
  322 + }
  323 + }
  324 + });
  325 + }
  326 +
  327 +
  328 + /**
  329 + * 绘制人脸框
  330 + */
  331 + private void showFrame(final LivenessModel model) {
  332 + runOnUiThread(new Runnable() {
  333 + @Override
  334 + public void run() {
  335 + Canvas canvas = textureView.lockCanvas();
  336 + if (canvas == null) {
  337 + textureView.unlockCanvasAndPost(canvas);
  338 + return;
  339 + }
  340 + if (model == null) {
  341 + // 清空canvas
  342 + canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
  343 + textureView.unlockCanvasAndPost(canvas);
  344 + return;
  345 + }
  346 + FaceInfo[] faceInfos = model.getTrackFaceInfo();
  347 + if (faceInfos == null || faceInfos.length == 0) {
  348 + // 清空canvas
  349 + canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
  350 + textureView.unlockCanvasAndPost(canvas);
  351 + return;
  352 + }
  353 + canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
  354 + FaceInfo faceInfo = faceInfos[0];
  355 +
  356 + rectF.set(FaceOnDrawTexturViewUtil.getFaceRectTwo(faceInfo));
  357 + // 检测图片的坐标和显示的坐标不一样,需要转换。
  358 + FaceOnDrawTexturViewUtil.mapFromOriginalRect(rectF,
  359 + mPreviewView, model.getBdFaceImageInstance());
  360 + paint.setColor(Color.GREEN);
  361 + paint.setStyle(Paint.Style.STROKE);
  362 + // 绘制框
  363 + canvas.drawRect(rectF, paint);
  364 + textureView.unlockCanvasAndPost(canvas);
  365 + }
  366 + });
  367 + }
  368 +
  369 +
  370 + // 判断人脸是否在园内
  371 + private void isInserLimit(final LivenessModel livenessModel) {
  372 + if (livenessModel == null) {
  373 + requestToInner = false;
  374 + return;
  375 + }
  376 + FaceInfo[] faceInfos = livenessModel.getTrackFaceInfo();
  377 + if (faceInfos == null || faceInfos.length == 0) {
  378 + requestToInner = false;
  379 + return;
  380 + }
  381 + pointXY[0] = livenessModel.getFaceInfo().centerX;
  382 + pointXY[1] = livenessModel.getFaceInfo().centerY;
  383 + pointXY[2] = livenessModel.getFaceInfo().width;
  384 + FaceOnDrawTexturViewUtil.converttPointXY(pointXY, mPreviewView,
  385 + livenessModel.getBdFaceImageInstance(), livenessModel.getFaceInfo().width);
  386 + float lfetLimitX = AutoTexturePreviewView.circleX - AutoTexturePreviewView.circleRadius;
  387 + float rightLimitX = AutoTexturePreviewView.circleX + AutoTexturePreviewView.circleRadius;
  388 + float topLimitY = AutoTexturePreviewView.circleY - AutoTexturePreviewView.circleRadius;
  389 + float bottomLimitY = AutoTexturePreviewView.circleY + AutoTexturePreviewView.circleRadius;
  390 +
  391 + if (pointXY[0] - pointXY[2] / 2 < lfetLimitX
  392 + || pointXY[0] + pointXY[2] / 2 > rightLimitX
  393 + || pointXY[1] - pointXY[2] / 2 < topLimitY
  394 + || pointXY[1] + pointXY[2] / 2 > bottomLimitY) {
  395 + requestToInner = true;
  396 + } else {
  397 + requestToInner = false;
  398 + }
  399 + }
  400 +}
  1 +package com.yhkj.test;
  2 +
  3 +import android.content.Context;
  4 +import android.graphics.Bitmap;
  5 +import android.graphics.BitmapFactory;
  6 +import android.graphics.Canvas;
  7 +import android.graphics.Color;
  8 +import android.graphics.Paint;
  9 +import android.graphics.PorterDuff;
  10 +import android.graphics.RectF;
  11 +import android.hardware.Camera;
  12 +import android.os.Bundle;
  13 +import android.view.Gravity;
  14 +import android.view.TextureView;
  15 +import android.view.View;
  16 +import android.widget.Button;
  17 +import android.widget.FrameLayout;
  18 +import android.widget.RelativeLayout;
  19 +import android.widget.TextView;
  20 +
  21 +import com.baidu.idl.main.facesdk.FaceInfo;
  22 +import com.baidu.idl.main.facesdk.callback.CameraDataCallback;
  23 +import com.baidu.idl.main.facesdk.callback.FaceDetectCallBack;
  24 +import com.baidu.idl.main.facesdk.camera.AutoTexturePreviewView;
  25 +import com.baidu.idl.main.facesdk.camera.CameraPreviewManager;
  26 +import com.baidu.idl.main.facesdk.manager.FaceSDKManager;
  27 +import com.baidu.idl.main.facesdk.model.LivenessModel;
  28 +import com.baidu.idl.main.facesdk.model.SingleBaseConfig;
  29 +import com.baidu.idl.main.facesdk.model.User;
  30 +import com.baidu.idl.main.facesdk.utils.DensityUtils;
  31 +import com.baidu.idl.main.facesdk.utils.FaceOnDrawTexturViewUtil;
  32 +import com.baidu.idl.main.facesdk.utils.FileUtils;
  33 +import com.baidu.idl.main.facesdk.view.CircleImageView;
  34 +
  35 +
  36 +/**
  37 + * @Time 2019/06/02
  38 + * @Author v_shishuaifeng
  39 + * @Description RGB关闭Debug页面
  40 + */
  41 +public class FaceRGBCloseDebugSearchActivity extends BaseActivity implements View.OnClickListener {
  42 +
  43 + // 图片越大,性能消耗越大,也可以选择640*480, 1280*720
  44 + private static final int PREFER_WIDTH = SingleBaseConfig.getBaseConfig().getRgbAndNirWidth();
  45 + private static final int PERFER_HEIGH = SingleBaseConfig.getBaseConfig().getRgbAndNirHeight();
  46 +
  47 + private Context mContext;
  48 +
  49 + // 关闭Debug 模式
  50 + private AutoTexturePreviewView mAutoCameraPreviewView;
  51 + private TextView mDetectText;
  52 + private CircleImageView mDetectImage;
  53 + private TextView mTrackText;
  54 + private TextureView mFaceDetectImageView;
  55 + private Paint paint;
  56 + private RectF rectF;
  57 + private RelativeLayout relativeLayout;
  58 + private int mLiveType;
  59 + private float mRgbLiveScore;
  60 +
  61 + // 包含适配屏幕后后的人脸的x坐标,y坐标,和width
  62 + private float[] pointXY = new float[3];
  63 + private boolean requestToInner = false;
  64 +
  65 +
  66 + @Override
  67 + protected void onCreate(Bundle savedInstanceState) {
  68 + super.onCreate(savedInstanceState);
  69 + setContentView(R.layout.activity_face_rgb_close_debug);
  70 + mContext = this;
  71 + initView();
  72 +
  73 + // 屏幕的宽
  74 + int displayWidth = DensityUtils.getDisplayWidth(mContext);
  75 + // 屏幕的高
  76 + int displayHeight = DensityUtils.getDisplayHeight(mContext);
  77 + // 当屏幕的宽大于屏幕宽时
  78 + if (displayHeight < displayWidth) {
  79 + // 获取高
  80 + int height = displayHeight;
  81 + // 获取宽
  82 + int width = (int) (displayHeight * ((9.0f / 16.0f)));
  83 + // 设置布局的宽和高
  84 + FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(width, height);
  85 + // 设置布局居中
  86 + params.gravity = Gravity.CENTER;
  87 + relativeLayout.setLayoutParams(params);
  88 + }
  89 + }
  90 +
  91 + /***
  92 + * 关闭Debug 模式view
  93 + */
  94 +
  95 + private void initView() {
  96 + // 活体状态
  97 + mLiveType = SingleBaseConfig.getBaseConfig().getType();
  98 + // 活体阈值
  99 + mRgbLiveScore = SingleBaseConfig.getBaseConfig().getRgbLiveScore();
  100 + // 获取整个布局
  101 + relativeLayout = findViewById(R.id.all_relative);
  102 + // 画人脸框
  103 + paint = new Paint();
  104 + rectF = new RectF();
  105 + mFaceDetectImageView = findViewById(R.id.draw_detect_face_view);
  106 + mFaceDetectImageView.setOpaque(false);
  107 + mFaceDetectImageView.setKeepScreenOn(true);
  108 +
  109 + // 返回
  110 + Button mButReturn = findViewById(R.id.btn_back);
  111 + mButReturn.setOnClickListener(this);
  112 + // 设置
  113 + Button mBtSetting = findViewById(R.id.btn_setting);
  114 + mBtSetting.setOnClickListener(this);
  115 +
  116 + // 单目摄像头RGB 图像预览
  117 + mAutoCameraPreviewView = findViewById(R.id.auto_camera_preview_view);
  118 + mAutoCameraPreviewView.setVisibility(View.VISIBLE);
  119 +
  120 + mDetectText = findViewById(R.id.detect_text);
  121 + mDetectImage = findViewById(R.id.detect_reg_image_item);
  122 + mTrackText = findViewById(R.id.track_txt);
  123 +
  124 + // 调试按钮
  125 + findViewById(R.id.debug_btn).setOnClickListener(this);
  126 +
  127 + }
  128 +
  129 +
  130 + @Override
  131 + protected void onResume() {
  132 + super.onResume();
  133 + startTestCloseDebugRegisterFunction();
  134 + }
  135 +
  136 + private void startTestCloseDebugRegisterFunction() {
  137 + // TODO : 临时放置
  138 + CameraPreviewManager.getInstance().setCameraFacing(CameraPreviewManager.CAMERA_USB);
  139 + CameraPreviewManager.getInstance().startPreview(this, mAutoCameraPreviewView,
  140 + PREFER_WIDTH, PERFER_HEIGH, new CameraDataCallback() {
  141 + @Override
  142 + public void onGetCameraData(byte[] data, Camera camera, int width, int height) {
  143 + // 摄像头预览数据进行人脸检测
  144 + FaceSDKManager.getInstance().onDetectCheck(data, null, null,
  145 + height, width, mLiveType, new FaceDetectCallBack() {
  146 + @Override
  147 + public void onFaceDetectCallback(LivenessModel livenessModel) {
  148 +
  149 + if (SingleBaseConfig.getBaseConfig().getDetectFrame().equals("fixedarea")) {
  150 + isInserLimit(livenessModel);
  151 + // 输出结果
  152 + checkCloseResult(livenessModel);
  153 + }
  154 +
  155 + if (SingleBaseConfig.getBaseConfig().getDetectFrame().equals("wireframe")) {
  156 + checkCloseResult(livenessModel);
  157 + }
  158 + }
  159 +
  160 + @Override
  161 + public void onTip(int code, String msg) {
  162 + displayTip(code, msg);
  163 + }
  164 +
  165 + @Override
  166 + public void onFaceDetectDarwCallback(LivenessModel livenessModel) {
  167 + if (SingleBaseConfig.getBaseConfig().getDetectFrame().equals("wireframe")) {
  168 + showFrame(livenessModel);
  169 + }
  170 +
  171 + }
  172 + });
  173 + }
  174 + });
  175 + }
  176 +
  177 + private void displayTip(final int code, final String tip) {
  178 + runOnUiThread(new Runnable() {
  179 + @Override
  180 + public void run() {
  181 + if (code == 0) {
  182 + mTrackText.setVisibility(View.GONE);
  183 + } else {
  184 + mTrackText.setVisibility(View.VISIBLE);
  185 + mTrackText.setText("识别失败");
  186 + mTrackText.setBackgroundColor(Color.RED);
  187 + mDetectImage.setImageResource(R.mipmap.ic_littleicon);
  188 + }
  189 + mDetectText.setText(tip);
  190 + }
  191 + });
  192 + }
  193 +
  194 + private void checkCloseResult(final LivenessModel livenessModel) {
  195 + // 当未检测到人脸UI显示
  196 + runOnUiThread(new Runnable() {
  197 + @Override
  198 + public void run() {
  199 + if (livenessModel == null || livenessModel.getFaceInfo() == null) {
  200 + mTrackText.setVisibility(View.GONE);
  201 + mDetectText.setText("未检测到人脸");
  202 + mDetectImage.setImageResource(R.mipmap.ic_littleicon);
  203 + return;
  204 + } else {
  205 + if (requestToInner) {
  206 + mTrackText.setVisibility(View.VISIBLE);
  207 + mTrackText.setText("预览区域内人脸不全");
  208 + mTrackText.setBackgroundColor(Color.RED);
  209 + mDetectText.setText("");
  210 + mDetectText.setVisibility(View.VISIBLE);
  211 + mDetectImage.setImageResource(R.mipmap.ic_littleicon);
  212 + return;
  213 + }
  214 + if (mLiveType == 1) {
  215 + User user = livenessModel.getUser();
  216 + if (user == null) {
  217 + mTrackText.setVisibility(View.VISIBLE);
  218 + mTrackText.setText("识别失败");
  219 + mTrackText.setBackgroundColor(Color.RED);
  220 + mDetectText.setText("搜索不到用户");
  221 + mDetectText.setVisibility(View.VISIBLE);
  222 + mDetectImage.setImageResource(R.mipmap.ic_littleicon);
  223 + } else {
  224 +
  225 + String absolutePath = FileUtils.getBatchImportSuccessDirectory()
  226 + + "/" + user.getImageName();
  227 + Bitmap bitmap = BitmapFactory.decodeFile(absolutePath);
  228 + mDetectImage.setImageBitmap(bitmap);
  229 + mTrackText.setVisibility(View.VISIBLE);
  230 + mTrackText.setText("识别成功");
  231 + mTrackText.setBackgroundColor(Color.rgb(66, 147, 136));
  232 + mDetectText.setText("欢迎您, " + user.getUserName());
  233 + }
  234 + } else {
  235 + float rgbLivenessScore = livenessModel.getRgbLivenessScore();
  236 + if (rgbLivenessScore < mRgbLiveScore) {
  237 + mTrackText.setVisibility(View.VISIBLE);
  238 + mTrackText.setText("识别失败");
  239 + mTrackText.setBackgroundColor(Color.RED);
  240 + mDetectText.setText("活体检测未通过");
  241 + mDetectImage.setImageResource(R.mipmap.ic_littleicon);
  242 + } else {
  243 + User user = livenessModel.getUser();
  244 + if (user == null) {
  245 + mTrackText.setVisibility(View.VISIBLE);
  246 + mTrackText.setText("识别失败");
  247 + mTrackText.setBackgroundColor(Color.RED);
  248 + mDetectText.setText("搜索不到用户");
  249 + mDetectText.setVisibility(View.VISIBLE);
  250 + mDetectImage.setImageResource(R.mipmap.ic_littleicon);
  251 + } else {
  252 +
  253 + String absolutePath = FileUtils.getBatchImportSuccessDirectory()
  254 + + "/" + user.getImageName();
  255 + Bitmap bitmap = BitmapFactory.decodeFile(absolutePath);
  256 + mDetectImage.setImageBitmap(bitmap);
  257 + mTrackText.setVisibility(View.VISIBLE);
  258 + mTrackText.setText("识别成功");
  259 + mTrackText.setBackgroundColor(Color.rgb(66, 147, 136));
  260 + mDetectText.setText("欢迎您, " + user.getUserName());
  261 + }
  262 + }
  263 + }
  264 +
  265 + }
  266 + }
  267 + });
  268 + }
  269 +
  270 + @Override
  271 + public void onClick(View v) {
  272 + switch (v.getId()) {
  273 + // 返回
  274 + case R.id.btn_back:
  275 + finish();
  276 + break;
  277 + // 设置
  278 + case R.id.btn_setting:
  279 +// Intent intent = new Intent(mContext, SettingMainActivity.class);
  280 +// intent.putExtra("page_type", "search");
  281 +// startActivityForResult(intent, PAGE_TYPE);
  282 +// finish();
  283 + break;
  284 + case R.id.debug_btn:
  285 +// mAutoCameraPreviewView.removeAllViews();
  286 +// startActivity(new Intent(this, FaceRGBOpenDebugSearchActivity.class));
  287 +// finish();
  288 + break;
  289 + default:
  290 + break;
  291 + }
  292 +
  293 +
  294 + }
  295 +
  296 + /**
  297 + * 绘制人脸框。
  298 + */
  299 + private void showFrame(final LivenessModel model) {
  300 + runOnUiThread(new Runnable() {
  301 + @Override
  302 + public void run() {
  303 + Canvas canvas = mFaceDetectImageView.lockCanvas();
  304 + if (canvas == null) {
  305 + mFaceDetectImageView.unlockCanvasAndPost(canvas);
  306 + return;
  307 + }
  308 + if (model == null) {
  309 + // 清空canvas
  310 + canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
  311 + mFaceDetectImageView.unlockCanvasAndPost(canvas);
  312 + return;
  313 + }
  314 + FaceInfo[] faceInfos = model.getTrackFaceInfo();
  315 + if (faceInfos == null || faceInfos.length == 0) {
  316 + // 清空canvas
  317 + canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
  318 + mFaceDetectImageView.unlockCanvasAndPost(canvas);
  319 + return;
  320 + }
  321 + canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
  322 + FaceInfo faceInfo = faceInfos[0];
  323 +
  324 + rectF.set(FaceOnDrawTexturViewUtil.getFaceRectTwo(faceInfo));
  325 + // 检测图片的坐标和显示的坐标不一样,需要转换。
  326 + FaceOnDrawTexturViewUtil.mapFromOriginalRect(rectF,
  327 + mAutoCameraPreviewView, model.getBdFaceImageInstance());
  328 + paint.setColor(Color.GREEN);
  329 + paint.setStyle(Paint.Style.STROKE);
  330 + // 绘制框
  331 + canvas.drawRect(rectF, paint);
  332 + mFaceDetectImageView.unlockCanvasAndPost(canvas);
  333 +
  334 + }
  335 + });
  336 + }
  337 +
  338 + // 判断人脸是否在园内
  339 + private void isInserLimit(final LivenessModel livenessModel) {
  340 + if (livenessModel == null) {
  341 + requestToInner = false;
  342 + return;
  343 + }
  344 + FaceInfo[] faceInfos = livenessModel.getTrackFaceInfo();
  345 + if (faceInfos == null || faceInfos.length == 0) {
  346 + requestToInner = false;
  347 + return;
  348 + }
  349 +
  350 + pointXY[0] = livenessModel.getFaceInfo().centerX;
  351 + pointXY[1] = livenessModel.getFaceInfo().centerY;
  352 + pointXY[2] = livenessModel.getFaceInfo().width;
  353 + FaceOnDrawTexturViewUtil.converttPointXY(pointXY, mAutoCameraPreviewView,
  354 + livenessModel.getBdFaceImageInstance(), livenessModel.getFaceInfo().width);
  355 + float lfetLimitX = AutoTexturePreviewView.circleX - AutoTexturePreviewView.circleRadius;
  356 + float rightLimitX = AutoTexturePreviewView.circleX + AutoTexturePreviewView.circleRadius;
  357 + float topLimitY = AutoTexturePreviewView.circleY - AutoTexturePreviewView.circleRadius;
  358 + float bottomLimitY = AutoTexturePreviewView.circleY + AutoTexturePreviewView.circleRadius;
  359 +
  360 + if (pointXY[0] - pointXY[2] / 2 < lfetLimitX
  361 + || pointXY[0] + pointXY[2] / 2 > rightLimitX
  362 + || pointXY[1] - pointXY[2] / 2 < topLimitY
  363 + || pointXY[1] + pointXY[2] / 2 > bottomLimitY) {
  364 + requestToInner = true;
  365 + } else {
  366 + requestToInner = false;
  367 + }
  368 + }
  369 +
  370 +}
  1 +package com.yhkj.test
  2 +
  3 +import android.content.Intent
  4 +import android.os.Bundle
  5 +import com.yhkj.rebotsdk.face.FaceApi
  6 +import com.yhkj.rebotsdk.face.ResultCallBack
  7 +import com.yhkj.test.util.ToastUtils
  8 +import kotlinx.android.synthetic.main.activity_main.*
  9 +
  10 +class MainActivity : BaseActivity() {
  11 +
  12 + private val faceApi=FaceApi.INSTANCE
  13 + override fun onCreate(savedInstanceState: Bundle?) {
  14 + super.onCreate(savedInstanceState)
  15 + setContentView(R.layout.activity_main)
  16 + test.setOnClickListener {
  17 + faceApi.init(applicationContext,
  18 + "HDDJ-PARX-TMWX-HUYX", "",
  19 + "", object : ResultCallBack {
  20 + override fun onLicenseResult(code: Int, response: String?) {
  21 + if(code==0){
  22 + ToastUtils.toast(applicationContext,"激活成功")
  23 + }else{
  24 + ToastUtils.toast(applicationContext,response)
  25 + }
  26 + }
  27 +
  28 + override fun onModelResult(code: Int, response: String?) {
  29 + if(code!=0){
  30 + ToastUtils.toast(applicationContext,response)
  31 + }else{
  32 + val intent=Intent(this@MainActivity,
  33 + FaceRGBCloseDebugSearchActivity::class.java)
  34 + startActivity(intent)
  35 + }
  36 + }
  37 +
  38 + })
  39 +
  40 +
  41 +
  42 + }
  43 +
  44 + }
  45 +}
  1 +package com.yhkj.test.util;
  2 +
  3 +import android.content.Context;
  4 +import android.os.Handler;
  5 +import android.os.Looper;
  6 +import android.widget.Toast;
  7 +
  8 +public class ToastUtils {
  9 +
  10 + private static Handler handler = new Handler(Looper.getMainLooper());
  11 +
  12 + public static void toast(final Context context, final String text) {
  13 + handler.post(new Runnable() {
  14 + @Override
  15 + public void run() {
  16 + Toast.makeText(context, text, Toast.LENGTH_SHORT).show();
  17 + }
  18 + });
  19 + }
  20 +
  21 + public static void toast(final Context context, final int resId) {
  22 + handler.post(new Runnable() {
  23 + @Override
  24 + public void run() {
  25 + Toast.makeText(context, resId, Toast.LENGTH_SHORT).show();
  26 + }
  27 + });
  28 + }
  29 +}
  1 +<vector xmlns:android="http://schemas.android.com/apk/res/android"
  2 + xmlns:aapt="http://schemas.android.com/aapt"
  3 + android:width="108dp"
  4 + android:height="108dp"
  5 + android:viewportWidth="108"
  6 + android:viewportHeight="108">
  7 + <path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
  8 + <aapt:attr name="android:fillColor">
  9 + <gradient
  10 + android:endX="85.84757"
  11 + android:endY="92.4963"
  12 + android:startX="42.9492"
  13 + android:startY="49.59793"
  14 + android:type="linear">
  15 + <item
  16 + android:color="#44000000"
  17 + android:offset="0.0" />
  18 + <item
  19 + android:color="#00000000"
  20 + android:offset="1.0" />
  21 + </gradient>
  22 + </aapt:attr>
  23 + </path>
  24 + <path
  25 + android:fillColor="#FFFFFF"
  26 + android:fillType="nonZero"
  27 + android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
  28 + android:strokeWidth="1"
  29 + android:strokeColor="#00000000" />
  30 +</vector>
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<shape xmlns:android="http://schemas.android.com/apk/res/android">
  3 + <corners android:radius="2dp" />
  4 + <solid android:color="#036838"></solid>
  5 +</shape>
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<shape xmlns:android="http://schemas.android.com/apk/res/android">
  3 + <corners android:radius="2dp" />
  4 + <solid android:color="#ec808d"></solid>
  5 +</shape>
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<shape xmlns:android="http://schemas.android.com/apk/res/android"
  3 + android:shape="oval"
  4 + android:useLevel="false">
  5 + <stroke
  6 + android:width="3dp"
  7 + android:color="#016838" />
  8 + <size android:width="60dp"
  9 + android:height="60dp" />
  10 +</shape>
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<vector xmlns:android="http://schemas.android.com/apk/res/android"
  3 + android:width="108dp"
  4 + android:height="108dp"
  5 + android:viewportWidth="108"
  6 + android:viewportHeight="108">
  7 + <path
  8 + android:fillColor="#3DDC84"
  9 + android:pathData="M0,0h108v108h-108z" />
  10 + <path
  11 + android:fillColor="#00000000"
  12 + android:pathData="M9,0L9,108"
  13 + android:strokeWidth="0.8"
  14 + android:strokeColor="#33FFFFFF" />
  15 + <path
  16 + android:fillColor="#00000000"
  17 + android:pathData="M19,0L19,108"
  18 + android:strokeWidth="0.8"
  19 + android:strokeColor="#33FFFFFF" />
  20 + <path
  21 + android:fillColor="#00000000"
  22 + android:pathData="M29,0L29,108"
  23 + android:strokeWidth="0.8"
  24 + android:strokeColor="#33FFFFFF" />
  25 + <path
  26 + android:fillColor="#00000000"
  27 + android:pathData="M39,0L39,108"
  28 + android:strokeWidth="0.8"
  29 + android:strokeColor="#33FFFFFF" />
  30 + <path
  31 + android:fillColor="#00000000"
  32 + android:pathData="M49,0L49,108"
  33 + android:strokeWidth="0.8"
  34 + android:strokeColor="#33FFFFFF" />
  35 + <path
  36 + android:fillColor="#00000000"
  37 + android:pathData="M59,0L59,108"
  38 + android:strokeWidth="0.8"
  39 + android:strokeColor="#33FFFFFF" />
  40 + <path
  41 + android:fillColor="#00000000"
  42 + android:pathData="M69,0L69,108"
  43 + android:strokeWidth="0.8"
  44 + android:strokeColor="#33FFFFFF" />
  45 + <path
  46 + android:fillColor="#00000000"
  47 + android:pathData="M79,0L79,108"
  48 + android:strokeWidth="0.8"
  49 + android:strokeColor="#33FFFFFF" />
  50 + <path
  51 + android:fillColor="#00000000"
  52 + android:pathData="M89,0L89,108"
  53 + android:strokeWidth="0.8"
  54 + android:strokeColor="#33FFFFFF" />
  55 + <path
  56 + android:fillColor="#00000000"
  57 + android:pathData="M99,0L99,108"
  58 + android:strokeWidth="0.8"
  59 + android:strokeColor="#33FFFFFF" />
  60 + <path
  61 + android:fillColor="#00000000"
  62 + android:pathData="M0,9L108,9"
  63 + android:strokeWidth="0.8"
  64 + android:strokeColor="#33FFFFFF" />
  65 + <path
  66 + android:fillColor="#00000000"
  67 + android:pathData="M0,19L108,19"
  68 + android:strokeWidth="0.8"
  69 + android:strokeColor="#33FFFFFF" />
  70 + <path
  71 + android:fillColor="#00000000"
  72 + android:pathData="M0,29L108,29"
  73 + android:strokeWidth="0.8"
  74 + android:strokeColor="#33FFFFFF" />
  75 + <path
  76 + android:fillColor="#00000000"
  77 + android:pathData="M0,39L108,39"
  78 + android:strokeWidth="0.8"
  79 + android:strokeColor="#33FFFFFF" />
  80 + <path
  81 + android:fillColor="#00000000"
  82 + android:pathData="M0,49L108,49"
  83 + android:strokeWidth="0.8"
  84 + android:strokeColor="#33FFFFFF" />
  85 + <path
  86 + android:fillColor="#00000000"
  87 + android:pathData="M0,59L108,59"
  88 + android:strokeWidth="0.8"
  89 + android:strokeColor="#33FFFFFF" />
  90 + <path
  91 + android:fillColor="#00000000"
  92 + android:pathData="M0,69L108,69"
  93 + android:strokeWidth="0.8"
  94 + android:strokeColor="#33FFFFFF" />
  95 + <path
  96 + android:fillColor="#00000000"
  97 + android:pathData="M0,79L108,79"
  98 + android:strokeWidth="0.8"
  99 + android:strokeColor="#33FFFFFF" />
  100 + <path
  101 + android:fillColor="#00000000"
  102 + android:pathData="M0,89L108,89"
  103 + android:strokeWidth="0.8"
  104 + android:strokeColor="#33FFFFFF" />
  105 + <path
  106 + android:fillColor="#00000000"
  107 + android:pathData="M0,99L108,99"
  108 + android:strokeWidth="0.8"
  109 + android:strokeColor="#33FFFFFF" />
  110 + <path
  111 + android:fillColor="#00000000"
  112 + android:pathData="M19,29L89,29"
  113 + android:strokeWidth="0.8"
  114 + android:strokeColor="#33FFFFFF" />
  115 + <path
  116 + android:fillColor="#00000000"
  117 + android:pathData="M19,39L89,39"
  118 + android:strokeWidth="0.8"
  119 + android:strokeColor="#33FFFFFF" />
  120 + <path
  121 + android:fillColor="#00000000"
  122 + android:pathData="M19,49L89,49"
  123 + android:strokeWidth="0.8"
  124 + android:strokeColor="#33FFFFFF" />
  125 + <path
  126 + android:fillColor="#00000000"
  127 + android:pathData="M19,59L89,59"
  128 + android:strokeWidth="0.8"
  129 + android:strokeColor="#33FFFFFF" />
  130 + <path
  131 + android:fillColor="#00000000"
  132 + android:pathData="M19,69L89,69"
  133 + android:strokeWidth="0.8"
  134 + android:strokeColor="#33FFFFFF" />
  135 + <path
  136 + android:fillColor="#00000000"
  137 + android:pathData="M19,79L89,79"
  138 + android:strokeWidth="0.8"
  139 + android:strokeColor="#33FFFFFF" />
  140 + <path
  141 + android:fillColor="#00000000"
  142 + android:pathData="M29,19L29,89"
  143 + android:strokeWidth="0.8"
  144 + android:strokeColor="#33FFFFFF" />
  145 + <path
  146 + android:fillColor="#00000000"
  147 + android:pathData="M39,19L39,89"
  148 + android:strokeWidth="0.8"
  149 + android:strokeColor="#33FFFFFF" />
  150 + <path
  151 + android:fillColor="#00000000"
  152 + android:pathData="M49,19L49,89"
  153 + android:strokeWidth="0.8"
  154 + android:strokeColor="#33FFFFFF" />
  155 + <path
  156 + android:fillColor="#00000000"
  157 + android:pathData="M59,19L59,89"
  158 + android:strokeWidth="0.8"
  159 + android:strokeColor="#33FFFFFF" />
  160 + <path
  161 + android:fillColor="#00000000"
  162 + android:pathData="M69,19L69,89"
  163 + android:strokeWidth="0.8"
  164 + android:strokeColor="#33FFFFFF" />
  165 + <path
  166 + android:fillColor="#00000000"
  167 + android:pathData="M79,19L79,89"
  168 + android:strokeWidth="0.8"
  169 + android:strokeColor="#33FFFFFF" />
  170 +</vector>
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<shape xmlns:android="http://schemas.android.com/apk/res/android">
  3 + <solid android:color="@null" />
  4 + <padding
  5 + android:bottom="1dip"
  6 + android:left="1dip"
  7 + android:right="1dip" />
  8 +
  9 + <stroke
  10 + android:width="1dip"
  11 + android:color="#000" />
  12 + <!--边框宽度-->
  13 + <size
  14 + android:width="10dip"
  15 + android:height="10dip" />
  16 +
  17 +</shape>
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<!--
  3 + ~ Copyright (C) 2018 Baidu, Inc. All Rights Reserved.
  4 + -->
  5 +<RelativeLayout
  6 + xmlns:android="http://schemas.android.com/apk/res/android"
  7 + android:layout_width="match_parent"
  8 + android:layout_height="match_parent">
  9 +
  10 + <com.baidu.idl.main.facesdk.camera.AutoTexturePreviewView
  11 + android:id="@+id/preview_view"
  12 + android:layout_width="match_parent"
  13 + android:layout_height="match_parent"/>
  14 +
  15 + <TextureView
  16 + android:id="@+id/texture_view"
  17 + android:layout_width="match_parent"
  18 + android:layout_height="match_parent" />
  19 +
  20 + <ImageView
  21 + android:id="@+id/image_preview"
  22 + android:layout_width="150dp"
  23 + android:layout_height="150dp"
  24 + android:layout_alignParentRight="true"
  25 + android:layout_marginBottom="30dp"
  26 + android:layout_marginRight="0dp"
  27 + android:visibility="invisible" />
  28 +
  29 +
  30 + <TextView
  31 + android:id="@+id/text_tip"
  32 + android:layout_width="wrap_content"
  33 + android:layout_height="wrap_content"
  34 + android:layout_centerHorizontal="true"
  35 + android:layout_marginTop="20dp"
  36 + android:textColor="#ffffff"
  37 + android:textSize="20dp"/>
  38 +
  39 + <LinearLayout
  40 + android:layout_width="match_parent"
  41 + android:layout_height="wrap_content"
  42 + android:layout_alignParentBottom="true"
  43 + android:orientation="vertical">
  44 +
  45 + <TextView
  46 + android:id="@+id/detect_text"
  47 + android:layout_width="wrap_content"
  48 + android:layout_height="wrap_content"
  49 + android:layout_marginLeft="20dp"
  50 + android:layout_marginTop="20dp"
  51 + android:textColor="#87CEFA"
  52 + android:textSize="20dp"/>
  53 +
  54 + <TextView
  55 + android:id="@+id/text_face_detect_duration"
  56 + android:layout_width="wrap_content"
  57 + android:layout_height="wrap_content"
  58 + android:layout_marginLeft="20dp"
  59 + android:layout_marginTop="20dp"
  60 + android:textColor="#87CEFA"
  61 + android:textSize="20dp"/>
  62 +
  63 + <TextView
  64 + android:id="@+id/text_rgb_liveness_score"
  65 + android:layout_width="wrap_content"
  66 + android:layout_height="wrap_content"
  67 + android:layout_marginLeft="20dp"
  68 + android:layout_marginTop="8dp"
  69 + android:textColor="#87CEFA"
  70 + android:textSize="20dp"/>
  71 +
  72 + <TextView
  73 + android:id="@+id/text_rgb_livenss_duration"
  74 + android:layout_width="wrap_content"
  75 + android:layout_height="wrap_content"
  76 + android:layout_marginLeft="20dp"
  77 + android:layout_marginTop="8dp"
  78 + android:textColor="#87CEFA"
  79 + android:textSize="20dp"/>
  80 +
  81 + <TextView
  82 + android:id="@+id/text_attr"
  83 + android:layout_width="wrap_content"
  84 + android:layout_height="wrap_content"
  85 + android:layout_marginLeft="20dp"
  86 + android:layout_marginTop="8dp"
  87 + android:textColor="#87CEFA"
  88 + android:textSize="20dp"/>
  89 +
  90 + </LinearLayout>
  91 +
  92 + <Button
  93 + android:id="@+id/btn_back"
  94 + android:layout_width="60dp"
  95 + android:layout_height="30dp"
  96 + android:layout_marginLeft="15dp"
  97 + android:layout_marginTop="15dp"
  98 + android:background="@drawable/fillet_style_return_selector"
  99 + android:text="返回"
  100 + android:textColor="#ffffff" />
  101 +</RelativeLayout>
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3 + android:id="@+id/all_relative"
  4 + android:layout_width="match_parent"
  5 + android:background="@color/white"
  6 + android:layout_height="match_parent">
  7 +
  8 + <include
  9 + android:id="@+id/search_title"
  10 + layout="@layout/layout_search_title" />
  11 +
  12 + <Button
  13 + android:id="@+id/debug_btn"
  14 + android:layout_width="wrap_content"
  15 + android:layout_height="35dp"
  16 + android:layout_alignParentRight="true"
  17 + android:layout_below="@id/search_title"
  18 + android:layout_margin="5dp"
  19 + android:textColor="#000"
  20 + android:background="@drawable/logout_selector"
  21 + android:text="开启调试" />
  22 +
  23 + <TextureView
  24 + android:id="@+id/ir_camera_preview_view"
  25 + android:layout_width="1dp"
  26 + android:layout_height="1dp"
  27 + android:layout_above="@+id/layout_info"
  28 + android:layout_below="@id/search_title"
  29 + android:visibility="invisible" />
  30 +
  31 + <TextureView
  32 + android:id="@+id/camera_preview_view"
  33 + android:layout_width="match_parent"
  34 + android:layout_height="match_parent"
  35 + android:layout_above="@+id/layout_info"
  36 + android:layout_below="@id/search_title"
  37 + android:visibility="gone" />
  38 +
  39 + <com.baidu.idl.main.facesdk.camera.AutoTexturePreviewView
  40 + android:id="@+id/auto_camera_preview_view"
  41 + android:layout_width="match_parent"
  42 + android:layout_height="match_parent"
  43 + android:layout_above="@id/layout_info"
  44 + android:layout_below="@id/search_title" />
  45 +
  46 +
  47 + <TextureView
  48 + android:id="@+id/draw_detect_face_view"
  49 + android:layout_width="match_parent"
  50 + android:layout_height="match_parent"
  51 + android:layout_above="@+id/layout_info"
  52 + android:layout_below="@id/search_title" />
  53 +
  54 + <View
  55 + android:layout_width="match_parent"
  56 + android:layout_height="1dp"
  57 + android:layout_below="@id/auto_camera_preview_view"
  58 + android:background="#898989" />
  59 +
  60 + <TextView
  61 + android:id="@+id/track_txt"
  62 + android:layout_width="match_parent"
  63 + android:layout_height="30dp"
  64 + android:layout_above="@+id/layout_info"
  65 + android:layout_alignParentStart="true"
  66 + android:background="#016838"
  67 + android:gravity="center"
  68 + android:textColor="#ffffff"
  69 + android:visibility="gone" />
  70 +
  71 + <RelativeLayout
  72 + android:id="@+id/layout_info"
  73 + android:layout_width="match_parent"
  74 + android:layout_height="195dp"
  75 + android:layout_alignParentBottom="true"
  76 + android:gravity="center_horizontal">
  77 +
  78 + <include layout="@layout/activity_search_itme" />
  79 +
  80 + </RelativeLayout>
  81 +</RelativeLayout>
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3 + xmlns:app="http://schemas.android.com/apk/res-auto"
  4 + xmlns:tools="http://schemas.android.com/tools"
  5 + android:layout_width="match_parent"
  6 + android:layout_height="match_parent"
  7 + tools:context=".MainActivity">
  8 +
  9 + <TextView
  10 + android:id="@+id/test"
  11 + android:layout_width="wrap_content"
  12 + android:layout_height="wrap_content"
  13 + android:text="@string/active_license"
  14 + android:textSize="@dimen/sp_30"
  15 + app:layout_constraintBottom_toBottomOf="parent"
  16 + app:layout_constraintLeft_toLeftOf="parent"
  17 + app:layout_constraintRight_toRightOf="parent"
  18 + app:layout_constraintTop_toTopOf="parent" />
  19 +
  20 +</android.support.constraint.ConstraintLayout>
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3 + android:layout_width="match_parent"
  4 + android:layout_height="match_parent">
  5 +
  6 + <TextView
  7 + android:id="@+id/detect_text"
  8 + android:layout_width="match_parent"
  9 + android:layout_height="30dp"
  10 + android:layout_alignParentStart="true"
  11 + android:layout_marginBottom="12dp"
  12 + android:gravity="center"
  13 + android:textColor="#000000" />
  14 +
  15 + <RelativeLayout
  16 + android:layout_width="140dp"
  17 + android:layout_height="140dp"
  18 + android:layout_alignParentTop="true"
  19 + android:layout_centerHorizontal="true"
  20 + android:layout_marginTop="40dp"
  21 + android:background="@drawable/fillet_style_search_item">
  22 +
  23 + <com.baidu.idl.main.facesdk.view.CircleImageView
  24 + android:id="@+id/detect_reg_image_item"
  25 + android:layout_width="134dp"
  26 + android:layout_height="134dp"
  27 + android:layout_centerHorizontal="true"
  28 + android:layout_centerVertical="true"
  29 + android:src="@mipmap/ic_littleicon"
  30 + />
  31 +
  32 + </RelativeLayout>
  33 +</RelativeLayout>
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3 + android:layout_width="wrap_content"
  4 + android:layout_height="wrap_content">
  5 + <Button
  6 + android:id="@+id/btn_back"
  7 + android:layout_width="60dp"
  8 + android:layout_height="30dp"
  9 + android:layout_marginLeft="15dp"
  10 + android:layout_marginTop="15dp"
  11 + android:background="@drawable/fillet_style_return_selector"
  12 + android:text="返回"
  13 + android:textColor="#ffffff" />
  14 +
  15 + <TextView
  16 + android:id="@+id/textView4"
  17 + android:layout_width="match_parent"
  18 + android:layout_height="wrap_content"
  19 + android:layout_marginTop="15dp"
  20 + android:gravity="center"
  21 + android:text="视频流动态人脸搜索"
  22 + android:textColor="#363636"
  23 + android:textSize="22dp"
  24 + android:textStyle="bold" />
  25 +
  26 + <Button
  27 + android:id="@+id/btn_setting"
  28 + android:layout_width="60dp"
  29 + android:layout_height="30dp"
  30 + android:layout_alignParentEnd="true"
  31 + android:layout_alignTop="@+id/textView4"
  32 + android:layout_marginEnd="14dp"
  33 + android:background="@drawable/fillet_style_button_selector"
  34 + android:text="设置"
  35 + android:textColor="#ffffff" />
  36 +
  37 + <View
  38 + android:id="@+id/shang_view"
  39 + android:layout_width="match_parent"
  40 + android:layout_height="1dp"
  41 + android:layout_below="@id/btn_back"
  42 + android:layout_marginTop="15dp"
  43 + android:background="#898989" />
  44 +
  45 +</RelativeLayout>
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
  3 + <background android:drawable="@drawable/ic_launcher_background" />
  4 + <foreground android:drawable="@drawable/ic_launcher_foreground" />
  5 +</adaptive-icon>
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
  3 + <background android:drawable="@drawable/ic_launcher_background" />
  4 + <foreground android:drawable="@drawable/ic_launcher_foreground" />
  5 +</adaptive-icon>
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<resources>
  3 + <color name="colorPrimary">#6200EE</color>
  4 + <color name="colorPrimaryDark">#3700B3</color>
  5 + <color name="colorAccent">#03DAC5</color>
  6 +
  7 + <color name="buttonColor">#FF6600</color>
  8 + <color name="white">#FFFFFF</color>
  9 + <color name="black">#000000</color>
  10 + <color name="buttonBg">#016838</color>
  11 + <color name="red">#FF0033</color>
  12 +</resources>
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<resources>
  3 + <dimen name="dp_30">30dp</dimen>
  4 + <dimen name="sp_30">30sp</dimen>
  5 +</resources>
  1 +<resources>
  2 + <string name="app_name">test</string>
  3 + <string name="active_license">激活人脸算法</string>
  4 +</resources>
  1 +<resources>
  2 +
  3 + <!-- Base application theme. -->
  4 + <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
  5 + <!-- Customize your theme here. -->
  6 + <item name="colorPrimary">@color/colorPrimary</item>
  7 + <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
  8 + <item name="colorAccent">@color/colorAccent</item>
  9 + </style>
  10 +
  11 +</resources>
1 -package com.yhkj.rebotsdk 1 +package com.yhkj.test
2 2
3 import org.junit.Test 3 import org.junit.Test
4 4