Delphi でバーコード認識
Delphi でバーコード認識させる場合、色々なライブラリがあります。
ただ Android 上でバーコード認識させるとなると、多くが Zxing のラッパーとして提供されています。
…だったら、最初から Zxing 使えば良くね!?
ということで、今回は Zxing を Delphi Android アプリに組み込んで実際に QR コードをデコードする方法です。
Zxing とは
Zxing とは、Google が開発している1次元・2次元バーコード処理ライブラリで、オープンソースで提供されています(Apache 2.0 License)。
Zxing を使うとバーコードの生成や読み取りができます。
ただし、Java で記述されているので残念ながら Delphi では Android アプリからしか呼べません。
Zxing Delphi
Zxing の Native Delphi 実装として Zxing Delphi があります。
ですが…速度がすごく遅いです。すごく。実用に耐えないので、無かった物として話を進めます。
準備
Zxing.Core と Zxing.JavaSE をダウンロード
それぞれ Maven Repository からダウンロードします。
-
Zxing.Core
https://mvnrepository.com/artifact/com.google.zxing/core/3.3.3 -
Zxing.JavaSE
https://mvnrepository.com/artifact/com.google.zxing/javase/3.3.3
それぞれのページの「jar (xxxKB)」と書いてあるところをクリックするとダウンロードできます。
ここでは、この記事を書いている時点での最新 3.3.3 をダウンロードしたものとします。
- core-3.3.3.jar
- javase-3.3.3.jar
JDK のインストール
後述の Java2OP が JDK を必要とするので、JDK をダウンロードしてインストールします。
インストール後は環境変数の PATH に bin フォルダを追加しておきます。
もしくは Java2OP.exe を起動するときだけ
set PATH=%PATH%;C:\Program Files\Java\jdk1.8.0_201\bin
としても構いません。
要は Java2OP.exe が JDK にアクセス出来れば良いです。
Java2OP を確認
Java2OP は jar ファイルや class ファイルから、それらにアクセスするための Object Pascal ソースコードを吐き出します。
Java2OP は下記のディレクトリにあります。
(Delphiのインストールディレクトリ)\bin\converters\java2op\Java2OP.exe
ブリッジファイルの作成
Java2OP を使って jar ファイルからブリッジファイルを作ります。
(Java2OP.exeとJDKにはPATHが通っている物とします)
>cd \ダウンロード\Zxing\ ※ここはダウンロードした jar があるフォルダを指定
>java2op -jar core-3.3.3.jar -unit Zxing.Core
>java2op -jar javase-3.3.3.jar -unit Zxing.JavaSE
javase-3.3.3.jar ではエラーが出ますが、今回は気にしないで大丈夫です(今回は使わないクラスなどがブリッジ化できなかったというエラーです)
これで無事終了と行きたいのですが、Java2OP がインスタンスメソッドなのにクラスメソッドとして処理してしまう部分があるので、これを修正します。
今回は使用する decode だけ修正します。
Zxing の他の機能を使う時クラスメソッドではなくインスタンスメソッドが使用されている場合は同じように修正してください。
↓ここに定義されている decode メソッド3つをインスタンス側に移します
JMultiFormatReaderClass = interface(Jzxing_ReaderClass)
['{4673BE75-3623-4DFA-A0D6-C7953A38519D}']
// 下記の3つ
{class} function decode(P1: JBinaryBitmap): Jzxing_Result; cdecl; overload;
{class} function decode(P1: JBinaryBitmap; P2: JMap): Jzxing_Result; cdecl; overload;
{class} function decodeWithState(P1: JBinaryBitmap): Jzxing_Result; cdecl;
{class} function init: JMultiFormatReader; cdecl;
{class} procedure reset; cdecl;
{class} procedure setHints(P1: JMap); cdecl;
end;
[JavaSignature('com/google/zxing/MultiFormatReader')]
JMultiFormatReader = interface(Jzxing_Reader)
['{2E4C1DA2-C8B4-4908-BE12-8B814A523BB5}']
end;
TJMultiFormatReader = class(TJavaGenericImport<JMultiFormatReaderClass, JMultiFormatReader>) end;
JMultiFormatReaderClass = interface(Jzxing_ReaderClass)
['{4673BE75-3623-4DFA-A0D6-C7953A38519D}']
{class} function init: JMultiFormatReader; cdecl;
{class} procedure reset; cdecl;
{class} procedure setHints(P1: JMap); cdecl;
end;
[JavaSignature('com/google/zxing/MultiFormatReader')]
JMultiFormatReader = interface(Jzxing_Reader)
['{2E4C1DA2-C8B4-4908-BE12-8B814A523BB5}']
// ここに移動する
{class} function decode(P1: JBinaryBitmap): Jzxing_Result; cdecl; overload;
{class} function decode(P1: JBinaryBitmap; P2: JMap): Jzxing_Result; cdecl; overload;
{class} function decodeWithState(P1: JBinaryBitmap): Jzxing_Result; cdecl;
end;
TJMultiFormatReader = class(TJavaGenericImport<JMultiFormatReaderClass, JMultiFormatReader>) end;
組み込み
ファイルメニューの「新規作成」→「マルチデバイスアプリケーション」を選択して、マルチデバイスアプリケーションを作成します。
そして、プロジェクトマネージャでプラットフォームを Android にします。
ここで、core.3.3.3.jar と javase-3.3.3.jar をプロジェクトマネージャにドロップします。
すると↓下図のようにライブラリに core.3.3.3.jar と javase-3.3.3.jar が追加されます。
同様に Zxing.Core.pas と Zxing.JavaSE.pas も追加します。
コーディング
実際に Zxing を使って画像からバーコードを認識するコードを書いてみました。
ちょっと長いですが、こんな感じになります。
uses
System.IOUtils
, FMX.Surfaces
, FMX.Helpers.Android
, Androidapi.Helpers
, Androidapi.JNIBridge
, Androidapi.JNI.GraphicsContentViewText
, Androidapi.JNI.JavaTypes
, Zxing.Core
, Zxing.Javase
;
// TBitmap を渡すとその中のバーコードを認識してバーコードの内容を文字列として返す
function RecognizeCode(const iSrc: TBitmap): String;
var
Surf: TBitmapSurface;
Image: JBitmap;
W, H: Integer;
Pixels: TJavaArray<Int32>;
Source: JLuminanceSource;
Bitmap: JBinaryBitmap;
Binarizer: JHybridBinarizer;
Reader: JMultiFormatReader;
Res: Jzxing_Result;
BarCodeText: JString;
begin
Result := '';
try
Image := nil;
// TBitmap から JBitmap へ変換します
Surf := TBitmapSurface.Create;
try
Surf.Assign(iSrc);
Image :=
TJBitmap.JavaClass.createBitmap(
Surf.Width,
Surf.Height,
TJBitmap_Config.JavaClass.ARGB_8888);
if not SurfaceToJBitmap(Surf, Image) then
Image := nil;
finally
Surf.Free;
end;
if Image = nil then
Exit;
// Bitmap を Pixel の配列として取得します
W := Image.getWidth;
H := Image.getHeight;
Pixels := TJavaArray<Int32>.Create(W * H);
try
Image.getPixels(Pixels, 0, W, 0, 0, W, H);
// Pixel の配列から輝度情報を生成します
Source := TJRGBLuminanceSource.JavaClass.init(W, H, Pixels);
if Source = nil then
Exit;
finally
Pixels.DisposeOf;
end;
// 輝度情報を2値化します
Binarizer := TJHybridBinarizer.JavaClass.init(Source);
if Binarizer = nil then
Exit;
// 2値化 Bitmap を作成します
Bitmap := TJBinaryBitmap.JavaClass.init(Binarizer);
if Bitmap = nil then
Exit;
// バーコードリーダーを生成します(MultiFormatReader はサポートしているフォーマット全部に対応する)
Reader := TJMultiFormatReader.JavaClass.init;
if Reader = nil then
Exit;
// バーコードリーダーに2値化 Bitmap を読ませます
Res := Reader.decode(Bitmap);
if Res = nil then
Exit;
// 読み込んだ結果を受け取ります
BarCodeText := Res.toString;
if (BarCodeText = nil) then
Exit;
except
Exit;
end;
// Java 文字列型を Delphi String に変換します。
Result := JStringToString(BarCodeText);
end;
#実際に試す
##フォーム設計時画面
こんなフォームを作りました。
一番上の画像は TImage で、QR コードをカメラで撮影して、その jpg を読み込ませた物です(リアルタイムに変わったりはしません)
こんなに画質の悪い画像でもきちんと認識できるでしょうか?
二番目には TMemo があります。
三番目には TButton があり、これを押すと次のコードが走ります。
procedure TForm1.RecognizeClick(Sender: TObject);
begin
Memo1.Text := RecognizeCode(Image1.Bitmap);
end;
つまり、Image1 の画像をデコードして文字列を Memo1 に入れます。
Android 実機での動作
実際に実機で動かし[Recognize]ボタンを押したところです。
こんなに画質が悪いのにきちんと認識して、Memo1 に QR コードの内容が表示されています。
まとめ
Delphi なら Jar 化されているライブラリを組み込むのもお手の物です。
Android 限定で良いならば、ラッパーライブラリを買う必要はありません!
##ちなみに iOS は?
iOS では OS が提供している AVMetadataMachineReadableCodeObjectクラスを使ってバーコードを認識できます。
これら2つを纏めてライブラリ化すると使い出があるかもしれません。