OS X に OpenCV を Homebrew で入れて python で動画入出力をする


OpenCVちょっと動かしたいなーと思って、macに気軽にインストールしてみたらはまったので、備忘録代わりにAVIファイルを表示させて保存するまでの記事です。

## この記事で行うこと

+ OS X Mavericksに、Homebrew使ってOpenCVを導入
+ OpenCV2 + PythonでAVIファイルを読み込んで保存する


## この記事でわからないこと

+ Homebrewのインストール方法


## インストール
Homebrewは最新にしておきましょう。

pythonの環境は、pyenv系のバージョン切り替えて使う環境の場合、Homebrew経由のopencvインストールと相性が悪いようで、コンパイルが通りませんでした。

pyenvは諦めて、Homebrewからpythonをインストールする形にしています。
デフォルトのHomebrewにはOpenCVが用意されていないので、コンピュータサイエンス向けのライブラリのインストールが必要です。

参考1：[Homebrew でOpenCVをインストールする方法](http://qiita.com/kon_yu/items/f3eed19f397f2226c32c)

それではインストールと行きたいところですが、OpenCVを入れる前に動画を扱うためのあれこれをインストールしておきます。これをしないと静止画はいいのですが、動画を処理しようとしてエラーで悩みます。

参考2：[virtualenvwrapperによるOpenCV + Python環境の構築 (2)](http://kronus9.sblo.jp/article/68685319.html)
参考3:[Mac OS XでFFmpegのインストールとWebM動画の作り方](http://kujirahand.com/blog/index.php?Mac+OS+X%E3%81%A7FFmpeg%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E3%81%A8WebM%E5%8B%95%E7%94%BB%E3%81%AE%E4%BD%9C%E3%82%8A%E6%96%B9)

pythonが入っていないところから始めると以下のようになります。
numpyが入っていないと、OpenCVのインストールが失敗することがあるので先に入れています。

ffmpeg周りはやや過剰かもしれません。とりあえずOpenCVについては、後でエラーが出ると嫌なのでオプション全部載せにしています。

```
$ brew tap homebrew/science
$ brew install python
$ pip install numpy
$ brew install cmake automake celt faac fdk-aac git lame libass libtool libvorbis libvpx libvo-aacenc opencore-amr openjpeg opus sdl schroedinger shtool speex texi2html theora wget x264 xvid yasm
$ brew install ffmpeg --with-fdk-aac --with-libvo-aacenc --with-libvorbis --with-libvpx --with-openjpeg --with-theora --with-opencore-amr
$ brew install eigen
$ brew install jasper
$ brew install tbb
$ brew install qt
$ brew install opencv --with-eigen --with-jasper --with-libtiff --with-qt --with-tbb --with-ffmpeg
```

OpenCVのコンパイルにやたら時間がかかりますが(MacBookAir環境で10分ぐらい)、気長に待ちましょう。

## 動画(AVIファイル)を表示する

詳解OpenCVのサンプルで、AVIファイルを読んで画面にスライダー（トラックバー）付きで表示する例がありますので、これをPythonで実装します。

```example2-3.py
# coding: UTF-8
import numpy as np
import cv2

# 実装の参考　http://www.beechtreetech.com/opencv-exercises-in-python
# cv2を使うように変更

updatelock = False # トラックバー処理中のロックフラグ
windowname = 'frame' # Windowの名前
trackbarname = 'Position' # トラックバーの名前

# AVIファイルを読む
# aviは適当な長さのサンプルをインターネットから拾ってくる
# 参考:http://www.engr.colostate.edu/me/facil/dynamics/avis.htm
cap = cv2.VideoCapture('drop.avi')

# トラックバーを動かしたときに呼び出されるコールバック関数の定義
def onTrackbarSlide(pos):
	updatelock = True
	cap.set(cv2.cv.CV_CAP_PROP_POS_FRAMES, pos)
	updatelock = False

# 名前付きWindowを定義する
cv2.namedWindow(windowname, cv2.WINDOW_NORMAL)

# AVIファイルのフレーム数を取得する
frames = int(cap.get(cv2.cv.CV_CAP_PROP_FRAME_COUNT))

# フレーム数が1以上ならトラックバーにセットする
if (frames > 0):
	cv2.createTrackbar(trackbarname, windowname, 0, frames, onTrackbarSlide)

# AVIファイルを開いている間は繰り返し（最後のフレームまで読んだら終わる）
while(cap.isOpened()):

	# トラックバー更新中は描画しない
	if (updatelock):
		continue

	# １フレーム読む
	ret, frame = cap.read()

	# 読めなかったら抜ける
	if ret == False:
		break

	# 画面に表示
	cv2.imshow(windowname,frame)

	# 現在のフレーム番号を取得
	curpos = int(cap.get(cv2.cv.CV_CAP_PROP_POS_FRAMES))

	# トラックバーにセットする（コールバック関数が呼ばれる）
	cv2.setTrackbarPos(trackbarname, windowname, curpos)

	# qを押したら抜ける
	if cv2.waitKey(1) & 0xFF == ord('q'):
		break

# AVIファイルを解放
cap.release()

# Windowを閉じる
cv2.destroyAllWindows()
```
OpenCVのインストールが正常であれば、実行時にエラーが出ずに、Windowが開いて動画が表示されます。
最後まで行くと自動的に終わります。トラックバーを操作するとその場面から続きが再生されます。

![example2-3.png](https://qiita-image-store.s3.amazonaws.com/0/32778/fd07e985-d225-f483-db82-abbb6066eb5a.png "example2-3")

## カメラの映像を動画(AVIファイル)で保存する
次は、Mac内蔵のカメラを使って、カメラ映像をそのまま動画保存するサンプルです。

```example_cam.py
# coding: UTF-8
import numpy as np
import cv2

# カメラからのキャプチャ指定
cap = cv2.VideoCapture(0)

# コーデックの指定
fourcc = cv2.cv.CV_FOURCC('m', 'p', '4', 'v') 

# 保存ファイルとフレームレートとサイズの指定
out = cv2.VideoWriter('output.m4v',fourcc, 30, (640,480))

# カメラオープン中はずっと処理する
while(cap.isOpened()):

    # キャプチャ画像を取り出す
    ret, frame = cap.read()

    # データがなければ抜ける
    if ret==False:
        break

    # 画像をファイルに書き込む
    out.write(frame)

    # 画像を画面に出力する
    cv2.imshow('frame',frame)

    # "q"が押されたら抜ける
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

# 後始末
cap.release()
out.release()
cv2.destroyAllWindows()
```
画面上に、カメラからの映像が出力されます。また、同時にファイルに書き込みも行われています。
出力ファイルは、m4v形式でmacから直接読めるので、再生されるかどうか試してみましょう。

## 動画にフィルタをかける

最後はOpenCVらしく、読み込んだ動画にフィルタをかけてみましょう。元のaviファイルを入力、処理結果後の画像を出力情報として、どちらも画面上に表示されるようにしています。

```example2-6.py
# coding: UTF-8
import numpy as np
import cv2
import cv2.cv as cv

updatelock = False # トラックバー処理中のロックフラグ
windowname_in = 'inframe' # Window(元画像)の名前
windowname_out = 'outframe' # Window(変換画像)の名前
trackbarname = 'Position' # トラックバーの名前

# AVIファイルを読む
cap = cv2.VideoCapture('drop.avi')

# トラックバーを動かしたときに呼び出されるコールバック関数の定義
def onTrackbarSlide(pos):
	updatelock = True
	cap.set(cv.CV_CAP_PROP_POS_FRAMES, pos)
	updatelock = False

# Cannyエッジ検出
def doCanny(img):
	# カラー画像をグレー画像に変換
	gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
	return cv2.Canny(gray, 100, 200)
 
 
# 名前付きWindowを定義する
cv2.namedWindow(windowname_in, cv2.WINDOW_NORMAL)
cv2.namedWindow(windowname_out, cv2.CV_WINDOW_AUTOSIZE)

# AVIファイルのフレーム数を取得する
frames = int(cap.get(cv2.cv.CV_CAP_PROP_FRAME_COUNT))

# フレーム数が1以上ならトラックバーにセットする
if (frames > 0):
	cv2.createTrackbar(trackbarname, windowname_in, 0, frames, onTrackbarSlide)

# AVIファイルを開いている間は繰り返し
while(cap.isOpened()):

	# トラックバー更新中は描画しない
	if (updatelock):
		continue

	# １フレーム読む
	ret, frame = cap.read()

	# 読めなかったら抜ける
	if ret == False:
		break

	# 画面に表示
	cv2.imshow(windowname_in,frame)
	cv2.imshow(windowname_out,doCanny(frame))

	# 現在のフレーム番号を取得
	curpos = int(cap.get(cv.CV_CAP_PROP_POS_FRAMES))

	# トラックバーにセットする（コールバック関数が呼ばれる）
	cv2.setTrackbarPos(trackbarname, windowname_in, curpos)

	# qを押したら抜ける
	if cv2.waitKey(1) & 0xFF == ord('q'):
		break

# AVIファイルを解放
cap.release()

# Windowを閉じる
cv2.destroyAllWindows()
```
動画のエッジを検出して、outframeのWindowに表示します。

![example2-6.png](https://qiita-image-store.s3.amazonaws.com/0/32778/ed08c64f-b7b0-b6bf-281a-db7c0f5ac97f.png "example2-6")

## まとめ

+ OpenCV + Pythonは気軽に静止画、動画を表示、加工できるのでちょっと試すのに便利です。
+ 動画の入出力の最初で詰まると面白さ半減なので、ちゃんと動く事を確認しましょう。
+ インターネット上には、OpenCV1 + Pythonのサンプルと、OpenCV2 + Pythonのサンプルがごちゃごちゃ（前者が多い印象）なので、間違えないようにしましょう。