ブラウザでWebカメラ制御＆OCR処理してみる

## OCR is 何

>光学文字認識（こうがくもじにんしき、Optical character recognition）は、活字の文書の画像（通常イメージスキャナーで取り込まれる）を文字コードの列に変換するソフトウェアである。一般にOCRと略記される。

>[Wikipedia](https://ja.wikipedia.org/wiki/%E5%85%89%E5%AD%A6%E6%96%87%E5%AD%97%E8%AA%8D%E8%AD%98#:~:targetText=%E5%85%89%E5%AD%A6%E6%96%87%E5%AD%97%E8%AA%8D%E8%AD%98%EF%BC%88%E3%81%93%E3%81%86%E3%81%8C%E3%81%8F,%E7%A0%94%E7%A9%B6%E5%88%86%E9%87%8E%E3%81%A8%E3%81%97%E3%81%A6%E5%A7%8B%E3%81%BE%E3%81%A3%E3%81%9F%E3%80%82)

カメラで写真撮ったり、スキャナで取り込んだデータを文字起こししてくれるあれです。
サーバサイドで処理してそうな機能ですが、ブラウザでも実装可能でしたのでやってみました。

## 動作イメージ

ページにアクセスすると、Webカメラが起動し、ブラウザ上でWebカメラのストリームが見られる
OCR処理したい紙などをWebカメラにかざす
ブラウザ上のボタンを押して、スナップショットを撮る
スナップショットの画像が表示され、OCR処理される
処理結果が表示される

## 使用したライブラリ

**[Tesseract.js](https://tesseract.projectnaptha.com/)**
JavascriptのOCR処理ライブラリです。
100種類くらいの言語に対応しているようで、Githubのstar数がすごいです。
もちろん日本語にも対応しています。

## 成果物

codepenで実装しました
（権限関連で表示されないかもなので、リンク先での動作確認が吉）

<p class="codepen" data-height="265" data-theme-id="default" data-default-tab="js,result" data-user="umaniel" data-slug-hash="ExxpZbG" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Camera + OCR Demo">
  <span>See the Pen <a href="https://codepen.io/umaniel/pen/ExxpZbG">
  Camera + OCR Demo</a> by UMA (<a href="https://codepen.io/umaniel">@umaniel</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

ブラウザのカメラアクセスを許可すると、カメラのキャプチャ映像がリアルタイムで描画されるので、文字をカメラにかざして「Capture」ボタンを押してください。
しばらくすると画面下のテキストエリアに処理結果が表示されます。
英語と日本語の認識切り替えはjs側のコメントアウトでできます。

## 解説的なもの

```html
<body>
    <video id="player" controls autoplay></video>
    <button id="capture">Capture</button>
    <canvas id="snapshot" width="320" height="240"></canvas>
    <textarea id="result" style="width:640px;height:240px;"></textarea>
</body>
```

video要素：カメラのキャプチャ映像を描画させるところ
button要素：スナップショットを撮るボタン
canvas要素：撮ったスナップショットを描画するところ
textarea要素：OCR処理の結果を表示するところ

```javascript
const player = document.getElementById('player')

navigator.mediaDevices.getUserMedia({ video: true }).then(stream => {
    player.srcObject = stream
})
```

デバイスのカメラを使う許可を求めるメソッドです、OKだった場合のMediastreamの処理なんかも書いてます。
デバイス側を制御するAPIって難しそうでしたが、こんな短いんですね、びっくりしました。
よくあるブラウザ上でのビデオチャットとかも、根っこの部分はこんな感じの実装だと思うので、案外簡単に作れそうです。
＊動画をWebSocketで配信する感じ？だと思います
メソッドの詳しいことは[こちら](https://developer.mozilla.org/ja/docs/Web/API/MediaDevices/getUserMedia)で。

```javascript
const captureButton = document.getElementById('capture')

captureButton.addEventListener('click', function() {
  ~~
  ~~
```

「Capture」ボタンのイベントリスナです。
'click'した時に発火し、function以下を実行します。

```javascript
const snapshotZone = document.getElementById('snapshot')
  ~~
  ~~
captureButton.addEventListener('click', function() {
    const context = snapshot.getContext('2d')
    context.drawImage(player, 0, 0, snapshotZone.width, snapshotZone.height)
  ~~
  ~~
```

スナップショットを撮る処理です。
HTMLのcanvas APIを使うだけでできました。
動画->スナップショットを特にライブラリを使わずに実装できるのって結構驚きポイントでは・・・？
canvasについての詳細は[こちら](https://developer.mozilla.org/ja/docs/Web/Guide/HTML/Canvas_tutorial)
お絵かきチャット（黒歴史）を触ったことがある方はあんな感じのやつが作れるもの、的なイメージで問題ないです。
canvasと言うと線とか図形しかできないように聞こえますが、画像も描画できまして、今回はそれを使ってます。

```javascript
const result = document.getElementById('result')

  ~~
  ~~
// Tesseract.recognize(snapshotZone, 'jpn', { logger: m => console.log(m) }) // 日本語
Tesseract.recognize(snapshotZone, 'eng', { logger: m => console.log(m) }) // 英語
    .then(({ data: { text } }) => {
        result.value = text
    })
  ~~
  ~~
```

OCR処理の部分です。
canvasのHTML elementをそのまま渡すとよしなに処理してくれます。
返ってきた結果はtextareaに渡しています。
今回やっていませんが、progressの取得ができるので、進捗を表示したりもできます。
文字が多いと結構時間がかかるので、状況により実装が必要かもです。

## やってみて

カメラの解像度の問題？なのかもしれませんが、性能はそれなり、、、でした。
試しにMacbookProで自分の名刺を読み込ませてみましたが、かなり近づいて撮ってどうにか一部読み込めたかな、のレベルでした。
チューニングなしでは難しそうです、学習とかできるのかな。
ただ、全く使えない、というわけではなく、フォント大きめではっきり文字が見える画像であれば難なく読み込めるので、使い所はあると思います。
デバイスのカメラで撮って、という本来の目的からは外れますが。。。