zoukankan      html  css  js  c++  java
  • Android平台二维码之生成,扫描 & 识别

    1.二维码的前世今生

    “二维条码/二维码(2-dimensional bar code)是用某种特定的几何图形按一定规律在平面(二维方向上)分布的黑白相间的图形记录数据符号信息的;在代码编制上巧妙地利用构成计算机内部逻辑基础的“0”、“1”比特流的概念,使用若干个与二进制相对应的几何形体来表示文字数值信息,通过图象输入设备或光电扫描设备自动识读以实现信息自动处理:它具有条码技术的一些共性:每种码制有其特定的字符集;每个字符占有一定的宽度;具有一定的校验功能等。同时还具有对不同行的信息自动识别功能、及处理图形旋转变化点。 [1] ”

    上面是百度百科的解释。既然有二维码,那么肯定有一维码。

    一维码。最为常见的就是食品 & 书本后面的条码。

    条码起源与20世纪40年代,后来在1970年 UPC码发明,并开始广泛应用与食品包装。

    具体的介绍可以看百度百科 一维码。

    其实二维码与一维码本质上是类似的,就跟一维数组和二维数组一样。

     

    2.二维码的java支持库

    为了让java或者说android方便继承条码的功能,google就开发了一个zxing的库:

    https://github.com/zxing/zxing

    3.生成二维码

    public class EncodeThread {
    
        public static void encode(final String url, final int width, final int height, final EncodeResult result) {
    
            if (result == null) {
                return;
            }
    
            if (TextUtils.isEmpty(url)) {
                result.onEncodeResult(null);
                return;
            }
    
            new Thread() {
                @Override
                public void run() {
                    try {
                        MultiFormatWriter writer = new MultiFormatWriter();
                        Hashtable<EncodeHintType, String> hints = new Hashtable<>();
                        hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
                        BitMatrix bitMatrix = writer.encode(url, BarcodeFormat.QR_CODE, width, height, hints);
                        Bitmap bitmap = parseBitMatrix(bitMatrix);
                        result.onEncodeResult(bitmap);
                        return;
                    } catch (WriterException e) {
                        e.printStackTrace();
                    }
                    result.onEncodeResult(null);
                }
            }.start();
    
        }
    
        /**
         * 生成二维码内容<br>
         *
         * @param matrix
         * @return
         */
        public static Bitmap parseBitMatrix(BitMatrix matrix) {
            final int QR_WIDTH = matrix.getWidth();
            final int QR_HEIGHT = matrix.getHeight();
            int[] pixels = new int[QR_WIDTH * QR_HEIGHT];
            //this we using qrcode algorithm
            for (int y = 0; y < QR_HEIGHT; y++) {
                for (int x = 0; x < QR_WIDTH; x++) {
                    if (matrix.get(x, y)) {
                        pixels[y * QR_WIDTH + x] = 0xff000000;
                    } else {
                        pixels[y * QR_WIDTH + x] = 0xffffffff;
                    }
                }
            }
            Bitmap bitmap = Bitmap.createBitmap(QR_WIDTH, QR_HEIGHT, Bitmap.Config.ARGB_8888);
            bitmap.setPixels(pixels, 0, QR_WIDTH, 0, 0, QR_WIDTH, QR_HEIGHT);
            return bitmap;
        }
    
        public interface EncodeResult {
            void onEncodeResult(Bitmap bitmap);
        }
    }

    zxing 支持很多条码格式:我们这里使用QR_CODE码。也就是我们常见的微信里面的二维码。

    我们先来分析下这段代码:

     MultiFormatWriter writer = new MultiFormatWriter();

    这个是一个工具类,把所有支持的几个write写在里面了。

    public BitMatrix encode(String contents,
                              BarcodeFormat format,
                              int width, int height,
                              Map<EncodeHintType,?> hints) throws WriterException {
    
        Writer writer;
        switch (format) {
          case EAN_8:
            writer = new EAN8Writer();
            break;
          case UPC_E:
            writer = new UPCEWriter();
            break;
          case EAN_13:
            writer = new EAN13Writer();
            break;
          case UPC_A:
            writer = new UPCAWriter();
            break;
          case QR_CODE:
            writer = new QRCodeWriter();
            break;
          case CODE_39:
            writer = new Code39Writer();
            break;
          case CODE_93:
            writer = new Code93Writer();
            break;
          case CODE_128:
            writer = new Code128Writer();
            break;
          case ITF:
            writer = new ITFWriter();
            break;
          case PDF_417:
            writer = new PDF417Writer();
            break;
          case CODABAR:
            writer = new CodaBarWriter();
            break;
          case DATA_MATRIX:
            writer = new DataMatrixWriter();
            break;
          case AZTEC:
            writer = new AztecWriter();
            break;
          default:
            throw new IllegalArgumentException("No encoder available for format " + format);
        }
        return writer.encode(contents, format, width, height, hints);
      }

    这是官方最新支持的格式,具体看引入的jar里面支持的格式。

    对与bitmatrix的结果,通过摸个算法,设置每个点白色,或者黑色。

    最后创建一张二维码的图片。

    4.识别二维码

    如何从一张图片上面,识别二维码呢:

    public class ReDecodeThread {
    
        public static void encode(final Bitmap bitmap, final ReDecodeThreadResult listener) {
    
            if (listener == null) {
                return;
            }
    
            if (bitmap == null) {
                listener.onReDecodeResult(null);
                return;
            }
    
            new Thread() {
                @Override
                public void run() {
                    try {
                        MultiFormatReader multiFormatReader = new MultiFormatReader();
                        BitmapLuminanceSource source = new BitmapLuminanceSource(bitmap);
                        BinaryBitmap bitmap1 = new BinaryBitmap(new HybridBinarizer(source));
                        Result result1 = multiFormatReader.decode(bitmap1);
                        listener.onReDecodeResult(result1.getText());
                        return;
                    } catch (NotFoundException e) {
                        e.printStackTrace();
                    }
                    listener.onReDecodeResult(null);
                }
            }.start();
    
        }
    
        public interface ReDecodeThreadResult {
            void onReDecodeResult(String url);
        }
    }
    View Code

    过程也是很简单,使用MultiFormatReader来分析图片,这里不需要缺人图片的条码格式。

    如果分析下源码,就是依次使用每种格式的reader来分析,直到找到合适的为止。

    当然回了能够把Bitmap转化成Bitmatrix,然后在分析。

    public final class BitmapLuminanceSource extends LuminanceSource{
        private final byte[] luminances;
    
        public BitmapLuminanceSource(String path) throws FileNotFoundException {
            this(loadBitmap(path));
        }
    
        public BitmapLuminanceSource(Bitmap bitmap) {
            super(bitmap.getWidth(), bitmap.getHeight());
    
            int width = bitmap.getWidth();
            int height = bitmap.getHeight();
    
            int[] pixels = new int[width * height];
            bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
    
            // In order to measure pure decoding speed, we convert the entire image
            // to a greyscale array
            // up front, which is the same as the Y channel of the
            // YUVLuminanceSource in the real app.
            luminances = new byte[width * height];
            for (int y = 0; y < height; y++) {
                int offset = y * width;
                for (int x = 0; x < width; x++) {
                    int pixel = pixels[offset + x];
                    int r = (pixel >> 16) & 0xff;
                    int g = (pixel >> 8) & 0xff;
                    int b = pixel & 0xff;
                    if (r == g && g == b) {
                        // Image is already greyscale, so pick any channel.
                        luminances[offset + x] = (byte) r;
                    } else {
                        // Calculate luminance cheaply, favoring green.
                        luminances[offset + x] = (byte) ((r + g + g + b) >> 2);
                    }
                }
            }
        }
    
        @Override
        public byte[] getRow(int y, byte[] row) {
            if (y < 0 || y >= getHeight()) {
                throw new IllegalArgumentException("Requested row is outside the image: " + y);
            }
            int width = getWidth();
            if (row == null || row.length < width) {
                row = new byte[width];
            }
    
            System.arraycopy(luminances, y * width, row, 0, width);
            return row;
        }
    
        // Since this class does not support cropping, the underlying byte array
        // already contains
        // exactly what the caller is asking for, so give it to them without a copy.
        @Override
        public byte[] getMatrix() {
            return luminances;
        }
    
        private static Bitmap loadBitmap(String path) throws FileNotFoundException {
            Bitmap bitmap = BitmapFactory.decodeFile(path);
            if (bitmap == null) {
                throw new FileNotFoundException("Couldn't open " + path);
            }
            return bitmap;
        }
    }
    BitmapLuminanceSource

    5.扫描二维码

    扫描二维码,其实比上面只多了一步,就是把camera获取的东西直接转换,然后进行识别。

      public void requestPreviewFrame(Handler handler, int message) {
        if (camera != null && previewing) {
          previewCallback.setHandler(handler, message);
          if (useOneShotPreviewCallback) {
            camera.setOneShotPreviewCallback(previewCallback);
          } else {
            camera.setPreviewCallback(previewCallback);
          }
        }
      }

    首先把camera预览的数据放入previewCallback中。

    final class PreviewCallback implements Camera.PreviewCallback
    public void onPreviewFrame(byte[] data, Camera camera) {
        Point cameraResolution = configManager.getCameraResolution();
        if (!useOneShotPreviewCallback) {
          camera.setPreviewCallback(null);
        }
        if (previewHandler != null) {
          Message message = previewHandler.obtainMessage(previewMessage, cameraResolution.x,
              cameraResolution.y, data);
          message.sendToTarget();
          previewHandler = null;
        } else {
          Log.d(TAG, "Got preview callback, but no handler for it");
        }
      }

    可以看到,预览的数据data,回传递过来,然后handler的方式传递出去。

    接收data的地方:

      @Override
      public void handleMessage(Message message) {
        switch (message.what) {
          case R.id.decode:
            //Log.d(TAG, "Got decode message");
            decode((byte[]) message.obj, message.arg1, message.arg2);
            break;
          case R.id.quit:
            Looper.myLooper().quit();
            break;
        }
      }

    然后是decode data

    private void decode(byte[] data, int width, int height) {
        long start = System.currentTimeMillis();
        Result rawResult = null;
        
        //modify here
        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;
        
        PlanarYUVLuminanceSource source = CameraManager.get().buildLuminanceSource(rotatedData, width, height);
        BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
        try {
          rawResult = multiFormatReader.decodeWithState(bitmap);
        } catch (ReaderException re) {
          // continue
        } finally {
          multiFormatReader.reset();
        }
    
        if (rawResult != null) {
          long end = System.currentTimeMillis();
          Log.d(TAG, "Found barcode (" + (end - start) + " ms):
    " + rawResult.toString());
          Message message = Message.obtain(activity.getHandler(), R.id.decode_succeeded, rawResult);
          Bundle bundle = new Bundle();
          bundle.putParcelable(DecodeThread.BARCODE_BITMAP, source.renderCroppedGreyscaleBitmap());
          message.setData(bundle);
          //Log.d(TAG, "Sending decode succeeded message...");
          message.sendToTarget();
        } else {
          Message message = Message.obtain(activity.getHandler(), R.id.decode_failed);
          message.sendToTarget();
        }
      }

    当把camera上的图片转换成BinaryBitmap以后,剩下的事情,就更直接从图片识别是一样的。

    PlanarYUVLuminanceSource source = CameraManager.get().buildLuminanceSource(rotatedData, width, height);
        BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));

    参考:

    http://www.cnblogs.com/weixing/archive/2013/08/28/3287120.html

  • 相关阅读:
    jQuery点击弹出遮罩层(弹出模态框),点击模态框消失
    ubuntu delect opencv
    protobuf安装
    caffe中使用matlab接口
    protobuf安装
    caffe安装2
    caffe安装1
    PHP学习笔记,js
    PHP学习笔记-动态属性添加
    Ubuntu14.04安装Moodle
  • 原文地址:https://www.cnblogs.com/deman/p/5570526.html
Copyright © 2011-2022 走看看