1. The past and present life of QR code
"A two-dimensional bar code/QR code (2-dimensional bar code) uses a black and white graph distributed in a plane (two-dimensional direction) in a certain geometric figure according to a certain rule. In code compilation, the concept of "0" and "1" bit streams that form the basis of internal logic of the computer are cleverly used, and several geometric shapes corresponding to the binary are used to represent the numerical information of text, and automatically read through the image input device or photoelectric scanning device to realize automatic information processing: it has some commonalities of barcode technology: each code system has its own specific character set; each character occupies a certain width; has certain verification functions, etc. It also has the function of automatically identifying information in different rows and processing graphic rotation and change points. [1] "
The above is the explanation of Baidu Encyclopedia. Since there is a QR code, there must be a QR code.
One-R code. The most common one is the barcode behind the food & book.
The origin of barcodes was with the 1940s, and later in 1970 UPC codes were invented and began to be widely used in food packaging.
For specific introductions, please refer to Baidu Encyclopedia 1R code.
In fact, QR codes and one-dimensional codes are essentially similar, just like one-dimensional arrays and two-dimensional arrays.
2. The java support library for QR code
In order to facilitate the inheritance of barcode functions by Java or Android, Google has developed a zxing library:
/zxing/zxing
3. Generate QR code
public class EncodeThread { public static void encode(final String url, final int width, final int height, final EncodeResult result) { if (result == null) { return; } if ((url)) { (null); return; } new Thread() { @Override public void run() { try { MultiFormatWriter writer = new MultiFormatWriter(); Hashtable<EncodeHintType, String> hints = new Hashtable<>(); (EncodeHintType.CHARACTER_SET, "utf-8"); BitMatrix bitMatrix = (url, BarcodeFormat.QR_CODE, width, height, hints); Bitmap bitmap = parseBitMatrix(bitMatrix); (bitmap); return; } catch (WriterException e) { (); } (null); } }.start(); } /** * Generate QR code content<br> * * @param matrix * @return */ public static Bitmap parseBitMatrix(BitMatrix matrix) { final int QR_WIDTH = (); final int QR_HEIGHT = (); 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 ((x, y)) { pixels[y * QR_WIDTH + x] = 0xff000000; } else { pixels[y * QR_WIDTH + x] = 0xffffffff; } } } Bitmap bitmap = (QR_WIDTH, QR_HEIGHT, .ARGB_8888); (pixels, 0, QR_WIDTH, 0, 0, QR_WIDTH, QR_HEIGHT); return bitmap; } public interface EncodeResult { void onEncodeResult(Bitmap bitmap); } }
zxing supports many barcode formats: we use QR_CODE code here. That is the QR code in WeChat that we commonly use.
Let's analyze this code first:
MultiFormatWriter writer = new MultiFormatWriter();
This is a tool class that writes all the supported writes in it.
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 (contents, format, width, height, hints); }
This is the latest supported format of the official, please see the format supported in the introduced jar.
For the results with bitmatrix, set each point white or black by touching an algorithm.
Finally, create a picture of the QR code.
4. Identify the QR code
How to identify the QR code from a picture:
public class ReDecodeThread { public static void encode(final Bitmap bitmap, final ReDecodeThreadResult listener) { if (listener == null) { return; } if (bitmap == null) { (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 = (bitmap1); (()); return; } catch (NotFoundException e) { (); } (null); } }.start(); } public interface ReDecodeThreadResult { void onReDecodeResult(String url); } }
The process is also very simple. Use MultiFormatReader to analyze pictures. There is no need for barcode formats for missing people's pictures.
If you analyze the source code, you will use the reader of each format to analyze it in turn until you find the right one.
Of course, I can convert Bitmap into Bitmatrix and then analyze it.
public final class BitmapLuminanceSource extends LuminanceSource{ private final byte[] luminances; public BitmapLuminanceSource(String path) throws FileNotFoundException { this(loadBitmap(path)); } public BitmapLuminanceSource(Bitmap bitmap) { super((), ()); int width = (); int height = (); int[] pixels = new int[width * height]; (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 || < width) { row = new byte[width]; } (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 = (path); if (bitmap == null) { throw new FileNotFoundException("Couldn't open " + path); } return bitmap; } }
5. Scan the QR code
Scan the QR code, in fact, there is only one more step than above, which is to directly convert the things obtained by camera and then identify them.
public void requestPreviewFrame(Handler handler, int message) { if (camera != null && previewing) { (handler, message); if (useOneShotPreviewCallback) { (previewCallback); } else { (previewCallback); } } }
First put the camera preview data into previewCallback.
final class PreviewCallback implements public void onPreviewFrame(byte[] data, Camera camera) { Point cameraResolution = (); if (!useOneShotPreviewCallback) { (null); } if (previewHandler != null) { Message message = (previewMessage, , , data); (); previewHandler = null; } else { (TAG, "Got preview callback, but no handler for it"); } }
You can see that the preview data is passed back and then passed out in the form of handler.
Where to receive data:
@Override public void handleMessage(Message message) { switch () { case : //(TAG, "Got decode message"); decode((byte[]) , message.arg1, message.arg2); break; case : ().quit(); break; } }
Then there is decode data
private void decode(byte[] data, int width, int height) { long start = (); Result rawResult = null; //modify here byte[] rotatedData = new byte[]; 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 = ().buildLuminanceSource(rotatedData, width, height); BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source)); try { rawResult = (bitmap); } catch (ReaderException re) { // continue } finally { (); } if (rawResult != null) { long end = (); (TAG, "Found barcode (" + (end - start) + " ms):\n" + ()); Message message = ((), .decode_succeeded, rawResult); Bundle bundle = new Bundle(); (DecodeThread.BARCODE_BITMAP, ()); (bundle); //(TAG, "Sending decode succeeded message..."); (); } else { Message message = ((), .decode_failed); (); } }
After converting the picture on camera into BinaryBitmap, the rest of things will be recognized directly from the picture.
PlanarYUVLuminanceSource source = ().buildLuminanceSource(rotatedData, width, height);
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));