如果不是CameraX不能使用,建议使用CameraX《基于CameraX实现人脸》

不推荐Camera2,适配难!还巨麻烦,要写的代码配置太多了!

一代我是直接新建的Class继承了Texture.SurfaceTextureListener,非Activity

一、初始化

public class FaceAnalyzeByOne implements TextureView.SurfaceTextureListener {
    private static final String TAG = "一代Camera";
    private Camera mCamera;
    private Camera.CameraInfo mCameraInfo=new Camera.CameraInfo();
    private int cameraID=0;
    private Activity that;
    private TextureView mTextureView;
    private FaceEngine faceObj;
    private SurfaceTexture mSurface;
    private int afCode = -1; // sdk状态

    public boolean needPreview=true; // 是否需要预览框?
    public int previewRotation=Surface.ROTATION_0; // 预览的旋转角度?
    private boolean isFrontCamera = true; // 用前置摄像头?
    private int lastFaceID = -1; // 上次识别到的人脸id,每次镜头里的同一人脸会是固定id,同一人离镜再入镜会是新ID

    public CameraCallback faceEmit;
    public Point screenPoint= new Point();
    public Point previewPoint; // 最终相机预览的的一个分辨率,不是预览容器的宽高

    // 同名方法直接初始化构造
    public FaceAnalyzeByOne(Activity context,CameraCallback callback){
        that = context;
        that.getWindowManager().getDefaultDisplay().getSize(screenPoint);
        initEngine();// 初始化SDK
        faceEmit=callback;
        
        //动态创建预览的view
        RelativeLayout layout=that.findViewById(.camerax_preview);
        mTextureView =new TextureView(that);
        layout.addView(mTextureView);
        mTextureView.setSurfaceTextureListener(this);//监听这个viewOK等
    }
}

二、SDK相关

//注销arcsoft 引擎
    public void unInitEngine() {
        if (afCode == 0) {
            afCode = faceObj.unInit();
            Log.i(TAG, "unInitEngine: " + afCode);
        }
    }
    //初始化arcsoft引擎
    public void initEngine(){
        faceObj = new FaceEngine();
        afCode = faceObj.init(that.getApplicationContext(), 
            DetectMode.ASF_DETECT_MODE_VIDEO, 
            DetectFaceOrientPriority.ASF_OP_ALL_OUT,// 全角度检测
            16, 3,faceObj.ASF_FACE_DETECT | faceObj.ASF_AGE 
            | faceObj.ASF_FACE3DANGLE | faceObj.ASF_GENDER
            | faceObj.ASF_LIVENESS| faceObj.ASF_FACE_RECOGNITION
        );
        if (afCode != ErrorInfo.MOK) {
            if(afCode == ErrorInfo.MERR_ASF_NOT_ACTIVATED){
                Log.e(Tag,"设备SDK未激活");
            }else{
                Log.e(Tag,"初始化失败:"+afCode);
            }
        }
    }

 三、因为引入了TextureView.SurfaceTextureListener所以有四个方法重写一下,第一个是关键

// 监听view可用了
    @Override
    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
//        Log.i(TAG, "onSurfaceTextureAvailable: " + width + ", " + height);
        mSurface=surface;
        startCamera();
    }
    @Override
    public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
        closeCamera();
        return false;
    }

    @Override
    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {}

    @Override
    public void onSurfaceTextureUpdated(SurfaceTexture surface) {}

四、预览的view初始化OK,初始化相机

// 初始化相机
    public void startCamera(){
        mCamera = createCamera();
        setOrientation();
        try {
            mCamera.setPreviewTexture(mSurface);
            mCamera.setPreviewCallback(new Camera.PreviewCallback() {
                private long lastDrawTime = 0;
                private int timerSpace = 300; // 识别间隔
                @Override
                public void onPreviewFrame(byte[] data, Camera camera) {
                    long start = System.currentTimeMillis();
                    if (start - lastDrawTime < timerSpace) {
                        return;
                    }
                    lastDrawTime = System.currentTimeMillis();
                    getFaceInfo(data);// 获取到的直接是NV21数据,不需要像其他两代相机一样转换
                }
            });
        } catch (IOException e) {
            e.printStackTrace();
        }
        mCamera.startPreview();
    }   

    //创建相机的方法
    private Camera createCamera() {
        cameraID=isFrontCamera?Camera.CameraInfo.CAMERA_FACING_FRONT:Camera.CameraInfo.CAMERA_FACING_BACK;

        Camera camera = Camera.open(cameraID);
        Camera.Parameters mParameters = camera.getParameters();
        Camera.getCameraInfo(cameraID, mCameraInfo);
        List<String> supportedFocusModes = mParameters.getSupportedFocusModes();
        if (supportedFocusModes != null && supportedFocusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
            mParameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO); // 设置聚焦模式(自动)
        }
        mParameters.setPreviewFormat(ImageFormat.NV21); //预览格式
        mParameters.setExposureCompensation(0); // 设置曝光强度

        Camera.Size bestSize = getBestPreview(mParameters.getSupportedPreviewSizes(),screenPoint);

        Log.e(TAG, "最佳预览尺寸: " + bestSize.width + ", " + bestSize.height);
        previewPoint = new Point(bestSize.width,bestSize.height);
        mParameters.setPreviewSize(bestSize.width, bestSize.height); // 设置预览大小

        camera.setParameters(mParameters);
        return camera;
    }

    // 确认画面的方向
    public void setOrientation(){
        int rotation=that.getWindowManager().getDefaultDisplay().getRotation();
        int degrees = rotation * 90;
        switch (rotation) {
            case Surface.ROTATION_0: degrees = 0; break;
            case Surface.ROTATION_90: degrees = 90; break;
            case Surface.ROTATION_180: degrees = 180; break;
            case Surface.ROTATION_270: degrees = 270; break;
        }

        int result;
        if (isFrontCamera) {
            result = (mCameraInfo.orientation + degrees) % 360;
            result = (360 - result) % 360;
        } else {
            result = (mCameraInfo.orientation - degrees + 360) % 360;
        }
        mCamera.setDisplayOrientation(result);
    }

