OpenCV はオープンソースのコンピュータビジョンライブラリ。 画像だけでなく動画も処理できたり、それ関連の機械学習アルゴリズムまで実装してたりする。 本体は C++ で書かれているけど Python バインディングもあるので、今回はそれを試してみる。
表題の通り Homebrew を使って OpenCV をインストールするため、環境には Mac OS X を使用している。
$ sw_vers ProductName: Mac OS X ProductVersion: 10.14.4 BuildVersion: 18E226
OpenCV をインストールする
OpenCV を Homebrew からインストールする。
$ brew install opencv glog
尚、そのままでは OpenCV のパッケージが Python のパスに追加されないので、以下のようにしてパスを通す必要がある。
$ echo /usr/local/opt/opencv/lib/python2.7/site-packages >> /usr/local/lib/python2.7/site-packages/opencv.pth
Python 3 を使っている場合にはバージョンを書き換える。
$ echo /usr/local/opt/opencv/lib/python3.7/site-packages >> /usr/local/lib/python3.7/site-packages/opencv.pth
Homebrew 以外の Python を使っている場合
この場合は、上記に加えて下記の手順も必要となる。
これは例えば pyenv や pythonz といった Python マネージャや、あるいは手動でビルドした Python を使っている場合が該当する。
$ mkdir -p ~/Library/Python/2.7/lib/python/site-packages $ echo 'import site; site.addsitedir("/usr/local/lib/python2.7/site-packages")' >> ~/Library/Python/2.7/lib/python/site-packages/homebrew.pth
これも使用する Python に併せてバージョンは書き換える。
$ mkdir -p ~/Library/Python/3.7/lib/python/site-packages $ echo 'import site; site.addsitedir("/usr/local/lib/python3.7/site-packages")' >> ~/Library/Python/3.7/lib/python/site-packages/homebrew.pth
特に、先ほどのインストール手順のように --with-python3 オプションを付けた状態では Homebrew 経由で python3 だけが依存パッケージとしてインストールされる (python は入らない) ようなので注意が必要になる。
virtualenv 環境で使用する場合
もし、virtualenv 環境で OpenCV を使いたいという場合には以下のようにする。
$ echo /usr/local/opt/opencv/lib/python2.7/site-packages >> $(python -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())")/opencv.pth
当然これも使用する Python に併せてバージョンを書き換える。
$ echo /usr/local/opt/opencv/lib/python3.7/site-packages >> $(python -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())")/opencv.pth
ちなみに、上記のインラインで実行しているコマンドでは現在の Python 実行環境の site-packages ディレクトリを取得している。
$ python -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())" /Users/amedama/.virtualenvs/py37/lib/python3.7/site-packages
また、Homebrew 経由でインストールされた依存パッケージの NumPy にパスが通らなくなるので、改めてインストールが必要になる。
$ pip install numpy
Python から OpenCV をインポートする
次のようにして OpenCV のパッケージ (cv2) がインポートできれば成功。
$ python -c "import cv2;print(cv2.__version__)" 4.1.0
画像を扱う
まずは動作確認用の画像をダウンロードしておく。
$ wget --header="User-Agent:" https://pixabay.com/static/uploads/photo/2014/04/03/11/55/snake-312561_640.png -O image.png
こんなやつ。 尚、この画像は上記のサイトで CC0 ライセンスで配布されているもの。
Python の REPL を使って動作を確認してみよう。 cv2.imread() 関数でファイルから画像を読み込むことができる。 それを表示する場合には cv2.imshow() 関数を使う。
>>> import cv2 >>> img = cv2.imread('image.png') >>> cv2.imshow('python', img)
実行すると以下のようにウィンドウが表示されるはず。
ウィンドウを閉じるには以下のように cv2.destroyWindow() 関数を使う。
>>> cv2.destroyWindow('python')
あるいは cv2.destroyAllWindows() 関数を使っても手っ取り早くていい。
>>> cv2.destroyAllWindows()
ちなみに、読み込まれた画像は NumPy の配列オブジェクトになっている。
>>> type(img) <type 'numpy.ndarray'>
この配列は、画像の縦横のピクセルを表した二次元に、RGB の要素を加えた三次元になっているようだ。 つまり、OpenCV はデータを上手いことビットマップデータに変換してくれている。
>>> img.ndim 3 >>> len(img) 572 >>> len(img[0]) 640 >>> img[0][0] array([255, 255, 255], dtype=uint8)
なので、例えば手動で明るい (255 になっている) ところをこんな感じに塗りつぶす、みたいなことも楽にできてしまう。
>>> img[img == 255] = 0 >>> cv2.imshow('python', img)
上記を実行すると塗りつぶされたヘビが見える。
ウィンドウを閉じるには先程と同様の手順を踏む。
>>> cv2.destroyWindow('python')
スクリプトファイルにする
ひとまず、ここまでの内容を Python のスクリプトファイルにすると以下のようになる。 このファイルは、先ほどダウンロードしてきた画像ファイルと同じ場所に作る。 cv2.waitKey() 関数は初登場だけど、これはウィンドウにフォーカスが当たった状態で何らかのキーが押されたら終了させるために入れている。
#!/usr/bin/env python # -*- coding: utf-8 -*- import cv2 def main(): img = cv2.imread('image.png') cv2.imshow('python', img) cv2.waitKey(0) cv2.destroyAllWindows() if __name__ == '__main__': main()
実行するには以下のようにする。
$ python helloworld.py
表示される内容は先ほどと変わらないので省略する。
輪郭線を抽出する
OpenCV っぽい感じの機能を試すために、今度は Canny というフィルタを使って輪郭線を抽出してみよう。 使い方は至ってシンプルで、読み込んだ画像データをフィルタに通すだけ。 cv2.imread() 関数の第二引数に 0 を指定することでグレイスケールで読み込んでいる点に注意。
#!/usr/bin/env python # -*- coding: utf-8 -*- import cv2 def main(): img = cv2.imread('image.png', 0) edge_img = cv2.Canny(img, 100, 200) cv2.imshow('python', edge_img) cv2.waitKey(0) cv2.destroyAllWindows() if __name__ == '__main__': main()
上記を実行してみる。
$ python edge.py
すると以下のような画像が表示される。 見事に輪郭線が抽出されている。
動画を扱う
次は動画を扱ってみる。 先ほどと同様に CC0 ライセンスの動画を動作確認用にダウンロードしておく。
$ wget http://mirrors.creativecommons.org/movingimages/Building_on_the_Past.mp4 -O movie.mp4
動画を再生する
動画を扱う場合には cv2.VideoCapture() 関数で読み込む。 動画の情報は cv2.get() 関数経由で手に入れることができる。 読み込んだ動画からは VideoCapture#read() メソッドを使うことで 1 フレーム分のイメージデータ (要するに NumPy 配列) が取り出せる。 なんとシンプル! 再生スピードの制御には、取り出したフレームレートを元に各フレームを表示した後に time.sleep() メソッドでスレッドを休止させている。
#!/usr/bin/env python # -*- coding: utf-8 -*- from __future__ import division import time import cv2 def main(): video = cv2.VideoCapture('movie.mp4') fps = video.get(cv2.CAP_PROP_FPS) while video.isOpened(): success, frame = video.read() if not success: break cv2.imshow('video', frame) if cv2.waitKey(1) != -1: break time.sleep(1 / fps) video.release() cv2.destroyAllWindows() if __name__ == '__main__': main()
上記を実行する。
$ python movie.py
すると以下のような感じで動画が再生される。
動画を逆再生する
上記だけでは何だか寂しいので、先ほどの動画を逆再生してみよう。 といっても、やっていることは VideoCapture#set() を使って再生ポジションを切り替えているだけ。
#!/usr/bin/env python # -*- coding: utf-8 -*- from __future__ import division import time import cv2 def main(): video = cv2.VideoCapture('movie.mp4') fps = video.get(cv2.CAP_PROP_FPS) pos = video.get(cv2.CAP_PROP_FRAME_COUNT) video.set(cv2.CAP_PROP_POS_FRAMES, pos) while video.isOpened(): success, frame = video.read() if not success: break cv2.imshow('video', frame) if cv2.waitKey(1) != -1: break time.sleep(1 / fps) pos = pos - 1 video.set(cv2.CAP_PROP_POS_FRAMES, pos) video.release() cv2.destroyAllWindows() if __name__ == '__main__': main()
実行してみる。
$ python reverse_play.py
すると動画が末尾から逆方向に再生される。
ばっちり。
まとめ
今回は Homebrew でインストールした OpenCV を軽く使ってみた。 OpenCV の Python バインディングでは、画像から取り出したイメージデータが単なる NumPy 配列だったり、動画は 1 フレームずつをイメージデータとして処理できたりと、とてもシンプルで使いやすい API になっていると感じた。
スマートPythonプログラミング: Pythonのより良い書き方を学ぶ
- 作者: もみじあめ
- 発売日: 2016/03/12
- メディア: Kindle版
- この商品を含むブログ (1件) を見る