CloudVision APIのテキスト認識をpython3から利用する

CloudVision APIのテキスト認識を使うと安価に高精度の画像のOCRが利用できるのですが、pythonのサンプルが公式ドキュメント内で整理されていない感があり、少し手間取ったので自分用のメモとして残しておきます。

やったことは以下です。

  • テキスト認識のヒントに日本語を渡す
  • 結果解析用にAPIのレスポンスをJSON形式のファイルで残す
# -*- coding: utf_8 -*-
import io
import os
import json
import glob

from pprint import pprint

# Google Cloud client libraryのインポート
from google.cloud import vision
from google.cloud.vision import types

from google.protobuf.json_format import MessageToJson

def annotate(basename):
    # 画像のファイルパス
    file_name = os.path.join(
        os.path.dirname(__file__),
        'resources/{}'.format(basename))

    # 画像のロード
    with io.open(file_name, 'rb') as image_file:
        content = image_file.read()

    image = types.Image(content=content)

    # 日本語のヒントを与える
    image_context = types.ImageContext(language_hints=['ja'])

    response = client.text_detection(image=image,image_context=image_context)

    # JSON ファイル書き出し
    serialized = MessageToJson(response)
    data = json.loads(serialized)

    f = open('results/{}.json'.format(os.path.splitext(basename)[0]), 'w')
    f.write(json.dumps(data,ensure_ascii=False))
    f.close()

# クライアント初期化
client = vision.ImageAnnotatorClient()

# resources フォルダ内の画像をすべてAPIに投げる
paths = glob.glob("resources/*.jpg")

for path in paths:
    annotate(os.path.basename(path))

リポジトリ:

github.com

余談

  • 技術検証の段階だったので、curlからやればすんなりできたんですけどね。。
  • 内部実装protobuf使ってるんですね、しかもJSONのコンバータつけてるのはおしゃれ。複数言語をターゲットにするならそっちの方が効率いいのかな。かっこいいなあ。

AWSのSQS FIFOキューをプロダクションで利用してみた感想とか

SQS について

AWSのサービスの一つでメッセージキューイングのマネージドサービス。 FIFOキューを選択するとメッセージの取り出しがFIFO先入れ先出し)が保証されます。

安価に性能保証されたFIFOのメッセージキューが利用できたので、かなり良かったです。

FIFOとスケール

メッセージがFIFOだと大量にエンキューしても1個ずつしか処理されない(FIFOなので当然)のでスループット全然出ない状況になります。。

これはメッセージに適切なグループIDをつけることで解決できます。 このグループID単位でFIFOが保証されるので、リソースの更新キューであれば、リソースIDなどを設定しておけば、そのリソースに対する更新処理はFIFOで処理されるのでとても便利でした。

例えば、商品Aの価格を更新するキューがあるとして、キャンペーン価格の100円に更新するメッセージAと通常価格の200円に更新するメッセージBがあるときに、 A -> B の順番にエンキューされたら A -> B の順番に取り出されて更新されないと最終的に商品Aは200円になってくれません。(標準キューでは B -> A の順番に取り出される可能性があり、その場合商品Aは古い価格の100円のままになってしまいます、、) なので、A,Bは商品AのIDをグループIDに指定して、このグループ単位でFIFOを保証されるように指定するのがよいのです。

また、開発中はこのグループIDの概念を理解しておらずすべてにランダムなIDを振ったところ、あたりまえですが全くFIFOにならなかったので注意が必要です。(後述の重複呼出削除機能だけ使いたければ、そうすればいいと思います。)

重複呼出削除機能

同じ内容のメッセージを送ってしまったときに削除してくれる機能です。デフォルトの設定では5分以内にそのように操作した際に最初のメッセージ以外をキューから削除してくれます。

仕様としてわかっていても、テスト中に2回送ったのに1度しか処理されなかった場面に直面したときにちょっとパニックになりました。(単体テストNGか、、、?スケジュールがやばい、など)

メッセージ数の制限について

FIFOキューでは2万メッセージが限界とドキュメントに書いてありましたが、オレゴンFIFOキューをつかってみたところ、10万目メッセージくらい入りました。仕様外の動作なのでまったくおススメできないですが、障害発生時でメッセージ取得側が止まってエンキューだけ大量にされて、メッセージがたまっちゃったときに2万以上溜まっててくれたので復旧作業が楽でした。

docs.aws.amazon.com

For FIFO queues, there can be a maximum of 20,000 inflight messages per queue. If you reach this limit, Amazon SQS returns no error messages.

開発中の参考ページなど

公式のドキュメントのほかに、クラスメソッドさんのブログ記事が参考になりました。

docs.aws.amazon.com

dev.classmethod.jp

ブートメニューでHyper-Vの有効化のON/OFFを簡単に切り替える

概要

Hyper-Vを有効化するとDocker for Windowsが利用できますが、一方でVagrant(Virtual Box)やAndroid Emulator を動かすことができません。

開発プロジェクトによってはDockerを採用したりVagrantを採用したり状況によって様々なので、PC起動時のブートメニューでHyper-Vの有効化のON/OFFを切り替えられるようにしています。

元ネタ:

Switch easily between VirtualBox and Hyper-V with a BCDEdit boot Entry in Windows 8.1 - Scott Hanselman

続きを読む

デザイナーから受領したSVGをSVGKitでiOSアプリで使えるようにするまで

TL; TR

  • SVGKitで表示できるかどうかをシンプルなプロジェクトであらかじめ調べる
  • SVGOで最適化してみる
  • inkspaceのパスの簡略化(simplify)でうまく表示できないパスを変換する
    • 余裕があればパスをシェイプ(図形)に書き直すのもよい

SVGのメリット・デメリット

メリット

  • SVGファイルはベクターデータなので、どんな解像度でも滑らかに表示することができます。
  • サイズ別、解像度別に書き出す必要がなくなるので、容量の削減になります。

デメリット

  • 書き出したSVGiOSでイメージ通りに表示するには一手間かかる場合があります。
    • 単純なパスや図形に置き換えてSVGを表示するライブラリで表示できるように修正するなど。

SVGKitについて

SVGファイルをパースしてiOSのネイティブ機能(CoreGraphics CoreImageなど)で描画するライブラリです。

github.com

SVGKitの利用のしかた

iOSでSVGKitを使う方法は以下のページを参考にしました。

dev.classmethod.jp

SVGKitでillustratorで書き出したSVGを表示みる

今回はillustratorで書き出した検索アイコンを表示してみようとしてみます。

以下のようにうまく表示できていません。

SVGKitでillustratorで書き出したSVGのプレビュー

f:id:fly1tkg:20170305002825p:plain

SVGKitの表示

f:id:fly1tkg:20170305002836p:plain

SVGOで最適化してみる

SVGOはSVGファイルを最適化するためのものですが、副次的な作用でパスデータを単純なものに書き直すため、表示できるようになることがあるようです。

github.com

SVGOMGはブラウザで利用できるので手軽です。

SVGOMG - SVGO's Missing GUI

Show original のスイッチで元データと最適化データを見比べながらSVGファイルが変化していないかどうかをチェックしながら進められます。

各最適化オプションや Precision でどの程度最適化するかをコントロールできます。

f:id:fly1tkg:20170305004140g:plain

inkspaceの単純なパスに変更する

inkspaceのパスの簡略化機能で単純なパスすることもできます。

f:id:fly1tkg:20170305005526g:plain

大体はこれで対応できそうです。

inkscapeの使い方を覚える必要がありますが、うまく表示できなかったパスのみにフォーカスをあてて簡略化したり、シェイプ(図形)に置き換えたりする作業を手作業で行えば確実にSVGKitで表示できます。

検索アイコンはinkscapeでパスを修正し、SVGKitでも表示できるようになりました。

f:id:fly1tkg:20170305005950p:plain

今回の検索アイコンの例だと、SVGKitのエラー出力を見る感じだと clippath の表示でうまく行かなかったようですが、inkscapeを利用してclippathを利用せずパスのみにしたり、circle (円のシェイプ)に変更したりするとSVGKitでも表示できるようになるようです。

SVG仕様について

今回、そもそものSVGの仕様を理解するために色々調べたのですが、mozillaが公開しているSVGチュートリアルが分かりやすかったです。

developer.mozilla.org

illustratorSVG書き出しディスられている、、

developer.mozilla.org

実際にSVGKitで表示できる形式にする作業をした所感

SVGKitでSVGが正しく表示されるかどうかチェックするためのプロジェクトを作成して、イメージ通りに表示されるかチェックする作業をしました。

