美文网首页
zxing、摄像机相关

zxing、摄像机相关

作者: 天下第九九八十一 | 来源:发表于2020-07-27 15:44 被阅读0次

    看了下“自行”这个二维码扫码组件的demo,UI代码挺多的。

    这速度太快了吧!

    两个handler,一个处理摄像机,一个处于酱油线程中处理解码,。。。

    干得好,我选择用一个 handler,不过那个酱油线程还是省不了,不然解码过程发生在主线程就不好了。


    简要流程是:首先开启摄像机,调用startPreview,开始预览。

    解码是通过在第一个handler中不断给摄像机设置setOneShotPreviewCallback(cb),在这个回调中拿到一帧图像的字节数组,传给解码Handler最后传给zxing-core。

    解码的时候会将数据旋转一下,似乎没有必要,传送横着的数据,只要矩形范围设对了,zxing-core照样能识别出来。(不过也有可能zxing-core内部进行了旋转。)


    原来摄像机的自动对焦是需要开发者手动调用并延时、连续调用autoFocus(cb)的。

    于是,demo里面每次都新开一个 AsyncTask ,在 AsyncTask 里面调用 Thread.sleep(2000) 完成延时的效果。

    干得好,我选择 postDelayed ……

    方向传感器触发autoFocus:

    https://www.cnblogs.com/wjw334/p/6744741.html?utm_source=itdadao&utm_medium=referral



    于onResume中进行初始化,于onPause中销毁,这波难道都成了demo的标配了么(参考vlc官方demo)

    干得好,我选择 onCreate 和 onDestory ……


    与视频组件一样,camera除了可以用surfaceView预览,也可以选用textureView。后者暂停和恢复不会出现问题。

    暂停久了,onResume的时候,摄像机会抛出 startPreview failedsetPreviewTexture failed等异常,此时,需要在 onResume 中重新初始化摄像机。

    zxing-demo中还有许多没有用到的代码,比如闪光灯、曝光值,可以利用一下。



    and:

    处理画面拉伸。

    扫码机,你得像个相机。不过这样一来,矩形范围要重新计算,倒是变得复杂了。

    处理拉伸很简单,根据摄像机宽高分辨率、屏幕分辨率,计算textview.layoutparams的width、height。

    和处理视频播放器的逻辑差不多,不多有一点是反着的,播放器一般是要把全部画面纳入(fit_center),摄像机最好还是屏幕上不留空白(center_crop)。

    上面原版的修改版,下面别人的精简版(zxing-lite)

    分屏模式,当宽度大于高度时,也算作 Configuration.ORIENTATION_LANDSCAPE的一种情况。所以横竖屏不能以 Configuration 中的数值为依据,而是要用到Display.getRotation

        public int getScreenOrientation() {
            int PORT=1,LAND=2;
            
            WindowManager windowService = (WindowManager)getSystemService(Context.WINDOW_SERVICE);
            
            int rotation = windowService.getDefaultDisplay().getRotation();
            
            if (Surface.ROTATION_0 == rotation) {
                rotation = PORT;
            } else if(Surface.ROTATION_180 == rotation) {
                rotation = PORT;
            } else if(Surface.ROTATION_90 == rotation) {
                rotation = LAND;
            } else if(Surface.ROTATION_270 == rotation) {
                rotation = LAND;
            }
            return rotation;
        }
    

    来自 https://stackoverflow.com/questions/28041137/android-get-current-device-orientation

    横屏、分屏时的处理




    then:
    可以让激光飞一会儿,飞出画面上的方框,此时,调大postInvalidateDelayed的间隔:

    然后调低平时的间隔,使激光更流畅。

    精简:

    归并、精简,最终剩下九个相关类。

    不过这也差不多了,区区一个二维码的界面效果,还不至于耗费太多时间。



    ISBN码是有方向的。结果 zxing-lite 横着竖着都能扫,为啥?检测了多次而已。一次旋转数据,一次不旋转。

    try 
                            rawResult = multiFormatReader.decodeWithState(bitmap);
                        } catch (ReaderException re) {// continue
                        } finally {
                            multiFormatReader.reset();
                        }
    

    旋转数据,这就不可避免了,较慢。如果handler放在主线程,就会发现摄像机明显掉帧了。

    可以通过只旋转、并传送扫码框内部的数据,达到减少计算量、节省内存的目的。

    before:(旋转并传送整张图片)

    https://github.com/yangxch/ScanZxing/blob/master/app/src/main/java/com/example/xch/scanzxing/zxing/decode/DecodeHandler.java搜索/***************竖屏更改3**********************/

    byte[] rotatedData = new byte[data.length];   
             for (int y = 0; y < height; y++) {   
             for (int x = 0; x < width; x++)   
             rotatedData[x * height + height - y - 1] = data[x + y * width];   
             }   
             int tmp = width; // Here we are swapping, that's the difference to #11   
             width = height;   
             height = tmp;   
             data = rotatedData;  
    

    after: (只处理框内内容)

    if(rotate) {
                byte[] rotatedData = acquireTmpData(heightheight*widthwidth);
                for (int y = 0; y < heightheight; y++) {
                    for (int x = 0; x < widthwidth; x++)
                        rotatedData[x + y * widthwidth] =  data[(y+left)  + (x+top) * sWidth];
                }
                data = rotatedData;
                return new PlanarYUVLuminanceSource(data, heightheight, widthwidth, 0, 0,  heightheight, widthwidth, false);
            }
    

    数学关系还没搞明白,但灵感一闪就这么来了。上下文如下:
    PlanarYUVLuminanceSource(源图像数据,原宽, 原高,竖屏模式,翻转一下图像再传给zxing-core)

    public PlanarYUVLuminanceSource buildLuminanceSource(byte[] data, int sWidth, int sHeight, boolean rotated, boolean rotate) {
            Rect rect = getFramingRect();
            if (rect == null) {
                return null;
            }
            // Go ahead and assume it's YUV rather than die.
            //if(true) return new PlanarYUVLuminanceSource(data, sWidth, sHeight, 0, 0, sWidth, sHeight, false);
            //CMN.Log("buildLuminanceSource", width, height, rect.left, rect.top, rect.width(), rect.height());
            //CMN.Log("scale="+a.scale, "trans="+a.vTranslate);
            
            float scale = a.scale; // 源图像显示后的缩放比,比如0.5、0.9
            
            int left=(int)((rect.left-a.vTranslate.x)/scale);
            int top=(int)((rect.top-a.vTranslate.y)/scale);
            
            
            if(rotated) { //竖屏模式下,图像的数据其实还是横屏的样子。所以交换一下宽高。
                int tmp=sWidth;
                sWidth = sHeight;
                sHeight = tmp;
                tmp=left;
                left=top;
                top=tmp;
            }
            
            if(left<0) left=0;
            int widthwidth=(int)(rect.height()/scale);
            if(widthwidth+left>sWidth) {
                widthwidth=sWidth-left;
            }
            
            if(top<0) top=0;
            int heightheight=(int)(rect.height()/scale);
            
            if(heightheight+top>sHeight) {
                heightheight=sHeight-top;
            }
            
            if(rotate) {//必须旋转数据时,较为耗费CPU时间,故只处理必要的部分。
                byte[] rotatedData = acquireTmpData(heightheight*widthwidth);
                for (int y = 0; y < heightheight; y++) {
                    for (int x = 0; x < widthwidth; x++)
                        rotatedData[x + y * widthwidth] =  data[(y+left)  + (x+top) * sWidth];
                }
                data = rotatedData;
                return new PlanarYUVLuminanceSource(data, heightheight, widthwidth, 0, 0,  heightheight, widthwidth, false);
            }
            
    
            return new PlanarYUVLuminanceSource(data, sWidth, sHeight, left, top,  widthwidth, heightheight, false);
        }
    

    这里的旋转应该只是翻转,不知道有无翻转敏感的条码格式……

    相关文章

      网友评论

          本文标题:zxing、摄像机相关

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