Android 虹软人脸识别SDK-人脸对比

Posted yangchaojie

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 虹软人脸识别SDK-人脸对比相关的知识,希望对你有一定的参考价值。

准备 :

登录官方网站,获取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) 

                for (int i = 0; i < faceInfoList.size(); i++) 
                    //绘制人脸框
                    paint.setStyle(Paint.Style.STROKE);
                    canvas.drawRect(faceInfoList.get(i).getRect(), paint);
                    //绘制人脸序号
                    paint.setStyle(Paint.Style.FILL_AND_STROKE);
                    paint.setTextSize(faceInfoList.get(i).getRect().width() / 2);
                    canvas.drawText("" + i, faceInfoList.get(i).getRect().left, faceInfoList.get(i).getRect().top, paint);

                
            

            int faceProcessCode = faceEngine.process(nv21, width, height, FaceEngine.CP_PAF_NV21, faceInfoList, FaceEngine.ASF_AGE | FaceEngine.ASF_GENDER | FaceEngine.ASF_FACE3DANGLE);
            Log.i(TAG, "processImage: " + faceProcessCode);
            if (faceProcessCode != ErrorInfo.MOK) 
                showToast("face process finished, code is " + faceProcessCode);
                return;
            
            //年龄信息结果
            List<AgeInfo> ageInfoList = new ArrayList<>();
            //性别信息结果
            List<GenderInfo> genderInfoList = new ArrayList<>();
            //三维角度结果
            List<Face3DAngle> face3DAngleList = new ArrayList<>();
            //获取年龄、性别、三维角度
            int ageCode = faceEngine.getAge(ageInfoList);
            int genderCode = faceEngine.getGender(genderInfoList);
            int face3DAngleCode = faceEngine.getFace3DAngle(face3DAngleList);

            if ((ageCode | genderCode | face3DAngleCode) != ErrorInfo.MOK) 
                showToast("at lease one of age、gender、face3DAngle detect failed! codes are: " + ageCode
                        + " ," + genderCode + " ," + face3DAngleCode);
                return;
            

            //人脸比对数据显示
            if (faceInfoList.size() > 0) 
                if (type == TYPE_MAIN) 
                    int size = showInfoList.size();
                    showInfoList.clear();
                    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!");
        
    

    /**
     * 从本地选择文件
     *
     * @param action 可为选择主图@link #ACTION_CHOOSE_MAIN_IMAGE或者选择item图@link #ACTION_ADD_RECYCLER_ITEM_IMAGE
     */
    public void chooseLocalImage(int action) 
//        Intent intent = new Intent(Intent.ACTION_PICK);
//        intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*");
//        startActivityForResult(intent, action);
        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(MultiImageActivity.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, action);

    

    public void addItemFace(View view) 
        if (faceEngineCode != ErrorInfo.MOK) 
            showToast("引擎未初始化,错误码为" + faceEngineCode);
            return;
        
        if (mainBitmap == null) 
            showToast("\"请先选择主图\"");
            return;
        
        chooseLocalImage(ACTION_ADD_RECYCLER_ITEM_IMAGE);
    

    public void chooseMainImage(View view) 

        if (faceEngineCode != ErrorInfo.MOK) 
            showToast("引擎未初始化,错误码为" + faceEngineCode);
            return;
        
        chooseLocalImage(ACTION_CHOOSE_MAIN_IMAGE);
    

    private void showToast(String s) 
        if (toast == null) 
            toast = Toast.makeText(this, s, Toast.LENGTH_SHORT);
            toast.show();
         else 
            toast.setText(s);
            toast.show();
        
    

    private boolean checkPermissions(String[] neededPermissions) 
        if (neededPermissions == null || neededPermissions.length == 0) 
            return true;
        
        boolean allGranted = true;
        for (String neededPermission : neededPermissions) 
            allGranted &= ContextCompat.checkSelfPermission(this, neededPermission) == PackageManager.PERMISSION_GRANTED;
        
        return allGranted;
    

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) 
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == ACTION_REQUEST_PERMISSIONS) 
            boolean isAllGranted = true;
            for (int grantResult : grantResults) 
                isAllGranted &= (grantResult == PackageManager.PERMISSION_GRANTED);
            
            if (isAllGranted) 
                initEngine();
             else 
                showToast("权限被拒绝");
            
        
    

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    tools:context=".activity.MultiImageActivity">
    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <LinearLayout
            android:layout_margin="20dp"
            android:orientation="vertical"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center">

            <Button
                android:id="@+id/bt_choose_main_image"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:onClick="chooseMainImage"
                android:text="注册"/>
            <ImageView
                android:id="@+id/iv_main_image"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:minHeight="100dp"
                android:maxHeight="200dp"/>
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:id="@+id/tv_main_image_info"/>
        </LinearLayout>
        <View
            android:layout_width="match_parent"
            android:layout_height="1dp"
            android:background="#88000000"/>
        <LinearLayout
            android:layout_margin="20dp"
            android:orientation="vertical"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center">
            <Button
                android:id="@+id/bt_add_item"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:onClick="addItemFace"
                android:text="验证"/>
            <android.support.v7.widget.RecyclerView
                android:minHeight="300dp"
                android:id="@+id/recycler_faces"
                android:layout_width="match_parent"
                android:layout_height="match_parent" />
        </LinearLayout>
    </LinearLayout>
