美文网首页
人脸考勤Android端(一体机)实现

人脸考勤Android端(一体机)实现

作者: 獭祭鱼 | 来源:发表于2022-08-31 16:41 被阅读0次

注:本文仅介绍Android端实现思路和代码 不提供相应的算法代码,所以如果要看算法代码,先劝退

公司原本的考勤打卡是通过外置摄像头进行打卡,不管是使用还是调试都有很大的局限性和不够便捷,而且因为是通过socket长链接进行数据实时更新,所以稳定性也较差。

部署人员反馈后,考虑使用一体机自带的前置摄像头实现人脸打卡功能。

方案一:实时推流给后台,通过算法对视频流就行识别,实现考勤(否决,对设备网络要求较高,实现后容错率极低)

方案二:考勤人员手动点击设备上的触屏按钮进行打卡(否决,需要人机交互,不够智能,且人不一定会配合)

方案三:开启设备的前置摄像头,定时捕获摄像头画面,传递图片给后台,然后通过算法识别实现打卡(通过)

确认好方案,接下来就是开发了。

首先需要开启Camera

private void openCamera() {

try {

        if(mCamera!=null){

closeCamera();

        }

        mCamera = Camera.open();

        mCamera.startPreview();

        if (mCamera !=null) {

SurfaceHolder mHolder =surfaceView.getHolder();

            mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

            mHolder.addCallback(new CameraCallBack());

        }

    }catch (RuntimeException e){

     Log.d("发生异常了",e.toString());

    }

}

private class CameraCallBackimplements SurfaceHolder.Callback {

@Override

    public void surfaceCreated(SurfaceHolder holder) {

try {

mCamera.setDisplayOrientation(0);

            Camera.Parameters params =mCamera.getParameters();

            params.setPreviewSize(640, 480);

            List focusModes = params.getSupportedFocusModes();

            if (focusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) {

// set the focus mode

                params.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);

                // set Camera parameters

                mCamera.setParameters(params);

            }

mCamera.setPreviewDisplay(holder);

            final Camera.Size size =mCamera.getParameters().getPreviewSize();

            previewHeight = size.height;

            previewWidth = size.width;

        }catch (Exception e) {

e.printStackTrace();

        }

}

@Override

    public void surfaceChanged(final SurfaceHolder holder, int format, int width, int height) {

if(mCamera!=null){

mCamera.addCallbackBuffer(new byte[((previewWidth *previewHeight) * ImageFormat.getBitsPerPixel(ImageFormat.NV21)) /8]);

            mCamera.setPreviewCallbackWithBuffer(new Camera.PreviewCallback() {

@Override

                public void onPreviewFrame(byte[] bytes, Camera camera) {

count++;

                    if (null == bytes) {

camera.addCallbackBuffer(new byte[((previewWidth *previewHeight) * ImageFormat.getBitsPerPixel(ImageFormat.NV21)) /8]);

                    }else {

if (count %5 ==0) {

data1 = bytes;

                        }

camera.addCallbackBuffer(bytes);

                    }

if (count >100) {

count =0;

                    }

}

});

        }

}

@Override

    public void surfaceDestroyed(SurfaceHolder holder) {

}

}

这一步是开启摄像头并将摄像头画面绘制在SufceView上

然后是启动摄像头数据处理线程。

public class FaceCompareRunnableimplements Runnable{

@Override

        public void run() {

while (!threadExit) {

try {

if (data1 !=null) {

Bitmap bitmap = NV21ToBitmap.nv21ToBitmap(data1, previewWidth, previewHeight);

                        ByteArrayOutputStream bos =new ByteArrayOutputStream();

//                        bitmap = BitmapUtil.adjustPhotoRotation(bitmap,270);

                        bitmap.compress(Bitmap.CompressFormat.JPEG, 60, bos);

//                        BitmapUtil.saveBitmap(bitmap,3);

//                        tu.setImageBitmap(bitmap);

                        bitmap =bitmap.copy(Bitmap.Config.RGB_565,true);

                        FaceDetector faceDetector =new FaceDetector(bitmap.getWidth(),bitmap.getHeight(),1);

                        FaceDetector.Face[] faces =new FaceDetector.Face[1];

                        int haveface = faceDetector.findFaces(bitmap,faces);

                        if(haveface>0){

facecheckonworkattendance(bos.toByteArray());

                        }

bos.close();

                    }

Thread.sleep(1*1000);

                }catch (Exception e) {

Log.d("报错了",e.toString());

                    e.printStackTrace();

                }

}

}

}

我这需要讲解下,因为摄像头捕获到面前画面再绘制到SurfaceView上并不一定是正向的 所以如果后台算法只能识别正向图片,那么久需要我们使用代码把图片进行一个旋转,如果算法都能识别,那就无所谓了 我在这块吃过亏,所以记录一下,希望能帮助到大家

bitmap = BitmapUtil.adjustPhotoRotation(bitmap,270);

以下为相关方法

/**

* 旋转bitmap

* @param bm

* @param orientationDegree

* @return

*/

public static  BitmapadjustPhotoRotation(Bitmap bm, final int orientationDegree) {

Matrix m =new Matrix();

    m.setRotate(orientationDegree, (float) bm.getWidth() /2, (float) bm.getHeight() /2);

    try {

Bitmap bm1 = Bitmap.createBitmap(bm, 0, 0, bm.getWidth(), bm.getHeight(), m, true);

        return bm1;

    }catch (OutOfMemoryError ex) {

}

return null;

}

还有就是需要对图片进行压缩,因为摄像头像素原因,图片可能会很快,如果网络如果不好,不压缩会上传得很慢,与考勤需要的效率相悖。所以最好还是压缩一下。

  bitmap.compress(Bitmap.CompressFormat.JPEG, 60, bos);

然后是这段代码的解释:

  bitmap =bitmap.copy(Bitmap.Config.RGB_565,true);

                        FaceDetector faceDetector =new FaceDetector(bitmap.getWidth(),bitmap.getHeight(),1);

                        FaceDetector.Face[] faces =new FaceDetector.Face[1];

                        int haveface = faceDetector.findFaces(bitmap,faces);

因为当时部门老大提了个要求 即要保证考勤的实时效率 也要保证服务器的压力不会太大(意思就是图中出现人脸才去请求服务器),因为如果一秒往后台传递一次图片,那么服务器虽说压力不大 但是不够智能优雅,所以重担又落在了客户端,这块代码是Android自带的算法,即识别图中是否存在人脸。如果有就去把图片传给后台。最后这是图片转换代码

/**

    * 将NV21的图像数据转换为ARGB_4444的图像

    * @param yv12

    * @param width

    * @param height

    * @return

    */

    public static Bitmapnv21ToBitmap(byte[] yv12, int width, int height) {

//        byte[] nv21 = YV12toNV21(yv12,width,height);

        if (yuvType ==null) {

yuvType =new Type.Builder(rs, Element.U8(rs)).setX(yv12.length);

            in = Allocation.createTyped(rs, yuvType.create(), Allocation.USAGE_SCRIPT);

            rgbaType =new Type.Builder(rs, Element.RGBA_8888(rs)).setX(width).setY(height);

            out = Allocation.createTyped(rs, rgbaType.create(), Allocation.USAGE_SCRIPT);

        }

in.copyFrom(yv12);

        yuvToRgbIntrinsic.setInput(in);

        yuvToRgbIntrinsic.forEach(out);

        Bitmap bmpout = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_4444);

        out.copyTo(bmpout);

        return bmpout;

    }

目前为止,功能就算做好了,如果有问题可以留言 剩下的就是把图片发送给后台进行识别了,上传图片的代码我就不贴了

if(haveface>0){

facecheckonworkattendance(bos.toByteArray());

                        }

这块代码就是上传

Thread.sleep(1*1000);这个的作用是让线程休眠一秒再去抓取图片

就这些了,谢谢观看,希望对你们有用

相关文章

网友评论

      本文标题:人脸考勤Android端(一体机)实现

      本文链接:https://www.haomeiwen.com/subject/mrdsnrtx.html