【问题标题】:QR Code encoding and decoding using zxing使用zxing进行二维码编解码
【发布时间】:2011-01-30 03:35:53
【问题描述】:

好的,所以我要排除这里有人以前使用过 zxing 的机会。我正在开发一个 Java 应用程序,它需要做的一件事就是将一个字节数组的数据编码为一个二维码,然后在稍后对其进行解码。

这是我的编码器的示例:

byte[] b = {0x48, 0x45, 0x4C, 0x4C, 0x4F};
//convert the byte array into a UTF-8 string
String data;
try {
    data = new String(b, "UTF8");
}
catch (UnsupportedEncodingException e) {
 //the program shouldn't be able to get here
 return;
}

//get a byte matrix for the data
ByteMatrix matrix;
com.google.zxing.Writer writer = new QRCodeWriter();
try {
 matrix = writer.encode(data, com.google.zxing.BarcodeFormat.QR_CODE, width, height);
}
catch (com.google.zxing.WriterException e) {
 //exit the method
 return;
}

//generate an image from the byte matrix
int width = matrix.getWidth(); 
int height = matrix.getHeight(); 

byte[][] array = matrix.getArray();

//create buffered image to draw to
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);

//iterate through the matrix and draw the pixels to the image
for (int y = 0; y < height; y++) { 
 for (int x = 0; x < width; x++) { 
  int grayValue = array[y][x] & 0xff; 
  image.setRGB(x, y, (grayValue == 0 ? 0 : 0xFFFFFF));
 }
}

//write the image to the output stream
ImageIO.write(image, "png", outputStream);

此代码中的开始字节数组仅用于测试它。实际的字节数据会有所不同。

这是我的解码器的样子:

//get the data from the input stream
BufferedImage image = ImageIO.read(inputStream);

//convert the image to a binary bitmap source
LuminanceSource source = new BufferedImageLuminanceSource(image);
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));

//decode the barcode
QRCodeReader reader = new QRCodeReader();

Result result;
try {
 result = reader.decode(bitmap, hints);
} catch (ReaderException e) {
 //the data is improperly formatted
 throw new MCCDatabaseMismatchException();
}

byte[] b = result.getRawBytes();
System.out.println(ByteHelper.convertUnsignedBytesToHexString(result.getText().getBytes("UTF8")));
System.out.println(ByteHelper.convertUnsignedBytesToHexString(b));

convertUnsignedBytesToHexString(byte) 是一种将字节数组转换为十六进制字符串的方法。

当我尝试同时运行这两个代码块时,输出如下:

48454c4c4f
202b0b78cc00ec11ec11ec11ec11ec11ec11ec

显然文本正在被编码,但数据的实际字节完全关闭。任何帮助将不胜感激。

【问题讨论】:

  • captureActivity 从摄像头捕获二维码图像,解码后,根据二维码中存储的数据类型显示结果。例如如果网站 URL 以 QR 码编码,结果屏幕将有一个按钮来打开该 URL 并点赞。我需要从 SD 卡读取图像,对其进行解码并以与 zxing 在通过 captureActivity 解码的情况下相同的方式处理输出。在“Result result”中得到输出后我需要做什么?
  • 您应该发布一个新问题来询问这个问题,并附上您的代码示例。这样做你会得到比我在这里提供的更好的答案。

标签: java barcode qr-code zxing


【解决方案1】:

所以,对于那些不想花两天时间在互联网上搜索来弄清楚这一点的人来说,为了将来参考,当你将字节数组编码成二维码时,你必须使用ISO-8859-1character set,而不是@987654322 @。

