写真から電話番号を文字認識するアプリ作ったよ
作ったもの
カメラで撮影した電話番号画像を認識して電話がかけられるスマホ用Webアプリです。
使い方
ファイル選択ボタンを押して電話番号の数字が写っている写真を指定します。このとき「写真を撮る」を選ぶとカメラで撮影した画像を保存せずにそのまま使うこともできます。
画像を選択したらすぐにサーバに送信され数秒で結果が返ります。画像の中に電話番号と解釈できる文字列があれば画面下にリンクとして表示されます。さまざまなフォントや手書き文字もわりといい感じに認識します。
リンクをタップすると発信ダイアログが出て電話をかけられます*1。まずは無料お試しセットから。
画面下の共有ボタンをタップして「ホーム画面に追加」でアイコンを作っておくとネイティブアプリのようにすぐ起動できて便利。
ユースケース
たとえばあなたの洗濯機が夜中に水漏れしたとする。しかしサポート業者の電話受付は10時-17時なのだ。そこであなたはとりあえず連絡先の電話番号をメモ代わりに写真に撮っておく。
翌日、勤務先で空き時間に業者へ電話をかけようとして、あなたははたと困る。なんと驚くべきことに、iPhoneは写真ライブラリを見ながら電話をかけることができないのだ。最近のiPadのように2画面分割することもできず、どんなことをしようと電話のキーパッドを表示すると写真が隠れてしまう。これが名高いヒューマンインタフェースガイドラインか、これが世界の選択か。人生に疲れたあなたは天を仰ぎ、思いつく限り呪詛の言葉を叫び続けることだろう。
写真に撮った電話番号にかけたい場面はわりとあるように思います。上記のような大型家電のサポート、自販機で商品が出てこないトラブル、移動中に気になる看板広告を見かけたとき、テレビCMの電話番号など、いずれも何かの事情でその場では電話できず、カメラでメモしておいて後で連絡するような状況です。そしてこれらは1回かけたらたぶんもう使わない、電話帳に登録するにはちょっと躊躇するような番号です。
以前、困り果ててツイッターで何か良い方法がないか募ってみました。
【緩募】iPhoneで、電話番号が書かれた紙の写真を見ながら電話かける方法。
— aike (@aike1000) 2018年10月12日
このとき結城浩さんからいただいたアイデアは、公式メモアプリに電話番号を書くと自動でリンクになることを利用して、メモに貼り付けた写真を見ながら番号をタイプする、というものでした。
できました。写真からシェアメニューで「メモに追加」して、メモ帳に移って、写真見ながらタイプ。そして番号をタップ。
— 結城浩 (@hyuki) 2018年10月12日
この方法であれば10桁程度の数字を短期記憶する必要がないのでかなり楽になります。とはいえ、全桁打ち直すのはもう少しなんとかならないものか。
最近話題のGoogleレンズは、カメラ映像や写真からテキストをはじめかなり高度な各種情報の画像認識をするようです。iPhoneでもGoogleフォトアプリの一機能として使えるみたいです。ただ、そのためにiOS標準とは異なる写真ライブラリ管理アプリを入れるのも大仰な気がします。*2
つまり、ぼくらがほしいのは電話番号だけ認識するGoogleレンズの単機能版なのです。そんなわけで「電話番号専用Googleレンズもどき(怒られそうなので仮称)」、略して「電話レンズ(仮)」を作りました。
使った技術
Reactは、まあなんというか勉強中なのでとりあえず使ってみた感じ。機能を追加するたびに複雑になっていくアプリを、DOMっぽい単位でコンポーネント化することで考えることをシンプルに保つことができるのは慣れると快適ですね。慣れるまでに3年かかりましたが。
Cloud Vision APIは、Googleの機械学習技術で画像に何が写っているかを認識してくれるめちゃくちゃすごいAPI。今回のアプリはCloud Vision APIにガワをつけただけの200行くらいのシンプルなプログラムです。 APIに送る処理はこの辺の記事を参考にコピペ。ぼくの人生だいたいコピペ。
let body = { requests: [ {image: {content: base64string}, features: [{type: 'TEXT_DETECTION'}]} ] }; let xhr = new XMLHttpRequest(); const url = 'https://vision.googleapis.com/v1/images:annotate?key='+key; xhr.open('POST', url, true); xhr.setRequestHeader('Content-Type', 'application/json'); const p = new Promise((resolve, reject) => { xhr.onreadystatechange = () => { if (xhr.readyState !== XMLHttpRequest.DONE) return; if (xhr.status >= 400) return reject({message: `Failed with ${xhr.status}:${xhr.statusText}`}); resolve(JSON.parse(xhr.responseText)); }; }) xhr.send(JSON.stringify(body));
認識結果はJSONで返ってきます。断片的な言葉の切れ端が多いので、そこから意味のある単語を見つけ出すのが腕の見せどころです。ここでは、最初に正規表現を使って、数字、ハイフン、カッコ、スペースが並んでいたら電話番号っぽい文字列と判断して抽出して、次に抽出した文字列からさらに数字だけ抜き出して10〜11桁なら電話番号と最終判断しています。
const arr_annotations = json.responses[0].textAnnotations; const tel_numbers = []; arr_annotations.forEach((item) => { // 高速化のために短すぎる文字列はここで無視する if (item.description.length >= 10) { const str = item.description; // 数字、ハイフン、カッコ、スペースの連続を電話番号とみなす const match_str = str.match(/[\d\-() ]+/g); // 電話番号が存在するか if (match_str !== null) { // 電話番号が複数存在する場合を考慮してループ match_str.forEach((item) => { // ハイフン、カッコ、スペースを削除して数字のみを得る const num = item.replace(/[-() ]/g, ''); // 数字のみで10〜11桁のものを電話番号として配列にする if ((num.length >= 10) && (num.length <= 11)) { tel_numbers.push({str:item, num:num}); } }); } } });
数字の頭に“tel:"という文字列をくっつけてリンクにすると電話をかけるリンクになります。
const hash = {}; tel_numbers.forEach(item => { // 重複チェック if (!hash[item.num]) { hash[item.num] = true; const parent = document.querySelector('#resultarea'); const li = document.createElement('li'); const a = document.createElement('a'); a.innerText = item.str; a.href = 'tel:' + item.num; li.appendChild(a); parent.appendChild(li); } });
おしまい。
*1:連続して利用すると「このWebサイトから自動的に電話をかけることは禁止されています」というダイアログが出ることがありますが、その場合は「通話を許可」すれば発信できます
*2:追記:この記事を投稿した3日後にiOS向けGoogleレンズの提供を開始したと公式アナウンスがありました https://japan.cnet.com/article/35130001/ 頑張って作ったアプリが3日で役目を終える気持ちを皆さんちょっとでいいので想像してみてください