Saturday, November 12, 2016

IOT: RaspberryPi + Camera Module - Scan For QR Codes Using ZXing in Java

Objective:  The objective of this post is give overview on how to taka a snap using Camera module and decode the snap for the QR codes using ZXing API’s in Java.
YouTube Video:
The YouTube Video for this tutorial is available at this link

GitHub Source:
Overview about QR Codes:
QR code (abbreviated from Quick Response Code) is the trademark for a type of matrix barcode (or two-dimensional barcode) first designed for the automotive industry in Japan. A barcode is a machine-readable optical label that contains information about the item to which it is attached. A QR code uses four standardized encoding modes (numeric, alphanumeric, byte/binary, and kanji) to efficiently store data; extensions may also be used.
The QR code system became popular outside the automotive industry due to its fast readability and greater storage capacity compared to standard UPC barcodes. Applications include product tracking, item identification, time tracking, document management, and general marketing.
A QR code consists of black squares arranged in a square grid on a white background, which can be read by an imaging device such as a camera, and processed using Reed–Solomon error correction until the image can be appropriately interpreted. The required data are then extracted from patterns that are present in both horizontal and vertical components of the image.
ZXing:
ZXing ("zebra crossing") is an open-source, multi-format 1D/2D barcode image processing library implemented in Java, with ports to other languages. 
Supported Formats:
1D Product
1D industrial
2D
UPC-A
Code 39
QR Code
UPC-E
Code 93
Data Matrix
EAN-8
Code 128
Aztec (beta)
EAN-13
Codabar
PDF 417 (beta)
ITF
RSS14
RSS-Expanded
Besides the Java version, developers can leverage other ported projects such as QZXing, zxing-cpp, zxing_cpp.rb, python-zxing and ZXing .NET to quickly make barcode reader or writer software.
RaspiCam:
 The Raspberry Pi Camera Module can be used to take high-definition video, as well as stills photographs. It is a custom designed add-on for Raspberry Pi. It attaches to Raspberry Pi by way of one of the two small sockets on the board upper surface. This interface uses the dedicated CSI interface, which was designed especially for interfacing to cameras. The CSI bus is capable of extremely high data rates, and it exclusively carries pixel data. More details on how to setup the Raspberry Pi Camera module can be found in my other blog.

Dependencies:

