【React-Webcam + image-js + Tesseract.js】WEBアプリからゲームに表示されるIDを読み取る
はじめに
こんにちは、メガいちです。
Javascriptで使用可能なOCRライブラリTesseract.js
を使ってゲームの部屋IDをクリップボードにコピーするWEBアプリを制作しました。
React、Typescriptの勉強も兼ねて制作したものとなっています。
アプリの公開自体は半年ほど前になりますが、使用したライブラリの処理内容などをまとめておきたいと思います。
リポジトリ
制作の経緯
ゲームでオンライン対戦をする際に、合言葉である部屋IDを共有して入力する必要がありますが、Nintendo Switchなどのコンソールでは、IDを共有するために目視でIDを読み取り、手打ちで入力をする必要があります。
ランダムな文字列を目視で手入力するのはそれなりに面倒なので、部屋IDをクリップボードにコピーするアプリを作りたいと思いました。
記録用や配信用にHDMIキャプチャボードを所有しているプレイヤーは多く、その機材を流用すればOCRに必要な映像を取得できます。
処理の流れ
Webカメラ等のビデオデバイス一覧を取得して、プルダウンメニューに表示する。
↓
選択したデバイスの映像を表示する。React-Webcam
↓
キャプチャボタンを押すと、スクリーンショットをキャプチャしてOCRを実行する。
React-Webcam
→image-js
→Tesseract.js
↓
OCRの結果を表示し、クリップボードにコピーする。
使用したライブラリについて解説
使用例を実際の処理内容を単体で動作するようにしたものになります。
React-Webcam
- WebカメラなどのビデオデバイスをReactで簡単に使用するためのライブラリです。
使用例
ビデオデバイスの一覧を取得し、選択したデバイスの映像を表示する
長いので折りたたみ
import React, { useState, useCallback, useRef, useEffect } from "react";
import Webcam from "react-webcam";
const Capture = () => {
const [videoDevices, setVideoDevices] = useState<MediaDeviceInfo[]>([]);
const [selectDeviceId, setSelectDeviceId] = useState<string>('');
const [capImage, setCapImage] = useState<string>('');
const webcamRef = useRef<Webcam>(null!);
// ビデオデバイスの配列をvideoDevicesに格納
useEffect(() => {
const constraints = {
audio: false,
video: { width: 1920, height: 1080 }
};
navigator.mediaDevices.getUserMedia(constraints)
.then(() => {
const getDevice = async () => {
const addList = (await navigator.mediaDevices.enumerateDevices())
.filter((device) => device.kind === 'videoinput');
setVideoDevices(addList);
}
getDevice();
})
.catch((err) => {
console.error(err);
});
}, []);
// captureボタンを押した時の処理
const capture = useCallback(
// imageSrcにBase64形式でpngを格納
() => {
const imageSrc = webcamRef.current.getScreenshot();
if(imageSrc){
setCapImage(imageSrc);
}
},
[webcamRef]
);
return(
<>
{selectDeviceId != '' &&
<Webcam
audio={false}
imageSmoothing={false}
ref={webcamRef}
screenshotFormat={'image/png'}
forceScreenshotSourceSize
videoConstraints={{
width: 1920,
height: 1080,
facingMode: "user",
deviceId: selectDeviceId
}}
/>
}
{videoDevices.map((device) => (
<button
key={device.deviceId}
onClick={() => setSelectDeviceId(device.deviceId)}
>
{device.label}
</button>
))}
<button onClick={capture}>Capture</button>
</>
)
}
image-js
- ブラウザとNode.jsに対応した画像処理ライブラリです。フロントエンドで動作する画像処理ライブラリは
Jimp
が有名ですが、制作当時メンテナンスが止まっていたためこちらを使用しました。(現在はプロジェクトが再開されています。)
使用例
OCRの前処理として、画像を加工する
import { Image } from 'image-js';
const getTextImage = async ({ base64CapImg }: { base64CapImg: string }) => {
const image = await Image.load(base64CapImg);
if(image){ // 画像が取得できた場合のみ処理を続行する
const ocrImg = image
.crop({y:100}) // 画像の上部100pxを切り取り
.flipY() // 上下反転
.crop({x:1798, y:950}) // 画像の右下(反転前の右上)を切り取り
.flipY() // 反転を戻す
.grey() // グレースケール化
.invert() // 色反転
.toBase64('image/png'); // base64形式に変換
return (`data:image/png;base64,${ocrImg}`)
}
}
Tesseract.js
- Googleがオープンソースで開発しているOCRエンジン
Tesseract
を、WebAssemblyでJavascriptに移植したライブラリ(Version.2まではAsm.jsでもサポートされていました。)
使用例
画像から文字列を抽出する
import { createWorker } from "tesseract.js";
// ホワイトリスト形式で認識する文字を指定
const whitelist = '0123456789ABCDEFGHJKLMNPQRSTUVWXY'
const imageToText = async ({ base64Image }: { base64Image: string }) => {
// Tesseract.jsのWorkerを生成
const worker = await createWorker({
logger: m => console.log(m)
});
await worker.loadLanguage('eng');
await worker.initialize('eng');
await worker.setParameters({
tessedit_char_whitelist: whitelist,
});
const { data: { text } } = await worker.recognize(base64Image);
await worker.terminate();
navigator.clipboard.writeText(text); // クリップボードにコピー
}
UI関連
React + twin.macro(Emotion + TailwindCSS) を使用しています。
twin.macroはCSS-in-JSの記述にTailwindCSSが使えるようになるライブラリで、ビルド時にCSS-in-JSとして変換されます。
今後について
画面の特定の位置に表示される文字列を抽出するという、特定の用途に特化したアプリケーションとして作成しましたが、文字列を抽出する範囲を自由に選択出来るようにするなど、より汎用性の高いアプリケーションにすることも出来ると思います。
また、今回は画像の前処理にimage-js
を使用しましたが、Tesseract.js
v4.0から画像の前処理機能(回転、グレースケール、二値化)が追加されました。可能な処理はTesseract.js
を使用すれば、よりシンプルな実装も出来そうです。
Discussion