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

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);                        //根据切片的清晰度判断是否需要聚焦                        /* 这里清晰度标准写死为5.0,但由于手机的性能不一,对图像清晰度的要求也不能完全一样,所以我更推荐的方案是:                        * 每一次成功得到数据时(比如扫码成功),表示当前的清晰度正处于可解析的程度,记录一下当前切片的清晰度                        * 然后多采集几次,每次都把历史采集的切片清晰度重新算一次平均值,这样几十次以后,就可以得到当前手机最适用的清晰度                        */                        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) {                }            }        }    };

更多相关文章

  1. Android(安卓)bugs——RecyclerView scrollToPosition不会触发sc
  2. Android(安卓)camera: Metadata\Image从HAL到framework
  3. android学习之EditText需要点击两次触发onclick问题解决
  4. init.rc 中on propert: 触发无效
  5. Android触碰事件
  6. android入门 SeekBar
  7. LeakCanary原理分析
  8. Android长按连续触发的具体实现
  9. Android(安卓)版本兼容问题(一)

随机推荐

  1. conversion to dalvik format failed wit
  2. Android(安卓)TableLayout 实现边框
  3. Android学习——Android(安卓)RIL结构分
  4. android异步网络连接开源:Android(安卓)As
  5. Android应用程序四大组件
  6. Android(安卓)JNI开发入门之二
  7. android开机动画(bootanimation)
  8. Android的Message机制(简单小结)
  9. Android,UI主线程与子线程
  10. Android(安卓)2.3应用开发实战