LoginSignup
4
5

More than 3 years have passed since last update.

Delphi で Let's Zxing

Last updated at Posted at 2019-03-04

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 からダウンロードします。

それぞれのページの「jar (xxxKB)」と書いてあるところをクリックするとダウンロードできます。
koko.png

ここでは、この記事を書いている時点での最新 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が通っている物とします)

Zxing.Core.pas,Zxing.JavaSE.pasの作成
>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つをインスタンス側に移します

Zxing.Core.pas(元)
  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;
Zxing.Core.pas(修正)
  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 が追加されます。

image.png

同様に Zxing.Core.pas と Zxing.JavaSE.pas も追加します。

image.png

コーディング

実際に 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;

実際に試す

フォーム設計時画面

こんなフォームを作りました。

image.png

一番上の画像は TImage で、QR コードをカメラで撮影して、その jpg を読み込ませた物です(リアルタイムに変わったりはしません)
こんなに画質の悪い画像でもきちんと認識できるでしょうか?

二番目には TMemo があります。
三番目には TButton があり、これを押すと次のコードが走ります。

procedure TForm1.RecognizeClick(Sender: TObject);
begin
  Memo1.Text := RecognizeCode(Image1.Bitmap);
end;

つまり、Image1 の画像をデコードして文字列を Memo1 に入れます。

Android 実機での動作

実際に実機で動かし[Recognize]ボタンを押したところです。
こんなに画質が悪いのにきちんと認識して、Memo1 に QR コードの内容が表示されています。

image.png

まとめ

Delphi なら Jar 化されているライブラリを組み込むのもお手の物です。
Android 限定で良いならば、ラッパーライブラリを買う必要はありません!

ちなみに iOS は?

iOS では OS が提供している AVMetadataMachineReadableCodeObjectクラスを使ってバーコードを認識できます。
これら2つを纏めてライブラリ化すると使い出があるかもしれません。

4
5
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
5