Кодирование и декодирование QR-кода с помощью zxing

Итак, я рискну, что кто-то здесь уже использовал zxing. Я разрабатываю приложение Java, и одна из вещей, которые ему нужно сделать, это закодировать байтовый массив данных в QR-код, а затем декодировать его позже.

Вот пример того, как выглядит мой кодировщик:

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

Ясно, что текст кодируется, но фактические байты данных полностью отключены. Любая помощь будет оценена здесь.


person LandonSchropp    schedule 21.03.2010    source источник
comment
CaptureActivity захватывает изображение QR-кода с камеры и после его декодирования показывает результаты в соответствии с типом данных, хранящихся в QR-коде. например если URL-адрес веб-сайта закодирован в QR-коде, на экране результатов будет кнопка для открытия этого URL-адреса и т.п. Мне нужно прочитать изображение с SD-карты, декодировать его и обработать вывод так же, как это делает zxing в случае декодирования через CaptureActivity. Что мне нужно сделать после получения вывода в результате?   -  person Kulin Choksi    schedule 28.03.2012
comment
Вы должны опубликовать новый вопрос, задав этот вопрос, с примерами вашего кода. Вы получите лучший ответ, чем я мог бы дать здесь.   -  person LandonSchropp    schedule 29.03.2012


Ответы (6)


Итак, на будущее для тех, кто не хочет тратить два дня на поиски в Интернете, чтобы понять это, когда вы кодируете массивы байтов в QR-коды, вы должны использовать набор символов ISO-8859-1, а не UTF-8.

person LandonSchropp    schedule 22.03.2010
comment
неправда - пожалуйста, посмотрите мой ответ с прикрепленным РАБОЧИМ кодом о UTF-8 wi zxing qrencoder, и каждый декодер, который я пробовал с iphone, работал - person Shaybc; 13.10.2011
comment
На самом деле это хороший ответ. Спецификация QR-кода не допускает ничего, кроме ISO-8859-1. Декодеры иногда правильно угадывают UTF-8, но не всегда могут это сделать правильно. - person Sean Owen; 14.10.2011
comment
Это правда. пока я использовал UTF-8, мое приложение работало на некоторых саннерах Android и Iphone, но не на всех. Когда я изменил эту кодировку на ISO-8859-1, все сканеры/декодеры смогли сканировать закодированное QR-изображение. - person Khushboo; 18.04.2012
comment
Этот ответ частично правильный, но он находится в неправильном контексте. Поскольку это было задано 10 лет назад, в то время zxing еще не реализовал ResultMetadataType.BYTE_SEGMENTS. Таким образом, в то время единственный способ извлечь необработанный контент без применения эвристики — это вычислить number of bits in the length field (см. Википедию). Используя вычисленное поле длины, биты, следующие за ним, представляют собой необработанное содержимое, сдвинутое на 4 бита. Чтобы вычислить length field, вы должны выполнить точно такой же алгоритм, как QR-код выделяет размер на основе ECC и размера данных. - person eigenfield; 02.06.2020

это мой рабочий пример Java-кода для кодирования QR-кода с использованием ZXing с кодировкой UTF-8, обратите внимание: вам нужно будет изменить путь и данные 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());
    }
}

}
person Shaybc    schedule 13.10.2011
comment
Этот фрагмент кода работает и на моем конце. Хотя мой код не совсем такой. Я не сохраняю изображение, возвращаемое функцией encode(). Однако я конвертирую экземпляр BitMatrix в BitMap. - person Khushboo; 16.04.2012
comment
Shaybc, ваше закодированное QR-изображение может сканироваться некоторыми приложениями Android или Iphone, но не всеми. Вам нужно использовать ISO-8859-1 вместо UTF-8 для успешного кодирования QR-изображения. Вы можете протестировать закодированное изображение Qr с помощью приложения Red Laser или приложения QR Droid из Google Play. - person Khushboo; 18.04.2012
comment
Только что проверил как Red Laser, так и QR Droid, и им обоим удалось прочитать данные в кодировке UTF8. - person GetUsername; 17.09.2013
comment
Этот ответ не имеет отношения к вопросу. - person eigenfield; 02.06.2020

Что бы это ни стоило, мой отличный шип работает как с кодировками символов 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()        
    }

}
person klassek    schedule 25.11.2011

Если вам действительно нужно кодировать UTF-8, вы можете попробовать добавить знак порядка байтов Unicode. Я понятия не имею, насколько широко распространена поддержка этого метода, но ZXing, по крайней мере, поддерживает его: http://code.google.com/p/zxing/issues/detail?id=103

Я недавно читал о QR-режиме и думаю, что видел ту же практику, упомянутую где-то еще, но не знаю, где именно.

person perennialmind    schedule 29.03.2010

Я попытался использовать 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;
person BrunoJCM    schedule 13.02.2011
comment
Это наиболее подходящий справочный ответ. - person eigenfield; 02.06.2020

Возможно, стоит взглянуть на 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();
person lucrussell    schedule 12.06.2014