Subterranean Flower

Barcode Detection APIでブラウザ上からQRコードを読み取る

Author
古都こと
ふよんとえんよぷよぐやま!!!

我々の生活にQRコードが浸透してから何年経ったでしょうか。QRコードの読み取り機能はOS標準で可能になり、もはや「QRコードアプリ」すら不要な時代になりました。

一方でブラウザにおけるQRコードの取り扱いは、全くサポートされていませんでした。今まではゼロから実装した有志のライブラリを使ってQRコードの読み取りを実装していました。

しかしBarcode Detection APIの実装で、そういったライブラリも不要になるかもしれません。

Shape Detection API

近年になって、ブラウザにShape Detection APIというAPIの策定・実装が始まっています。

Shape Detection APIは現在以下の2つのAPIから構成されています。

  • Barcode Detection API
  • Face Detection API

また、「親戚」として、OCRを実現するText Detection APIというものも存在します。

Barcode Detection APIはバーコードや二次元コードを読み取るためのAPIです。画像データさえ渡せばあとは自動でデータを検出してくれます。

Face Detection APIはその名の通り顔を検出するためのAPIです。Barcode Detection APIと同様に画像を渡すだけで動作します。

このうちBarcode Detection APIがChrome 83からはフラグなしでの使用が可能になるため、ここで紹介したいと思います。

ブラウザ対応状況

ブラウザの対応状況は以下のとおりです。

  • Chrome

    • Barcode Detection API(Chrome 83)
    • Face Detection API(フラグ付き)
    • Text Detection API(フラグ付き)
  • Firefox

    • 未対応
  • Safari

    • 未対応

Barcode Detection APIの使い方

Barcode Detection APIを使って、QRコードを読み込んでみましょう。Barcode Detection APIを使用するには、ページがlocalhostまたはhttpsで通信している必要があります。

Barcode Detection APIの使い方は簡単です。windowオブジェクト直下に BarcodeDetector が追加されているので、まずはサポートされているバーコードフォーマットを調べます。

(async function main() {
  // 対応フォーマットの取得
  const formats = await BarcodeDetector.getSupportedFormats();
  console.log(formats);
})();

BarcodeDetector.getSupportedFormats メソッドはサポートされているバーコードフォーマットの文字列配列として解決するPromiseを返します。PC上で実行している場合などは空配列が返ってくるかもしれません。空配列の場合はバーコード検出はサポートされていないということになります。そういった場合はAndroidなどで実行してみましょう。

返ってきた配列の中に "qr_code" が存在すればQRコードを読み込むことができます。あとは BarcodeDetector のインスタンスを作り、 detect メソッドを呼び出すだけです。

例えば以下のようにします:

(async function main() {
  // 対応フォーマットの取得
  const formats = await BarcodeDetector.getSupportedFormats();

  if(!formats.includes('qr_code')) {
    throw new Error('QRコードがサポートされていません…');
  }

  // 画像の読み込み(qr.jpgというファイルを読むこむ場合)
  const img = await fetch('./qr.jpg').then((res) => res.blob());

  // 検出
  const detector = new BarcodeDetector();
  const detectionList = await detector.detect(img);
  console.log(detectionList);
})();

detect の対象にできるのは現時点では以下のものです:

  • img要素
  • video要素
  • canvas要素
  • Blob
  • ImageData
  • ImageBitmap
  • OffscreenCanvas

detect メソッドを実行することで検出オブジェクトの配列として解決するPromiseを得ることができます。何も検出できなかった場合は空の配列になります。

配列には検出オブジェクトの情報が入っています。以下のような形式です:

const detection = {
  boundingBox: {
   x: 0,
   y: 0,
   width: 100,
   height: 100,
   top: 10,
   right: 10,
   bottom: 10,
   left: 10
  },
  rawValue: "QRコードに書き込まれた文字列",
  format: "qr_code",
  cornerPoints: [
    {x: 40, y: 40},
    {x: 250, y: 40},
    {x: 250, y: 250},
    {x: 40, y: 250}
  ]
};

boundingBox はQRコードが含まれている範囲の矩形です。rawValue はQRコードの値がそのまま入っています。format はバーコードのフォーマットで、QRコードを読み込んだ場合は "qr_code" になります。cornerPoints にはQRコードの各頂点の座標が左上・右上・右下・左下の順番で入っています。

detect メソッドから得られた検出オブジェクト配列をfor文などで回せば、欲しいデータを得ることができます。

(async function main() {
  // 対応フォーマットの取得
  const formats = await BarcodeDetector.getSupportedFormats();

  if(!formats.includes('qr_code')) {
    throw new Error('QRコードがサポートされていません…');
  }

  // 画像の読み込み(qr.jpgというファイルを読みこむ場合)
  const img = await fetch('./qr.jpg').then((res) => res.blob());

  // 検出
  const detector = new BarcodeDetector();
  const detectionList = await detector.detect(img);
  
  // 検出できたオブジェクトの配列をforで回す
  for(const detected of detectionList) {
    console.log(detected.rawValue);
  }
})();

検出フォーマットの限定

BarcodeDetector はデフォルトでは、サポートされている全てのフォーマットを検出しようとします。これはときにパフォーマンスに悪影響を与えます。

検出フォーマットを限定したい場合、たとえばQRコードのみを検出する場合、コンストラクタにオプションを渡します。

const detector = new BarcodeDetector({
  formats: ['qr_code']
});

カメラとの連動

QRコードを読み込むときは、一般にカメラが使用されると思います。カメラ映像と共に使用する例を紹介します。

(async function main() {
  // 対応フォーマットの取得
  const formats = await BarcodeDetector.getSupportedFormats();

  if(!formats.includes('qr_code')) {
    throw new Error('QRコードがサポートされていません…');
  }

  // 背面カメラへアクセスする
  const stream = await navigator.mediaDevices.getUserMedia({
    video: {
      facingMode: { exact: "environment" }
    }
  });

  // カメラをvideo要素と結びつける
  const video = document.createElement('video');
  video.srcObject = stream;

  // 画面にvideo要素を追加する(確認用)
  video.width = 720;
  video.height = 480;
  document.body.appendChild(video);

  // 再生を開始する
  await video.play();
  
  // 検出をQRコードのみに限定する
  const detector = new BarcodeDetector({
    formats: ['qr_code']
  });

  // 一定時間ごと(ここでは500ミリ秒間隔)に
  // QRコードの検出を試みる
  setInterval(async () => {
    const detectionList = await detector.detect(video);

    for(const detected of detectionList) {
      console.log(detected.rawValue);
    }
  }, 500);
})();

まとめ

今まではQRコードの検出に有志のライブラリを用いていましたが、これからは標準のAPIのみで検出できるようになるかもしれません。Barcode Detection APIは実質的にAndroidのみの対応となっていますが、他のプラットフォームやブラウザでも対応されれば嬉しいですね。

また、Face Detection APIやText Detection APIについてもほぼ同様の方法で使用できます。こちらも機会があれば紹介したいと思います。