Software:
·         Pi4j
·         Java
·         Maven (Please find my blog on how to install Maven on Raspberry Pi. 
Hardware:
·         Raspberry Pi (installed with Java, Maven)
·         RasPiCam (Camera Module)
Task Details:
Once the application is successfully setup (as per the instructions below), here is what you see as the output.
1.   Once you run the below program from Raspberry PI SSH using the following commandmvn exec:java -Dexec.mainClass="camera.QRScanner" -Dexec.classpathScope=runtime, the program will ask the following input from you:
Enter how many QR codes you want to Scan?(For example, enter 2, if you want to scan for 2 images..): 
Incase if you want to scan 2 images of QR Code sources, you can enter 2 (or any number you wish). In this case, I will enter 1.
2.   Now you will see the following message in the screen:
Keep the QR source before the Camera and once you are ready, press "ENTER" to continue...
3.   Once you keep the QR Source before the Camera (as shown in the above picture), you can hit ENTER on your keyboard and the Camera module will take the snap, decode the snap image file for the QR code and prints the decoded QR Code text, for which you will see the output similar to the one below.
Started taking the snap: 1
About to scan the snap file:snap_20160807092913.png for the QR code..
Scan Decode is successful!!! The decoded QR Code text is:
http://www.shopify.com
========End of processing the snap 1========
4.   Incase if you are entering a number >=1, then the steps in 2 and 3 will repeat for that many times.
Steps:
The following are the steps to run the setup this application and explanation of the Java code:
1.  Before switching ON the Raspberry Pi, connect the Camera module to the black slot in Raspberry Pi which is located between Ethernet port slot and HDMI slot. 
2.  Now connect to Raspberry Pi using Putty with your Raspberry Pi Credentials and by-default, you will in the path /home/pi. 
3.  For proper organization purpose, I created folders (using mkdir command) \projects. But this is not mandatory. You can even skip this step and move to next step.
mkdir projects
4.  From the \projects folder, execute the following command to Git clone the code to your local raspberry Pi.
git clone https://github.com/agilerules/IOT.git
5.  Once the git clone is complete, you will see IOT folder created in \projects folder. Now go to IOT folder (using the command cd IOT) and you will see the folder iot-pi4j-raspicamera-QRScanner inside this folder IOT.
6.  Move to iot-pi4j-raspicamera-QRScanner folder (using the command cd iot-pi4j-raspicamera-QRScanner).
7.  Now run the following commands to perform maven clean and maven package. (Incase if mvn command is not recognized then it means that maven is not installed on your Raspberry Pi.  Please follow my blog (http://agilerule.blogspot.com/2016/06/how-to-install-maven-on-raspberry-pi.html) on how to install Maven on Raspberry Pi.
mvn clean

mvn package

8. Now execute the following command to start the maven build and run the Java class QRScanner.java in standalone mode. 
mvn exec:java -Dexec.mainClass="camera.QRScanner" -Dexec.classpathScope=runtime
9.  If everything is successful, the program will print the following information asking input from you:
Enter how many QR codes you want to Scan?(For example, enter 2, if you want to scan for 2 images..):
Incase if you want to scan 2 sources of QR Codes, you can enter 2 (or any number you wish). In this case, I will enter 1.
10.  Now you will see the following message in the screen:
Keep the QR source before the Camera and once you are ready, press "ENTER" to continue...
11.  Once you keep the QR Source before the Camera, you can hit ENTER on your keyboard and the Camera module will take the snap, decode the snap image file for the QR code and prints the decoded QR Code text, for which you will see the output similar to the one below.
Started taking the snap: 1
About to scan the snap file:snap_20160807092913.png for the QR code..
Scan Decode is successful!!! The decoded QR Code text is:

http://www.shopify.com

========End of processing the snap 1========
12.  Incase if you are entering a number >=1, then the steps in 2 and 3 will repeat for that many times.
13.  Here is the source code explanation below:
The complete project structure is available here:
a)      Pom.xml:
The below is the library dependency that we need for this application:
ZXing core: This is required for QR Code decoding.
<dependency>
          <groupId>com.google.zxing</groupId>
          <artifactId>core</artifactId>
          <version>3.2.1</version>
</dependency>
ZXing javase: This is required for QR Code decoding.
<dependency>
          <groupId>com.google.zxing</groupId>
          <artifactId>javase</artifactId>
          <version>3.2.1</version>
</dependency>
pi4j-core – This is for Pi4J to communicate with Raspberry Pi GPIO pins.
      <dependency>
           <groupId>com.pi4j</groupId>
           <artifactId>pi4j-core</artifactId>
           <version>1.1-SNAPSHOT</version>
      </dependency>
Note: At the time of wiring this program, the Pi4j 1.1 is available as a SNAPSHOT version and is not available in Maven repository yet. So had to use the OSS Sonatype Repository URL.
<repository>
            <id>oss-snapshots-repo</id>
            <name>Sonatype OSS Maven Repository</name>
            <url>https://oss.sonatype.org/content/groups/public</url>
            <snapshots>
                  <enabled>true</enabled>
                  <updatePolicy>always</updatePolicy>
            </snapshots>
</repository>
b)      QRScanner.java
The Java class uses ZXing API to decode the QR Codes using decode() method of Reader object. The following are the steps involved in this:
1.       The program will ask for your input on how many QR sources you want to scan and get the user input using Scanner object and assign it to variable count. User can enter any number here.
System.out.println("Enter how many QR codes you want to Scan?(For example, enter 2, if you want to scan for 2 images..):");
       //Wait for the user input
      scanner = new Scanner(System.in);
int count = scanner.nextInt();
2.  Now the program will loop the below logic for whatever input (count) that you provided in the previous step.  In this loop, it will ask you to keep the QR source before the Camera module and hit ENTER (which is again read from Scanner object) so that the Camera module will be instructed to take a snap by invoking the raspistill command using exec() method of Process RunTime and the snap file image is snap followed by date time stamp (Ex: snap_<yyyyMMddHHmmss>.png), which will be created in the root project location (i.e inside \iot-pi4j-raspicamera-QRScanner\)
   for (int i=1; i<=count; i++){
System.out.println("\nKeep the QR source before the Camera and once you are ready, press \"ENTER\" to continue...");
             //Wait for the user input
            scanner = new Scanner(System.in);
                scanner.nextLine();
                System.out.println("Started taking the snap: "+i);
dateTime = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
                fileName="snap_" + dateTime + ".png";
                //System.out.println("fileName="+fileName);
//The below raspistill command will take photos with the image name as "snap_<dateformat_in_yyyyMMddHHmmss>.jpg" with nopreview
                Process snap;
                  try {
snap = rt.exec("raspistill --timeout 5 --output " + fileName + " --nopreview");
                        snap.waitFor(); // Sync
                        //int code = snap.waitFor(); // Sync
                        //System.out.println("code="+code);
                  } catch (IOException e1) {
                        // TODO Auto-generated catch block
                        e1.printStackTrace();
                  } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
               }
3.       Now read the image file path that you would like to decode using ImageIO.read() method. Get the pixels of the image using getRGB() method. Also fetch the instance of RBGLuminanceSource() and pass that to  BinaryBitmap() to fetch the BinaryBitmap() instance.
File imageFile = new File(fileName);
      if(imageFile.exists()){
            //System.out.println("imageFile exists..");
System.out.println("About to scan the snap file:"+fileName+" for the QR code..");
            try{
                    BufferedImage image = ImageIO.read(imageFile);
  int[] pixels = image.getRGB(0, 0, image.getWidth(), image.getHeight(), null, 0, image.getWidth());
RGBLuminanceSource source = new RGBLuminanceSource(image.getWidth(), image.getHeight(), pixels);
                  bitmap = new BinaryBitmap(new HybridBinarizer(source));
                       
                        } catch (IOException e) {
System.out.println("Error while reading the image:"+imageFile);
                          e.printStackTrace();
                  }
4.       Create the instance of QRCodeReader() and invoke the decode() method to fetch the Result instance, from which get the decoded text using getText() method and print them. Incase if there are any failures during this process, it will throw the Exception as shown below.
QRCodeReader reader = new QRCodeReader();
                    try {
                        Result result = reader.decode(bitmap);
System.out.println("Scan Decode is successful!!! The decoded QR Code text is:\n");
                        System.out.println(result.getText()+"\n");
System.out.println("========End of processing the snap "+i+"========");
                    } catch (NotFoundException e) {
System.out.println("Image is not in a recognizable format, hence the Decode is unsuccessful because of the following exception:");
                        e.printStackTrace();
System.out.println("This NotFoundException is thrown when a QR Code was not found in the image. It might have been partially detected but could not be confirmed.");
                    } catch (ChecksumException e) {
System.out.println("Image is not in a recognizable format, hence the Decode is unsuccessful because of the following exception:");
                        e.printStackTrace();
System.out.println("This ChecksumException is thrown when a QR Code was successfully detected and decoded, but was not returned because its checksum feature failed.");
                    } catch (FormatException e) {
System.out.println("Image is not in a recognizable format, hence the Decode is unsuccessful because of the following exception:");
                        e.printStackTrace();
System.out.println("This FormatException is thrown when a QR Code was successfully detected, but some aspect of the content did not conform to the barcode's format rules. This could have been due to a mis-detection.");
                 }
5.       That’s the end of the tutorial. Hope it was useful.



Related Blogs:
You can additionally refer my blog on how to create QR and Bar Codes and decode them using Java.

No comments:

Post a Comment