百度人脸SDK安卓平板USB红外(IR)/RGB摄像头图像混淆问题

百度人脸SDK安卓平板USB红外(IR)/RGB摄像头图像混淆问题

最近在做一个法院文件柜项目,涉及人脸识别。我们用的是百度的人脸识别SDK,使用安卓工控板通过USB口连接两个摄像头进行人脸识别。一个红外、一个RGB。红外数据用于活体检测,RGB数据用于匹配用户。

开发过程中会出现一个奇怪的现象,在图像预览界面,有时候RGB的图像会被当初红外图像处理,红外图像则被当初RGB图像,导致SDK无法正确识别。

如下图


百度人脸SDK安卓平板USB红外(IR)/RGB摄像头图像混淆问题_第1张图片
image

一开始我怀疑是摄像头问题,换了新的摄像头还是如此,最后排查发现是安卓工控板的问题。在百度官方给的SDK demo中,使用的是老的Camera类来调用相机,即通过cameraId来获取相机。在 Camera源码中,cameraId是被写死的。

        /**
         * The facing of the camera is opposite to that of the screen.
         */
        public static final int CAMERA_FACING_BACK = 0;

        /**
         * The facing of the camera is the same as that of the screen.
         */
        public static final int CAMERA_FACING_FRONT = 1;

        /**
         * The direction that the camera faces. It should be
         * CAMERA_FACING_BACK or CAMERA_FACING_FRONT.
         */

然而安卓工控板好像不怎么认USB设备(也有可能是我学艺不精),在开机状态下,如果出现摄像头IR/RGB数据混淆的情况,来回拔插切换摄像头的USB口,有时候能修正这个问题,有时候不能,==修复后保持USB插口不变的情况下,断电重启有时又会出现同样的问题。==个人感觉这已经脱离代码的范围了,属于玄学问题。

仔细看百度官方的Demo可以发现,在FaceRGBIROpenDebugSearchActivity中预留了两个方法:rgbOrIr(int index, byte[] data)choiceRgbOrIrType(int index, byte[] data)没有用到,实际上所有用到双目的Activity中都预留了这两个方法,我们可以直接拿来使用。

先看rgbOrIr(int index, byte[] data)方法

private synchronized void rgbOrIr(int index, byte[] data) {
        byte[] tmp = new byte[PREFER_WIDTH * PERFER_HEIGH];
        try {
            System.arraycopy(data, 0, tmp, 0, PREFER_WIDTH * PERFER_HEIGH);
        } catch (NullPointerException e) {
            Log.e(TAG, String.valueOf(e.getStackTrace()));
        }
        int count = 0;
        int total = 0;
        for (int i = 0; i < PREFER_WIDTH * PERFER_HEIGH; i = i + 10) {
            total += byteToInt(tmp[i]);
            count++;
        }
        if (index == 0) {
            camemra1DataMean = total / count;
        } else {
            camemra2DataMean = total / count;
        }
        if (camemra1DataMean != 0 && camemra2DataMean != 0) {
            if (camemra1DataMean > camemra2DataMean) {
                camemra1IsRgb = true; //惊了,居然是把两个摄像头一帧数据的所有byte值加起来比大小
            } else {
                camemra1IsRgb = false;
            }
            rgbOrIrConfirm = true;
        }
    }

这个方法能标记出摄像头1(cameraId:0)是不是RGB摄像头。而第二个方法就简单了,就是判断一下用不同的方法处理不同的摄像头而已。

choiceRgbOrIrType(int index, byte[] data)

private void choiceRgbOrIrType(int index, byte[] data) {
        // camera1如果为rgb数据,调用dealRgb,否则为Ir数据,调用Ir
        if (index == 0) {
            if (camemra1IsRgb) {
                dealRgb(data);
            } else {
                dealIr(data);
            }
        } else {
            if (camemra1IsRgb) {
                dealIr(data);
            } else {
                dealRgb(data);
            }
        }
    }

事已至此,已经可以解决这个BUG了。在官方demo的FaceRGBIROpenDebugSearchActivity的两个摄像头回调处做如下修改。

 mCamera[0].setPreviewCallback(new Camera.PreviewCallback() {
                    @Override
                    public void onPreviewFrame(byte[] data, Camera camera) {
                        rgbOrIr(0,data);
                        if (rgbOrIrConfirm){
                            //要等两个摄像头都返回一帧数据,rgbOrIrConfirm才会被赋值,此时才能判断到底哪个是RGB摄像头
                            choiceRgbOrIrType(0,data);
                        }
//                            dealRgb(data);
                    }
                });

                mCamera[1].setPreviewCallback(new Camera.PreviewCallback() {
                    @Override
                    public void onPreviewFrame(byte[] data, Camera camera) {
                        rgbOrIr(1,data);
                        if (rgbOrIrConfirm){
                            //要等两个摄像头都返回一帧数据,rgbOrIrConfirm才会被赋值,此时才能判断到底哪个是RGB摄像头
                            choiceRgbOrIrType(1,data);
                        }
//                        dealIr(data);
                    }
                });

这样修改即使摄像头预览的RGB/IR数据是反的,对识别人脸也没有问题。如果需要让预览正常,建议在APP初始化阶段写一个Activity,调用一下两个摄像头,使用这个方法判断一下,写个APP全局变量,如果摄像头反了,在人脸识别界面反着打开摄像头就行了。

//demo开启摄像头的代码,在这用初始化阶段赋值的全局变量做个判断,来决定open(0)和open(1)是否反过来
                mCamera[0] = Camera.open(0);
                mCamera[1] = Camera.open(1);
                mPreview[0].setCamera(mCamera[0], PREFER_WIDTH, PERFER_HEIGH);
                mPreview[1].setCamera(mCamera[1], PREFER_WIDTH, PERFER_HEIGH);

==注意:百度SDK4.0版本已经修改此处代码,开启RGB摄像头的方法已变更为startTestCloseDebugRegisterFunction();==

实测过程中,发现在完全遮挡摄像头的情况下,该方法可能会判断失误。在摄像头面前有物体晃动的情况下,也会出现几帧误判,因此建议多取几帧数据进行判断。


这个问题虽然暂时解决了,但是感觉判断方法也太神奇了叭,凭什么RGB图像的byte值和比IR图像要大?
两个相机回调函数如下:

CameraPreviewManager.getInstance().startPreview(this, mAutoCameraPreviewView,
                PREFER_WIDTH, PERFER_HEIGH, new CameraDataCallback() {
                    @Override
                    public void onGetCameraData(byte[] data, Camera camera, int width, int height) {
                        // 摄像头预览数据进行人脸检测
                        ......
                    }
                });

两个相机参数data[]的长度是一致的,图像长宽也是一致的,说明RGB返回的date[]并不是真彩图,而是256色图,将8位即一个字节的数据映射到色表中,一个字节最多可以表示256种颜色。

百度人脸SDK安卓平板USB红外(IR)/RGB摄像头图像混淆问题_第2张图片
image

256色表

IR摄像头返回的是8位灰度数据,灰度图的R、G、B值相同。

至于为什么RGB图像的byte值和比IR图像要大,我至今未搞清楚,可能和图像处理这方面知识有关,好想把写这个方法的大神抓出来问一下

你可能感兴趣的:(百度人脸SDK安卓平板USB红外(IR)/RGB摄像头图像混淆问题)