安卓人脸识别笔记

人脸识别的SDK来自虹软的人脸识别SDK,开源免费
虹软的官网 http://www.arcsoft.com.cn/ai/arcface.html
faceDemo实现效果:
这里写图片描述
在项目实现过程中遇到的一些问题,记一下。

一、调用系统相机方法

这里使用FileProvider.getUriForFile();获取Uri 而不使用Uri.fromFile()android7.0对于系统权限做了一些更爱,为了提高私有文件的安全性。当我们在访问文件的时候,安卓禁止你的应用外部公开file://uri 会报错:android.os.FileUriExposedException:
由于我的手机是Android7.1故使用以下,Android7.0以下的可以直接使用Uri.fromFile(),为了保持兼容性可以在程序中加一个判断当前系统版本进行调用

// 获取SD卡状态
String state = Environment.getExternalStorageState();
if(state.equals(Environment.MEDIA_MOUNTED)){            
    photoPath = Environment.getExternalStorageDirectory() + "/face.png";
    File imageDir = new File(photoPath);
    if(!imageDir.exists()){
    // 根据地址生成新的文件
    try {
        imageDir.createNewFile();
    } catch (IOException e) {
        e.printStackTrace();
    }
}
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
//Uri uri = Uri.fromFile(imageDir);
Uri uri =  FileProvider.getUriForFile(MainActivity.this, "com.demo.cjh.facedemo.fileprovider", imageDir);
intent.putExtra(MediaStore.EXTRA_OUTPUT,uri);
startActivityForResult(intent,11);
}else {
    Toast.makeText(MainActivity.this,"SD卡未插入",Toast.LENGTH_SHORT).show();
}

关于FileProvider.getUriForFile()调用的设置

第一步 在清单文件下注册
<provider  
    android:name="android.support.v4.content.FileProvider"  

    //此处的provider需要和代码中的provider保持一致  
    android:authorities="你的包名.provider"  
    android:exported="false"  
    android:grantUriPermissions="true">  
    <meta-data  
        android:name="android.support.FILE_PROVIDER_PATHS"  
        android:resource="@xml/provider_paths" />  
</provider>  
第二步 res/xml/provider_paths类
<?xml version="1.0" encoding="utf-8"?>  
<paths>  
    <external-path  
        name="external_files"  
        path="." />  
</paths>  

注意你的代码里面的provider名字需要和清单的provider名字一致,否则会报空

二、activity回调接收相机的图片

@Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        // requestCode 回调代码,用来识别是哪个activity回调回来的,和startActivityForResult();第二个参数比较
        // resultCode 系统确认代码,有Activity.RESULT_OK |Activity.RESULT_CANCELED  前者表示成功回调,后者表示取消

    }

