OpenCV 3.1.0 C++ Macに環境構築

事前準備

CMakeを使用しますので事前にインストールします。

brew install cmake

ソースからのインストール手順はこちらを参考に以下のコマンドを順次実行します。
OpenCV: Using OpenCV with gcc and CMake

mkdir /tmp/opencv/
cd /tmp/opencv/
git clone https://github.com/Itseez/opencv.git
git clone https://github.com/Itseez/opencv_contrib.git
cd opencv
mkdir release
cd release
cmake -D CMAKE_BUILD_TYPE=Release -D CMAKE_INSTALL_PREFIX=/usr/local ..
make -j7
sudo make install

CLion

IDEにはJetBrainsのCLionを使用します。
www.jetbrains.com

動作確認

実際にCMakeで動かしてみます。
OpenCV: Using OpenCV with gcc and CMake

New Projectを選択しプロジェクト名を入力。「opencv-sample」にしました。
f:id:yustam:20160305075533p:plain

CMakeLists.txtの末尾に以下を追加します。「opencv_sample」の部分はプロジェクト名に置き換えます。

...
find_package(OpenCV REQUIRED)
include_directories(${OpenCV_INCLUDE_DIRS})
target_link_libraries(opencv_sample ${OpenCV_LIBS})

次にmain.cppを編集してopencvの動作確認をします。
ローカルにある画像を表示するサンプルです。画像ファイルへのパスは絶対パスを入力します。

#include <stdio.h>
#include <opencv2/opencv.hpp>

using namespace cv;

int main(int argc, char** argv ) {
    Mat image;
    image = imread("<画像ファイルのパス>", 1 );
    if ( !image.data ) {
        printf("No image data \n");
        return -1;
    }
    namedWindow("Display Image", WINDOW_AUTOSIZE );
    imshow("Display Image", image);
    waitKey(0);
    return 0;
}

実行

Control+Rで実行できます。Targetをプロジェクトに変更して実行。 f:id:yustam:20160305080132p:plain

画像が表示されれば成功です。

"15926 Insufficient free space for journals"

最近Dockerを使用してEC2上に並列テスト環境構築みたいなことをしていて、EC2(t2.small)上にMongoDBを3台ほど立てようとした時に15926 Insufficient free space for journalsというエラーが発生しました。

mongodb - Why getting error mongod dead but subsys locked and Insufficient free space for journal files on Linux? - Stack Overflow

起動時にJournaling用に確保しているディスク容量が足りないということ。
EC2はデフォルトで8GBしかディスクがないので立ち上げの時に容量を確保できなかったために発生しているようでした。

対策

対策はEBSのサイズを大きくしても良いですが、上のStackoverflowにも書いてあるようにsmallFilestrueに設定すること。これでJournalのサイズを抑えることができるそうです。

設定

設定方法は立ち上げ方によって違います。
普通にMongoDBをインストールして起動している場合は/etc/mongo.confを編集する。

smallFiles: true

Configuration File Options — MongoDB Manual 3.0

--

Dockerで起動している場合は起動コマンドにオプションをつける。

docker run -d -p 27017:27017 --name my-mongo mongo mongod --smallfiles

mongod — MongoDB Manual 3.0

--

docker-composeを使用している場合はdocker-compose.ymlにて起動コマンドを上書きする。

mongo:
  image: mongo
  command: mongod --smallfiles

Flask on Heroku でセッションを使う際の設定

EC2上では動作したのにHeroku上へ移した時に以下のエラーが出るようになった。

