Tesseract OCR iOSで認識させた文字の情報を取得する

前回の続きです。
ndanah.hatenablog.com

日本語の認識まではできたので、認識結果の情報を取得してみました。
APIリファレンスをみつつ、全てはXcodeの導きのままに…

ソース


結果

入力
f:id:ndanah:20180416230659p:plain

出力
f:id:ndanah:20180416234126p:plain

あえてちょっと斜めにしてみましたが、いい感じに認識していますね!

技術的なメモ

認識結果とその表示
let box = block.boundingBox(atImageOf: actualImageRect.size)

Tesseractでは元画像のサイズを1×1としたときの相対位置で対象(文字)の座標を認識しているようです。
なので、デバイスの画面に収まるように画像を自動レイアウトした場合は、レイアウト後のサイズを取得し、それをatImageOfに指定して変換してもらう必要があります。
レイアウト後の画像の実サイズを取得するExtension、UIImageView.ContentClippingRectは下記より。
www.hackingwithswift.com
Extensionって最高ですね…!初めてSwiftって便利だと思った。

文字のまとまり
let blocks = tesseract.recognizedBlocks(by: G8PageIteratorLevel.symbol)

G8PageIteratorLevelで認識結果のまとまりの大きさを指定できます。
意味深な定数名が付いていますが、もちろん日本語の形態素解析をして云々なんてしていないので、一通り試してみました。

block:ブロック。段落を複数認識したら、そのまとまりということかな?
f:id:ndanah:20180416234131p:plain

paragraph:段落
f:id:ndanah:20180416234231p:plain

textline:行
f:id:ndanah:20180416234249p:plain

word:語。やはり英語前提でスペース区切りで認識されていそう。
f:id:ndanah:20180416234301p:plain

symbol:文字
f:id:ndanah:20180416234316p:plain

Tesseract OCR iOSで日本語を読ませる

Googleが出資しているという文字認識APIiOS用ラッパー、Tesseract OCR iOSを使って文字認識をさせてみた備忘録です。
環境:macOS HighSierra, MacBook Pro 2016, XCode9.3

本体のリポジトリはここ。ただし、CocoaPodsから読ませるので、直接Cloneしない。
github.com

CocoaPodsが入ってない場合は、まずCocoaPodsをインストール。
qiita.com

終わったらここを参考にインストールしていく。
a244.hateblo.jp

テストコードは公式Wikiの方を使用した。シミュレータで動かす分にはこちらのほうがシンプルで良い。
上の記事でも言っているとおり、BridgingHeaderはいらないので惑わされないように(リンクが通らなくて1時間くらい悩んだ)。
Using Tesseract OCR iOS · gali8/Tesseract-OCR-iOS Wiki · GitHub

Tesseract OCR iOSの中身はTesseract3.03らしく、最新(2018/4/8時点でmasterは4.00)の学習データ(tessdata)は使えない。
Githubで公開されている3.04か、Wikiからそれ以前のものを選んで使う。
GitHub - tesseract-ocr/tessdata at 3.04.00
Data Files · tesseract-ocr/tesseract Wiki · GitHub

英語版(eng.tessdata)は3.04そのままでも動くが、日本語版(jpn.tessdata)は3.04を使うとエラーが出る。

read_params_file: parameter not found: allow_blob_division

これを解決するために、下記に従って学習データ内のパラメータを変更するのだが、このときビルドに使うソースは3.04を使うこと
GitHub - tesseract-ocr/tesseract at 3.04.01
4.00だと加工後のtessdataを読み込んでくれない。

a244.hateblo.jp
それから、./configureにパラメータをつけると私の環境では失敗してしまったので、パラメータをなくしたら上手くいった。
これは上の記事を読みながら事前に

$ brew link icu4c --force

しておいたからかも。

  ./autogen.sh
  ./configure
  make
  sudo make install
  make training
  sudo make training-install

なお、一度4.00をインストールしてしまったときは、makeしたディレクトリで

  sudo make uninstall
  sudo make training-uninstall

したらアンインストールできた…気がする。

LINE Messaging APIでオウム返しbotを作りながらRuby on Railsをかじる

