Help us understand the problem. What is going on with this article?

Shape Detection API + PWAでブラウザからそのまま使えるバーコードリーダーアプリを作ってみる

More than 1 year has passed since last update.

ラクス Advent Calendar 2018 繋ぎの3番バッターです。

今回はChrome 70から実験的に使えるようになっているShape Detection APIについてまとめてみます。使ってみると意外と実用的だったのでPWA化してブラウザだけでスマホアプリ相当のことが実現できそうなことも検証してみました。

Shape Detection APIとは

Webの新しい技術の普及を推進する団体の WICG が仕様策定を進めているJavaScriptのAPIです。

Accelerated Shape Detection in Images

まだEditor's Draft(草案レベル)ですが以下の3つの事ができます。

  • Face Detection(顔検出)
  • Barcode Detection(バーコードスキャン)
  • Text Detection(テキスト認識)

Shape Detection API - Chrome Platform Status

Chrome 70でこの機能がOrigin Trial(実験的に提供する機能)になりました。

Chromium Blog: Chrome 70 beta: shape detection, web authentication, and more

Google I/O 2018でML Kitが発表されましたが、そのうち3つのMLモデルがChromeに直接組み込まれたということになります。

image.png

実装方法と使い方

Shape Detection APIを使えるようにするには、まず chrome://flags からExperimental Web Platform featuresを Enabled にする必要があります。アドレスバーに以下を入力して設定します。設定後、Chromeを再起動します。

chrome://flags/#enable-experimental-web-platform-features

image.png

Face Detection

FaceDetector をインスタンス化して image オブジェクトを detect() に渡すことで検出結果を取得できます。

const faceDetector = new FaceDetector();
const image = document.getElementById('image');
// 取得した画像をdetect()に渡す
faceDetector.detect(image)
    .then(faces =>
        {
            for (let face of faces) {
                console.log(face);
            }
        })
    .catch((e) => {
        console.error("Face Detection failed: " + e);
    });

取得できる情報(上記 face の中身)

[{
boundingBox: { // 検出した顔の座標
        bottom: 204.38605880737305
        height: 49.07900619506836
        left: 177.3531951904297
        right: 226.4322052001953
        top: 155.3070526123047
        width: 49.079010009765625
        x: 177.3531951904297
        y: 155.3070526123047
    },
    landmarks: [ // 目や口や鼻の位置
        {

            locations: {
                x: 189.6961212158203,
                y: 169.10687255859375
            },
            type: "eye"
        }, {
            locations: {
                x: 198.4111328125,
                y: 185.17288208007812
            },
            type: "mouth"
        }, {
            {
                x: 202.0699462890625,
                y: 169.6812286376953
            },
            type: "nose",
        }
    ]
}]

APIを使えるかどうかを調べる場合は window.FaceDetector をチェックします。

if (window.FaceDetector == undefined) {
    console.error("Face Detection not supported");
    return;
}

Face Detection DEMO

image.png

Barcode Detection

実装方法は BarcodeDetector を使う以外は同じなので割愛します。 サンプルコード でご確認ください。

取得できる情報も基本的にFaceDetectorと同じですが、 バーコードの値は rawValue を取得します。

Barcode Detection DEMO

image.png

Text Detection

テキストについても BarcodeDetector と同じです。サンプルコード でご確認ください。テキスト認識の結果は rawValue を取得しますが、Macでは座標しか取得できず、Androidはテキストの内容も取得できました。

image.png

Text Detection DEMO

WICGのドキュメント によると実行するOSに依存しているようです。

Example implementations of Text code detection are e.g. Google Play Services, Apple’s CIDetector (bounding box only, no OCR) or Windows 10 OCR API.

ブラウザからカメラを操作して画像を取得する

ここからはBarcodeDetectorを使ってバーコードリーダーを作る方法を検討します。上記のサンプルコードではあらかじめ用意しておいた画像を使いましたが、実際にはカメラを使ってインタラクティブにバーコードを読み取ることが求められます。

カメラを操作して画像を取得する方法はGoogle DevelopersのWeb Fundamentalsの記事が参考になります。

ユーザーから画像を取得する  |  Web  |  Google Developers

inputタグを使う

以下の指定でAndroidではファイル選択ボタンからカメラを起動させることができます。

<input type="file" accept="image/*" capture>

サンプルコード

残念ながらこの方法はあまり実用的ではないうえに、実用性を上げるにはいろいろと追加実装も必要になります。

  • 最近のスマホの高性能なカメラの画像は大きすぎるので圧縮が必要
  • カメラのローテーションを考慮する必要がある

バーコードリーダーの用途では取得した画像を再利用するユースケースもあまり考えられないので、fileプロパティで画像を取得するメリットも無く、現実的ではない選択肢と言えます。

WebRTC APIを使う

WebRTC はビデオチャットなどの実現で使われる技術で、 <video> タグを使ってカメラのリアルタイムプレビューを行ったり、カメラストリームからスナップショットを取得することができるため、それを解析すればバーコードリーダーが実現できます。

