スマートフォンでバーコードを読みっとって何かしたい!
ただ、Android/iOSのアプリを作りたくないとき、Javascriptだけで完成させたい。
そこで、今回は「QuaggaJS」を使ってバーコードを読み取っていきます。
QuaggaJSとは?
QuaggaJSは、Javascriptで書かれたバーコードを読み取るためのライブラリです。
様々なバーコードの種類に対応していて、読みこんたバーコードの位置もリアルタイムで取得することができます。
Quaggaには2つのモードがあります。
- 静的な画像を解析するモード
- カメラを使ってライブストリームから画像をデコードするモード(* こちらはMediaDevices APIの存在が必要です。)
各モードで使用するWeb-APIの互換性を確認することができます。
また、QuaggaJSは二段階の処理で動きます。
- ロケーター: バーコードがある領域の特定する処理
- デコーダー: バーコードの内容をデコードする処理
リアルタイムの検出精度は、完全に検出できるわけでは無いので注意が必要! 一定期間検出し続けて、検出回数が多い検出結果を採用するような仕組みが必要になります。 QuaggaJSを使う
QuaggaJSのメソッドリファレンス
QuaggaJSを使いこなし、バーコードを読み取るためにQuaggaJSのリファレンスを読んでいきます。
Quagga.init(config, callback): カメラを使ったリアルタイム検出
このメソッドはライブラリを与えられたconfigで初期化を行い、起動が完了すると与えられたcallback(err)を実行します。
初期化処理のプロセスでは、リアルタイム検知を実行しようとすると、カメラのアクセス許可のリクエストが実施されます。
第2引数のcallbackは、引数にエラーオブジェクトが渡されます。このオブジェクトには初期化が失敗した時の情報が含まれます。 エラーはブラウザが対応していなかったり、カメラアクセスの許可がえられなかったなどがあります。
configのtargetを指定しない場合、QuaggaJSはCSSセレクタ#interactive.viewportに該当するエレメントが使用されます。 targetは文字列(DOMノードにマッチするCSSセレクタ)かDOMノードになります。
使用例は下記のようになる。
Quagga.init({
inputStream : {
name : "Live",
type : "LiveStream",
target: document.querySelector('#yourElement') // Or '#yourElement' (optional)
},
decoder : {
readers : ["code_128_reader"]
}
}, function(err) {
if (err) {
console.log(err);
return
}
console.log("Initialization finished. Ready to start");
Quagga.start();
});
Quagga.start(): カメラを使ったリアルタイム検出
ライブラリの初期化が完了すると、startメソッドを使ってデコーダーを開始します。 デコーダーが開始するとビデオストリームの開始や位置と画像のデコードを開始します。
Quagga.stop(): カメラを使ったリアルタイム検出
デコーダーが開始した後に、stopメソッドを実行すると、デコーダーは処理を停止し、以降画像を処理することをやめます。 加えて、カメラストリームを使うように初期化を行っていた場合、カメラは切断されます。
Quagga.onProcessed(callback): カメラを使ったリアルタイム検出
onProcessedは、コールバックを設定するメソッドです。 このコールバックは、デコード処理が完了する毎に毎回呼ばれます。 デコード処理の結果を引数dataとして渡します。 このdataオブジェクトは操作の成功/失敗に関する詳細な情報を含んでいます。 このdataオブジェクトの内容は「検知」と「検出」がそれぞれ成功したかどうかで変化します。
Quagga.onDetected(callback): カメラを使ったリアルタイム検出
onDetectedは、コールバックを設定するメソッドです。 このコールバックは、バーコードパターンの「位置」・「デコード」が成功すると呼ばれます。 デコード処理の結果を引数dataとして渡します。 dataオブジェクトは、検出コードを含むデコード処理の情報を含みます。 バーコードはdata.codeResult.codeでアクセスできます。
Quagga.decodeSingle(config, callback): 画像から検出
このメソッドを使うことでgetUserMediaに依存せずに、一枚の画像に対して操作します。 configとcallbackを引数として設定します。 callbackはdataオブジェクトを引数として渡されます、このオブジェクトはonDetectedのcallbackに渡されるものと同じ情報を含んでいます。
Quagga.offProcessed(handler)
onProcessedで設定したコールバックを削除することができます。 offProcessed はイベントキューから与えられたハンドラを削除します。
Quagga.offDetected(handler)
onDetectedで設定したコールバックを削除することができます。 offDetected はイベントキューから与えられたハンドラを削除します。
リザルトオブジェクトのリファレンス
下記のコールバックは同じリザルトオブジェクトを受け取ります。
- onProcessed
- onDetected
- decodeSingle
このデータオブジェクトは下記の情報を含みます。 処理の成功・失敗によって、オブジェクトのフィールドが"undefined"になっていたり、ただの空になっていることがあります。
{
"codeResult": {
"code": "FANAVF1461710", // the decoded code as a string
"format": "code_128", // or code_39, codabar, ean_13, ean_8, upc_a, upc_e
"start": 355,
"end": 26,
"codeset": 100,
"decodedCodes": [
{
"code": 104,
"start": 21,
"end": 41
},
// stripped for brevity
{
"error": 0.8888888888888893,
"code": 106,
"start": 328,
"end": 350
}
],
"startInfo": {
"error": 1.0000000000000002,
"code": 104,
"start": 21,
"end": 41
},
"endInfo": {
"error": 0.8888888888888893,
"code": 106,
"start": 328,
"end": 350
},
"direction": -1
},
"line": [
{
"x": 25.97278706156836,
"y": 360.5616435369468
},
{
"x": 401.9220519377024,
"y": 70.87524989906444
}
],
"angle": -0.6565217179979483,
"pattern": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, /* ... */ 1],
"box": [
[77.4074243622672, 410.9288668804402],
[0.050203235235130705, 310.53619724086366],
[360.15706727788256, 33.05711026051813],
[437.5142884049146, 133.44977990009465]
],
"boxes": [
[
[77.4074243622672, 410.9288668804402],
[0.050203235235130705, 310.53619724086366],
[360.15706727788256, 33.05711026051813],
[437.5142884049146, 133.44977990009465]
],
[
[248.90769330706507, 415.2041489551161],
[198.9532321622869, 352.62160512937635],
[339.546160777576, 240.3979259789976],
[389.5006219223542, 302.98046980473737]
]
]
}
configオブジェクトのリファレンス
QuaggaJSに同梱されている設定はデフォルトのユースケースをカバーしており、特定の要件に合わせて微調整することができます。 設定は、以下のハイレベルなプロパティを定義したconfigオブジェクトによって管理されます。
{
numOfWorkers: 4,
locate: true,
inputStream: {...},
frequency: 10,
decoder:{...},
locator: {...},
debug: false,
}
numOfWorkers
QuaggaJSはWebワーカーをサポートしており、デフォルトの設定では4つのワーカーで動作します。この数は、対象となるデバイスで利用可能なコア数と一致している必要があります。 事前に数がわからない場合や、デバイスの種類が多すぎる場合は、navigator.hardwareConcurrency (こちらを参照)を使用するか、core-estimatorを使用してください。
locate
QuaggaJSの主な機能の一つは、与えられた画像の中のバーコードの位置を特定する機能です。locateプロパティは、この機能をオン(デフォルト)にするかオフにするかをコントロールします。
なぜこの機能をオフにするのでしょうか?バーコードのローカライズは計算量が多く、デバイスによっては正常に動作しない場合があります。もう一つの理由は、オートフォーカスがないために画像がぼやけてしまい、ローカリゼーション機能が非常に不安定になってしまうことでしょう。
しかし、上記のいずれにも当てはまらなくても、ロケートを無効にすると便利な場合がもう一つあります。バーコードの向きやおおよその位置がわかっている場合や、長方形のアウトラインを使ってユーザーを誘導したい場合です。これは、パフォーマンスとロバスト性を同時に向上させることができます。
inputStream
inputStreamプロパティはQuaggaJS内の画像/動画のソースを定義します。
{
name: "Live",
type: "LiveStream",
constraints: {
width: 640,
height: 480,
facingMode: "environment",
deviceId: "7832475934759384534"
},
area: { // defines rectangle of the detection/localization area
top: "0%", // top offset
right: "0%", // right offset
left: "0%", // left offset
bottom: "0%" // bottom offset
},
singleChannel: false // true: only the red color-channel is read
}
inputStream.type
typeプロパティは3つの値を取ることができ、ユースケースに応じて選択する必要があります。(ほとんどの場合は、デフォルトで十分ですが…)
- ImageStream
- VideoStream
- LiveStream (default)
inputStream.constraints
constraintは入力画像の物理的な寸法(width,height)と、複数のカメラが存在するデバイスの場合に、追加プロパティ(facingMode)を使用してデコード処理に使用するカメラのソースを定義します。 さらに、必要に応じて、カメラの選択がユーザに与えられた場合には、deviceIdを設定することができます。 このdeviceIdはMediaDevices.enumerateDevices()から取得することができます。
inputStream.area
areaは画像のデコード領域を制限を定義します。 値はパーセンテージで与えられ、CSSスタイルプロパティで position: absolute を使用している場合と同様です この領域は、locateプロパティがfalseに設定されている場合にも有用で、ユーザーのために矩形を定義します。
inputStream.singleChannel
singleChannel は、デコーダの誤った動作をデバッグしたい場合にのみ意味を持ちます。 true に設定すると、入力画像の赤のカラーチャンネルが、ソースの RGB のグレースケール値を計算する代わりに読み込まれます。 これは、誤って識別された画像のグレースケール表現を保存する ResultCollector と組み合わせて使うと便利です。
frequency
frequencyプロパティは、ビデオストリームのスキャン頻度を制御します。 これはオプションで、1秒あたりの最大スキャン回数を定義します スキャンセッションの実行時間が長く、CPU パワーなどのリソースが気になる場合に便利です。
decoder
QuaggaJSは通常2段階の方法で動作します(locateがtrueに設定されている)が、バーコードの位置が特定された後、デコード処理が始まります。 デコードとは、バーコードを本当の意味に変換するプロセスです。 decoder内の設定オプションのほとんどは、デバッグや可視化のためだけのものです。
{
readers: [
'code_128_reader'
],
debug: {
drawBoundingBox: false,
showFrequency: false,
drawScanline: false,
showPattern: false
},
multiple: false
}
decoder.readers
readersプロパティは、デコードしたいバーコードのタイプを列挙して指定します。 下記の値を取ります。
- code128reader (default)
- ean_reader
- ean8reader
- code39reader
- code39vin_reader
- codabar_reader
- upc_reader
- upcereader
- i2of5_reader
- 2of5_reader
- code93reader
全てのタイプがデフォルトで無効になっています。 必要に応じて、タイプを追加しましょう。 ただし、タイプを追加しすぎた場合、クラッシュや偽陽性(誤ったバーコードを検出する)が増えることに注意をしてください。 デコーダが増えるということは、衝突や偽陽性の可能性が増えることを意味します。 正しいタイプでなくても値を返すものがあるかもしれないので(EAN-13 vs. UPC-A)、リーダーが与えられる順番に注意しなければなりません。
decoder.multiple
multipleプロパティは、有効なバーコードを見つけた後にデコードを継続するかどうかをデコーダに伝えます。 multipleがtrueに設定されている場合、結果は結果オブジェクトの配列として返されます。 配列内の各オブジェクトはボックスを持ち、個々のボックスのデコードの成功に応じてcodeResultを持つことがあります。
decoder.debug
下記のプロパティは主にデバッグや可視化の際に使用されます。
- drawBoundingBox:
- showFrequency:
- drawScanline:
- showPattern:
拡張EANを有効にする ean_readerのデフォルト設定では、EAN-2やEAN-5のような拡張子を読み取ることができません。これらの拡張機能を有効にするには、以下のように設定しなければなりません。
decoder: {
readers: [
{
format: "ean_reader",
config: {
supplements: [
'ean_5_reader',
'ean_2_reader'
]
}
}
]
}
supplementsの順番には気をつけてください。 リーダーは配列内で先頭から順にデコードをしていき見つかった時点でデコードはとまります。 なので、EAN-2とEAN-5の拡張子に興味がある場合は、上に描かれている順番を使いましょう。
ここで重要なのは、サプリが提供されている場合、通常のEAN-13コードは同じリーダーではこれ以上読み取れないということです。 拡張子の有無にかかわらずEAN-13を読みたい場合は、別のean_readerリーダーを設定に追加する必要があります。
locator
ロケータ設定は、locate フラグが true に設定されている場合にのみ意味を持ちます。 これはローカリゼーションプロセスの動作を制御するもので、特定のユースケースに合わせて調整する必要があります。 デフォルトの設定は、開発に最適な値を組み合わせたものです。
Quaggaでの使用に関連するのは2つのプロパティ(halfSampleとpatchSize)だけで、残りは開発やデバッグに必要なものだけです。
{
halfSample: true,
patchSize: "medium", // x-small, small, medium, large, x-large
debug: {
showCanvas: false,
showPatches: false,
showFoundPatches: false,
showSkeleton: false,
showLabels: false,
showPatchLabels: false,
showRemainingPatchLabels: false,
boxFromPatches: {
showTransformed: false,
showTransformedBox: false,
showBB: false
}
}
}
locator.halfSample
halfSampleプロパティは、ロケータプロセスが縮小された画像(幅/高さの半分、ピクセル数4分の1)に対して処理を行うかどうかを指定します。halfSampleをオンにすると、処理時間が大幅に短縮され、暗黙のスムージングによりバーコードパターンを見つけるのに役立ちます。バーコードが本当に小さく、位置を見つけるために完全な解像度が必要な場合にはオフにする必要があります。必要に応じて、オンにしておき、より高解像度のビデオ画像を使用することをお勧めします。
locator.patchSize
patchSizeプロパティは検索グリッドの密度を定義します。 このプロパティにはx-small, small, medium, large, x-largeの文字列を指定することができます。 patchSizeはスキャンしたバーコードのサイズに比例します。 本当に大きなバーコードをクローズアップして読み取る場合は、large または x-large を使用することをお勧めします。 バーコードがカメラレンズから離れている場合(オートフォーカスがない場合やバーコードが小さい場合)は、サイズをsmallまたはx-smallに設定することをお勧めします。 後者の場合は、バーコードを見つけるために解像度を上げることをお勧めします。