未来の自分へ知識貯金

ゆーきのエンジニアブログ

約5分 Webプログラミング

バーコードリーダー作ってみたい【JavaScript】【QuaggaJS】【Webカメラ】

バーコードリーダー作ってみたい【JavaScript】【QuaggaJS】【Webカメラ】

自宅の物の在庫管理アプリを作りたいと思ったとき、まず欲しくなるのが「商品のバーコードをスキャンして登録する」機能です。専用のハード?ネイティブアプリ?——実は、スマホのブラウザと JavaScript だけでバーコードリーダーが作れます。

使うのは QuaggaJS というライブラリです。Web カメラの映像をリアルタイムに解析して、検出したバーコードの位置に枠を描きながら、コードの数値を読み取ってくれます。ページを開くだけで動くので、アプリのインストールも不要。在庫管理・蔵書管理のようなツールと相性抜群です。

この記事では、カメラ映像を表示して JAN コード(商品バーコード)を読み取るところまでを作ります。

動くもの:HTML 一枚のバーコードリーダー

まずはコピペで動く形で全体を載せます。これを barcode.html として保存し、HTTPS のサーバー越しにスマホで開くと動きます(HTTPS が必要な理由は後述のハマりポイント参照)。

<!DOCTYPE html>
<html>
  <head>
    <style>
      body, html {
        margin: 0;
        overflow: hidden;
      }
      #camera-view {
        width: 100%;
        height: auto;
      }
      .drawingBuffer {
        position: absolute;
        left: 0;
      }
    </style>
  </head>
  <body>
    <p id="detected-code">-</p>
    <div id="camera-view"></div>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/quagga/0.12.1/quagga.min.js"
      integrity="sha512-bCsBoYoW6zE0aja5xcIyoCDPfT27+cGr7AOCqelttLVRGay6EKGQbR6wm6SUcUGOMGXJpj+jrIpMS6i80+kZPw=="
      crossorigin="anonymous" referrerpolicy="no-referrer"></script>
    <script>
      const elemCameraView = document.getElementById('camera-view');
      const elemDetectedCode = document.getElementById('detected-code');

      const config = {
        locate: false,
        frequency: 2,
        inputStream: {
          type: 'LiveStream',
          target: elemCameraView,
          constraints: {
            frameRate: 10,
            facingMode: 'environment',  // 背面カメラを使う
          },
          area: {                       // 画面中央だけを解析対象にする
            top: '20%',
            right: '20%',
            left: '20%',
            bottom: '20%',
          },
        },
        decoder: {
          readers: ['ean_reader'],      // JAN(EAN)コード用
          multiple: false,
        },
      };

      const OnInit = (error) => {
        if (error) {
          console.error(error);
          return;
        }
        Quagga.start();
      };

      const OnProcessed = (result) => {
        const drawingCtx = Quagga.canvas.ctx.overlay;
        const drawingCanvas = Quagga.canvas.dom.overlay;

        if (result) {
          // 検出中の緑の線の枠
          if (result.boxes) {
            drawingCtx.clearRect(0, 0,
              parseInt(drawingCanvas.getAttribute("width")),
              parseInt(drawingCanvas.getAttribute("height")));
            result.boxes.filter(function (box) {
                return box !== result.box;
            }).forEach(function (box) {
                Quagga.ImageDebug.drawPath(box, {x: 0, y: 1}, drawingCtx, {color: "green", lineWidth: 2});
            });
          }
          // 読込中の青枠
          if (result.box) {
            Quagga.ImageDebug.drawPath(result.box, {x: 0, y: 1}, drawingCtx, {color: "#00F", lineWidth: 2});
          }
          // 検出完了時の赤線
          if (result.codeResult && result.codeResult.code) {
            Quagga.ImageDebug.drawPath(result.line, {x: 'x', y: 'y'}, drawingCtx, {color: 'red', lineWidth: 3});
          }
        }
      };

      const OnDetected = (result) => {
        let code = result.codeResult.code;
        AddItem(code);
      };

      const AddItem = (code) => {
        elemDetectedCode.textContent = code;

        let msg = code + ' コードを登録しますか?';
        if (!confirm(msg)) {
          return;
        }

        // ここに検出したコードに対する処理を書く
        // (在庫管理ならDB登録、蔵書管理なら書誌APIへの問い合わせなど)
      };

      Quagga.init(config, OnInit);
      Quagga.onProcessed(OnProcessed);
      Quagga.onDetected(OnDetected);
    </script>
  </body>