前回の記事でherokuを導入した目的は、LINE botを作ってみようと思ったからです。
さっそく下記の記事を参照して、もっとも基本的なオウム返しbotを作成してみました。
qiita.com
これを教材にRuby on Railsの言語仕様やLINE Messaging APIの仕組みについて学んだことを書き残しておきます。
コードは上記記事からの切り貼りなので、単純に作り進めたい人は元記事を参照してください。

はじめる前に

LINE Messaging APIには前身となるLINE botと呼ばれていたバージョンがあり、使い方がかなり違います*1
情報を探すときにはどちらのバージョンを使っている記事なのか注意しましょう。

routes.rbとController

RailsにおけるMVC(モデル/ビュー/コントローラ) - Ruby on Rails入門
サーバーにURLのリクエストが来たとき、何を返すかをroutes.rbに記述する。
今回の場合は(コメント部筆者追記)

Rails.application.routes.draw do
  post '/callback' => 'linebot#callback'   #post '/URI' => 'コントローラ名#メソッド'
end

ここでlinebotはapp/controllers/linebot_controller.rbを暗黙的に意味している*2
コントローラを生成するときは、Terminalから

$rails generate controller linebot

を叩く*3
これで、/callbackのリクエストはlinebot_controller.rb内の

class LinebotController < ApplicationController
…中略…
  def callback

と紐つけられた。

linebot#client

    def client
      @client ||= Line::Bot::Client.new { |config|
        config.channel_secret = ENV["LINE_CHANNEL_SECRET"]
        config.channel_token = ENV["LINE_CHANNEL_TOKEN"]
      }
    end

def callback ~ endがメソッドの定義範囲。
@で始まるのはインスタンス変数(Javaでいうフィールド)*4

a ||= xxx

この記法は自己代入。aがFalseか未定義ならxxxを代入。

newでインスタンスを生成しているっぽいが、引数は|config|ととらえて、引数を渡す前の処理をブロックの部分に追記できると理解するのが良さそう*5

オブジェクト.メソッド{|ブロックパラメータ|
  # ブロック
}

ブロックパラメータというらしい。

戻り値がないじゃん、と思うのだが、Rubyは最後に評価した値が戻るらしい。つまりここでの戻り値は@client*6

linebot#callback

      body = request.body.read

突如出てくるrequestオブジェクトは親クラスであるActionControllerで定義されていて、ブラウザからのリクエストに含まれる情報を持っているオブジェクトを取得できる*7

      signature = request.env['HTTP_X_LINE_SIGNATURE']
      unless client.validate_signature(body, signature)
        error 400 do 'Bad Request' end
      end

request.envは指定されたヘッダ情報を返す。request.headersと同義?*8ここではlinebot#clientで意図したLine Botからのリクエストかどうかを判定している。
unlessはif notと同義。

      events = client.parse_events_from(body)
  
      events.each { |event|
        case event
        when Line::Bot::Event::Message
          case event.type
          when Line::Bot::Event::MessageType::Text
            message = {
              type: 'text',
              text: event.message['text']
            }
            client.reply_message(event['replyToken'], message)
          end
        end
      }

parse_events_fromでbodyに含まれているイベントのコレクションが返ってくるので、ひとつずつ調べて(each)、テキストメッセージの場合のみメッセージを作成する。
このときeventsの要素がeach内で変数eventに展開されているのだが、前述のブロックパラメータの挙動とはちょっと違っている。こちらはイテレータと呼ばれているらしいが、記法が同じだけで実装が違うのか、やはり同じ実装によるのか?
なお、この分岐はifで良いような気がするが、別のイベントを拾いたくなったときのためにcaseになっているのだろう。

messageの右辺はハッシュの記法で、キー:値。

{ a:"aaa", b:"bbb" }

感想

なんだか暗黙のルールや記号が多くて読みにくい…文章的に書けそうな気はするけども。
次はデータベースを使ってみよう。

Rubyド素人がherokuをGetting Startedするよ(Mac)

元旦から暇人なのでherokuをはじめてみました。
Rubyもサーバーサイドも一切いじったことない私が、Getting startedを完走するまでにつまづいたところの備忘録です。
Let's get started!
devcenter.heroku.com

環境とか

Introduction

Heroku accountを取得する。
Heroku | Sign up
送られてくるメールにあるURLからアクティベートする。
RubyとBundlerのインストールが指示されているが、一旦これをすっ飛ばす。