実際に開発中のアプリをビルドして確認していくのはビルドに時間がかかるためです。

macを利用しているデザイナーさんであればアプリエンジニアが手助けしてあげればなんとか自前でビルドして確認するのは難しくなさそうです。

表示できないSVGの対応ですが、SVGOMGとinkscapeのパスの簡略化の2つで大体解決できたと思います。

inkscapeできれいなSVGファイル(xmlファイル的な観点で)を目指していくこともできますが、最終的な見た目はそんなに変わらないと思いますので、こだわりすぎずに進めていくのが作業時間の節約になりそうです。

Docker Composeで起動したコンテナの時刻をコンテナ内で変更する

背景

日付に依存したバッチ処理のテストをするのにDocker Composeで起動したコンテナの時刻をコンテナ内で変更しようとしたら、rootユーザなのに operation not permitted と表示され変更できませんでした。

原因

DockerはLinux Capabilitiesの仕組みを使って、デフォルトではできることが制限されていました。

https://docs.docker.com/engine/security/security/#/linux-kernel-capabilities

Man page of CAPABILITIES

対応

Dockerコンテナ起動時に権限を渡してあげるオプションがありました。

docker run -it --rm --privileged example:image bash

https://docs.docker.com/engine/reference/run/#/runtime-privilege-and-linux-capabilities

docker-composeではコンテナの設定に privileged: true を追加しました。

version: '2'
services:
  sample:
    image: centos6:centos6
    privileged: true

https://docs.docker.com/compose/compose-file/#/cpushares-cpuquota-cpuset-domainname-hostname-ipc-macaddress-memlimit-memswaplimit-oomscoreadj-privileged-readonly-restart-shmsize-stdinopen-tty-user-workingdir

Docker ComposeでRails開発

最近はDocker for Windowsでの開発がいい感じにできるようになったのでメモ。

サンプルプロジェクト

GitHub - fly1tkg/rails_docker: docker composeの開発構成サンプル

Dockerfile

Dockerfileは開発用イメージでは bundle install しないほうがいいと思います。 これを含めてしまうと Gemfile が変更されるたび、このコマンドからイメージのビルドが始まるので、すべてのgemのインストールし直しが発生してしまうからです。

なので僕はdocker-composeでローカルのフォルダとリンクしてから vendor/bundle にgemをインストールするようにしています。

FROM ruby:2.3.3
RUN apt-get update -qq && apt-get install -y build-essential nodejs
RUN mkdir /myapp
WORKDIR /myapp
ADD Gemfile /myapp/Gemfile
ADD Gemfile.lock /myapp/Gemfile.lock
#開発向けはbundle installしない
#RUN bundle install
ADD . /myapp

Docker Compose

docker-compose.ymlの設定項目については以下のQiita記事が詳しいです。

qiita.com

僕はdocker-compose.yml は以下を使っています。

version: '2'
services:
  # Databaseのコンテナ
  db:
    image: mysql:5.7
    # Mysqlコンテナは以下の環境変数でrootのパスワードを指定できる
    environment:
      MYSQL_ROOT_PASSWORD: password
    # ホストOSの3306番ポートとコンテナ内の3306番ポートを設定
    ports:
      - "3306:3306"
    # ローカルのボリュームとフォルダを共有してしてデータを永続化する
    volumes:
      - db-data:/var/lib/mysql

  # railsのコンテナ
  web:
    # DBとは違って、こっちはカレントディレクトリでdocker buildしてイメージを作成
    build: .
    # コンテナ実行時のコマンドを指定
    command: bash -c "rm -f ./tmp/pids/server.pid && (bundle check || bundle install -j4 --retry 3) && bundle ex rails s -b 0.0.0.0 -p 3000"
    environment:
      BUNDLE_PATH: vendor/bundle
    # ホストOSのカレントディレクトリをコンテナ内の/myappと共有させる
    volumes:
      - .:/myapp
    # ホストOSの3000番ポートとコンテナ内の3000番ポートを設定
    ports:
      - "3000:3000"
    # 以下設定しておくとコンテナ内のホスト名dbがdbコンテナのIPアドレスとして引けるようになる
    links:
      - db

# ローカルのボリュームの設定
volumes:
  db-data:
    driver: local