サンプルコード

const video = document.getElementById('player');
// カメラにアクセスして作成されたストリームを取得
navigator.mediaDevices.getUserMedia({
        video: {
            width: 320,
            height: 240
        },
        audio: false
    })
    .then(function(stream) {
        video.srcObject = stream;
        if (window.BarcodeDetector) {
            const detector = new BarcodeDetector();
            captureTimer = setInterval(function() {
                detector.detect(video).then(
                    function(barcodes) {
                        let barcode = null;
                        for (barcode of barcodes) {
                            console.log(barcode);
                        }
                    }).catch(function(err) {
                    console.log(err);
                });
            }, 100); // 100ミリ秒ごとに解析
        } else {
            console.error(
                'BarcodeDetection is not enable!');
        }
    }).catch(function(err) {
        console.log(err);
    });

スタバのレシートに付いていたQRコードを読み取ることに成功しました。

Barcode Detection(video) DEMO

PWA化する

最後にこれをPWA化します。そもそもPWAとは?については 過去記事 をご覧ください。

今回はPWA化のために主に2つの作業が必要です。

  1. ServiceWorkerを実装してオフライン実行できるように必要なファイルをキャッシュする
  2. Manifestファイルを作成してホーム画面に追加できるようにする

WorkboxでPWA化

まずは1.の作業を行います。PWAの導入に便利なWorkboxを使います。Workboxの使い方はCodelabsの以下のガイドを読めば30分ほどで理解できるのでおすすめです。

Build a PWA using Workbox

基本的にはCodelabsと同じことをやるだけですが、ざっと流れだけまとめておきます。

workbox-cli をインストール

$ npm install --global workbox-cli

sw.js の作成

sw.js
importScripts('https://storage.googleapis.com/workbox-cdn/releases/3.5.0/workbox-sw.js');

if (workbox) {
  console.log(`Yay! Workbox is loaded 🎉`);

  workbox.precaching.precacheAndRoute([]);

} else {
  console.log(`Boo! Workbox didn't load 😬`);
}

workbox.config.js の生成

$ workbox wizard --injectManifest

今回はGithubに置いたサンプルコードをそのままGithub Pagesで動作できるように pwa/barcode.html にサンプルコードを作り、それをリポジトリのルートにビルドさせます(普通はこんな構成にしないと思いますが)。

workbox.config.js
module.exports = {
  "globDirectory": "../",  // リポジトリルートにビルド
  "globPatterns": [
    "pwa/barcode.html"    // キャッシュするファイル
  ],
  "swDest": "../sw.js",   // ServiceWorkerのビルド先
  "swSrc": "sw.js"        // ServiceWorkerのソースコード
};

Barcode Detectionの実行の前にServiceWorkerをロードする処理を追加

if ('serviceWorker' in navigator) {
    window.addEventListener('load', () => {
        navigator.serviceWorker.register('sw.js')
            .then(registration => {
                console.log(`Service Worker registered! Scope: ${registration.scope}`);
            })
            .catch(err => {
                console.error(`Service Worker registration failed: ${err}`);
            });
    });
}

ServiceWorkerがロードされていることを確認

image.png

サンプルコードがキャッシュされていることを確認

image.png

Manifestファイルの作成

Manifestファイルはジェネレーターを使って自動で作成できます。

App Manifest Generator

必要項目を設定し、512x512のアイコンをアップロードすると他のサイズのアイコンファイルと manifest.json をzipでまとめてダウンロードできます。

image.png

ファイルを配置してhtmlからmanifestファイルをロードするように設定

<link rel="manifest" href="manifest.json">

Manifestが設定できていることを確認

image.png

ホーム画面に追加できることを確認

サンプルコードとデモ

まとめ

Shape Detection APIはまだ実験的な機能ですが、API自体はそれほど複雑なものではなく既にある程度実用可能な精度も実現されています。WebRTCやPWAのように既存技術との組み合わせによってブラウザだけで様々な用途に活用できる可能性も感じました。それにしても、ML Kitが発表された半年後にブラウザで直接これらのMLモデルが使えるようになっているスピード感には驚きです。

補足

別の勉強会で発表したスライドと今回のサンプルコードのGitHubリポジトリです。


参考記事

radiocat
Web系技術とAndroidアプリ開発が中心。スクラムマスターもやってます。 よろしくお願いします。
http://radiocat.hatenablog.com/
rakus
「IT技術で中小企業を強くします!」というミッションを掲げ、中小企業の業務効率化に貢献する複数のクラウドサービスを提供しているIT企業です。「楽楽精算」「メールディーラー」など、国内トップシェアを誇る複数のサービスを開発し、累計導入社数は5万社を超えています。次の時代の"楽"を創るための、まだ見ぬサービスや機能を生み出す取り組みは、今日も続いています。
https://www.rakus.co.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした