Android 高效聚焦方案:计算图像模糊度触发聚焦

之前一直被摄像头聚焦的问题困扰,因为我遇到的需求是要快速的连续扫描条码、二维码、手机号,所以摄像头是一只开着的,经常会遇到焦点模糊了,但聚焦不够及时的情况

Android中常用的聚焦方案有几种,我也都用上了,还还是无法避免一些不能及时聚焦的情况,常用方式如下:
__
1、定时聚焦(一般1-2秒自动聚焦一次,但是因为频繁聚焦,会导致有相当一部分时间,相机处于聚焦中的模糊状态,很影响体验)
2、手动点击聚焦(需要手动操作,操作麻烦)
3、传感器触发聚焦,当手机位置或角度发生改变时触发聚焦(当手机位置没动,但拍摄的内容变动引起焦点模糊时,无法触发聚焦)

如上三种方式,我同时使用的情况下(传感器监听+手动聚焦,另外再计时超过2秒没有触发过聚焦,则自动聚焦一次),最影响体验就是:在手机没动,拍摄内容变容引起焦点模糊时,不能及时触发聚焦,只能等待自动聚焦的计时达到2秒,而且手机性能参差不齐,对于配置较低的手机,聚焦一次花的时间较长,这样一来大部分时间都浪费在了聚焦的模糊过程中,扫描速度变得很慢

解决思路 : 手机聚焦一次花的时间,和聚焦的成功率,这个要主要靠系统和硬件,我们没办法优化,所以主要的目的就是
减少聚焦的次数,同时提高聚焦的准确性,在只有焦点模糊需要聚焦时才聚焦,画面清晰时就算等1分钟也不会聚焦一次,才是最佳效果(经过测试在聚焦频率上已经和系统相机差不多了,只要画面模糊,马上能触发聚焦,画面清晰则始终不聚焦)


集成OpenCv

集成步骤资料很多,重复的我就不写了,我是参考这篇文章配置的OpenCv :
https://www.cnblogs.com/yunfang/p/6149831.html

不过这篇文章中用的不是最新的Opencv-android-sdk,我下载的是最新版本,同时这里需要注意:导入opencv-android-sdk 的 .so文件时,只兼容最基础的 armeabi 就好了,如果全部加进去,你最终打包的apk会非常大……


OpenCv实现

1、初始化OpenCv

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        if (!OpenCVLoader.initDebug()) {
            Log.d(TAG, "Internal OpenCV library not found. Using OpenCV Manager for initialization");
            OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_3_0_0, this, loaderCallback);
        } else {
            Log.d(TAG, "OpenCV library found inside package. Using it!");
            loaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS);
        }
    }

    private BaseLoaderCallback loaderCallback = new BaseLoaderCallback(this) {
        @Override
        public void onManagerConnected(int status) {
            switch (status) {
                case BaseLoaderCallback.SUCCESS:
                    Log.d(TAG, "加载成功");
                    break;
                default:
                    super.onManagerConnected(status);
                    Log.d(TAG, "加载失败");
                    break;
            }
        }
    };

2、在帧数据回调中切片计算清晰度


    private int blurringFrame = 0;
    private int clearFrame = 0;
    private boolean isHandling = false;
  public void onPreviewFrame(final byte[] data, final Camera camera) {
      //如果有一帧图像正在处理中,先等待其处理结束,避免过度耗费性能(这里更好优化方案是记录聚焦动作,如果正在聚焦过程中,那画面大多是模糊的,没有必要处理这些图像,但是由于部分手机的聚焦回调时间不够精准,有的甚至经常收不到回调,所以不太可靠)
      if(isHandling)
        return;
        //识别中不处理其他帧数据
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                         isHandling = true;
                        //获取Camera预览尺寸
                        Camera.Size size = camera.getParameters().getPreviewSize();
                        int left = (int) (size.width / 2 - getResources().getDimension(R.dimen.x40));
                        int top = (int) (size.height / 2 - getResources().getDimension(R.dimen.x40));
                        int right = (int) (left + getResources().getDimension(R.dimen.x20));
                        int bottom = (int) (top + getResources().getDimension(R.dimen.x120));

                        final YuvImage image = new YuvImage(data, ImageFormat.NV21, size.width, size.height, null);
                        if (image != null) {
                            //切片,这里默认取了屏幕中央的一小部分,也是默认的焦点
                            ByteArrayOutputStream stream = new ByteArrayOutputStream();
                            image.compressToJpeg(new Rect(left, top, right, bottom), getQuality(size.height), stream);
                            Bitmap bmp = BitmapFactory.decodeByteArray(stream.toByteArray(), 0, stream.size());

                            if (bmp == null) 
                                return;

                            //计算清晰度
                            double laplacian = getLaplacian(bmp);
                            Log.d(TAG, "清晰度: " + laplacian);

                        //清晰度不够
                        if (laplacian < 5.0f) {
                            blurringFrame++;
                            //如果没有再聚焦过程中,且连续5帧清晰度较低,则触发一次聚焦
                            if (!isFocusing && blurringFrame >= 5)
                                startFocus();
                            //有的手机摄像头太差,大部分时间都达不到5.0的清晰度,导致无论画面清晰还是模糊,始终都是 isFocusing=true,所以这里加个上限,当连续40帧清晰度都不够,直接触发聚焦(类似定时聚焦功能)
                            if (blurringFrame > 40)
                                startFocus();
                        } else {
                            //如果连续3帧清晰度足够,解除“正在聚焦”的状态(不能完全依赖聚焦成功回调,有的手机经常收不到回调)
                            if (++clearFrame > 3) {
                                clearFrame = 0;
                                blurringFrame = 0;
                                isFocusing = false;
                            }
                        }
                        isHandling = false;

                        //清晰度正常,开始处理图像(文字识别、扫码 等)
                            ......
                    } catch (Exception ex) {
                          isHandling = false;
                    }

                }
            }).start();
    }

       /**
     * 计算图像清晰度
     */
    private double getLaplacian(Bitmap bmp) {

        Mat img = new Mat();
        //bitmap->mat
        Utils.bitmapToMat(bmp, img);

        Mat imageGrey = new Mat();
        Imgproc.cvtColor(img, imageGrey, Imgproc.COLOR_RGB2GRAY);
        Mat imageSobel = new Mat();
//        Imgproc.Sobel(imageGrey, imageSobel, CV_16U, 1, 1)  // sobel 梯度
        Imgproc.Laplacian(imageGrey, imageSobel, CV_16U);    //拉普拉斯梯度
        //图像的平均灰度
        return Core.mean(imageSobel).val[0];
    }

      /**
     * 聚焦
     * 回调
     */
    Camera.AutoFocusCallback autoFocusCB = new Camera.AutoFocusCallback() {
        public void onAutoFocus(boolean success, Camera camera) {
            //聚如果焦失败,重新聚焦
            if (!success) {
                postDelayed(doAutoFocus, 500);
            } else {
               // isFocusing = false; //有的手机再聚焦成功后,画面还没有清晰,就执行了回调,这时候解除聚焦状态,直接开始计算模糊度,会触发无限聚焦 ,所以这里不再依赖回调来判断聚焦状态
            }
        }
    };

    /**
     * 开始聚焦
     */
    private Runnable doAutoFocus = new Runnable() {
        public void run() {
            if (mCamera != null) {
                try {
                    mCamera.autoFocus(autoFocusCB);
                } catch (Exception e) {
                }
            }
        }
    };

猜你喜欢

转载自blog.csdn.net/Mr_Sk/article/details/79496910
今日推荐