dimarts, 6 de desembre del 2011

Codis QR amb Java i ZXing

Cada cop és més comú trobar arreu codis QR. Els codis QR apareixen en promocions i en publicitat de tots tipus. Sens dubte que les aplicacions per a telèfons mòbils que permeten descodificar aquests  codis de barres han contribuït a popularitzar los i, a la vegada, són un estímul més per a fer-se amb un smartphone que permeti gaudir-ne d'aquesta capacitat. Es tracta de dues tecnologies que es reforcen l'una a l'altre.

Vet aquí el que en diu la Viquipèdia dels codis QR:

"El codi QR (en anglès QR Code) és un codi de barres en 2 dimensions (codi matriu) que pot emmagatzemar fins a 7089 caràcters numèrics, 4296 caràcters alfanumèrics (contràriament al codi de barres "tradicional" que només pot emmagatzemar de 10 a 13 caràcters) o 2953 octets .

Per accedir a la informació continguda o encriptada en un qr-code, és necessari un dispositiu digital de captura d'imatges ( Ex: càmera de fotos d'un mòbil, webcam...) i un software específic lector de qr-codes.

Té l'avantatge de poder emmagatzemar moltes informacions tot i ser petit i ràpid d'escanejar. Així, les sigles «QR» deriven de «Quick Response» ja que el contingut pot ser desxifrat ràpidament.
La informació oculta en el codi pot estar en forma de:

TEXT
URL (ens permetrà visitar una pàgina web concreta amb un sol clic)
SMS ( podrem enviar un sms predeterminat a un número de predeterminat amb un sol clic)
VCARD (targes de presentació)

Les possibilitats i aplicacions d'aquests codis en gestió i màrqueting (especialment en opt-in màrqueting i promocions) són novedoses i interessants ja que mitjançant els codis qr es poden unir dos suports històricament aïllats: paper i internet o tinta i bits.

El codi QR ha estat creat per l'empresa japonesa Denso-Wave el 1994. El codi QR és molt emprat al Japó, on actualment és el codi de dues dimensions més popular.

L'estàndard japonès per als codis QR, JIS X 0510, ha estat publicat el 1999, i la norma ISO corresponent, ISO/IEC 18004, ha estat aprovada el juny de 2000.

Un detall important sobre el codi QR, seria que es tracta de codi obert i els seus drets de patent (propietat de Denso Wave) no són exercits."

Que els codis QR siguin d'alguna manera codi open source tampoc deu ser aliè al seu èxit. 

Però, i com es poden crear codis QQ? i com descodificar-los? Més concretament, com crear o llegir codis QR amb Java ( o amb JVM)? Una cerca ràpida amb Google ens mostra diverses llibreries. Em crida l'atenció la llibreria ZXing.

ZXing és una llibreria per al processat d'imatges de codis de barres de 1D/2D per a clients Android i Java. 

Dit i fet, descarrego la llibreria (versió 1.7) d'aquí: http://code.google.com/p/zxing/downloads/detail?name=ZXing-1.7.zip, i la descomprimeixo.

El paquet té la següent estructura:


albert@athena:~/zxing$ ls
actionscript         androidtest  build.properties  COPYING  csharp  javase  README   zxing.appspot.com
android              AUTHORS      build.xml         core     iphone  jruby   rim      zxingorg
android-integration  bug          CHANGES           cpp      javame  objc    symbian
albert@athena:~/zxing$ 

Observem que no hi han jars. Cal compilar-los. És tan senzill com, en un terminal, moure's primrer a la carpeta core i executar ant, i després a la carpeta javase i tornar a excutar ant. Cal, evidentment, tenir ant instal·lat.

De moment, seguiré les instruccions que posen al lloc web de ZXing. 

albert@athena:~/zxing$ cd core
albert@athena:~/zxing/core$ ls
build  build.xml  lib  pom.xml  src  test
albert@athena:~/zxing/core$ ant
Buildfile: /home/albert/zxing/core/build.xml

clean:
   [delete] Deleting directory /home/albert/zxing/core/build

build:

init:

compile:
    [mkdir] Created dir: /home/albert/zxing/core/build
    [javac] Compiling 177 source files to /home/albert/zxing/core/build
    [javac] warning: [options] bootstrap class path not set in conjunction with -source 1.3
    [javac] 1 warning
      [jar] Building jar: /home/albert/zxing/core/core.jar

BUILD SUCCESSFUL
Total time: 34 seconds
albert@athena:~/zxing/core$ cd ../javase
albert@athena:~/zxing/javase$ ls
build  build.xml  pom.xml  src
albert@athena:~/zxing/javase$ ant
Buildfile: /home/albert/zxing/javase/build.xml

init:

build:
      [jar] Building jar: /home/albert/zxing/javase/javase.jar

BUILD SUCCESSFUL
Total time: 6 seconds
albert@athena:~/zxing/javase$ 

Si examinem de nou les carpetes core i javase trobarem core.jar i javase.jar a les carpetes de mateix nom:

albert@athena:~/zxing/javase$ ls
build  build.xml  javase.jar  pom.xml  src
albert@athena:~/zxing/javase$ cd ../core
albert@athena:~/zxing/core$ ls
build  build.xml  core.jar  lib  pom.xml  src  test
albert@athena:~/zxing/core$ 

En aquest moment es pot fer una prova. Faré servir la següent  imatge:


I ara, fem la prova executant la classe CommandLineRunner que permet descodificar una imatge:
albert@athena:~/zxing$ java -cp javase/javase.jar:core/core.jar com.google.zxing.client.j2se.CommandLineRunner ./sticipl.png
file:/home/albert/zxing/./sticipl.png (format: QR_CODE, type: TEXT):
Raw result:
Serveis TIC i Programari Lliure
Parsed result:
Serveis TIC i Programari Lliure
Found 4 result points.
  Point 0: (14.0,102.0)
  Point 1: (14.0,14.0)
  Point 2: (102.0,14.0)
  Point 3: (90.0,90.0)
albert@athena:~/zxing$ 

Pel que fa a la documentació, el javadoc de ZXing el podem trobar a http://zxing.org/w/docs/javadoc/index.html

També ens pot ajudar la pàgina de les DeveloperNotes (http://code.google.com/p/zxing/wiki/DeveloperNotes)

El que faré serà un parell de programes d'exemple, un per a crear codis de barra  QR i un altre per a descodificar-los. Un coder i un decoder d'exemple.

Primer de tot, el decoder. L'aplicaré al següent QR:


Vet aquí el codi:


package com.sticipl.prova.zxing;


import com.google.zxing.Reader;
import com.google.zxing.qrcode.QRCodeReader;
import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
import com.google.zxing.LuminanceSource;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.ResultPoint;
import com.google.zxing.Result;
import java.awt.image.BufferedImage;
import java.io.File;
import javax.imageio.ImageIO;






public class ProvaZxing {
    public ProvaZxing() {
    try {
        Reader reader = new QRCodeReader();    
     
        BufferedImage myImage = ImageIO.read(
                                    new File("/home/albert/Workspace/wk-java/prova-zxing/img/albert.png")
                                );
        LuminanceSource source = new BufferedImageLuminanceSource(myImage);
        BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
    
        Result result = reader.decode(bitmap);


        String text = result.getText();
        System.out.println("Text llegit: " + text);
              
        byte[] rawBytes = result.getRawBytes();
        for (byte byteElem : rawBytes) { 
            System.out.println("Byte: " + byteElem);
        }
        
        BarcodeFormat format = result.getBarcodeFormat();
        System.out.println("Format del codi: " + format);
        
        ResultPoint[] punts = result.getResultPoints();    
        for (ResultPoint punt : punts) { 
            System.out.println("Punt.x: " + punt.getX() + "; Punt.y: " + punt.getY());
        }
      } catch(Exception e) {
        System.out.println("Error: " + e.toString());      
      }  
    }

  public static void main(String args[]) {
        new ProvaZxing();  
    }
}


El programa és directe: instancia un reader del tipus QRCodeReader, crea una BufferedImage a partir de la imatge del  QR. El reader s'aplica al QRReder i, senzillament, el descodifica. El resultat s'obté en diferents formats.

L'aplicació del programa sobre la darrera imatge proporciona la sortida:


Text llegit: Albert Baranguer Codina
Byte: 65
Byte: 116
Byte: 22
Byte: -58
Byte: 38
Byte: 87
Byte: 39
Byte: 66
Byte: 4
Byte: 38
Byte: 23
Byte: 38
Byte: 22
Byte: -26
Byte: 119
Byte: 86
Byte: 87
Byte: 34
Byte: 4
Byte: 54
Byte: -10
Byte: 70
Byte: -106
Byte: -26
Byte: 16
Byte: -20
Byte: 17
Byte: -20
Format del codi: QR_CODE
Punt.x: 14.0; Punt.y: 86.0
Punt.x: 14.0; Punt.y: 14.0
Punt.x: 86.0; Punt.y: 14.0
Punt.x: 74.0; Punt.y: 74.0

La creació de codis QR no té dificultat i segueix un procediment similar al de la descodificació: primer de tot, cal instanciar un QRCodeWriter i amb aquesta instància, directament es codifica la informació. El resultat és una matriu de bits que es fa servir per a crear una BufferedImage. Vet aquí el mètode codifica(String sText), que rep com a argument el text a codificar


    // genera un codi QR per al text passat
    public void codifica(String sCadena) {
      try {      
        Writer writer = new QRCodeWriter();
        BitMatrix bitmat = writer.encode(sCadena, BarcodeFormat.QR_CODE, 200, 200);
      
        // genera la imatge. 
        // Obté ample i alt
        int width = bitmat.getWidth(); 
        int height = bitmat.getHeight(); 
      
        // crea BufferedImage
        BufferedImage imatge = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);


        // estableix els bits de la imatge (true, negre; false, blanc)
        for (int y = 0; y < height; y++) { 
          for (int x = 0; x < width; x++) { 
   boolean boolPunt = bitmat.get(x,y); 
   imatge.setRGB(x, y, (boolPunt ? 0 : 0xFFFFFF));
 }
        }

        // genera el fitxer d'imatge a partir de la BufferedImage
        File file = new File("/home/albert/Workspace/wk-java/prova-zxing/img/generat.png");
        FileOutputStream fos = new FileOutputStream(file);
        ImageIO.write(imatge, "png", fos);
        fos.flush();
        fos.close(); 
      } catch(Exception e) {
System.out.println("Error: " + e.toString());      
      }       
    }


Aplicant el mètode amb els argument mostrats:


    public static void main(String args[]) {
        ProvaZxing prova = new ProvaZxing();
        // prova.decode();  
        prova.codifica("Generat amb Zxing - Serveis TIC i Programari Lliure - Albert Baranguer Codina - accents: àèò éíóú ç ñ ...");
    }


obtinc la imatge:


Per acabar, una altre imatge generada amb el mètode codifica(). Endevineu quin és el text que amaga?

Cap comentari:

Publica un comentari a l'entrada