Traceback (most recent call last):
  File "/app/blueprint/pages.py", line 51, in valid_login
    session['username'] = user['username']
  File "/app/.heroku/python/lib/python2.7/site-packages/werkzeug/local.py", line 341, in __setitem__
    self._get_current_object()[key] = value
  File "/app/.heroku/python/lib/python2.7/site-packages/flask/sessions.py", line 126, in _fail
    raise RuntimeError('the session is unavailable because no secret '
RuntimeError: the session is unavailable because no secret key was set.  Set the secret_key on the application to something unique and secret.

調べたところFlask-Sessionという拡張モジュールを利用するそう。

pip install Flask-Session

今回はMongoLabアドオンを使用していたためSESSIN_TYPEを変えるとファイルやDBに保存できるっぽい。 以下のようにfilesystemを指定した場合はflask_sessionディレクトリが作成されてそこに保存される。

SESSION_TYPE = 'filesystem'
app.config.from_object(__name__)
Session(app)

今回はMongoLabを使用していたためmongodbに設定したかったけれどHeroku上で以下のエラーが発生。

SESSION_TYPE = 'mongodb'
SESSION_PERMANENT = True
MONGOLAB_URI = os.environ.get('MONGOLAB_URI', None)
if MONGOLAB_URI:
    SESSION_MONGODB = MONGOLAB_URI.split('/')[2]
    SESSION_MONGODB_DB = MONGOLAB_URI.split('/')[3]
    SESSION_MONGODB_COLLECT = 'sessions'
Traceback (most recent call last):
  File "/app/.heroku/python/bin/gunicorn", line 11, in <module>
    sys.exit(run())
  File "/app/.heroku/python/lib/python2.7/site-packages/gunicorn/app/wsgiapp.py", line 74, in run
    WSGIApplication("%(prog)s [OPTIONS] [APP_MODULE]").run()
  File "/app/.heroku/python/lib/python2.7/site-packages/gunicorn/app/base.py", line 189, in run
    super(Application, self).run()
  File "/app/.heroku/python/lib/python2.7/site-packages/gunicorn/app/base.py", line 72, in run
    Arbiter(self).run()
  File "/app/.heroku/python/lib/python2.7/site-packages/gunicorn/arbiter.py", line 201, in run
    self.halt(reason=inst.reason, exit_status=inst.exit_status)
  File "/app/.heroku/python/lib/python2.7/site-packages/gunicorn/arbiter.py", line 297, in halt
    self.stop()
  File "/app/.heroku/python/lib/python2.7/site-packages/gunicorn/arbiter.py", line 342, in stop
    time.sleep(0.1)
  File "/app/.heroku/python/lib/python2.7/site-packages/gunicorn/arbiter.py", line 214, in handle_chld
    self.reap_workers()
    raise HaltServer(reason, self.WORKER_BOOT_ERROR)
gunicorn.errors.HaltServer: <HaltServer 'Worker failed to boot.' 3>
  File "/app/.heroku/python/lib/python2.7/site-packages/gunicorn/arbiter.py", line 459, in reap_workers

ローカルだと動くんだけど原因がわからん。。。 気が向いたら調べます。

Jubatusハッカソンに参加してきました

jubatus.connpass.com

作ったもの

github.com

何をしたか(だいたいの時系列で)

  • 13:30- Hackathon開始
  • -14:00 チームビルディング。機械学習初心者で特に目的もないので1人で触ってみることに
  • -16:00 用意された記事データとJubatus/言語解析系の資料を読む
  • -23:00 Web上から使えそうなデータ取得を試みる(結局使わず)。
  • -24:00 全くJubatusに触っていないことに気づいてとりあえずサーバを立てる
  • -01:00 試しにチュートリアル通り記事データを放り込むと簡単なレコメンドが完成
  • -09:00 色々調べつつAPIサーバとChrome拡張を作る
  • -16:00 発表資料を作成して寝る。
  • -18:00 発表&解散

思ったこと

  • チュートリアルだけでも手軽に動かせて十分に面白い
  • 機械学習に疎くてもそれっぽいものを作れて簡単に組み込むことができた
  • Jubatus本体はあまりハックできなかったので何ができるかもっと試してみたい
  • Flask便利。MeCab初めて使ったけど面白い
  • 何をしたいか考えたりデータを用意したりするところが結構時間かかると思った
  • 参加者に詳しい人が多かったので発表を聞いてとても参考になった
  • 次は時系列の数値データとか使って予測とかを作ってみたい
  • 一人だと気楽だけど寂しいので次はチームで何か作ってみたい

www.slideshare.net

軽い気持ちで来たらガチな人が多くて不安でしたが発表まで出来たので満足。
使い方がわかったところで、もう少し深掘りしてみたいと思います。
皆様お疲れ様でした。

DynamoDBのお手軽バックアップ

アプリの状態だけ保持しているようなあまり大きくないDynamoDBのテーブルを
バックアップするのにDataPipeline使うのもどうかと思うのでツール作ろうかと考えていたら
既にあった。

github.com

使ってみたら結構便利だったので使い方をまとめます。

AmazonLinuxへインストール

このツールAWSpython向けSDKであるbotoを利用しますが、
AmazonLinuxにはpython + botoが初めから入っています。
dynamodumpをgitからチェックアウトするだけで使う準備は完了です。

$ yum -y install git
$ git clone https://github.com/bchew/dynamodump.git
$ cd dynamodump

バックアップ

バックアップ対象のDynamoDBテーブルへの読み込み権限があれば、
以下の3つのオプションのみでバックアップが取得できます。

パラメータ名
-m (Mode) backup
-r (Region) ap-northeast-1
-s (SrcTable) 対象のテーブル名
$ python dynamodump.py -m backup -r ap-northeast-1 -s my-table
INFO:root:Starting backup for my-table..
INFO:root:Dumping table schema for my-table
INFO:root:Dumping table items for my-table
INFO:root:Backup for my-table table completed. Time taken: 0:00:00

$ tree
.
|-- LICENSE
|-- README.md
|-- dump
|   `-- my-table
|       |-- data
|       |   `-- 0001.json
|       `-- schema.json
|-- dynamodump.py
`-- requirements.txt

リストア

リストアはバックアップ時と同じパラメータでも実行可能です。
しかしそのまま実行すると前処理でテーブルを削除してしまうので、
データだけを戻したい場合は--dataOnlyオプションを指定します。

パラメータ名
-m (Mode) restore
-r (Region) ap-northeast-1
-s (SrcTable) 対象のテーブル名
--dataOnly スキーマの作成/削除を行わない
$ python dynamodump.py -m restore -r ap-northeast-1 -s my-table
INFO:root:my-table table is being deleted..
INFO:root:my-table table deleted!
INFO:root:Starting restore for my-table to my-table..
INFO:root:Creating my-table table with temp write capacity of 25
INFO:root:Waiting for my-table table to be created.. [CREATING]
INFO:root:my-table created.
INFO:root:Restoring data for my-table table..
INFO:root:Processing 0001.json of my-table
INFO:root:Updating my-table table read capacity to: 1, write capacity to: 1
INFO:root:Restore for my-table to my-table table completed. Time taken: 0:00:21
$ python dynamodump.py -m restore -r ap-northeast-1 -s my-table --dataOnly
INFO:root:Starting restore for my-table to my-table..
INFO:root:Restoring data for my-table table..
INFO:root:Processing 0001.json of my-table
INFO:root:Restore for my-table to my-table table completed. Time taken: 0:00:01

S3へ定期バックアップするように設定する

cronでも良いと思いますが今回はJenkinsを使用してジョブを定義しました。
Git:RepositoryURLhttps://github.com/bchew/dynamodump.gitを指定して、
シェルの実行で以下のように定義するのがオススメです。
対象のバケット名やテーブルはお好みで変更してください。

BUCKET_NAME=yustam-dynamo-backup
ARCHIVE_NAME=dump_`date +%Y%m%d%H%M`.tgz

python dynamodump.py -m backup -r ap-northeast-1 -s my-table

tar czf $ARCHIVE_NAME ./dump
rm -rf ./dump

aws s3 cp $ARCHIVE_NAME s3://$BUCKET_NAME/dynamo/$ARCHIVE_NAME
rm -f $ARCHIVE_NAME

exit 0;

ビルドトリガ定期的に実行にして好きな時間に動かしたらいいと思います。

AWS CLIを使用してEC2のバックアップを定期的に取得する

AWS CLIjqを使用した実装です。
設定した世代数分保持して古いバックアップから消していきます。

設定値 説明
MAX_IMAGE_NUM バックアップのイメージを保持する世代数
INSTANCE_ID バックアップを取得する対象のEC2インスタンスID
AMI_NAME 取得するAMIの名前。実行毎に重複しないようにする
DESCRIPTION バックアップの種類を識別するために使用します

cronやJenkinsのジョブに設定して定期的に実行します。
再起動を許容する場合は--no-rebootオプションを外します。
EBS付きのインスタンスは追加のオプションが必要なので注意。

#!/bin/sh
MAX_IMAGE_NUM=2
REGION=ap-northeast-1
INSTANCE_ID=i-00000000
AMI_NAME=ec2-backup-$(date +%m%d%H%M)
DESCRIPTION="daily_backup"

IMAGES=$(aws ec2 describe-images\
  --region ap-northeast-1\
  --owners self\
  --filters Name=description,Values=$DESCRIPTION\
  | jq .Images)

# Delete oldest image
if [[ $(echo $IMAGES | jq length) -ge $MAX_IMAGE_NUM ]]; then
  OLDEST_IMAGE_ID=$(echo $IMAGES | jq 'min_by(.CreationDate)' | jq -r .ImageId)
  echo "Delete: $OLDEST_IMAGE_ID"
  aws ec2 deregister-image\
    --region $REGION\
    --image-id $OLDEST_IMAGE_ID
fi

# Create image
echo "Create: $AMI_NAME"
aws ec2 create-image\
  --region ap-northeast-1\
  --instance-id $INSTANCE_ID\
  --name $AMI_NAME\
  --no-reboot\
  --description $DESCRIPTION

exit 0

Packer + Jenkins(on EC2)で自動AMI作成

Packerを使用してAMIの作成を自動化が出来たのでJenkinsでCIしてみようと思います。
Packerの使い方やJenkinsの使い方は割愛して簡単にポイントのみまとめます。

Jenkinsの設定

Gitリポジトリを指定

こちらのようなリポジトリを作成します。 template.jsonがPackerのテンプレートファイルです。

ジョブの設定

シェルの実行で以下のようにシェルを設定します。
packerをインストールしてしまえば毎回ダウンロードする必要はありません。

mkdir packer
cd packer
wget -o - https://dl.bintray.com/mitchellh/packer/packer_0.8.0_linux_amd64.zip
unzip packer_0.8.0_linux_amd64.zip
cd ../

./packer/packer build template.json

rm -rf ./packer
exit 0;

設定は以上です。あとは好きなタイミングでジョブが動くように設定します。

注意する点

Jenkinsからpackerを使用してsshするとsudoが使用できない

packerを使用するとEC2にsshで接続してコマンドを実行しようとしますが、
スクリプト内でsudoを使用するとエラーが出て失敗してしまいます。

sudo: sorry, you must have a tty to run sudo

解決法

そのため、ここではEC2のUserDataにて/etc/sudoersを編集するようにしました。
packerではAMI作成用のEC2にUserDataを指定できるため、そこでsudoの設定を変更します。

#!/bin/bash -ex
sed -i 's/Defaults    requiretty/Defaults    !requiretty/g' /etc/sudoers

その他、sudoで実行したいコマンドがあればここで実行しておくと良いです。
ビルダーのuser_data_fileにファイル名を指定すると起動時にUserDataへ設定されます。

{
  "builders": [{
    "type": "amazon-ebs",
    "region": "ap-northeast-1",
    "source_ami": "ami-cbf90ecb",
    "instance_type": "t2.micro",
    "ssh_username": "ec2-user",
    "ami_name": "packer-ami-{{timestamp}}",
    "user_data_file": "files/user-data.txt"
  }],
  "provisioners": [{
    "type": "shell",
    "scripts": [
      "scripts/create-user-dirs.sh",
      "scripts/install-packages.sh"
    ]
  }]
}