Set up

Download the Heroku CLI for...でMac OS Xを選択して、ダウンロードしたapkを実行。
ターミナルを起動して、

$ heroku login
Enter your Heroku credentials.
Email: 登録メールアドレス
Password: 登録パスワード
...

ここまでいってから、Rubyを入れていきます。こちらを参考に。
[Ruby入門] 01.導入(Macに最新版のRubyを入れる) - Qiita

$ brew install rbenv
$ git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build

次にPATHを通します。こちらを参考に。
MacでPATHを通す - Qiita

$ vi ~/.bash_profile

Vimが開きます。Insertモードにして

i

PATHを入力

export PATH="$HOME/.rbenv/bin:$PATH"
if which rbenv > /dev/null; then eval "$(rbenv init -)"; fi

Insertモードから出て、上書きして終了*1

esc
:w
:q

ターミナルに戻ったら、変更を反映させます。反映されたかどうかはechoで確認。

source ~/.bash_profile
echo $PATH

まず、herokuのRubyバージョンを調べる*2

heroku run "ruby -v"

表示されたバージョンのRubyをインストールする。執筆時点では2.3.5だったので、この表記で進めます。
インストールが成功したら、デフォルトではSystemバージョンになっているので変更しておく。

$ rbenv install 2.3.5
$ rbenv global 2.3.5

次にBundlerとPostgreSQLをインストール。
bundle installのpgインストールでエラー | EasyRamble

$ gem install bundler
$ brew install postgresql

Declare app dependencies

実際には、上の手順を踏まなかったために、ここでbundle installが失敗した。
食らったエラーと原因の一覧。

command not found

Bundlerがインストールされていない。

Your Ruby version is 2.5.0, but your Gemfile specified 2.3.5

RubyとGemfileのバージョンが違っている。

An error occurred while installing pg (0.20.0), and Bundler cannot continue.
Make sure that `gem install pg -v '0.20.0'` succeeds before bundling.

PostgreSQLがインストールされていない。

Run the app locally

ここでPostgreSQLが起動していないと

could not connect to server: No such file or directory

などのエラーで失敗する。

psqlでエラー発生時の対処 - のしメモ アプリ開発ブログ

$ cd /usr/local/Cellar/postgresql
$ curl -o fixBrewLionPostgresql.sh http://nextmarvel.net/blog/downloads/fixBrewLionPostgres.sh
$ chmod 777 fixBrewLionPostgresql.sh
$ ./fixBrewLionPostgresql.sh
$ pg_ctl -D /usr/local/var/postgres start

カレントディレクトリが移動してしまっているので、戻すのを忘れずに。

$ cd ~/ruby-getting-started

Provision add-ons

Please verify your account to install this add-on plan (please enter a credit card)

アカウントにクレジットカード情報を入力。Add-onが無料のものでも必要。

Use database

レコードが1件入っているていになっているが、テーブルは空なのでwidgetsにアクセスしたときにブラウザ側から自分で追加する。

$ heroku pg:psql

に入って出てこれなくなる。終了コマンドは¥q。helpを叩くと教えてくれる。

おわりに

無事完走できてよかった!

*1:これが噂の100万人を飲み込んだVim迷宮か…。Stack Overflow: 100万人の開発者を手助けするVim終了方法 | プログラミング | POSTD

*2:執筆時点のIntroductionにはRuby 2.3.4を前提と書いてあるが、gemfileには2.3.5が指定されている。トラップ…?

円筒形ガーメントバッグ Henty Wingmanを買ったよ

先日、友人の結婚式に出席するために前泊で移動する機会があったのですが、前日は私服で過ごしたかったこともあり、ガーメントバッグを購入しようと思い立ちました。
選定するうえで、私が絶対に譲れなかった条件は、革靴を収納できること
行き着いたのはHentyのWingmanでした。チャリ通勤で会社に着いてからスーツに着替えたい!みたいなニーズを叶えた円筒形ガーメントバッグです。
Henty Wingman Messenger 2 | MJSOFT (mjsoft.co.jp)

youtu.be

どれにしようかな

