虹软人脸识别AndroidDEMO学习总结,利用SurfaceView预览,以及相机调用的一些坑

第一篇博客献给了虹软人脸识别,写的不好欢迎指正。

这几天,接到需求要加上人脸识别的功能,抱着面向百度编程的心态,我果然搜到了虹软人脸识别API。阅读开发文档,研究DEMO实现流程,经过这几天的斗争,终于搞了一个适合需求的DEMO出来。将这几天的成果总结一下,有需要的朋友可以参考一下哈。

推荐大家去看这篇文章,很详细的讲解了从无到有利用虹软SDK搭建一个Android人脸识别应用的DEMO,Android人脸识别开发入门--基于虹软免费SDK实现


遇到的问题:

demo里人脸注册页与人脸识别页的Activity生命周期不会并存,即只有返回其中一页才能进入另一页,我的需求是在识别页点击进入注册页,所以遇到了“相机被占用”等坑。demo中用的是CameraSurfaceView的setOnCameraListener预览画面的,可能是我太渣了,要么是不能很好的释放相机资源,要么是释放了之后返回报错等等。


解决:

使用我熟悉一点的SurfaceView预览
实现定义好用到的各种对象参数等:
SurfaceView surfaceView;
private SurfaceHolder surfaceHolder;
private Camera camera;//摄像头
TextView tv_name;

public static FaceDB mFaceDB;
FRAbsLoop mFRAbsLoop = null;
private AFR_FSDKFace mAFR_FSDKFace;
Handler mHandler = new Handler();
private final static int MSG_CODE = 0x1000;
private final static int MSG_EVENT_REG = 0x1001;
private final static int MSG_EVENT_NO_FACE = 0x1002;
private final static int MSG_EVENT_NO_FEATURE = 0x1003;
private final static int MSG_EVENT_FD_ERROR = 0x1004;
private final static int MSG_EVENT_FR_ERROR = 0x1005;
AFT_FSDKVersion version = new AFT_FSDKVersion();
ASAE_FSDKVersion mAgeVersion = new ASAE_FSDKVersion();
ASGE_FSDKVersion mGenderVersion = new ASGE_FSDKVersion();

private int mWidth = 1280;
private int mHeight = 960;
private int mFormat = ImageFormat.NV21;
AFT_FSDKFace mAFT_FSDKFace = null;
byte[] mImageNV21 = null;
AFT_FSDKEngine engine = new AFT_FSDKEngine();
List<AFT_FSDKFace> result1 = new ArrayList<>();
ASAE_FSDKEngine mAgeEngine = new ASAE_FSDKEngine();
ASGE_FSDKEngine mGenderEngine = new ASGE_FSDKEngine();
List<ASAE_FSDKAge> ages = new ArrayList<>();
List<ASGE_FSDKGender> genders = new ArrayList<>();

在onCreate方法中初始化SurfaceView和SurfaceHolder以及初始化FaceDB和获取人脸信息:

tv_name = findViewById(R.id.name);
surfaceView = findViewById(R.id.surfaceView);
surfaceHolder = surfaceView.getHolder();

String filePath="/sdcard/FaceTestMine/";
File file=new File(filePath);
if(!file.exists()){
    file.mkdirs();
}
mFaceDB = new FaceDB(file.getPath());
mFaceDB.loadFaces();

在onResume方法中为surfaceHolder设置回调并初始化虹软引擎:

surfaceHolder.addCallback(this);

AFT_FSDKError err = engine.AFT_FSDK_InitialFaceEngine(FaceDB.appid, FaceDB.ft_key, AFT_FSDKEngine.AFT_OPF_0_HIGHER_EXT, 16, 5);
err = engine.AFT_FSDK_GetVersion(version);

ASAE_FSDKError error = mAgeEngine.ASAE_FSDK_InitAgeEngine(FaceDB.appid, FaceDB.age_key);
error = mAgeEngine.ASAE_FSDK_GetVersion(mAgeVersion);

