冒頭からタイトル詐欺であることを読者には謝罪したい。
Shape Detection API を使用して、テキストの読み取りは動作確認ができなかった。
代わりにtesseract.jsを使用して、テキストの読み取りを試みたので、そちらをレポートします。
目次
参考
実装 1(画像の中の文字を読み取る)
test1.html1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <html> <head> <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha256-4+XzXVhsDmqanXGHaHvgh1gMQKX40OUvDEBTu8JcmNs=" crossorigin="anonymous" ></script> <script src="https://cdn.rawgit.com/naptha/tesseract.js/1.0.10/dist/tesseract.js"></script> </head> <body> <img src="text1.png" id="source" /> <div id="result"></div>
<script type="text/javascript" src="app1.js"></script> </body> </html>
|
app1.js1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| const image = document.getElementById("source");
let texts = null;
if (!("TextDetector" in window)) { alert("TextDetector is not available"); }
window.onload = async () => { const result = await Tesseract.recognize( image, { lang: "jpn" }, { logger: (m) => console.log(m) } ); console.log(result);
let resultText = ""; for (const block of result.blocks) { let resultWord = ""; block.words.forEach((word, i) => { resultWord += `<li>word${i} = ${word.text}</li>`; });
resultText += ` <ul> <li>text = ${block.text}</li> ${resultWord} <li>左上 {x = ${block.bbox.x0},y = ${block.bbox.y0}}</li> <li>右下 {x = ${block.bbox.x1},y = ${block.bbox.y1}}</li> </ul> `; } $("#result").html(resultText); };
|
recognize
の第二引数には言語の指定を行います。
指定できる言語は、tessdocに紹介されています。
こちらを動かすと、次のようになります。
ひらがなの「う」をカタカナの「ラ」と誤判定しているようです。
たしかに似ているといえば、似ています。
tesseract.js
の判定結果のオブジェクトは、単語単位、行単位、さらに大きな塊の単位など階層構造になっています。
今回は抜粋し、大きな単位での、文字列全体。
単語単位での表示。そして全体の左上と右下座標を表示してみました。
実装 2 カメラから読み込む
前回同様に、カメラから取り込みし文字の認識を試みます。
test2.html1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <html> <head> <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha256-4+XzXVhsDmqanXGHaHvgh1gMQKX40OUvDEBTu8JcmNs=" crossorigin="anonymous" ></script> <script src="https://cdn.rawgit.com/naptha/tesseract.js/1.0.10/dist/tesseract.js"></script> </head> <body> <div id="canvas_area"> <canvas id="result" width="900" height="900"></canvas> </div> <div id="result_text"></div> <script type="text/javascript" src="app2.js"></script> </body> </html>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
| const image = document.createElement("video");
const offscreen_canvas = document.createElement("canvas"); const offscreen_context = offscreen_canvas.getContext("2d");
const canvas = document.querySelector("#result"); const context = canvas.getContext("2d");
offscreen_canvas.width = canvas.width; image.videoWidth = canvas.width; offscreen_canvas.height = canvas.height; image.videoHeight = canvas.height;
let texts = null;
window.onload = async () => { console.log("onload"); const stream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: { exact: "environment" }, }, });
image.srcObject = stream; image.play();
analysis();
setInterval(() => { reflesh(); }, 800); };
const analysis = async () => { offscreen_context.drawImage(image, 0, 0);
try { texts = await Tesseract.recognize(offscreen_context, { lang: "jpn", }).progress(function (p) { console.log(p); }); } catch (e) { console.log(e); window.requestAnimationFrame(analysis); return; }
console.log(texts);
let state = true;
if (texts == null) { state = false; } if (state == true && texts.text.length == 0) { state = false; }
if (state) { offscreen_context.strokeStyle = "rgb(255, 0, 0) "; offscreen_context.lineWidth = 10;
texts.blocks.forEach((block) => { block.words.forEach((word) => { offscreen_context.beginPath(word.bbox.x0, word.bbox.y0); offscreen_context.lineTo(word.bbox.x1, word.bbox.y0); offscreen_context.lineTo(word.bbox.x1, word.bbox.y1); offscreen_context.lineTo(word.bbox.x0, word.bbox.y1); offscreen_context.lineTo(word.bbox.x0, word.bbox.y0); offscreen_context.closePath(); offscreen_context.stroke(); }); }); }
context.drawImage(offscreen_canvas, 0, 0, canvas.width, canvas.height); window.requestAnimationFrame(analysis); };
const reflesh = () => { $("#result_text").empty(); if (texts == null) { $("#result_text").text("ERROR"); return; } if (texts.text.length == 0) { $("#result_text").text("ERROR"); return; } let resultText = "";
texts.blocks.forEach((block) => { console.log(block); resultText += ` <ul> <li>text = ${block.text}</li> </ul> `; });
$("#result_text").html(resultText); };
|
コメントでも記載していますが、recognize
の第一引数に video オブジェクトのimage
を与えると動作が非常に不安定になりました。
動作させたときのは以下のようになります。
うまくいかないときは、文字のないところを誤検知もします。
カメラの入力を基に、画像上の文字の取得、位置の取得ができました。
今回は、Shape Detection API での文字認識がかなわず、tesseract.js
での、文字認識を試みました。
recognize
の第一引数に最初はimage
を渡していたわけですが、この場合の動作が不安定でした。
この点にはまって数時間かけてしまいました。
文字の認識処理は、バーコードと異なり数秒から十数秒かかるものでした。
結果としてバーコード読み取りの速さを再認識することになりました。
今回は日本語で検知を行ったことも処理が遅くなった要因の 1 つであるます。
Shape Detection API の記事 3 部作、これにて終了です。
実戦レベルなのは、バーコード読み取りだけかなーというのが一通り(最後のは動作確認できませんでしたが)触った所感でした。
ではでは。