【Android】Google MLKit & Android X Cameraを使用してAndroidの高速QRコードスキャンアプリを実装

本記事のサンプルコード


コードの書き方を直接見たい場合は:コード解析へ
このプロジェクトでは、Kotlin、GoogleのMLKit、およびネイティブのAndroidXカメラを使用してQRスキャナーを実装しています


実装効果




前書き

過去には、いくつかのサードパーティライブラリを使用してQRコードスキャナーを作成していました
例えばzxingやzbarなどです
過去のプロジェクトで、現在のQRコードスキャナーの精度がそれほど高くないというQAのフィードバックを受けました
そこで調査し、実際にテストを行った結果、なぜ精度がそれほど高くないのかがわかりました

実際の原因は:
プロジェクトで使用していたのは古いandroid.hardware.Cameraカメラとzbarを組み合わせてQRコードを解析していたためです
QRコードからの距離が遠かったり、斜めの角度からスキャンしていたりしました

そのため、このようなQRコードスキャナーはiOSほど速く感じられませんでした

そこで今回は、Googleのmlkitを使用して実装してみることにしました

MLkitの違いは何ですか?
  • このAPIはAndroid API 21以上が必要です
  • このAPIは2つのバージョンを提供しています
    一つはgmsから動的にモジュールをダウンロードするもの
    もう一つはライブラリに組み込まれたモジュールを使用するものです。以下の図のように:

  • 私は新しいネイティブカメラライブラリandroidx.cameraを使用してML Kitと組み合わせて使用する例を参考にしました
    元のzbarとandroid.hardware.Cameraを組み合わせて使用するプロジェクトと比較して:
    ↪ どちらのカメラライブラリもAndroid API 21以上でサポートされています
    ↪ zbarとandroid.hardware.Cameraを組み合わせて使用するバージョンのコードには3つのワークアラウンドがあります
    これは元々このプロジェクトを書いた人や以前の例が、アクティビティ内でカメラの初期化問題を解決するために以下のような書き方を採用していたためだと推測されます:






    ↪ 実際に見てみると、android.hardware.Cameraはスレッドセーフではなく、後に@Deprecatedになっています



    ↪ 一方、新しいandroidx.camera.core.Cameraはライフサイクルを持っており、カメラのライフサイクル問題を処理できます
    これにより、いくつかの奇妙なライフサイクル問題を回避できます
    ✅ したがって、新しいandroidx.camera.core.Cameraに切り替えることで、ライフサイクルの問題をより効果的に回避できると推測されます(自分で制御できるため)

  • 新しいandroidx.camera.core.Cameraはライフサイクルを持っており、カメラのライフサイクル問題を処理でき、いくつかの奇妙なライフサイクル問題を回避できます

  • スキャナーのデコードにはMLkitのBarcodeScannerOptionsを使用します
    これにより、解析可能なバーコードの種類が増えました
    サポートされているフォーマットについてはこのURLを参照してください。以下はスキャナーのデコードコードです:

MLKitの実装後の実際のテスト
  • LG K520 Android 11でテスト

  • LG K520 Android 11で実際にテストしたところ、カメラの全体的な動作時間に大きな差はありませんでした。
    実際には、カメラの初期化からQRコードの解析まで約1秒かかります。
    しかし、ライフサイクルによるカメラのクラッシュリスクを回避できます。
  • 実際のテストでは、元の方法よりも応答速度が少し速くなりました(良いスマホでは違いがわかりにくく、体感は小さいです)。
    例えば、zbar+ android.hardware.Cameraは完全にフォーカスが完了してからQRコードの解析を開始しますが、
    mlkit + androidx.camera.core.Cameraフォーカスが半分完了した時点でQRコードの解析を開始できます。

  • 左の画像はzbarを使用、右の画像はML kitを使用しています(このGIFは0.25倍に減速しています)。
  • androidx.camera.core.Cameraはライフサイクルに関連する問題を処理できます。
    新しい方法では0.25倍に減速して見ると、体感的には少し待ってからカメラが表示される(右の画像)ようになります。

  • しかし、より感じられるのは、奇妙な角度やカメラが遠い場合でもスキャンしやすくなることです。