ASGE_FSDKError error1 = mGenderEngine.ASGE_FSDK_InitgGenderEngine(FaceDB.appid, FaceDB.gender_key);
error1 = mGenderEngine.ASGE_FSDK_GetVersion(mGenderVersion);

mFRAbsLoop = new FRAbsLoop();
mFRAbsLoop.start();


在onPause方法中释放注册占用的系统资源:
@Override
protected void onPause() {
    super.onPause();
    mFRAbsLoop.shutdown();
    AFT_FSDKError err = engine.AFT_FSDK_UninitialFaceEngine();
    ASAE_FSDKError err1 = mAgeEngine.ASAE_FSDK_UninitAgeEngine();
    ASGE_FSDKError err2 = mGenderEngine.ASGE_FSDK_UninitGenderEngine();
}
重写SurfaceHolder.Callback的方法,surfaceCreated,surfaceChanged,surfaceDestroyed
surfaceCreated中打开相机并设置各种参数,为相机设置预览回调函数,编码方式为ImageFormat.NV21
surfaceChanged中开启相机预览

surfaceDestroyed中关闭相机资源,经实验,这样关闭时可以很好的释放相机资源的,不会造成进入下一页注册调用相机拍照时报相机被占用了。

@Override
public void surfaceCreated(SurfaceHolder holder) {
    camera = Camera.open(Camera.CameraInfo.CAMERA_FACING_FRONT);
    try {
        camera.setPreviewDisplay(surfaceHolder);
        camera.setPreviewCallback(myPreviewCallback);
        Camera.Parameters parameters = camera.getParameters();
        parameters.setPreviewSize(mWidth, mHeight);
        parameters.setPreviewFormat(mFormat);
        for( Camera.Size size : parameters.getSupportedPreviewSizes()) {
        }
        for( Integer format : parameters.getSupportedPreviewFormats()) {
        }
        camera.setParameters(parameters);
    } catch (Exception e) {
        e.printStackTrace();
    }
    if (camera != null) {
        mWidth = camera.getParameters().getPreviewSize().width;
        mHeight = camera.getParameters().getPreviewSize().height;
    }
}

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
    camera.startPreview();
}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
    if (null != camera) {
        System.out.println("销毁相机");
        holder.removeCallback(this);
        camera.setPreviewCallback(null);
        camera.stopPreview();
        camera.release();
        System.out.println("销毁完成");
    }
}

相机的预览回调类:将预览画面信息转换成人脸信息List<AFT_FSDKFace>中,并转换成Rect[],供人脸识别类识别处理。
MyPreviewCallBack myPreviewCallback = new MyPreviewCallBack();
class MyPreviewCallBack implements Camera.PreviewCallback {

    @Override
    public void onPreviewFrame(byte[] data, Camera camera) {
        AFT_FSDKError err = engine.AFT_FSDK_FaceFeatureDetect(data, mWidth, mHeight, AFT_FSDKEngine.CP_PAF_NV21, result1);
        for (AFT_FSDKFace face : result1) {
        }
        if (mImageNV21 == null) {
            if (!result1.isEmpty()) {
                mAFT_FSDKFace = result1.get(0).clone();
                mImageNV21 = data.clone();
            }
        }
        //copy rects
        Rect[] rects = new Rect[result1.size()];
        for (int i = 0; i < result1.size(); i++) {
            rects[i] = new Rect(result1.get(i).getRect());
        }
        result1.clear();
    }
}