両肩で背負えるBackpackと斜め掛けのMessengerの二種類がありますが、私はMessengerを選びました。
理由は、

  1. 容量が小さそうなので、普段使っているリュックサックをメインにしたい
  2. キャリーバッグを持っているときは持ち手にくくりたい

この二点を鑑みるに、ストラップが二つあるBackpackだと取り回しが悪いと判断しました。

Wingmanより一回り大きいCopilotというシリーズもありますが、私はサブバッグとして使うので小さいWingmanにしました。それぞれのシリーズに、さらにサイズ違いのCPTとSTDがあり、CPTは女性用の小さいタイプなので注意が必要です*1

開封の儀

六角形のパッケージで届きます。

開けて中身を引き出すとこんな感じ。ハンガーが曲がって届いたというレビューが多数ありましたが、私の場合は全く問題ありませんでした。というか、どうしたらこれが曲がるのか分からないレベル。

使ってみよう

今回詰めてみた荷物はこちら。パジャマ代わりの薄手のスウェット一式と、二泊分の下着類です。革靴は…正確にわからないですが26~27cmくらいでしょう。

内側のジムバッグに詰めていきます。タオルや多少の洗面用具ならまだ入りそうです。

ハンガーはこのように外側の部分と連結できるようになっています。

また、ジムバッグも同じように固定できるので、ジムバッグ部分だけを持っても分解してしまうことはありません。
ぐるっと巻いて出来上がり。

レインカバーも付属しています。

良かったところ

もちろん、スーツを収納することに関しては全く問題なく、作りもしっかりしていて概ね満足です。
特に内側のジムバッグだけを分離して使えるのはとても便利です。専用のショルダーストラップが付属しています*2

あとPC用バッグが付属してますが、入れる気が無かったので使ってません*3

イマイチなところ

これは予想通りですが、メインバッグに使うには容量が心許ないです。靴と服を入れたら、他にはほぼ何も入りません。
また、ジムバッグが円筒形なので四角いものの収納が苦手です。スーツケースやリュックに収納することが前提の長方形のメッシュバッグなどは、先の写真のように丸めて入れることになります。丸められないものは…あきらめましょう。

Messengerに特有の問題だとは思いますが、やはりある程度の距離を歩くなど振動を伴う運動には向いていません。どんどんずり落ちてきます*4
これ一つだけで何とかしたい!という人にはCopilot Backpackがおすすめです。

*1:単に容量が小さいだけでなく、肩幅部分が狭いというレビューあり。

*2:ガーメントバッグとして使うときには、ショルダーストラップは取り外す必要があります。コンパクトなので、中にしまってもさほど嵩張りません。

*3:そんなとこ入れたら明らかに曲がるやろ…というレビューは多数。

*4:自転車だと大丈夫なんでしょうか?

「内向型人間のすごい力」を読んで人生を振り返る

スーザン・ケイン著「内向型人間のすごい力」という本を読みました。
面白いと思った部分を、自分の経験を交えて引用していこうと思います。

練習はひとりでするものじゃないの?

中学生の頃、ギターにのめり込んでいた私は、ギターを触ったこともないクラスメイトに言われました。
「今度、ギター教えてよ!」
そんなの、ギターと教本買って自分で練習するだけじゃないのか?案の定、彼とギターや音楽について語り合うことはありませんでした。

これまで会った発明家やエンジニアの大半は僕と似ているー内気で自分の世界で生きている。

もしきみが、発明家とアーティストの要素を持ったたぐい稀なエンジニアならば、僕はきみに実行するのが難しい助言をしようーひとりで働け。独力で作業してこそ、革新的な品物を生みだすことができる。委員会もチームも関係なく。

p116、スティーブ・ウォズニアックの自伝から引用

昨今では技術の規模が大きくなってしまい、独力で達成しうるイノベーションの機会は失われつつあるように思いますが、実際、独力による練習や学習には効果があるようです。

バイオリン専攻の学生を3つのグループに分けた。第一のグループは、将来世界的なソリストになれるほどの実力を持つ学生たち。第二のグループは、「すぐれている」という評価にとどまる学生たち。第三のグループは演奏者にはなれず、バイオリン教師をめざす学生たち。そして、全員に時間の使い方について同じ質問をした。
その結果、グループごとに驚くべき違いがあることが判明した。3つのグループが音楽関連の活動にかける時間は同じで、週に五〇時間以上だった。課題の練習にかける時間もほぼ同じだった。だが、上位の二つのグループは音楽関連の時間の大半を個人練習にあてていた。

