正在显示
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 | -} |
arcface/src/main/java/com/arcsoft/arcfacedemo/activity/IrRegisterAndRecognizeActivity.java
已删除
100644 → 0
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 | -} |
arcface/src/main/java/com/arcsoft/arcfacedemo/activity/RegisterAndRecognizeActivity.java
已删除
100644 → 0
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 | +} |
bdface/src/main/res/values/attr.xml
0 → 100644
@@ -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 | +} |
test/.gitignore
0 → 100644
1 | +/build |
test/build.gradle
0 → 100644
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 | +} |
test/proguard-rules.pro
0 → 100644
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 | } |
test/src/main/AndroidManifest.xml
0 → 100644
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 | + 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> |
test/src/main/res/layout/activity_main.xml
0 → 100644
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> |
3.5 KB
5.2 KB
2.6 KB
3.3 KB
4.8 KB
7.3 KB
7.7 KB
11.6 KB
15.5 KB
10.4 KB
16.2 KB
test/src/main/res/values/colors.xml
0 → 100644
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> |
test/src/main/res/values/dimens.xml
0 → 100644
test/src/main/res/values/strings.xml
0 → 100644
test/src/main/res/values/styles.xml
0 → 100644
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> |
-
请 注册 或 登录 后发表评论