絵本をAI(人工知能)に読んでもらいたい。今回はGoogleが提供するCloud Vision APIを使い、OCR(文字認識)を行ってみます。最終的には絵本読み聞かせWebアプリを作ります。
前段、絵本の読み聞かせがつらい
お母さんが長期入院中です。そんな状況もお構いなしで、三男と四男は図書館から40冊くらい絵本を借りてきます。読み聞かせ3冊目くらいで、もう嫌になりました。GoogleのAIに助けてもらいましょう。
絵本読み聞かせアプリの全体設計
Webの技術、HTML・CSS・JavaScriptで開発します。
処理の流れは、
- ブラウザでカメラを起動する
- 撮影ボタンが押して、絵本のページを写真で取り込む
- 写真をCloud Vison APIに渡す
- OCRの結果が、Cloud Vison APIから返ってくる
- OCRの結果をブラウザ上に表示する
- Cloud Text-to-SpeechでOCRの結果を読み上げる
Cloud Vison APIを使いこなせるか?OCRの精度はどれほどか?この2点を先に知りたかったため、3. 4. 5.を先行して開発します。
Cloud Vision APIのOCR準備
Googleの画像認識系AIが使えるサービス「Cloud Vision API」。私の場合は登録を昔行っていたため、すぐに使える状況でした。
Cloud Vision APIの導入部分の仕方は、Cloud Vision APIの使い方まとめ (サンプルコード付き)が分かりやすそうです。
最低限、APIキーを作成できればOKです。
念のためAPI キーを使えるWebサイトを制限します。
認証情報>APIキーの制限と名前変更>ウェブサイトの制限に「localhost/*」と自分のドメインを登録して、大量課金のリスクを減らします。
なお、利用料金はOCR(テキスト検出)だけなら1,000回/月まで無料。次の1,000回から1.5ドルとなります。
写真をアップしてOCRの結果を表示するコード
Cloud Vision APIを使って、まずはOCRの精度を確かめます。
下記のHTMLをlocalhostで動作させます。
このHTMLは、JavaScriptもCSSも1ページに含まれているため、単体で動作します。・・・旧時代のJavaScriptで書いており、IEでも動きます。
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 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 |
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title>OCRサンプル</title> <style> canvas { width: 640px; height: 480px; background-color: #EEE; margin: 10px 0; } textarea{ width: 640px; height: 240px; box-sizing: border-box; } </style> </head> <body> <h1>OCRサンプル</h1> <div> <input type="file" id="file" accept="image/*"> </div> <div> <canvas id="canvas"></canvas> </div> <div> <textarea id="textarea"></textarea> </div> <script> var VISION = {}; VISION.OCR = function(){ "use strict"; var API_KEY = 'あなたのAPIのキー', API_URL = 'https://vision.googleapis.com/v1/images:annotate', canvas = document.getElementById('canvas'), ctx = canvas.getContext('2d'), canvasW = 640, canvasH = 480, originX = 0, originY = 0, scale = 1; var init = function(){ setUploadBtn(); }, setUploadBtn = function(){ var file = document.getElementById('file'); var loadLocalImage = function(e){ var fileData = e.target.files[0], reader, img; if(!fileData.type.match('image.*')) { alert('画像ではありません。'); return false; } reader = new FileReader(); reader.onload = function() { setCanvas(reader.result); sendAPI(reader.result.replace(/^data:image\/(png|jpeg);base64,/, '')); } reader.readAsDataURL(fileData); } file.addEventListener('change', loadLocalImage, false); }, setCanvas = function(src){ var img = new Image(); canvas.width = canvasW; canvas.height = canvasH; ctx.clearRect(0, 0, canvasW, canvasH); img.src = src; img.onload = function() { scale = Math.min(canvas.width / this.width, canvas.height / this.height); originX = (canvasW / 2) - (this.width / 2) * scale; originY = (canvasH / 2) - (this.height / 2) * scale; ctx.drawImage(img, originX, originY, this.width * scale, this.height * scale); } }, drawCanvas = function(data){ var x = data[0].x * scale + originX, y = data[0].y * scale + originY, w = (data[1].x - data[0].x) * scale, h = (data[2].y - data[0].y) * scale; ctx.beginPath () ; ctx.rect(x, y, w, h) ; ctx.strokeStyle = "#76FF03"; ctx.lineWidth = 2; ctx.stroke(); }, sendAPI = function(img){ "use strict"; var httpObj = new XMLHttpRequest(), data, body = { "requests": [ { "image": { "content": img }, "features": [ { "type":"TEXT_DETECTION", } ] } ] }; httpObj.open('post', API_URL + '?key=' + API_KEY, true); httpObj.setRequestHeader('Content-Type', 'application/json'); httpObj.onreadystatechange = function(){ if(this.readyState === 4 && this.status === 200){ data = JSON.parse(httpObj.responseText); setResult(data.responses[0].textAnnotations); } }; httpObj.send(JSON.stringify(body)); }, setResult = function(data){ if(typeof data === 'undefined'){ alert('文字を検出できませんでした。') }else{ document.getElementById('textarea').value = data[0].description; drawCanvas(data[0].boundingPoly.vertices); } }; init(); }; VISION.OCR(); </script> </body> </html> |
ブラウザでOCRのテスト
絵本の表紙を撮影して、動作を確認します。
現段階では、カメラ撮影機能は付けず、絵本の写真をアップロードして使う仕様です。
写真をアップすると、結果が返ってきました。
OCRの結果は、誤字なし。
文字を認識したところに、緑色の四角を描画しています。HTML5のcanvasを利用しています。
他のが写真でもいくつか試すと、誤認するパターンが見えてきました。
手書き文字の認識は、やっぱり難しそうです。
また、クモの絵を「美」と誤認しているため、出来るだけ文字だけを写真で撮ったほうが良さそうです。
それでは引き続き、Cloud Text-to-Speechで絵本を読み上げる機能を開発します。