【讨论】:

  • 不正确 - 请查看我的答案,附带关于 UTF-8 wi zxing qrencoder 的工作代码,并且我从 iphone 尝试的每个解码器都有效
  • 这实际上是一个很好的答案。 QR 码规范除了 ISO-8859-1 之外不允许任何其他内容。解码器有时碰巧猜对了 UTF-8,但并不总是正确。
  • 确实如此。当我使用“UTF-8”时,我的应用程序正在一些 android 和 Iphone 上运行,但不是全部。当我将此编码更改为“ISO-8859-1”时,所有扫描仪/解码器都能够扫描编码的 QR 图像。
  • 这个答案部分正确,但在错误的上下文中。正如 10 年前提出的那样,当时zxing 还没有实现ResultMetadataType.BYTE_SEGMENTS。所以当时,在没有应用启发式的情况下提取原始内容的唯一方法是计算number of bits in the length field(请参阅维基百科)。使用计算出的长度字段,它后面的位是移位了 4 位的原始内容。要计算length field,您必须在二维码如何根据ECC 和数据大小分配大小时执行完全相同的算法。
【解决方案2】:

这是我使用 ZXing 和 UTF-8 编码对二维码进行编码的工作示例 Java 代码,请注意:您需要将路径和 utf8 数据更改为您的路径和语言字符

package com.mypackage.qr;

import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.util.Hashtable;

import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.*;

public class CreateQR {

public static void main(String[] args)
{
    Charset charset = Charset.forName("UTF-8");
    CharsetEncoder encoder = charset.newEncoder();
    byte[] b = null;
    try {
        // Convert a string to UTF-8 bytes in a ByteBuffer
        ByteBuffer bbuf = encoder.encode(CharBuffer.wrap("utf 8 characters - i used hebrew, but you should write some of your own language characters"));
        b = bbuf.array();
    } catch (CharacterCodingException e) {
        System.out.println(e.getMessage());
    }

    String data;
    try {
        data = new String(b, "UTF-8");
        // get a byte matrix for the data
        BitMatrix matrix = null;
        int h = 100;
        int w = 100;
        com.google.zxing.Writer writer = new MultiFormatWriter();
        try {
            Hashtable<EncodeHintType, String> hints = new Hashtable<EncodeHintType, String>(2);
            hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
            matrix = writer.encode(data,
            com.google.zxing.BarcodeFormat.QR_CODE, w, h, hints);
        } catch (com.google.zxing.WriterException e) {
            System.out.println(e.getMessage());
        }

        // change this path to match yours (this is my mac home folder, you can use: c:\\qr_png.png if you are on windows)
                String filePath = "/Users/shaybc/Desktop/OutlookQR/qr_png.png";
        File file = new File(filePath);
        try {
            MatrixToImageWriter.writeToFile(matrix, "PNG", file);
            System.out.println("printing to " + file.getAbsolutePath());
        } catch (IOException e) {
            System.out.println(e.getMessage());
        }
    } catch (UnsupportedEncodingException e) {
        System.out.println(e.getMessage());
    }
}

}

【讨论】:

  • 这段代码也适用于我。虽然,我的代码并不完全相同。我没有保存 encode() 返回的图像。但是,我正在将 BitMatrix 转换为 BitMap 实例。
  • Shaybc,您编码的 QR 图像可能会被某些 android 或 Iphone 应用程序扫描,但不是全部。您需要使用“ISO-8859-1”而不是“UTF-8”来成功进行 QR 图像编码。您可以使用 Google Play 中的 Red Laser 应用程序或 QR Droid 应用程序测试您的编码 Qr 图像。
  • 刚刚检查了 Red Laser 和 QR Droid,它们都成功读取了 UTF8 编码数据
  • 这个答案与问题无关。
【解决方案3】:

不管怎样,我的 groovy 尖峰似乎适用于 UTF-8 和 ISO-8859-1 字符编码。不确定当非 zxing 解码器尝试解码 UTF-8 编码图像时会发生什么...可能因设备而异。

// ------------------------------------------------------------------------------------
// Requires: groovy-1.7.6, jdk1.6.0_03, ./lib with zxing core-1.7.jar, javase-1.7.jar 
// Javadocs: http://zxing.org/w/docs/javadoc/overview-summary.html
// Run with: groovy -cp "./lib/*" zxing.groovy
// ------------------------------------------------------------------------------------

import com.google.zxing.*
import com.google.zxing.common.*
import com.google.zxing.client.j2se.*

import java.awt.image.BufferedImage
import javax.imageio.ImageIO

