准备 :
登录官方网站,获取SDK,进行个人验证后新建项目,获取APP_ID,和SDK_KEY;
https://ai.arcsoft.com.cn/ucenter/resource/build/index.html#/ucenter/resource/openPlatform/application
实现 :
激活引擎 :
/**
* 激活引擎
*
* @param view
*/
public void activeEngine(final View view) { if (!checkPermissions(NEEDED_PERMISSIONS)) { ActivityCompat.requestPermissions(this, NEEDED_PERMISSIONS, ACTION_REQUEST_PERMISSIONS); return; } if (view != null) { view.setClickable(false); } Observable.create(new ObservableOnSubscribe<Integer>() { @Override public void subscribe(ObservableEmitter<Integer> emitter) throws Exception { FaceEngine faceEngine = new FaceEngine(); //申请的APP_ID 和SDK_key int activeCode = faceEngine.active(FaceMainActivity.this, Constants.APP_ID, Constants.SDK_KEY); emitter.onNext(activeCode); } }) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Observer<Integer>() { @Override public void onSubscribe(Disposable d) { } @Override public void onNext(Integer activeCode) { if (activeCode == ErrorInfo.MOK) { showToast("OK"); } else if (activeCode == ErrorInfo.MERR_ASF_ALREADY_ACTIVATED) { showToast("Not Active"); } else { showToast("shibai"); } if (view != null) { view.setClickable(true); } } @Override public void onError(Throwable e) { } @Override public void onComplete() { } }); }
初始化引擎 :
private void initEngine() { faceEngine = new FaceEngine(); faceEngineCode = faceEngine.init(this, FaceEngine.ASF_DETECT_MODE_IMAGE, FaceEngine.ASF_OP_0_HIGHER_EXT, 16, 10, FaceEngine.ASF_FACE_RECOGNITION | FaceEngine.ASF_FACE_DETECT | FaceEngine.ASF_AGE | FaceEngine.ASF_GENDER | FaceEngine.ASF_FACE3DANGLE | FaceEngine.ASF_LIVENESS); VersionInfo versionInfo = new VersionInfo(); faceEngine.getVersion(versionInfo); Log.i(TAG, "initEngine: init: " + faceEngineCode + " version:" + versionInfo); if (faceEngineCode != ErrorInfo.MOK) { Toast.makeText(this, "TEST", Toast.LENGTH_SHORT).show(); } }
注册相机事件 :
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) { File outImage = new File(getExternalCacheDir(), "output_image.jpg"); try { if (outImage.exists()) { outImage.delete(); } outImage.createNewFile(); } catch (IOException e) { e.printStackTrace(); } if (Build.VERSION.SDK_INT >= 24) { uri = FileProvider.getUriForFile(FaceMainActivity.this, "com.example.gdzc.cameraalbumtest.fileprovider", outImage); } else { uri = Uri.fromFile(outImage); } Intent intent = new Intent("android.media.action.IMAGE_CAPTURE"); intent.putExtra(MediaStore.EXTRA_OUTPUT, uri); startActivityForResult(intent, TAKE_POTHO); } });
显示提示返回结果信息 :
/**
* 展示提示信息并且关闭提示框
*
* @param stringBuilder 带格式的提示文字
*/
private void showNotificationAndFinish(final SpannableStringBuilder stringBuilder) { runOnUiThread(new Runnable() { @Override public void run() { if (tvNotice != null) { tvNotice.setText(stringBuilder.toString()); } if (progressDialog != null && progressDialog.isShowing()) { progressDialog.dismiss(); } } }); } /** * 追加提示信息 * * @param stringBuilder 提示的字符串的存放对象 * @param styleSpan 添加的字符串的格式 * @param strings 字符串数组 */ private void addNotificationInfo(SpannableStringBuilder stringBuilder, ParcelableSpan styleSpan, String... strings) { tvNotice.append(stringBuilder.toString()); if (stringBuilder == null || strings == null || strings.length == 0) { return; } int startLength = stringBuilder.length(); for (String string : strings) { stringBuilder.append(string); } int endLength = stringBuilder.length(); if (styleSpan != null) { stringBuilder.setSpan(styleSpan, startLength, endLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } }
人脸验证 :
首先人脸验证需要一个参照的人脸数据,与之后的人脸进行匹配,返回结果为通过与不通过。
//人脸比对数据显示
if (faceInfoList.size() > 0) {
if (type == TYPE_MAIN) {
int size = showInfoList.size(); showInfoList.clear(); showInfoAdapter.notifyItemRangeRemoved(0, size); ivMainImage.setImageBitmap(mainBitmap); mainFeature = new FaceFeature(); int res = faceEngine.extractFaceFeature(nv21, width, height, FaceEngine.CP_PAF_NV21, faceInfoList.get(0), mainFeature); if (res != ErrorInfo.MOK) { mainFeature = null; } ivMainImage.setImageBitmap(bitmap); StringBuilder stringBuilder = new StringBuilder(); if (faceInfoList.size() > 0) { stringBuilder.append("face info:\n\n"); } for (int i = 0; i < faceInfoList.size(); i++) { stringBuilder.append("face[") .append(i) .append("]:\n") .append(faceInfoList.get(i)) .append("\nage:") .append(ageInfoList.get(i).getAge()) .append("\ngender:") .append(genderInfoList.get(i).getGender() == GenderInfo.MALE ? "MALE" : (genderInfoList.get(i).getGender() == GenderInfo.FEMALE ? "FEMALE" : "UNKNOWN")) .append("\nface3DAngle:") .append(face3DAngleList.get(i)) .append("\n\n"); } tvMainImageInfo.setText(stringBuilder); } else if (type == TYPE_ITEM) { FaceFeature faceFeature = new FaceFeature(); int res = faceEngine.extractFaceFeature(nv21, width, height, FaceEngine.CP_PAF_NV21, faceInfoList.get(0), faceFeature); if (res == 0) { FaceSimilar faceSimilar = new FaceSimilar(); int compareResult = faceEngine.compareFaceFeature(mainFeature, faceFeature, faceSimilar); if (compareResult == ErrorInfo.MOK) { String resStr = ""; BigDecimal bigDecimal = new BigDecimal(faceSimilar.getScore()); if (bigDecimal.compareTo(new BigDecimal(0.8)) >= 0) { resStr = "通过"; }else{ resStr = "不通过"; } ItemShowInfo showInfo = new ItemShowInfo(bitmap, ageInfoList.get(0).getAge(), genderInfoList.get(0).getGender(), faceSimilar.getScore(),resStr); showInfoList.add(showInfo); showInfoAdapter.notifyItemInserted(showInfoList.size() - 1); } else { showToast("error"); } } } } else { if (type == TYPE_MAIN) { mainBitmap = null; } } } else { showToast("can not get nv21 from bitmap!"); }
通过对比结果,对比结果值越接近1就越相似,此处识别通过标准为0.8。
代码 :
对比 :
package com.example.ttlock.activity;
import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.provider.MediaStore; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; import android.support.v4.content.FileProvider; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.DividerItemDecoration; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.util.Log; import android.view.View; import android.view.WindowManager; import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; import com.arcsoft.face.AgeInfo; import com.arcsoft.face.ErrorInfo; import com.arcsoft.face.Face3DAngle; import com.arcsoft.face.FaceEngine; import com.arcsoft.face.FaceFeature; import com.arcsoft.face.FaceInfo; import com.arcsoft.face.FaceSimilar; import com.arcsoft.face.GenderInfo; import com.example.ttlock.R; import com.example.ttlock.adapter.widget.ShowInfoAdapter; import com.example.ttlock.model.ItemShowInfo; import com.example.ttlock.utils.ImageUtil; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class MultiImageActivity extends AppCompatActivity { private static final String TAG = "MultiImageActivity"; private static final int ACTION_CHOOSE_MAIN_IMAGE = 0x201; private static final int ACTION_ADD_RECYCLER_ITEM_IMAGE = 0x202; public static final int TAKE_POTHO = 1; private static final int ACTION_REQUEST_PERMISSIONS = 0x001; private Uri uri; private ImageView ivMainImage; private TextView tvMainImageInfo; /** * 选择图片时的类型 */ private int TYPE_MAIN = 0; private int TYPE_ITEM = 1; /** * 主图的第0张人脸的特征数据 */ private FaceFeature mainFeature; private ShowInfoAdapter showInfoAdapter; private List<ItemShowInfo> showInfoList; private FaceEngine faceEngine; private int faceEngineCode = -1; private Bitmap mainBitmap; Toast toast = null; private static String[] NEEDED_PERMISSIONS = new String[]{ Manifest.permission.READ_PHONE_STATE }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); setContentView(R.layout.activity_multi_image); /** * 在选择图片的时候,在android 7.0及以上通过FileProvider获取Uri,不需要文件权限 */ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) { List<String> permissionList = new ArrayList<>(Arrays.asList(NEEDED_PERMISSIONS)); permissionList.add(Manifest.permission.READ_EXTERNAL_STORAGE); NEEDED_PERMISSIONS = permissionList.toArray(new String[0]); } if (!checkPermissions(NEEDED_PERMISSIONS)) { ActivityCompat.requestPermissions(this, NEEDED_PERMISSIONS, ACTION_REQUEST_PERMISSIONS); } else { initEngine(); } initView(); } private void initView() { ivMainImage = findViewById(R.id.iv_main_image); tvMainImageInfo = findViewById(R.id.tv_main_image_info); RecyclerView recyclerFaces = findViewById(R.id.recycler_faces); showInfoList = new ArrayList<>(); showInfoAdapter = new ShowInfoAdapter(showInfoList, this); recyclerFaces.setAdapter(showInfoAdapter); recyclerFaces.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL)); recyclerFaces.setLayoutManager(new LinearLayoutManager(this)); } private void initEngine() { faceEngine = new FaceEngine(); faceEngineCode = faceEngine.init(this, FaceEngine.ASF_DETECT_MODE_IMAGE, FaceEngine.ASF_OP_0_HIGHER_EXT, 16, 6, FaceEngine.ASF_FACE_RECOGNITION | FaceEngine.ASF_AGE | FaceEngine.ASF_FACE_DETECT | FaceEngine.ASF_GENDER | FaceEngine.ASF_FACE3DANGLE); Log.i(TAG, "initEngine: init " + faceEngineCode); if (faceEngineCode != ErrorInfo.MOK) { Toast.makeText(this, "f", Toast.LENGTH_SHORT).show(); } } private void unInitEngine() { if (faceEngine != null) { faceEngineCode = faceEngine.unInit(); Log.i(TAG, "unInitEngine: " + faceEngineCode); } } @Override protected void onDestroy() { unInitEngine(); super.onDestroy(); } @Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); // // if (data == null || data.getData() == null) { // showToast("failed"); // return; // } // if (requestCode == ACTION_CHOOSE_MAIN_IMAGE) { // mainBitmap = ImageUtil.getBitmapFromUri(data.getData(), this); // if (mainBitmap == null) { // showToast("failed"); // return; // } // processImage(mainBitmap, TYPE_MAIN); // } else if (requestCode == ACTION_ADD_RECYCLER_ITEM_IMAGE) { // Bitmap bitmap = ImageUtil.getBitmapFromUri(data.getData(), this); // if (bitmap == null) { // showToast("failed"); // return; // } // if (mainFeature == null) { // return; // } // processImage(bitmap, TYPE_ITEM); // } switch (requestCode) { case ACTION_CHOOSE_MAIN_IMAGE: try { mainBitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(uri)); // imageView.setImageBitmap(mBitmap); //将图片解析成Bitmap对象,并把它显现出来 processImage(mainBitmap, TYPE_MAIN); } catch (FileNotFoundException e) { e.printStackTrace(); } break; case ACTION_ADD_RECYCLER_ITEM_IMAGE: Bitmap bitmap = null; try { bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(uri)); } catch (FileNotFoundException e) { e.printStackTrace(); } if (bitmap == null) { showToast("failed"); return; } if (mainFeature == null) { return; } processImage(bitmap, TYPE_ITEM); default: break; } } public void processImage(Bitmap bitmap, int type) { if (bitmap == null) { return; } if (faceEngine == null) { return; } //NV21宽度必须为4的倍数,高度为2的倍数 bitmap = ImageUtil.alignBitmapForNv21(bitmap); if (bitmap == null) { return; } int width = bitmap.getWidth(); int height = bitmap.getHeight(); //bitmap转NV21 final byte[] nv21 = ImageUtil.bitmapToNv21(bitmap, width, height); if (nv21 != null) { List<FaceInfo> faceInfoList = new ArrayList<>(); //人脸检测 int detectCode = faceEngine.detectFaces(nv21, width, height, FaceEngine.CP_PAF_NV21, faceInfoList); if (detectCode != 0 || faceInfoList.size() == 0) { showToast("face detection finished, code is " + detectCode + ", face num is " + faceInfoList.size()); return; } //绘制bitmap bitmap = bitmap.copy(Bitmap.Config.RGB_565, true); Canvas canvas = new Canvas(bitmap); Paint paint = new Paint(); paint.setAntiAlias(true); paint.setStrokeWidth(10); paint.setColor(Color.YELLOW); if (faceInfoList.size() > 0) {