新しいカメラandroid.hardware.Cameraのアプリ内での調整
  • ImageAnalysisを作成する際に目標解像度設定を追加します.setTargetResolution(Size(screenResolutionForCamera.x, screenResolutionForCamera.y))
  • ここで、screenResolutionForCameraは画面解像度を使用してカメラの目標解像度を設定します。
    カメラの目標解像度を設定するかどうかの違いは、カメラが遠くからスキャンする成功率が高いことです。
  • 補足:このAPIのJavaドキュメントには以下のことが記載されています。
    1. 目標解像度を設定しない場合、カメラのデフォルト解像度は640*480です。
    2. 設定後も優先順位に従って実際の解像度が選択されます。
    3. ここでは概要を示します:設定に近く、かつ大きい目標解像度を優先的に選択します。次に、設定に近く、かつ小さい目標解像度を選択します。その際、解像度の比率は提供されたサイズによって優先的に決定されます。
    4. したがって、ハードウェアの制約により、各デバイスで解析できる距離の程度が異なる可能性があります。

結果
  • 上記の調整と最適化を行った後、QAの同僚にテストを依頼しました。
    彼のフィードバックによると、以前提供したアプリよりも精度が向上したとのことです。
    詳細を尋ねると、側面遠くからのスキャン成功率検出される確率が確かに高くなったと言っていました。 これで一つの問題が解決しましたXD
コード開発チュートリアル

  • 上記の分析を読んでいただきありがとうございます。
    このページの最上部にソースコードを掲載しました。
    興味がある方は直接クローンして変更してみてください。

  • 以下では、主なQRコードスキャナーのコード解析を共有します。
    興味がある方は参考にしてください。

BaseCameraLifecycleObserver

コード解説

BaseCameraLifecycleObserverクラスは、他のクラスが継承するための基本的なクラスです。
基本的なカメラの初期化内容が含まれています。
また、ライフサイクルに関連する内容も処理しています。
(例:ライフサイクルに対応したカメラの処理)
また、カメラを手動でオンまたはオフにするためのメソッドも公開しています。

cameraProviderDeferred

特筆すべきは、ここでcameraProviderDeferredを使用していることです。
これはProcessCameraProviderを取得するためのものです。
この操作は時間がかかるため、非同期操作を使用して柔軟に対応しています。
(ただし、ProcessCameraProviderを直接インスタンス化することも試しました。
ほとんどの場合、ANRやフリーズの問題は発生しませんが、
古いデバイス(例:Android 5.0)では稀に1、2回発生することがあります。)

ここでの概念は
CompletableDeferredの中で.applyを使用していることです
applyの範囲内でProcessCameraProviderを作成します
必要な場所で`await()`を使用して待機します
非同期操作を行いながら、実際に作成されるのを待って操作を行うことができます

CompletableDeferredと一般的なDeferredの違いは
complete()を使用して手動で完了のタイミングを制御できることです
この例に適しています
ProcessCameraProviderのリスナーが返されるのを待つ必要があります
初期化が完了したことを知るためです


ScannerLifecycleObserver

コード解説

ScannerLifecycleObserverは比較的シンプルです
BaseCameraLifecycleObserverを継承しています
主にプロジェクトの要件に応じて必要な機能をいくつか記述しています
例えば、いくつかの拡張内容
getCameraDisplayOrientationのようなものです
カメラがサポートする解像度を調整するために使用できます
またはgetImageAnalysisでImageAnalysisを取得します
ImageAnalysisは主にMLKitでどのバーコードタイプを解析できるかを設定するために使用されます


ScannerAnalyzer

コード解説

ScannerAnalyzerはバーコードを解析するための実装です
BaseCameraLifecycleObserverを継承しています
QRコードを解析するためにFORMAT_QR_CODEを使用しています


You might also enjoy