def class zxing {
    def static main(def args) {
        def filename = "./qrcode.png"
        def data = "This is a test to see if I can encode and decode this data..."
        def charset = "UTF-8" //"ISO-8859-1" 
        def hints = new Hashtable<EncodeHintType, String>([(EncodeHintType.CHARACTER_SET): charset])

        writeQrCode(filename, data, charset, hints, 100, 100)

        assert data == readQrCode(filename, charset, hints)
    }

    def static writeQrCode(def filename, def data, def charset, def hints, def width, def height) {
        BitMatrix matrix = new MultiFormatWriter().encode(new String(data.getBytes(charset), charset), BarcodeFormat.QR_CODE, width, height, hints)
        MatrixToImageWriter.writeToFile(matrix, filename.substring(filename.lastIndexOf('.')+1), new File(filename))
    }

    def static readQrCode(def filename, def charset, def hints) {
        BinaryBitmap binaryBitmap = new BinaryBitmap(new HybridBinarizer(new BufferedImageLuminanceSource(ImageIO.read(new FileInputStream(filename)))))
        Result result = new MultiFormatReader().decode(binaryBitmap, hints)

        result.getText()        
    }

}

【讨论】:

    【解决方案4】:

    如果您确实需要编码 UTF-8,可以尝试在 unicode 字节顺序标记前添加。我不知道对这种方法的支持有多广泛,但 ZXing 至少似乎支持它: http://code.google.com/p/zxing/issues/detail?id=103

    我最近一直在阅读 QR 模式,并且我认为我在其他地方看到过相同的做法,但我不知道在哪里。

    【讨论】:

      【解决方案5】:

      我尝试使用第一个答案中所述的 ISO-8859-1。编码一切正常,但是当我尝试在解码时使用结果字符串获取 byte[] 时,所有负字节都变成了字符 63(问号)。以下代码不起作用:

      // Encoding works great
      byte[] contents = new byte[]{-1};
      QRCodeWriter codeWriter = new QRCodeWriter();
      BitMatrix bitMatrix = codeWriter.encode(new String(contents, Charset.forName("ISO-8859-1")), BarcodeFormat.QR_CODE, w, h);
      
      // Decodes like this fails
      LuminanceSource ls = new BufferedImageLuminanceSource(encodedBufferedImage);
      Result result = new QRCodeReader().decode(new BinaryBitmap( new HybridBinarizer(ls)));
      byte[] resultBytes = result.getText().getBytes(Charset.forName("ISO-8859-1")); // a byte[] with byte 63 is given
      return resultBytes;
      

      看起来很奇怪,因为在一个非常旧的版本(不确切知道)中的 API 有一个很好用的方法:

      Vector byteSegments = result.getByteSegments();
      

      所以我试图搜索为什么这个方法被删除并意识到有一种方法可以通过元数据获取 ByteSegments。所以我的解码方法如下:

      // Decodes like this works perfectly
      LuminanceSource ls = new BufferedImageLuminanceSource(encodedBufferedImage);
      Result result = new QRCodeReader().decode(new BinaryBitmap( new HybridBinarizer(ls)));
      Vector byteSegments = (Vector) result.getResultMetadata().get(ResultMetadataType.BYTE_SEGMENTS);  
      int i = 0;
      int tam = 0;
      for (Object o : byteSegments) {
          byte[] bs = (byte[])o;
          tam += bs.length;
      }
      byte[] resultBytes = new byte[tam];
      i = 0;
      for (Object o : byteSegments) {
          byte[] bs = (byte[])o;
          for (byte b : bs) {
              resultBytes[i++] = b;
          }
      }
      return resultBytes;
      

      【讨论】:

      • 这是最合适的参考答案。
      【解决方案6】:

      也许值得看一下QRGen,它建立在 ZXing 之上,支持 UTF-8 的这种语法:

      // if using special characters don't forget to supply the encoding
      VCard johnSpecial = new VCard("Jöhn Dɵe")
                              .setAdress("ëåäöƞ Sträät 1, 1234 Döestüwn");
      QRCode.from(johnSpecial).withCharset("UTF-8").file();
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多