はじめに
自分は HTML+JavaScript で「蔵書台帳」アプリを作っています。書籍の ISBN をバーコード読取できるようにしています。
ところが ISBN がバーコード表示されていないことがあって、表記された ISBN を OCR できないかと思っていました。
改めて調べてみると、ブラウザ上の JavaScript コードで OCR できるんですね。
JavaScript で OCR できるライブラリは幾つもありましたが、これがよさそうでした。
GitHub - naptha/tesseract.js: Pure Javascript OCR for more than 100 Languages
紹介している記事も多く、幾つか試してみました。
Tesseract.js を使ってみた
- Tesseract 3.0.3
インストールする
今回は CDN を使いました。
<script src="https://unpkg.com/tesseract.js@v3.0.3/dist/tesseract.min.js"></script>
Tesseract.js を使ってみた
Tesseract.js を使ってみます。
以下の記事を参考にしました。
javascript製のOCRライブラリ「tesseract.js」を試してみた | cupOF Interests
画像を img
タグで表示します。併せて読取の結果を表示する項目も用意します。
<img id="target" src="https://tesseract.projectnaptha.com/img/eng_bw.png">
<p id="progress"></p>
<p id="result"></p>
JavaScript のコードで Tesseract.js の機能を利用します。
window.onload = function(){
var buf = document.querySelector('#target');
Tesseract.recognize(
buf,
'eng',
{
logger: function(m) {
document.querySelector('#progress').textContent = m.status;
}
}
)
.then(function(result){
document.querySelector('#result').textContent = result.data.text;
});
};
Tesseract.recognize()
で読取します。
最初の引数に読取したい画像データを渡します。ここでは img
タグのエレメント。
読取できた結果は .then()
に処理を記述します。
カメラ画像の特定の場所の文字を OCR したい
上記の例は画像ファイルの内容を読取しましたが、
カメラで撮影しながらリアルタイムで読取できないでしょうか。
また、カメラで撮影した画像の中の全ての文字を読取したいのではないので、
画像にガイドになる四角を表示して、そこに収まった文字だけ読取するようにできないでしょうか。
JavaScript のコードでカメラ画像にブラウザ上に表示する
まず、JavaScript のコードでカメラ画像にブラウザ上に表示します。
以下の記事を参考にしました。
【JavaScript】Webカメラの映像をブラウザに表示する方法 | アールエフェクト
カメラ画像を表示するエリアを用意します。video
タグを使います。
<video id="video"></video>
<p id="result"></p>
#video {
width: 100%;
height: auto;
}
JavaScript のコードでカメラ画像を表示します。
var video = document.querySelector('#video');
navigator.mediaDevices.getUserMedia({
video: {
facingMode: 'environment'
},
audio: false
})
.then(function(stream){
video.srcObject = stream;
video.play();
})
.catch(function(e){
document.querySelector('#result').textContent = JSON.stringify(e);
});
navigator.mediaDevices.getUserMedia()
を使用します。
上記のコードは video
タグにカメラ画像を表示していますが、その内容を canvas
タグにコピーして表示するようにします。
以下の記事を参考にしました。
Webカメラの映像をcanvasに表示させる - Qiita
カメラ画像を表示するエリアを video
タグから canvas
タグに変えます。
<canvas id="canvas"></canvas>
<p id="result"></p>
#canvas {
width: 100%;
height: auto;
}
JavaScript のコードでカメラ画像を表示します。
var video = document.createElement('video');
var canvas = document.querySelector('#canvas');
navigator.mediaDevices.getUserMedia({
video: {
facingMode: 'environment'
},
audio: false
})
.then(function(stream){
video.srcObject = stream;
video.play();
setInterval(function(){
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
var ctx = canvas.getContext('2d');
ctx.drawImage(video, 0, 0, video.videoWidth, video.videoHeight, 0, 0, canvas.width, canvas.height);
}, 200);
})
.catch(function(e){
document.querySelector('#result').textContent = JSON.stringify(e);
});
video
タグは JavaScript コードで生成します。
canvas
の表示するサイズは CSS で指定しますが、画像データのサイズはコードで video
のサイズと同じに指定します。
setInterval()
を使って 0.2 秒ごとに video
の内容を canvas
にコピーします。
カメラ画像の特定の場所に四角を描く
カメラ画像を canvas
に表示するようにしたので、ここに四角を描きます。
画面の中央に高さ 100 ピクセルの四角を描くことにします。
(前略)
ctx.drawImage(video, 0, 0, video.videoWidth, video.videoHeight, 0, 0, canvas.width, canvas.height);
var box = {
x: 50,
h: 100
};
box.y = (canvas.height - box.h) / 2;
box.w = (canvas.width - box.x * 2);
ctx.beginPath();
ctx.strokeStyle = 'red';
ctx.lineWidth = 2;
ctx.rect(
box.x, box.y, box.w, box.h
);
ctx.stroke();
さらに、上記の四角の場所の内容を別の canvas
にコピーします。
(前略)
var canvas = document.querySelector('#canvas');
var buf = document.createElement('canvas');
document.body.append(buf);
(前略)
ctx.stroke();
buf.width = box.w;
buf.height = box.h;
buf.getContext('2d').drawImage(canvas, box.x, box.y, box.w, box.h, 0, 0, box.w, box.h);
カメラ画像の特定の場所の文字だけ読取する
上記のコードで切取できた画像データを Tesseract.js に渡して OCR します。
以下の記事を参考にしました。
[【JavaScript】ブラウザだけでカメラ撮影した文字を読み取る(OCR) – console dot log]
(https://blog.capilano-fw.com/?p=8085)
(前略)
buf.getContext('2d').drawImage(canvas, box.x, box.y, box.w, box.h, 0, 0, box.w, box.h);
Tesseract.recognize(
buf,
'eng',
{
logger: function(e) {
document.querySelector('#progress').textContent = e.status;
}
}
)
.then(function(result){
document.querySelector('#result').textContent = result.data.text;
});
このコードでは、Tesseract.recognize()
が 0.2 秒ごとに呼出されます。
ところが recognize()
して読取できて .then()
が呼出されるまでに、0.2 秒以上掛かります。
ある recognize()
の呼出が完了する前に次の recognize()
が呼出されるので、まずいですね。
フラグ isRecognizing
を用意して、recognize()
が完了するまでは新たに recognize()
を呼出しないようにします。
var isRecognizing = false;
setInterval(function(){
(中略)
buf.getContext('2d').drawImage(canvas, box.x, box.y, box.w, box.h, 0, 0, box.w, box.h);
if (isRecognizing) return;
isRecognizing = true;
Tesseract.recognize(
buf,
'eng',
{
logger: function(e) {
document.querySelector('#progress').textContent = e.status;
}
}
)
.then(function(result){
document.querySelector('#result').textContent = result.data.text;
isRecognizing = false;
});
}, 200);