So-net無料ブログ作成
  • ブログをはじめる
  • ログイン

Android+ZxingでQRコード操作 [Android]

手持ちのXperiaが、初期型によくある充電部分の接触不良を発動させて泣きたいアシアです。
今は会社の先輩が機種変したため使わなくなったXperiaを借りてなんとかしのぎ中。

それはともかく、Android開発の仕事にはついていないのですが、
仕事があまりにもひm…勉強兼ねて、
先輩にお題を出してもらってAndroidアプリを作ってみました。
アプリの内容はさておき、Zxingを使ったのでそのメモ。
解説サイトはさほど多くはありませんが、その代わりどこも丁寧に解説してくれてるので、
似たり寄ったりな記事になりそうだ…。

開発環境
1.Android開発環境
これがないと始まらないけど、ここでは割愛。
EclipseとAndroid端末を使って、
デバッグ実行できる環境を作ってあることを前提にします。
2.Zxing(ゼブラクロッシング)
1~2次元バーコードのエンコードやデコードをサポートするJavaライブラリ。
以下から最新版zipをダウンロードし、任意な場所に展開するだけでOK。
2.0が最新のようです。
http://code.google.com/p/zxing/downloads/list
3.Ant
Zxingの解凍先に下記2つがあれば不要。2.0では不要でした。

Zxing解凍先\core\core.jar


Zxing解凍先\javase\javase.jar

導入は以下からダウンロードし、任意な場所に展開の上、
環境変数のPATHにbinへのパスを通すだけです。
http://ant.apache.org/bindownload.cgi
パスを通したら、コマンドプロンプトから、
Zxing解凍先\core と Zxing解凍先\javase にそれぞれ移動して、

ant

を実行すればcore.jarとjavase.jarを作成できます。
4.プロジェクトに追加
利用したいプロジェクトのワークスペース\libs(無ければ作る)に
先程のcore.jarとjavase.jarをぶち込めば準備はOK!

サンプルを動かしてみる
開発の前に、Zxingには有名なバーコードリーダーのソースも
含まれているので、動かし方だけ。
ソースコードも追いかけてみましたが、
バーコードの読込部分以外はいろんな意味で複雑怪奇!
コマンドプロンプトから下記コマンドを実行します。
([メモ]2012/11/6追記: コメントのご指摘でprojectが抜けてましたので修正しました)

cd Zxing解凍先\android\

del build.xml

android update project -p . -t TargetID



TargetIDについては、同じくコマンドプロンプトから、

android list targets

を実行し、各々の開発環境に応じたID番号を選択して下さい。
AndroidManifest.xmlから以下を削除。

<manifest android:installLocation="auto">

<supports-screens android:xlargeScreens="true">

あとはZxing解凍先\androidを一式Eclipseに取り込み、
libsフォルダを作成、core.jarとjavase.jarをぶち込めば実行できます。
@overrideでエラーが出る場合は、Java SDKのバージョンを1.6にします。
Package Explorerから[CaptureActivity]を選択し、右クリック
[Properties]->[Java complier]
[Enable project specific settings]にチェック
[Complier compliance level]に[1.6]を選択
[OK]


QRコード作ってみる
QRCodeWriter.encodeを利用すると、QRコードのbitデータが返却されます。
ここでは引数に渡されたバイナリデータqrDataを格納したQRコードを作成しています。

public class QRCodeControler {

 

 /// エンコード設定

 private static final String ENCORD_NAME = "ISO-8859-1";

 

 @SuppressWarnings({ "unchecked", "rawtypes" })

 public static Bitmap createQRCode(byte[] qrData){

  Bitmap ret = null;

  try{

   // QRコードを生成するにあたり、バイナリデータをStringへ変換

   String contents = new String(qrData, ENCORD_NAME);

   

   // QRコードを生成

   QRCodeWriter writer = new QRCodeWriter();

   Hashtable encodeHint = new Hashtable();

   encodeHint.put(EncodeHintType.CHARACTER_SET, ENCORD_NAME);

   encodeHint.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.L);

   BitMatrix bitData = writer.encode(contents, BarcodeFormat.QR_CODE, 350, 350, encodeHint);

   int width = bitData.getWidth();

   int height = bitData.getHeight();

   int[] pixels = new int[width * height];

   // All are 0, or black, by default

   for (int y = 0; y < height; y++) {

    int offset = y * width;

    for (int x = 0; x < width; x++) {

     pixels[offset + x] = bitData.get(x, y) ? 0xFF000000 : 0xFFFFFFFF;

    }

   }

   // Bitmapに変換

   ret = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);

   ret.setPixels(pixels, 0, width, 0, 0, width, height);

   return ret;

  }catch(Exception e){

   e.printStackTrace();

   return null;

  }

 }

}



QRコード読んでみる
QRCodeReader.decodeを利用すると、Resultに読み込み結果が格納され、
QRコードが見つからない場合はNotFoundExceptionをthrowします。
decodeにQRコードを渡す場合、BinaryBitmapインスタンスに加工する必要があります。
今回は、こちらを丸々コピーしてきて加工はお任せしています。
Zxing解凍先\androidtest\src\com\google\zxing\client\androidtest\RGBLuminanceSource.java


public class QRCodeControler {

 

 /// エンコード設定

 private static final String ENCORD_NAME = "ISO-8859-1";



 @SuppressWarnings({ "rawtypes", "unchecked" })

 public byte[] ReadQRCode(byte[] data){

  try {

   // Bitmapを作成する

   Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);



   // Bitmap内からQRコードを探索する

   LuminanceSource source = new RGBLuminanceSource(bitmap);

   Hashtable decodeHint = new Hashtable();

   decodeHint.put(DecodeHintType.CHARACTER_SET, ENCORD_NAME);

   decodeHint.put(DecodeHintType.TRY_HARDER, true);

   BinaryBitmap binaryBitmap = new BinaryBitmap(new HybridBinarizer(source));

   Reader reader = new QRCodeReader();

   Result result = reader.decode(binaryBitmap, decodeHint);

   

   // 探索結果から文字列を抽出し、byte配列に変換

   byte[] dataByte = result.getText().getBytes(ENCORD_NAME);

   return dataByte;

  } catch (Exception e) {

   return null;

  }

 }

}


おまけ(カメラと連携)
カメラで撮影したデータを渡す場合、カメラデータはYUV形式だと聞いたので、
zxing解凍先\android\src\com\google\zxing\client\android\PlanarYUVLuminanceSource.java


こちらを利用しています。
なお、カメラと連動するときは、シャッターをきらずに
常にオートフォーカス→QRコード検出とした方が認識率から見てもUI的にもいいですね。
ちなみにRGBLuminanceSourceだと、Zxingに作ってもらったQRコードにもかかわらず
認識できないケースが発生しました。なにがなにやら?

(2018/8/20追記)
6年後、再び同じことするとは…使い方とか微妙に変わってますね。 


nice!(1)  コメント(6)  トラックバック(0) 
共通テーマ:日記・雑感

nice! 1

コメント 6

kenji

とても参考になる解説ありがとうございます.

一つだけ気になるところがあるのですが
android update -p . -t TargetID
のところは
android update project -p . -t TargetID
じゃないでしょうか?
by kenji (2012-11-05 15:15) 

Wiアシア

> kenjiさん

コメントありがとうございます。
うわぁ、本当です。project忘れてますね。修正します。
わざわざご指摘頂きましてありがとうございました!
by Wiアシア (2012-11-06 08:52) 

ノット

とても参考になります。

1つ質問なのですが、QRコード読む際に、
データ部分だけではなくて、誤り訂正レベルやそのQRコードのバージョンを
知る方法はないでしょうか?
以上お分かりでしたら教えて下さい。
by ノット (2013-07-01 13:11) 

Wiアシア

>ノットさん

コメントありがとうございます。
ただ、私は簡単なアプリを試しで作っただけで終わってしまったので、誤り訂正レベルなどを読み込む機能までは調べませんでした。
とはいえ、そういった情報を読み込むアプリもあるみたいなので、なにかしら方法はあるんでしょうね〜
by Wiアシア (2013-07-07 21:14) 

pochi

教えてください。

camera2で撮影したQR-CODEをこのページの変換に従ったて変換しzxingに投げているのですがうまく行かないのです。
 ==>。  Toast.makeText(getApplicationContext(), "Not Found", Toast.LENGTH_SHORT).show();に行ってしまうのです。

カメラから渡されたbitmapにはQR-CODEが写っていました。
サイズは1080*1920でbinaryBitmapの中のbits/
heigthとwidthをみると同じく1080と1920が入っていました。
これでうまく変換されているのかなと思っているのですが、QR-CODEを認識してくれないのです。

何かアドバイスをいただけないでしょうか。
「ここをチェックしてみたら」と言うようなアドバイスでも欲しいのです。


mCamera2.takePicture(new ImageReader.OnImageAvailableListener() {

private static final String ENCORD_NAME = "ISO-8859-1";

@Override
public void onImageAvailable(ImageReader reader) {
// 撮れた画像をImageViewに貼り付けて表示。

final Image image = reader.acquireLatestImage();
ByteBuffer buffer = image.getPlanes()[0].getBuffer();
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);

Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
// image.close();

// オリジナルソース
// mImageView.setImageBitmap(bitmap);
// mImageView.setVisibility(View.VISIBLE);
// mTextureView.setVisibility(View.INVISIBLE);
// ここまで

LuminanceSource source = new RGBLuminanceSource(bitmap);
Hashtable decodeHint = new Hashtable();

decodeHint.put(DecodeHintType.CHARACTER_SET, ENCORD_NAME);
decodeHint.put(DecodeHintType.TRY_HARDER, true);
BinaryBitmap binaryBitmap = new BinaryBitmap(new HybridBinarizer(source));

image.close();

Reader reader2 = new MultiFormatReader();
Result result = null;
try {
result = reader2.decode(binaryBitmap);
String text = result.getText();
Toast.makeText(getApplicationContext(), text, Toast.LENGTH_LONG).show();
} catch (Exception e) {
Toast.makeText(getApplicationContext(), "Not Found", Toast.LENGTH_SHORT).show();
}
}
});
}

by pochi (2017-04-18 16:34) 

アシア

> pochiさん
私の知識が実用レベルじゃないのであまり有力な意見はできませんが、
・まずは例外の内容(Exception eの型やgetMessage()結果)を確認する(全てはココからかと)
・binaryBitmapを別ファイルに出力してみて、元データと同じQRコードが吐き出せるか確認する
・binaryBitmap(というかsource)の作り方を変えてみる(BufferedImageLuminanceSourceを使うとか)
あたりをまずはチェックして、「うまく行かない」と仰っているのが具体的にどううまく行っていないか特定するのがいいんではないでしょうか。
by アシア (2017-04-19 09:16) 

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

トラックバック 0