LoginSignup
17
12

More than 1 year has passed since last update.

Tesseract.js で OCR してみた

Last updated at Posted at 2022-10-01

はじめに

自分は 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 タグで表示します。併せて読取の結果を表示する項目も用意します。

app.html
    <img id="target" src="https://tesseract.projectnaptha.com/img/eng_bw.png">
    <p id="progress"></p>
    <p id="result"></p>

JavaScript のコードで Tesseract.js の機能を利用します。

app.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 タグを使います。

app.html
    <video id="video"></video>
    <p id="result"></p>
app.css
    #video {
        width: 100%;
        height: auto;
    }

JavaScript のコードでカメラ画像を表示します。

app.js
    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 タグに変えます。

app.html
    <canvas id="canvas"></canvas>
    <p id="result"></p>
app.css
    #canvas {
        width: 100%;
        height: auto;
    }

JavaScript のコードでカメラ画像を表示します。

app.js
    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 ピクセルの四角を描くことにします。

app.js
        (前略)
        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 にコピーします。

app.js
    (前略)
    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)

app.js
        (前略)
        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() を呼出しないようにします。

app.js
    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);
17
12
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
12