三、虹软SDK的使用

    // 把图形格式转化为软虹SDK使用的图像格式NV21
    byte[] data = new byte[bp.getWidth() * bp.getHeight() * 3 / 2];
    ImageConverter convert = new ImageConverter();
    convert.initial(bp.getWidth(), bp.getHeight(), ImageConverter.CP_PAF_NV21);
    if (convert.convert(bp, data)) {
        Log.d(TAG, "convert ok!");
    }
    convert.destroy();

    // 人脸信息检测 检测的结果包括人脸矩形框的位置
    AFD_FSDKEngine FD_engine = new AFD_FSDKEngine();
    AFD_FSDKVersion FD_version = new AFD_FSDKVersion();

    // 用来存放检测到的人脸信息列表
    List<AFD_FSDKFace> FD_result = new ArrayList<AFD_FSDKFace>();

    // 首先初始化人脸检测引擎
    AFD_FSDKError FD_error = FD_engine.AFD_FSDK_InitialFaceEngine(FaceDB.appid,FaceDB.fd_key,AFD_FSDKEngine.AFD_OPF_0_HIGHER_EXT, 16, 5);

    if(FD_error.getCode() != AFD_FSDKError.MOK ){
        Toast.makeText(Register.this, "FD初始化失败,错误码:" + FD_error.getCode(), Toast.LENGTH_SHORT).show();
    }

    // 输入的data数据为NV21格式,人脸检测返回结果保存在FD_result中
    FD_error = FD_engine.AFD_FSDK_StillImageFaceDetection(data,bp.getWidth(), bp.getHeight(), AFD_FSDKEngine.CP_PAF_NV21, FD_result);


    // 画box框
    Bitmap bitmap = Bitmap.createBitmap(bp.getWidth(), bp.getHeight(), bp.getConfig());
    Canvas canvas = new Canvas(bitmap);
    canvas.drawBitmap(bp, 0, 0, null);

    Paint mPaint = new Paint();
    for (AFD_FSDKFace face : FD_result) {
        mPaint.setColor(Color.RED);
        mPaint.setStrokeWidth(10.0f);
        mPaint.setStyle(Paint.Style.STROKE);
        canvas.drawRect(face.getRect(), mPaint);
    }
    //  canvas.restore();

    bp = bitmap;
    imageView.setImageBitmap(bp);

    if(!FD_result.isEmpty()) {

        // 检测人脸特征信息
        AFR_FSDKVersion FR_version1 = new AFR_FSDKVersion();
        AFR_FSDKEngine FR_engine1 = new AFR_FSDKEngine();

        // 存放人脸特征信息
        AFR_FSDKFace FR_result1 = new AFR_FSDKFace();

        // 初始化
        AFR_FSDKError FR_error1 = FR_engine1.AFR_FSDK_InitialEngine(FaceDB.appid, FaceDB.fr_key);

        if(FR_error1.getCode() != AFR_FSDKError.MOK ){
            Toast.makeText(Register.this, "FR初始化失败,错误码:" + FD_error.getCode(), Toast.LENGTH_SHORT).show();
        }

        // 检测人脸特征
        FR_error1 = FR_engine1.AFR_FSDK_ExtractFRFeature(data,bp.getWidth(),bp.getHeight(), AFR_FSDKEngine.CP_PAF_NV21, new Rect(FD_result.get(0).getRect()), FD_result.get(0).getDegree(), FR_result1);

        if(FR_error1.getCode() != AFR_FSDKError.MOK){
            Toast.makeText(Register.this, "人脸特征无法检测,请换一张图片", Toast.LENGTH_SHORT).show();
        }else{

            mAFR_FSDKFace = FR_result1.clone(); // 复制

            //  裁剪
            int width = FD_result.get(0).getRect().width();
            int height = FD_result.get(0).getRect().height();
            Bitmap face_bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
            Canvas face_canvas = new Canvas(face_bitmap);
            face_canvas.drawBitmap(bp, FD_result.get(0).getRect(), new Rect(0, 0, width, height), null);

            // 显示
            imageView2.setImageBitmap(face_bitmap);

            // 添加人脸特征信息到脸库
            // App.mFaceDB.addFace("name",mAFR_FSDKFace);

        }
        // 销毁
        FR_error1 = FR_engine1.AFR_FSDK_UninitialEngine();
    }else{
        Toast.makeText(Register.this, "未检测到人脸", Toast.LENGTH_SHORT).show();
    }
    FD_error = FD_engine.AFD_FSDK_UninitialFaceEngine();

人脸检测布局

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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="match_parent"
    tools:context="com.demo.cjh.facedemo.Detecter">


    <com.guo.android_extend.widget.CameraSurfaceView
        android:id="@+id/surfaceView"
        android:layout_width="1dp"
        android:layout_height="1dp"/>

    <com.guo.android_extend.widget.CameraGLSurfaceView
        android:id="@+id/glsurfaceView"
        android:layout_width="144dp"
        android:layout_height="176dp"
        android:layout_marginTop="80dp"
        android:layout_centerHorizontal="true"/>
    <TextView
        android:id="@+id/text1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="faceDemo"
        android:layout_below="@id/glsurfaceView"
        android:layout_centerHorizontal="true"
        android:paddingTop="10dp"/>

</RelativeLayout>

人脸检测代码

public class Detecter extends AppCompatActivity implements View.OnTouchListener, CameraSurfaceView.OnCameraListener, Camera.AutoFocusCallback {

    private final String TAG = this.getClass().getSimpleName();

    private CameraSurfaceView mSurfaceView;
    private CameraGLSurfaceView mGLSurfaceView;
    private Camera mCamera;
    private TextView textView1 ;

    int mCameraID;
    int mCameraRotate;
    boolean mCameraMirror;

    private int mWidth, mHeight, mFormat;

    List<AFT_FSDKFace> result = new ArrayList<>();
    AFT_FSDKVersion version = new AFT_FSDKVersion();
    AFT_FSDKEngine engine = new AFT_FSDKEngine();

