在 Android 上使用 ZXing 访问全帧

Accessing Full Frame Using ZXing on Android

我们正在开发 Android 应用程序并使用 ZXing 扫描二维码。有没有办法使用 ZXing 获得完整的解码 QR 码帧?

PlanarYUVLuminanceSource class 添加了以下方法。

public void saveFrame() {
    try {
        YuvImage image = new YuvImage(yuvData, ImageFormat.NV21, getWidth(), getHeight(), null);
        File file = new File(Environment.getExternalStorageDirectory().getPath() + "/frame.jpeg");
        FileOutputStream fos = new FileOutputStream(file);
        image.compressToJpeg(new Rect(0, 0, image.getWidth(), image.getHeight()), 100, fos);
    } catch (FileNotFoundException e) {
        Log.e(TAG, "FileNotFoundException!");
    }
}

DecodeHandler class 内调用如下,

private void decode(byte[] data, int width, int height) {
        long start = System.currentTimeMillis();
        Result rawResult = null;
        PlanarYUVLuminanceSource source = CameraManager.get()
                .buildLuminanceSource(data, width, height);
        BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
        try {
            rawResult = multiFormatReader.decodeWithState(bitmap);
        } catch (ReaderException re) {
            // continue
        } finally {
            multiFormatReader.reset();
        }

        if (rawResult != null) {
            // Don't log the barcode contents for security.
            long end = System.currentTimeMillis();
            Log.d(TAG, "Found barcode in " + (end - start) + " ms");
            Message message = Message.obtain(activity.getHandler(),
                    R.id.zxinglib_decode_succeeded, rawResult);
            Bundle bundle = new Bundle();
            bundle.putParcelable(DecodeThread.BARCODE_BITMAP,
                    source.renderCroppedGreyscaleBitmap());
            source.saveFrame();
            message.setData(bundle);
            message.sendToTarget();
        } else {
            Message message = Message.obtain(activity.getHandler(),
                    R.id.zxinglib_decode_failed);
            message.sendToTarget();
        }
    }

这是结果图片,

终于找到解决办法了。它基于 PlanarYUVLuminanceSource class 的 renderCroppedGreyscaleBitmap() 方法。这是我如何更改 decode() 方法。

private void decode(byte[] data, int width, int height) {
    long start = System.currentTimeMillis();
    Result rawResult = null;
    PlanarYUVLuminanceSource source = CameraManager.get().buildLuminanceSource(data, width, height);
    BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
    try {
        rawResult = multiFormatReader.decodeWithState(bitmap);
    } catch (ReaderException re) {
        // continue
    } finally {
        multiFormatReader.reset();
    }

    if (rawResult != null) {
        // Don't log the barcode contents for security.
        long end = System.currentTimeMillis();
        Log.d(TAG, "Found barcode in " + (end - start) + " ms");
        // Grab & save frame
        Bitmap wholeBmp = renderGrayScaleBitmap(data, width, height);
        if(wholeBmp != null)
            saveBitmap(wholeBmp, "frame.png");
        else
            Log.e(TAG, "Bitmap of frame is empty!");
        Message message = Message.obtain(activity.getHandler(), R.id.zxinglib_decode_succeeded, rawResult);
        Bundle bundle = new Bundle();
        bundle.putParcelable(DecodeThread.BARCODE_BITMAP, source.renderCroppedGreyscaleBitmap());
        message.setData(bundle);
        message.sendToTarget();
    } else {
        Message message = Message.obtain(activity.getHandler(), R.id.zxinglib_decode_failed);
        message.sendToTarget();
    }
}

从解码数据创建位图

private Bitmap renderGrayScaleBitmap(byte[] data, int width, int height) {
    int[] pixels = new int[width * height];
    int inputOffset = width;
    for (int y = 0; y < height; y++) {
        int outputOffset = y * width;
        for (int x = 0; x < width; x++) {
            int grey = data[inputOffset + x] & 0xff;
            pixels[outputOffset + x] = 0xFF000000 | (grey * 0x00010101);
        }
        inputOffset += width;
    }
    Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
    bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
    return bitmap;
}

将位图保存到 sdcard

private void saveBitmap(Bitmap bmp, String name) {
    FileOutputStream out = null;
    try {
        String filename = Environment.getExternalStorageDirectory().toString() + "/" + name;
        Log.i(TAG, "writtenPath=" + filename);
        out = new FileOutputStream(filename);
        bmp.compress(Bitmap.CompressFormat.PNG, 100, out); 
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        try {
            if (out != null) {
                out.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}