</ScrollView>

RecyclerView 适配器:

package com.example.ttlock.adapter.widget;

import android.content.Context;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import com.example.ttlock.R;
import com.example.ttlock.model.ItemShowInfo;

import java.util.List;

public class ShowInfoAdapter extends RecyclerView.Adapter<ShowInfoAdapter.ShowInfoHolder> 

    private List<ItemShowInfo> showInfoList;
    private LayoutInflater inflater;

    public ShowInfoAdapter(List<ItemShowInfo> showInfoList, Context context) 
        this.showInfoList = showInfoList;
        this.inflater = LayoutInflater.from(context);
    

    @NonNull
    @Override
    public ShowInfoHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) 
        View itemView = inflater.inflate(R.layout.item_show_info, viewGroup, false);
        ImageView ivHeadImage = itemView.findViewById(R.id.iv_item_head_img);
        TextView tvNotification = itemView.findViewById(R.id.tv_item_notification);
        ShowInfoHolder holder = new ShowInfoHolder(itemView);
        holder.ivHeadImage = ivHeadImage;
        holder.tvNotification = tvNotification;
        return holder;
    

    @Override
    public void onBindViewHolder(@NonNull ShowInfoHolder showInfoHolder, int i) 
        showInfoHolder.tvNotification.setText(showInfoList.get(i).getIsOk());
        showInfoHolder.ivHeadImage.setImageBitmap(showInfoList.get(i).getBitmap());
    

    @Override
    public int getItemCount() 
        return showInfoList==null?0:showInfoList.size();
    

    class ShowInfoHolder extends RecyclerView.ViewHolder
        ImageView ivHeadImage;
        TextView tvNotification;
        ShowInfoHolder(@NonNull View itemView) 
            super(itemView);
        
    

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="110dp"
    android:paddingTop="10dp"
    android:orientation="horizontal">
    <ImageView
        android:id="@+id/iv_item_head_img"
        android:layout_margin="10dp"
        android:layout_width="80dp"
        android:layout_height="80dp" />
    <TextView
        android:layout_gravity="center_vertical"
        android:id="@+id/tv_item_notification"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</LinearLayout>

item :

package com.example.ttlock.model;

import android.graphics.Bitmap;

import com.arcsoft.face.GenderInfo;


public class ItemShowInfo 
    private Bitmap bitmap;
    private int age;
    private int gender;
    private float similar;
    private String isOk;

    public ItemShowInfo() 
    

    public ItemShowInfo(Bitmap bitmap, int age, int gender, float similar, String isOk) 
        this.bitmap = bitmap;
        this.age = age;
        this.gender = gender;
        this.similar = similar;
        this.isOk = isOk;
    

    public String getIsOk() 
        return isOk;
    

    public void setIsOk(String isOk) 
        this.isOk = isOk;
    

    public Bitmap getBitmap() 
        return bitmap;
    

    public void setBitmap(Bitmap bitmap) 
        this.bitmap = bitmap;
    

    public int getAge() 
        return age;
    

    public void setAge(int age) 
        this.age = age;
    

    public int getGender() 
        return gender;
    

    public void setGender(int gender) 
        this.gender = gender;
    

    public float getSimilar() 
        return similar;
    

    public void setSimilar(float similar) 
        this.similar = similar;
    


    @Override
    public String toString() 
        return
                " age=" + age +
                        ", gender=" + (gender == GenderInfo.MALE ? "MALE" : (gender == GenderInfo.FEMALE ? "FEMALE" : "UNKNOWN")) +
                        ", similar=" + similar;
    

以上是关于Android 虹软人脸识别SDK-人脸对比的主要内容,如果未能解决你的问题,请参考以下文章

人脸识别SDK虹软的好用吗?

Android 虹软人脸识别SDK-人脸对比

[Android]虹软arcface人脸识别SDK引擎使用总结

基于Android 虹软人脸人证对比,活体检测

Android基于虹软SDK实现离线人脸识别

虹软的人脸SDK怎么集成啊 Android 的。有没有集成文档啥的呀?