webコンテナのコマンドは以下の感じになっています。

rm -f ./tmp/pids/server.pid

コンテナが正常終了しなかったときにpidが残っちゃうのであったら削除します。

bundle check || bundle install -j4 --retry 3

必要であればbundle installします。

bundle ex rails s -b 0.0.0.0 -p 3000

最後はrails起動コマンドです。

頑張って1行で納めてるけど、別途シェルスクリプトを準備してそれを呼び出したほうが可読性は高そうです、お好みで。

database.yml

docker-compose.yml の設定で dbdb ホストに起動していて、 root のパスワードは password なので、database.ymlを以下のように設定します。

default: &default
  adapter: mysql2
  encoding: utf8
  database: myapp_development
  pool: 5
  username: root
  password: password
  host: db

development:
  <<: *default
  database: myapp_development

開発開始

docker-compose up

出力が読みづらければ -d で呼び出して tail -f log/development.log とかでもいいと思います。

docker-compose up -d

rakeやrailsのコマンドなどコンテナ内での作業

docker-compose execでできます。

僕はcygwinのターミナル利用してるのでwinptyを挟んで実行してます。

winpty docker-compose exec web bundle exec rails g model user name:string

railsジェネレーター。

winpty docker-compose exec web rake db:create db:migrate

dbマイグレーション

winpty docker-compose exec web bash

コンテナのシェルにも入れます。

その他

linksでコンテナのホストが引けるようになっている

上のdocker-compose.ymlの例だとwebコンテナ内でdbのホスト名でdbコンテナのIPが引けるようになっている。

root@f69a3de1fdf2:/myapp# ping db
PING db (172.18.0.2): 56 data bytes
64 bytes from 172.18.0.2: icmp_seq=0 ttl=64 time=0.072 ms
64 bytes from 172.18.0.2: icmp_seq=1 ttl=64 time=0.197 ms
64 bytes from 172.18.0.2: icmp_seq=2 ttl=64 time=0.329 ms

エディタ設定

Windowsで開発してると文字コード utf-8 と改行コード LF の設定をエディタに忘れずするようにしないといろいろ混ざります、、、

Docker for Windows利用メモ

事前準備

Windows 10 Proを購入しましょう。

Docker for Windows

Hyper-Vの有効化

Docker for windowsを利用するにはHyper-Vを有効化する必要があります。

Install Hyper-V on Windows 10 | Microsoft Docs

インストール

アプリは公式サイトからダウンロードしてインストールします。

Get started with Docker for Windows - Docker

共有フォルダの設定

Docker for WindowsはボリュームのマウントにSambaを利用するため、以下の設定が必要です。 トレイを右クリックして Settings... > Shared Drives から マウントに使うパスを設定します。 一番上のフォルダが設定されていれば、配下のどこのフォルダもマウントできるので、特にこだわりがなければ C でよいと思います。(2016.12.26削除: できなかった、、)(この設定だとCドライブが丸々SambaWindowsの共有フォルダになってしまうのでセキュリティが気になる人はもっと絞った場所が良いと思います。)

f:id:fly1tkg:20161221010610p:plain

利用してみた感想

LinuxMacで動かしたときよりはインタラクティブモード周りがちょっとイケてない感ありますが、Ruby on Rails開発は一通りできる感じです。

Docker for Macよりは共有フォルダのアクセス速度が体感で早いと思いました。(Windowsのほうが設定がやや面倒ですが)

備考

Cygwinからdocker execを利用する

docker execをするときにCygwinからだとうまく動作しないのでwinptyを挟んで実行できるようにします。

winpty docker exec -it 123 bash

docker-compose run がインタラクティブに使えない

-d オプションで使うこともできますが、使い勝手が悪かったので、 docker-compose up した状態で docker execbashにはいって必要なことをしています。

buildなどで異様に時間かかってるときウイルス対策ソフトのリアルタイムスキャンをOffにする(2016.12.26追記)

Hyper-Vマネージャを開いてDockerのVMCPU使用率が0%なのに、build等が遅い場合はウイルス対策ソフトのリアルタイムスキャンがボトルネックになっている場合があります。

タスクマネージャーを開いて、リアルタイムスキャンのプロセスのCPU使用率が上がっていたらそうだと思います。

一時的にリアルタイムスキャンをOffにすることをおすすめします。