    byte[] mImageNV21 = null;
    FRAbsLoop mFRAbsLoop = null;
    AFT_FSDKFace mAFT_FSDKFace = null;

    boolean isOK = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mCameraID = getIntent().getIntExtra("Camera", 0) == 0 ? Camera.CameraInfo.CAMERA_FACING_BACK : Camera.CameraInfo.CAMERA_FACING_FRONT;
        mCameraRotate = getIntent().getIntExtra("Camera", 0) == 0 ? 90 : 270;
        mCameraMirror = getIntent().getIntExtra("Camera", 0) == 0 ? false : true;
        mWidth = 1280;
        mHeight = 960;
        mFormat = ImageFormat.NV21;

        setContentView(R.layout.activity_detecter);

        mGLSurfaceView = (CameraGLSurfaceView) findViewById(R.id.glsurfaceView);
        mGLSurfaceView.setOnTouchListener(this);
        mSurfaceView = (CameraSurfaceView) findViewById(R.id.surfaceView);
        mSurfaceView.setOnCameraListener(this);
        mSurfaceView.setupGLSurafceView(mGLSurfaceView, true, mCameraMirror, mCameraRotate);
        mSurfaceView.debug_print_fps(true, false);

        textView1 = (TextView) findViewById(R.id.text1);


        // 初始化人脸跟踪程序
        AFT_FSDKError err = engine.AFT_FSDK_InitialFaceEngine(FaceDB.appid, FaceDB.ft_key, AFT_FSDKEngine.AFT_OPF_0_HIGHER_EXT, 16, 5);
        Log.d(TAG, "AFT_FSDK_InitialFaceEngine =" + err.getCode());
        err = engine.AFT_FSDK_GetVersion(version);
        Log.d(TAG, "AFT_FSDK_GetVersion:" + version.toString() + "," + err.getCode());