</html>

実行するとカメラ映像が表示され、バーコードにかざすと候補に緑枠 → 読み取り中に青枠 → 確定で赤線が描かれて、コードの数値が取れます。

コードの解説

config:読み取りの設定

動きを決めているのは config です。主な項目の意味は次のとおりです。

設定意味
frequency: 21秒あたりの解析回数。上げると反応がよくなるが CPU 負荷も上がる
facingMode: 'environment'スマホの背面カメラを使う('user' だと自撮り側になる)
area映像のうち解析する範囲。中央 60% だけに絞ることで、狙ったバーコード以外を拾いにくくし、処理も軽くする
readers: ['ean_reader']読み取るバーコードの種類。日本の商品バーコード(JAN コード)は EAN と同じ規格なのでこれで読める
multiple: false1画面から複数のコードを同時検出しない

readers には code_128_reader(物流ラベル等)や code_39_reader(工場・会員証等)なども指定できます。ただし種類を増やすほど誤読も増えるので、読みたい種類だけに絞るのが精度のコツです。

3つのコールバック

QuaggaJS は3つのイベントをフックして使います。

  • Quagga.init(config, OnInit): 初期化完了(またはエラー)。成功したら Quagga.start() で解析開始
  • Quagga.onProcessed(OnProcessed): 毎フレーム呼ばれる。ここで検出枠のオーバーレイを描いている(見た目だけの処理なので、不要なら丸ごと削っても動作はする)
  • Quagga.onDetected(OnDetected): コードが確定したときに呼ばれる本命result.codeResult.code に読み取った数値(JAN なら 13 桁)が入っている

読み取り後の処理は AddItem に書きます。在庫管理なら DB への登録、蔵書管理なら ISBN として書誌 API に問い合わせる、といった具合です。

ハマりポイント

  • カメラが起動しない → HTTPS で開いているか確認 ブラウザのカメラ API(getUserMedia)は、セキュリティ上 HTTPS のページでしか動きませんlocalhost だけは例外)。ファイルを直接開いた file://http:// のページでは、エラーにすらならず沈黙することがあります
  • それでも起動しない → カメラの許可 初回アクセス時の許可ダイアログで「ブロック」してしまうと以後動きません。ブラウザのサイト設定からカメラ許可をリセットしてください
  • 誤読がたまに起きる 1次元バーコードの宿命で、角度や照明によってたまに違う数値を返します。実用にするなら「同じコードが連続して N 回読めたら確定」にするか、JAN コードのチェックディジットを検証すると誤登録をほぼ防げます
  • ピントが合わない 接写しすぎるとスマホカメラのピントが合いません。少し離してバーコードを画面中央の枠に収めるほうが早く読めます

おまけ:GAS(Google Apps Script)で使う場合

私はこれを GAS の Web アプリとして動かしていました。GAS では .css.js の単独ファイルが作れないため、HTML ファイルを分割して次のように読み込む形になります。

<head>
  <base target="_top">
  <?!= include('css'); ?>
</head>
<body>
  <p id="detected-code">-</p>
  <div id="camera-view"></div>
  <?!= include('script'); ?>
</body>

<?!= ... ?> は GAS 独自のテンプレート記法なので、普通の Web サーバーに置く場合は不要です(この記事の冒頭のコードのように1枚にまとめれば OK)。GAS を使うと無料でスプレッドシートを DB 代わりにできるので、「読み取ったコードをシートに記録する在庫管理」がサーバー代ゼロで作れます。

まとめ

  • ブラウザ+ QuaggaJS だけで、スマホカメラのバーコードリーダーが作れる
  • 日本の商品バーコード(JAN)は ean_reader で読める。readers は必要な種類だけに絞る
  • カメラ API は HTTPS 必須(動かないときはまずここを疑う)
  • 誤読対策は「連続 N 回一致で確定」かチェックディジット検証
  • GAS に載せればスプレッドシートを DB にした在庫管理がサーバー代ゼロで作れる

読み取った後の処理(AddItem の中身)を書き足せば、在庫管理・蔵書管理・持ち物リストなど色々に化けるベースになります。「バーコードで管理したい」と思ったときの取っかかりになれば幸いです。

この記事をシェアする

ゆーき
ゆーき

プログラマー経験10年以上。好きなプログラム言語はC#。使える言語はC++, Python他。好きな開発環境はVisual Studio。

プロフィールを見る →