QR Codes

From CompSciWiki
Jump to: navigation, search

Step 0: What are QR Codes? Why should I care?

a qr code for the URL www.cs.umanitoba.ca
QR codes ("quick response" codes) are two-dimensional barcodes which are readable by many smartphones. There are apps for reading QR codes for iOS (iPhone, iPod Touch), Android and Blackberry, for instance. Many organizations now use QR codes to allow people with smartphones to link directly to webpages by scanning a QR code. This process is known as mobile tagging. In general, a QR code represents a block of text -- this will usually be a URL that the scanning phone can visit, but could be any text at all.

In this lab, we will show how to create QR codes on the screen that could be decoded by a QR code-reading app.

Step 1: Get QR Code Library

Like the Twitter lab, we need some extra libraries before we can do QR code generation. You can find the zxing library ("zebra crossing") here.

For this class only, you can download the two premade jar files for use in the labs:


Add them to your eclipse project by right-clicking your project, then clicking Properties -> Libraries -> Add External Jars.

Step 2: Generate the QR Code

First, you need the following import statements:

import com.google.zxing.WriterException;
import com.google.zxing.qrcode.encoder.*;
import com.google.zxing.qrcode.decoder.*;

Generating a QR code is easy with the Encorder.encode method. It needs to be enclosed in a try-catch block to prevent the error.

	       QRCode qr = new QRCode();
		try {
			Encoder.encode("text to encode", ErrorCorrectionLevel.H, qr);
		} catch (WriterException e) {
			e.printStackTrace();
		}

The code creates a QR code which represents the text "text to encode". The new QR code is stored in the variable qr. The second parameter of the encode method is the correction level (H is high in this case): the QR code has an error correction ability so that even if some of the barcode is read improperly, the entire message can be decoded.

Step 3: Extract the QR Code as a 2-D array

Since the QR code is a grid of black and white squares, the easiest way to visualize it is as a two-dimensional array. (A two-dimensional array is a rectangular grid of values, like a matrix, rather than a list of values. To reference an element, we use two indices, like this mat[i][j]).

	               ByteMatrix bmat = qr.getMatrix();
			byte[][] x = bmat.getArray();
			for (int i = 0; i < x.length; i++) {
				for (int j = 0; j < x[i].length; j++) {
					System.out.print(x[i][j] + " " );
					
				}
				System.out.println();
			}

This code will get the 2D array of values from the variable qr using the first two lines. The array is of type byte, which is like int, but can store a smaller range of numbers.

This code then loops through all positions in the 2D array (using nested for loops) and prints them off. When you run this block of code, the output should be a matrix of 0s and 1s.

Step 4: Change to an Image

Now let's convert the 2D array to an image so we can display it. The following code needs a few more import statements:

import com.google.zxing.common.BitMatrix;
import java.awt.image.*;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import javax.swing.*;


There is a small hitch to converting to an image that I haven't been able to figure out: there are two different matrix types in the zxing package: BitMatrix and ByteMatrix. Some of the code uses one type and other parts use the other type. So we need to be able to convert between them. To do that, use this small method:

public static BitMatrix convert(ByteMatrix bMat) {
		BitMatrix output = new BitMatrix(bMat.getWidth(),bMat.getHeight());
		for (int i = 0; i < bMat.getHeight(); i++) {
			for (int j = 0; j < bMat.getWidth(); j++) {
				if (bMat.get(i,j)==1) 
					output.set(i, j);
			}
		}
		return output;
	}

Add this code to your program to convert from a ByteMatrix to a BitMatrix, as follows:

BitMatrix bitM = convert(bmat);

After this, the zxing package allows you to create an image from a BitMatrix. Define image to be a variable of type BufferedImage. Then use this line:

image = MatrixToImageWriter.toBufferedImage(bitM);

Step 5: Save the Image

To save the image, we need a few more imports:

import java.io.File;
import java.io.IOException;
import javax.imageio.*;

Now, add the following method to your code:

	public static void saveFile( BufferedImage img ) {

		JFileChooser jf = new JFileChooser();

		int result = jf.showSaveDialog(null);

		if (result==JFileChooser.APPROVE_OPTION) {
			File saveFile = jf.getSelectedFile();
			try {
				ImageIO.write(img, "png", saveFile);

			} catch (IOException e) { 
				System.out.println(e.getMessage());
			}
		}
	}

When called, this method opens a JFileChooser to ask the user while file to save, then writes using the ImageIO.write() method. This method takes the file type (png in this case) and writes to the file provided as a parameter. You could make your JFileChooser more sophisticated by using a file filter. Here are instructions on how to do that.


Once you have that code, you can then call the saveFile method whenever you want to save the file.

Step 6: Scale the Image

You'll notice that if you run the code as given, the QR image is really small!! So we need to scale it. To do so, we can write a little method that replaces every 1 in the image with a square of a bigger size. Here's a method that does that!

	public static BitMatrix scale(BitMatrix b, int scale) {
		BitMatrix output = new BitMatrix(b.getWidth()*scale, b.getHeight()*scale);
		
		for (int i = 0; i < b.getHeight(); i++) {
			for (int j = 0; j < b.getWidth(); j++) {
				if (b.get(i,j))
					output.setRegion(i*scale, j*scale, scale, scale);
			}
		}
		return output;
		
	}

Notice that is uses the methods setRegion which sets an entire square region to 1s. It also uses the method get method, which determines whether an element of the bitmatrix is 0 or 1. You can see the entire documentation for BitMatrix is here.

Full Example

A full GUI-based example is here.