2017/12/06

Quagga.jsを使ってブラウザ上からJavaScriptでバーコードを読み取る

Mac Book Proのインカメラを使ってバーコード(QRコードではない)を読み取れないか考えていたところ、Quagga.jsというライブラリを見つけた。

このライブラリは、画像ファイルだけでなくカメラを使ってリアルタイムでバーコードを読み取ることもできる。

今回はQuagga.jsを使って画像ファイルからバーコードを読み取れるようにする。
※ MBPのカメラだと認識率が悪いのでどうにかしたい。

Quagga.jsでバーコードを読み取る


<fieldset>
  <label>バーコードの画像</label>
  <input type="file" id="barcode">
</fieldset>

<div id="preview">
</div>

<fieldset>
  <label>結果</label>
  <input type="text" id="result" readonly>
</fieldset>
/* 画像用と認識場所を記したCanvasの2つが生成されるので重ねて表示する */
div#preview {
  position: resolve;
  width: 640px;
  height: 480px;
  
  > .imageBuffer {
    position: absolute;
    top: 0;
    left: 0;
  }
  /* オーバーレイ */
  > .drawingBuffer {
    position: absolute;
    left: 0;
  }
}
// import Quagga.js

class BarcodeReader {
  constructor() {
    // 処理が完了したときに実行される
    Quagga.onProcessed(this._onProcessed.bind(this));
    // バーコードが読み取れたときに実行される
    Quagga.onDetected(this._onDetected.bind(this));
  }

  get config() {
    return {
      // イメージソースの定義
      inputStream: {
        // イメージを表示する場所(デフォルトはid="interactive")
        target: '#preview',
        // Canvasのサイズ
        size: 640,
        singleChannel: false
      },
      locator: {
        patchSize: "medium",
        halfSample: true
      },
      // バーコードの種類
      decoder: {
        readers: [{
          format: "code_128_reader",
          config: {}
        }]
      },
      // Web Workerの数
      numOfWorker: navigator.hardwareConcurrency || 4,
      // 画像内にバーコードの位置を示す(認識したときに枠で囲む)
      locate: true,
      // ↓ここにイメージソースを設置する
      src: null
    };
  }

  /**
   * バーコードを読み込む
   * @param {DOMString} src イメージソース
   */
  decode(src) {
    const config = Object.assign({}, this.config, { src: src });

    return new Promise((resolve, reject) => {
      Quagga.decodeSingle(config, result => {
        resolve(result);
      });
    })
  }

  /**
   * 処理が完了したときに実行される
   */
  _onProcessed(data) {
    const ctx = Quagga.canvas.ctx.overlay;
    const canvas = Quagga.canvas.dom.overlay;

    if (!data) { return; }

    // 認識したバーコードを囲む
    if (data.boxes) {
      ctx.clearRect(0, 0, canvas.width, canvas.height);

      const hasNotRead = box => box !== data.box;
      data.boxes.filter(hasNotRead).forEach(box => {
        Quagga.ImageDebug.drawPath(box, { x: 0, y: 1 }, ctx, { color: 'green', lineWidth: 2 });
      });
    }

    // 読み取ったバーコードを囲む
    if (data.box) {
      Quagga.ImageDebug.drawPath(data.box, { x: 0, y: 1 }, ctx, { color: 'blue', lineWidth: 2 });
    }

    // 読み取ったバーコードに線を引く
    if (data.codeResult && data.codeResult.code) {
      Quagga.ImageDebug.drawPath(data.line, { x: 'x', y: 'y' }, ctx, { color: 'red', lineWidth: 3 });
    }
  }

  /**
   * バーコード読み取りが成功したときに実行される
   */
  _onDetected(data) {
  }

  getDataURL() {
    return Quagga.canvas.dom.image.toDataURL();
  }
}

function main() {
  const barcodeReader = new BarcodeReader();

  document.getElementById('barcode').addEventListener('change', async (e) => {
    const file = e.target.files[0];
    const src = window.URL.createObjectURL(file);

    const result = await barcodeReader.decode(src);

    document.getElementById('result').value = result.codeResult.code;
  });
}

main();



画像ファイルを選択したときに、Quagga.decodeSingleを実行しデコードしている。
Quagga.decodeSingleの第1引数で、バーコードの読み取り方や読み取った画像をCanvasに表示するための設定を行う。

基本的なプロパティは以下のとおり。

  • inputStream: イメージやビデオのソースを定義する
    • target: イメージやビデオを表示する要素のセレクター
    • size: 表示する画像(Canvas)のwidth
    • type: (ImageStream|VideoStream|LiveStream)などの指定
  • decoder: バーコードを読み取るときの設定
    • readers: バーコードの種類を指定


本来Class化する必要はないのだが、できればQuagga.jsをグローバルで持ちたくないのでスコープを狭くするために使っている(サンプルコードでは意味をなしておらず、本来は import Quagga from 'quagga'のように各ファイルでimportしたい)


onProcessedは、バーコードの読み込み処理が完了したときに実行される。
callbackの引数の中には成功・失敗に関する情報が含まれている。
サンプルコードでは、callbackの引数に含まれるbox, boxes, lineを使って、オーバーレイ用のCanvasにバーコードを認識した印を描画している。

onDetectedは、バーコードの読み込みが成功したとき(パターンを検出したとき)に実行される。
callbackの引数にあるdata.codeResult.codeにデコードされた文字列が格納されている。



こんな感じにわりと簡単にバーコードを読み取ることができる。
ただdecodeSingleだと1画像に複数バーコードが入っていても1つしか読み取れない。
それを解消するためにはLiveStreamでビデオからバーコードを読み取る必要があるのだが、それはそれでバーコードの認識率が非常に悪かった。

もしかするとMacbook Proのインカメラの性能の問題な気もするけど…。



以上

written by @bc_rikko

1 件のコメント :

  1. ありがとう! 参考にさせてもらいます!

    返信削除