p128、心理学者アンダース・エリクソンの実験

練習にかける総時間は同じ、というのが興味深いですね。

つぎつぎにさまざまな趣味や活動に興味を持つ外向型と違って、内向型はひとつのことに打ち込むことが多い。これは彼らにとって重要な長所である。なぜなら、自尊心は能力に由来し、その逆ではないからだ。ひとつのことに強い愛着を持って没頭することは、幸福と恩恵へと通じる確実な道だと立証されているのだ。才能や興味を育むことは、子供にとって大きな自信の源になりうる。

p420

小さい頃から、他人にはできない特技を持っていることが、自分が必要とされる条件だと思っていました。いまは、秀でたところが無ければ価値が無いとまでは思わなくなりました。しかし、そう思っていたときに身に着けた能力や、周囲を見渡して必要とされるであろう役割を見つけられる素養は、いまもとても役に立っています。

開放的なオフィスは誰のため?

中学生の頃、私はテレビとラジオを同時につけながら勉強をしていました。いまでは音楽を聞いていると読書の気が散るようになってしまい、当時はなぜそんな芸当ができたのか不思議なくらいです。

外向型の人と内向型の人に単語ゲームをするように指示する。ゲームは難しく、試行錯誤を重ねて、鍵となる原理を見つけなければならない。その最中に、ランダムに雑音が聞こえてくるヘッドホンをつける。ヘッドホンの音量は自分にとって「最適な」レベルに合わせるように言われる。その結果は、平均して、外向型の人は七二デシベル、内向型の人は五五デシベルだった。

p199

集中力の高まる環境ノイズの音量は人によって異なる、ということですね。私の職場は例によって大部屋のオフィスですが*1、今後さらなる大部屋化を計画しているようで…どうなることやら。

コミュニケーション能力ってなんなの?

私は小学校や中学校でしばしば学級委員の役割をあてられていました。学級委員というと、クラス会を取りまとめたり、体育祭や合唱コンクールなどの行事を先導するために、人前に立つことが多くあります。
一方、休み時間はひとりやふたりで過ごすことが多く、休みの日には一日中部屋にこもって絵を描いたりパソコンをいじったり楽器の練習をしたりしていました。このため、役割としてリーダーを演じることはできても、クラスの中心にいる人気者になることは、自分には向いていないと感じるようになりました。

外向型のふるまいがとくに上手な内向型は、「セルフモニタリング」と呼ばれる特質の得点が高いことがわかった。セルフモニタリングがうまい人は自分の言動や感情や思考を観察して、周囲の状況から必要性に応じて行動をコントロールできる。

彼らは郷に入れば郷に従うのだ。

p338

成長するにしたがい、自分の性格を受け容れながら、求められている役割を演じることができるようになっていきました。一方で、就職活動をはじめとして外交的な振る舞いが評価される状況を目の当たりにすると、自分の「コミュニケーション能力」の低さに失望することも多々ありました。

心から大切に思っている仕事を進めるために外交的にふるまっているのであって、この仕事が終われば本物の自分に戻ってゆっくりできる、そう自分に言い聞かせることもない。それどころか、心のうちで、自分ではない人間になることが成功への道だと言い聞かせていたのだ。これではセルフモニタリングではなく、自己否定だ。

p346

自己を見失っていた頃の自分に言い聞かせたいですね。就職活動では、あえて外交的に装わないことで、不必要に社交性を求められる職場からお祈りされる戦略を取りましたが、これは上手くいったと思います。

回復のための場所を見つけるのは、簡単とはかぎらない。土曜日の夜、あなたは暖炉のそばでゆっくり読書していたいのに、配偶者が大勢の仲間と食事に行きたいとしたら、どうすればいいのだろう?電話セールスの合間にはひとりで自室に閉じこもりたいのに、会社が職場をオープンオフィスに改装したら、どうすればいい?もしあなたが自由特性を実践しようとすれば、家族や友人や同僚の助けが必要だ。リトルはそれを「自由特性協定」を結ぶことと呼んでいる。

p352