五、分析图像获取特征

private void getFaceInfo(byte[] nv21) {
        List<FaceInfo> faceInfoList = new ArrayList<FaceInfo>();//装获取到的人脸列表
        FaceFeature faceFeature = new FaceFeature(); //装特征数据

        //第一步是送数据给arcsoft sdk,检查是否有人脸信息。
        int code = faceObj.detectFaces(nv21, previewPoint.x,
                previewPoint.y,
                faceObj.CP_PAF_NV21, faceInfoList);
//        Log.e("人脸数据长度", nv21.length+"人脸"+code);
        //数据检查正常,并且含有人脸信息,则进行下一步的人脸识别。
        if (code == ErrorInfo.MOK && faceInfoList.size() > 0) {
            int newFaceID = faceInfoList.get(0).getFaceId();
            if (lastFaceID == newFaceID) { // 相同脸则return
                Log.e("人脸ID相同", "旧" + lastFaceID);// + "、新" + newFaceID
                return;
            }
            lastFaceID = newFaceID;
            code = faceObj.extractFaceFeature(nv21, previewPoint.x,
                    previewPoint.y,
                    faceObj.CP_PAF_NV21, faceInfoList.get(0), faceFeature);
            if (code != ErrorInfo.MOK) {
                return;
            }
            byte[] featureData=faceFeature.getFeatureData();//最终的特征数据!!!
            post(featureData); //把数据给后端去查信息
        }
    }

六、其他

//销毁创建的相机
    public void closeCamera(){
        mCamera.setPreviewCallback(null);
        mCamera.stopPreview();
        mCamera.lock();
        mCamera.release();
    }
    /**
     * 回调接口定义
     */
    public interface CameraCallback {
        void onFind(JSONObject response) throws JSONException;
        void onUnFind(JSONObject response) throws JSONException;
    }
    // 切换前后置摄像头
    public void switchCamera(){
       isFrontCamera=!isFrontCamera;
       closeCamera();
       startCamera();
    }
     /**
     * 获取最佳预览分辨率,解决预览拉伸问题
     *
     * @param supportedSize    相机支持的预览分辨率
     * @param screenResolution 控件宽高或屏幕宽高
     * @return 与预览控件比例最接近预览分辨率
     */
    public static Size getBestPreview(List<Size> supportedSize, Point screenResolution) {
        // 如果x<y,交换x,y的值
        if (screenResolution.x < screenResolution.y) {
            int temp = screenResolution.x;
            screenResolution.x = screenResolution.y;
            screenResolution.y = temp;
        }
        // 计算控件宽高比
        float referRatio = screenResolution.x * 1.0f / screenResolution.y;
        float lastDiff = 0.5f;
        Size lastSize = null;
        for (Size size : supportedSize) {
//            Log.e("可用尺寸", size.width+"*"+size.height);
            // 只找出预览高度>=720 分辨率
            if (size.height < 720) {
                continue;
            }
            // 计算分辨率宽高比
            float supportRatio = size.width * 1.0f / size.height;
            // 计算差值的绝对值
            float currentDiff = Math.abs(referRatio - supportRatio);
            // 循环取出差值最小的分辨率
            if (currentDiff < lastDiff) {
                lastDiff = currentDiff;
                lastSize = size;
            }
        }
//        return new Point(lastSize.getWidth(), lastSize.getHeight());
        return lastSize;
    }

七、其他页面使用

//在activity页面
FaceAnalyzeByOneObj = new FaceAnalyzeByOne(this,
    new FaceAnalyzeByOne.CameraCallback() {
    @Override
    public void onFind(JSONObject response) throws JSONException {
//     Log.e("找到人脸", response.toString());
    }
    @Override
    public void onUnFind(JSONObject response) throws JSONException {
//     Log.e("未找到", response.toString());
    }
});



切换镜头
FaceAnalyzeByOneObj.switchCamera();