        mFRAbsLoop = new FRAbsLoop(); // 识别线程
        mFRAbsLoop.start();

    }

    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 = App.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);
            Log.d(TAG, "AFR_FSDK_InitialEngine = " + error.getCode());
            error = engine.AFR_FSDK_GetVersion(version);
            Log.d(TAG, "FR=" + version.toString() + "," + error.getCode()); //(210, 178 - 478, 446), degree = 1 780, 2208 - 1942, 3370
        }

        @Override
        public void loop() {

            if(isOK){
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                mFRAbsLoop.shutdown();
                Detecter.this.finish();
            }

            // 进行人脸识别检测
            if (mImageNV21 != null) {
                long time = System.currentTimeMillis();
                AFR_FSDKError error = engine.AFR_FSDK_ExtractFRFeature(mImageNV21, mWidth, mHeight, AFR_FSDKEngine.CP_PAF_NV21, mAFT_FSDKFace.getRect(), mAFT_FSDKFace.getDegree(), result);
                Log.d(TAG, "AFR_FSDK_ExtractFRFeature cost :" + (System.currentTimeMillis() - time) + "ms");
                Log.d(TAG, "Face=" + result.getFeatureData()[0] + "," + result.getFeatureData()[1] + "," + result.getFeatureData()[2] + "," + error.getCode());
                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);
                        Log.d(TAG,  "Score:" + score.getScore() + ", AFR_FSDK_FacePairMatching=" + error.getCode());
                        if (max < score.getScore()) {
                            max = score.getScore();
                            name = fr.mName;
                        }
                    }
                }

                //age & gender
                face1.clear();
                face2.clear();

                //crop 
                byte[] data = mImageNV21;
                YuvImage yuv = new YuvImage(data, ImageFormat.NV21, mWidth, mHeight, null);
                ExtByteArrayOutputStream ops = new ExtByteArrayOutputStream();
                yuv.compressToJpeg(mAFT_FSDKFace.getRect(), 80, ops);
                final Bitmap bmp = BitmapFactory.decodeByteArray(ops.getByteArray(), 0, ops.getByteArray().length);
                try {
                    ops.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }

                if (max > 0.6f) {// 分数大于6.0识别成功
                    //fr success.
                    final float max_score = max;
                    Log.d(TAG, "fit Score:" + max + ", NAME:" + name);
                    final String mNameShow = name;

                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            textView1.setText("识别成功!");
                            textView1.setTextColor(Color.BLACK);
                            isOK = true;
                        }
                    });



                } else {
                    final String mNameShow = "未识别";
                    Log.d(TAG,  "识别:" + name);
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            textView1.setText("识别失败!");
                            textView1.setTextColor(Color.RED);
                        }
                    });

                }
                mImageNV21 = null;
            }

        }

        @Override
        public void over() {
            AFR_FSDKError error = engine.AFR_FSDK_UninitialEngine();
            Log.d(TAG, "AFR_FSDK_UninitialEngine : " + error.getCode());
        }
    }

    @Override
    protected void onDestroy() {
        // TODO Auto-generated method stub
        super.onDestroy();

        // 销毁
        mFRAbsLoop.shutdown();
        AFT_FSDKError err = engine.AFT_FSDK_UninitialFaceEngine();
        Log.d(TAG, "AFT_FSDK_UninitialFaceEngine =" + err.getCode());

    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        CameraHelper.touchFocus(mCamera, event, v, this); // 点击聚焦
        return false;
    }

    @Override
    public Camera setupCamera() {
        // 在相机启动前设置一些属性,设置数据的格式为NV21,图像的宽高
        mCamera = Camera.open(mCameraID);
        try {
            Camera.Parameters parameters = mCamera.getParameters();
            parameters.setPreviewSize(mWidth, mHeight);
            parameters.setPreviewFormat(mFormat);

            for( Camera.Size size : parameters.getSupportedPreviewSizes()) {
                Log.d(TAG, "SIZE:" + size.width + "x" + size.height);
            }
            for( Integer format : parameters.getSupportedPreviewFormats()) {
                Log.d(TAG, "FORMAT:" + format);
            }

            List<int[]> fps = parameters.getSupportedPreviewFpsRange();
            for(int[] count : fps) {
                Log.d(TAG, "T:");
                for (int data : count) {
                    Log.d(TAG, "V=" + data);
                }
            }
            //parameters.setPreviewFpsRange(15000, 30000);
            //parameters.setExposureCompensation(parameters.getMaxExposureCompensation());
            //parameters.setWhiteBalance(Camera.Parameters.WHITE_BALANCE_AUTO);
            //parameters.setAntibanding(Camera.Parameters.ANTIBANDING_AUTO);
            //parmeters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
            //parameters.setSceneMode(Camera.Parameters.SCENE_MODE_AUTO);
            //parameters.setColorEffect(Camera.Parameters.EFFECT_NONE);
            mCamera.setParameters(parameters);
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (mCamera != null) {
            mWidth = mCamera.getParameters().getPreviewSize().width;
            mHeight = mCamera.getParameters().getPreviewSize().height;
        }
        return mCamera;
    }

    @Override
    public void setupChanged(int format, int width, int height) {

    }

    @Override
    public boolean startPreviewLater() {
        return false;
    }

    @Override
    public Object onPreview(byte[] data, int width, int height, int format, long timestamp) {
       // 获取数据流 检测人脸信息框
       AFT_FSDKError err = engine.AFT_FSDK_FaceFeatureDetect(data, width, height, AFT_FSDKEngine.CP_PAF_NV21, result);
        Log.d(TAG, "AFT_FSDK_FaceFeatureDetect =" + err.getCode());
        Log.d(TAG, "Face=" + result.size());
        for (AFT_FSDKFace face : result) {
            Log.d(TAG, "Face:" + face.toString());
        }
        if (mImageNV21 == null) {
            if (!result.isEmpty()) {
                mAFT_FSDKFace = result.get(0).clone();
                mImageNV21 = data.clone();
            } else {
                //mHandler.postDelayed(hide, 3000);
            }
        }
        //copy rects
        Rect[] rects = new Rect[result.size()];
        for (int i = 0; i < result.size(); i++) {
            rects[i] = new Rect(result.get(i).getRect());
        }
        //clear result.
        result.clear();
        //return the rects for render.
        return rects;
    }

    @Override
    public void onBeforeRender(CameraFrameData data) {

    }

    @Override
    public void onAfterRender(CameraFrameData data) {
        //  绘制人脸框
        mGLSurfaceView.getGLES2Render().draw_rect((Rect[])data.getParams(), Color.GREEN, 2);
    }

    @Override
    public void onAutoFocus(boolean success, Camera camera) {

    }
}

猜你喜欢

转载自blog.csdn.net/qq_37063860/article/details/79764356