画像ファイルからQRコードを読み取る

前回はJava用のバーコード処理ライブラリであるZXingを利用して、JavaプログラムからQRコードを読み取る方法を紹介した。ZXingはスマートフォンをはじめとしたカメラ付き端末のアプリケーションで利用されることを前提に開発されている点が特徴である。そこで今回は、AndroidアプリからZXingを使う方法を紹介する。

AndroidアプリはJavaをベースとして開発ができるので、ZXingの基本的な使用手順はJava SEの場合と同様である。ただし注意しなければならないのは、Java SEに用意されたクラスの中には、Android APIに含まれていないものも多数あるという点だ。ZXingでバーコード画像をデコードする際には、com.google.zxing.BinaryBitmapクラスを利用するが、その過程でjava.awt.image.BufferefImageクラスを必要とする。このBufferefImageクラスがAndroid APIには含まれていない。

BufferefImageを使う場合には、そこからcom.google.zxing.BufferedImageLuminanceSourceオブジェクトを生成し、それをBinaryBitmapのソースオブジェクトとして使用した。Android APIではこの方法が使えないので、代わりとなる別のソースオブジェクトを使わなければならない。そのためのクラスはZXingのcoreパッケージには用意されていないが、配布ファイル中のandroidtestパッケージ内に含まれている。androidtestパッケージのsrcフォルダを辿ると、com.google.zxing.client.androidtest.RGBLuminanceSourceというクラスがある。このクラスはBufferedImageLuminanceSourceと同様にLuminanceSourceインタフェースを実装したものであり、ファイルやbyte配列を元にインスタンスを生成することができるので、これをimportして利用すればよい。

コードを簡略化するために、まずはカメラを使わずに画像ファイルからバーコードを読み取る方法を説明する。RGBLuminanceSourceを使う場合、コンストラクタの引数にバーコード画像のファイル名を指定することで次のようにしてBinaryBitmapオブジェクトを生成できる。

リスト1

LuminanceSource source = new RGBLuminanceSource("/sdcard/mycode.png");
BinaryBitmap binaryBitmap = new BinaryBitmap(new HybridBinarizer(source));

BinaryBitmapオブジェクトさえ作れてしまえば、あとはJava SEの場合と同様の手順でデコードを行える。以下に、SDカードに保存されたQRコード画像(mycode.png)の内容を読み取って表示するコードの例を示す。本稿ではZXingを利用する部分のコードのみ解説するので、Androidアプリの作り方などについては[]Developer's Guide(http://developer.android.com/guide/index.html)などを参照していただきたい。

リスト2

/* import宣言は省略 */

public class BarcodeView extends TextView {
    public BarcodeView(Context context) {
    super(context);

        // マルチフォーマット対応の入力ストリームを生成
        Reader reader = new MultiFormatReader();

        Result result = null;
        try {
        // 画像ファイルを読み込んでビットマップデータと生成
        LuminanceSource source = new RGBLuminanceSource("/sdcard/mycode.png");
        BinaryBitmap binaryBitmap = new BinaryBitmap(new HybridBinarizer(source));

        // デコードを実行
        result = reader.decode(binaryBitmap);

        // 結果を表示
        BarcodeFormat format = result.getBarcodeFormat();
        this.setText(("フォーマット: " + format + "\n"));
        this.append("コンテンツ:\n  "+ result.getText() + "\n");
        ResultPoint[] points = result.getResultPoints();
            this.append("位置検出パターン/アライメントパターンの座標: \n");
            for (int i=0; i < points.length; i++) {
                this.append("    Point[" + i + "] = " + points[i] + "\n");
            }
        } catch (FileNotFoundException e) {
            Log.d("TEST", "FileNotFoundEception: " + e);
        } catch (NotFoundException e) {
            Log.d("TEST", "NotFoundEception: " + e);
        } catch (ChecksumException e) {
            Log.d("TEST", "ChecksumEception: " + e);
        } catch (FormatException e) {
            Log.d("TEST", "FromatEception): " + e);
        }
    }
}

ここでは結果の表示にandroid.widget.TextViewウィジェットを利用している。デコードの手順はJava SEの場合と同様で、MultiFormatReaderクラスのdecode()メソッドを使って行う。ただし、出力にはTextViewのsetText()やappend()を使う必要がある。/sdcard/mycode.pngが図1のようなQRコード(前回使用したものと同じ)だった場合、このアプリケーションを実行すると端末には図2のように表示される。

図1 テスト用のQRコード

図2 QRコードから読み取った情報を表示

カメラで撮影した画像からQRコードを読み取る

続いてカメラ撮影した画像からバーコードを読み取る方法だが、これも手順は同様で、RGBLuminanceSourceクラスを利用すればよい。Androidアプリにおけるカメラを使った撮影は、android.hardware.CameraクラスのtakePicture()メソッドにCamera.PictureCallbackなどのコールバックオブジェクトを渡すことで行う(詳細はAndroid APIリファレンスのandroid.hardware.Cameraクラスの項目を参照のこと)。PictureCallbackのコールバックメソッドonPictureTaken()の定義は次のようになっており、撮影した画像データのバイト配列が渡される。

リスト3

public void onPictureTaken(byte[] data, Camera  camera)

したがって、このbyte配列を元にBinaryBitmapオブジェクトを生成できればよいということになる。RGBLuminanceSourceクラスにはandroid.graphics.Bitmapbyteオブジェクトを渡してインスタンスを生成するコンストラクタも用意されている。このBitmapオブジェクトはandroid.graphics.BitmapFactoryクラスのdecodeByteArray()メソッドを使うことで、byte配列を元に生成することができる。したがって次のコードのように、byte配列->Bitmap->RGBLuminanceSourceという手順を辿ることでBinaryBitmapを取得できるというわけだ。ただし、ここでは処理を軽くするために画像サイズは2分の1に縮小して読み込んでいる。

リスト4

// オプションでサイズを指定してBitmapオブジェクトを生成
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inSampleSize = 2;
Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length,opts);
// BinaryBitmapオブジェクトを生成
LuminanceSource source = new RGBLuminanceSource(bitmap);
BinaryBitmap binaryBitmap = new BinaryBitmap(new HybridBinarizer(source));

BinaryBitmapが取得できたら、あとは先ほどと同様にMultiFormatReaderのdecode()メソッドでデコードを実行すればよい。

このように、Androidアプリの場合でも一部を除けばJava SEと同じように利用することができる。自作のアプリケーションにバーコード処理機能を付けたい場合には、ZXingは非常に有用なライブラリである。