人脸识别处理类:在原来的demo上未作改进,知识简化了一些我不需要的东西,比如画框,缩略图之类的:
class FRAbsLoop extends AbsLoop {
    AFR_FSDKVersion version = new AFR_FSDKVersion();
    AFR_FSDKEngine engine = new AFR_FSDKEngine();
    AFR_FSDKFace result = new AFR_FSDKFace();
    List<FaceDB.FaceRegist> mResgist = mFaceDB.mRegister;
    List<ASAE_FSDKFace> face1 = new ArrayList<>();
    List<ASGE_FSDKFace> face2 = new ArrayList<>();
    @Override
    public void setup() {
        AFR_FSDKError error = engine.AFR_FSDK_InitialEngine(FaceDB.appid, FaceDB.fr_key);
        error = engine.AFR_FSDK_GetVersion(version);
    }
    @Override
    public void loop() {
        if (mImageNV21 != null) {
            long time = System.currentTimeMillis();
            System.out.println("进入了FRAbsLoop   " + mResgist.size());
            AFR_FSDKError error = engine.AFR_FSDK_ExtractFRFeature(mImageNV21, mWidth, mHeight, AFR_FSDKEngine.CP_PAF_NV21, mAFT_FSDKFace.getRect(), mAFT_FSDKFace.getDegree(), result);
            AFR_FSDKMatching score = new AFR_FSDKMatching();
            float max = 0.0f;
            String name = null;
            for (FaceDB.FaceRegist fr : mResgist) {
                for (AFR_FSDKFace face : fr.mFaceList) {
                    error = engine.AFR_FSDK_FacePairMatching(result, face, score);
                    if (max < score.getScore()) {
                        max = score.getScore();
                        name = fr.mName;
                    }
                }
            }
            //age & gender
            face1.clear();
            face2.clear();
            face1.add(new ASAE_FSDKFace(mAFT_FSDKFace.getRect(), mAFT_FSDKFace.getDegree()));
            face2.add(new ASGE_FSDKFace(mAFT_FSDKFace.getRect(), mAFT_FSDKFace.getDegree()));
            ASAE_FSDKError error1 = mAgeEngine.ASAE_FSDK_AgeEstimation_Image(mImageNV21, mWidth, mHeight, AFT_FSDKEngine.CP_PAF_NV21, face1, ages);
            ASGE_FSDKError error2 = mGenderEngine.ASGE_FSDK_GenderEstimation_Image(mImageNV21, mWidth, mHeight, AFT_FSDKEngine.CP_PAF_NV21, face2, genders);
            final String age = ages.get(0).getAge() == 0 ? "年龄未知" : ages.get(0).getAge() + "";
            final String gender = genders.get(0).getGender() == -1 ? "性别未知" : (genders.get(0).getGender() == 0 ? "" : "");

            if (max > 0.6f) {
                //fr success.
                final float max_score = max;
                final String mNameShow = name;
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {

                        tv_name.setAlpha(1.0f);
                        tv_name.setText(mNameShow + "  置信度:" + (float)((int)(max_score * 1000)) / 1000.0);
                        tv_name.setTextColor(Color.RED);
                    }
                });
            } else {
                final String mNameShow = "未识别";
                Shibie2.this.runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        tv_name.setAlpha(1.0f);
                        tv_name.setText(mNameShow + "   " + gender + "," + age);
                        tv_name.setTextColor(Color.RED);
                    }
                });
            }
            mImageNV21 = null;
        }
    }
    @Override
    public void over() {
        AFR_FSDKError error = engine.AFR_FSDK_UninitialEngine();
    }
}

注册按钮点击事件, 此处有一个疑问,只有在此处再次调用camera.release(),并给camera赋值才能保证可以打开系统相机,不然相机就会被占用,上面surfaceDestroyed的时候已经调用过camera.release()方法了,我试过将camera=null写到surfaceDestroyed中也不行,希望知道的小伙伴留言回复,万分感谢。
public void regist(View view) {
    camera.release();
    camera = null;
    Intent intent = new Intent();
    intent.setClass(Shibie2.this, Register.class);
    startActivity(intent);
}

注册部分的代码未作改动,直接使用的demo里的
后期还要调试外接摄像头,等调通了再来补充。

最后有需要的小伙伴可以下载我的demo看看: 基于虹软的Android人脸识别demo
欢迎大家留言建议,希望以后能养成写博客的习惯。
 
 
 
 




猜你喜欢

转载自blog.csdn.net/wqy161109/article/details/79963555