一人暮らしをしている期間も長くなってきたので、身近な人との関係の保ち方はこれからの課題だと思っています。
また、大人なって共通のバックグラウンドを持たない人々との「世間話」を求めらる機会が増えてきました。

一般には、初対面の人と話をするとき、おたがいにリラックスするためにちょっとした無駄話をしてから、本題へと入っていくものだ。敏感な人々は、その逆を実践しているようだ。

p246

この特性を自覚しておくと、少し楽になれそうです。

これからの人生のために

私は「浮き沈みがなくて安定している」と評されることが多いですが、自分の中ではかなり大きな感情の波があると思っています。

セリアの問題は感情が欠けていることではない。コントロールを失わずに感情を表現することができないのだ。

まるで彼女は二つのギアを持っているかのようだー感情を溢れさせるギアと、超然とした冷静沈着さのギアと。

p369

感情を顕にしまいと思うがために無口になってしまいがちなので、うまくバランスを取っていきたいですね。

内向型の子供のためにあなたができる最良のことのひとつは、新しい体験に対応するのを助けてやることだ。

彼(彼女)は、人間との接触を恐れているのではなく、目新しさや過度の刺激によって不安を感じているのだ。

p400

私の両親はこの点、とても上手く育てくれたと思い感謝しています。当時は、放ったらかしで愛情が無いとふさぎ込むこともありましたが。私も子供をもつ機会があれば、同じように見守ってあげたいと思います。

過去の挫折体験をどのように語るかは、現状にどれほど満足しているかに大きく影響される。現在が幸福でない人は、過去の挫折を否定的に語る傾向が強く(たとえば「妻が去ってから、僕はすっかり変わってしまった」)、前向きに生きている人は過去の挫折を「一見すると不幸に見えて、じつはありがたいもの」として肯定的に語る傾向がある(たとえば、「離婚はなによりつらい体験だったけれど、再婚した妻との暮らしはもっと大きな幸福をもたらしてくれた」)。

p428

肯定的に語る人が幸福になるのか、幸福な人が肯定的に語るのか、因果がどちらにあるか定かではありません。私は「そのときどきの最善の選択の積み重ね」であると思っているので、過去は肯定的に捉えています。もし今後、過去を否定的に捉えることがあれば、いちど立ち止まって考え直してみたいと思います。

iOS11以降でPQI Air Card 2を使う方法(無料)

WiFi接続できる便利なSDカードアダプタPQI Air Card 2ですが、公式アプリの更新が止まっていてiOS11以降では起動しなくなります。

itunes.apple.com非公式の有料アプリもあるのですが、こちらの更新も止まっていて対応状況が不明です。*1
itunes.apple.com
AirPict for PQI Air Card

AirPict for PQI Air Card

  • itok
  • 写真/ビデオ
  • ¥240
数百円とはいえ、動くか分からないものに投げ銭するわけにもいかない…
というわけで調べていたところ、無料アプリで接続する方法を発見しました。

ftp/tenet が開いてる?
https://plus.google.com/+KenichiroMATOHARA/posts/5Ufnzs6qs1p

ということはFTPクライアントがあればいいわけですね。

Try Documents by Readdle. Despite the name, it is a very good file manager app with FTP and SFTP support. And it is free :)
Best iOS FTP client? ... | MacRumors Forums

itunes.apple.com

Documents by Readdle

Documents by Readdle

  • Readdle Inc.
  • 仕事効率化
  • 無料

手順
  • Documents by Readdleをインストール
  • サービス → アカウントを追加 → FTPサーバ と進む
  • ホストに「192.168.1.1」を入力、タイトルには好きな名前をつけて保存

  • デジカメ側のWiFiを起動、iOS側から接続する
  • Documents by Readdleのサービスに先ほど作ったアカウントをタップする


これでSDカード内に入れます。どのフォルダに写真が入っているかは、デジカメの種類によります。
写真は一旦ダウンロードフォルダに移動させましょう。iOS内の「写真」に直接入れようとするとなぜかアプリが落ちるようです。*2「写真」が表示されてない場合は
設定 → ファイルマネージャ → 写真を表示
をチェックしてみてください。

ついでに

PQI Air Card 2に接続中にキャリアの回線が使える設定もしておくと便利です。
goryugo.comhttp://goryugo.com/20140101/pqi_setting/