例題17-1:Webカメラを試す

B: うひゃー、ひどい目にあいました。

A: ん? ずぶ濡れだな。雨降ってんのか。

B: 雨降ってののかってAさん、今日は朝から結構降ってるじゃないですか。

A: んあ。昨日の晩からずっと作業してるからな。そういえば飯食ってねえや。

B: 飯って、今5時ですよ。夕方の。昨日からずっと食べてないんですか??

A: ふむ。集中すると空腹が気にならなくなるタイプなんでな。腹が減らないんじゃないぞ。腹が減っても食いに行くのが面倒くさいのだ。

B: タイプって、そんなタイプ聞いたことありません。

A: 学生の時に何日か飯食うの忘れてて、ある朝起きたら体が起こせなくて焦ったことがある。とりあえず台所にあった生の米を食ったら動けるようになったからコンビニへ買い出しに行った。

B: 食べるのは面倒くさいくせに寝るのは寝るんですか。

A: うむ。睡眠は重要だぞ。規則正しく睡眠をとらないと結局集中力が続かなくて仕事がはかどらない。

B: そんな乱れた食生活をしている人が言うセリフじゃないと思いますが。

A: んー。なんかBくんと話していたら無性に何か食べたくなってきたな。おやつにするか。

B: 本当に信じられないおっさんだな…。で、そんなに熱中して何してたんですか。

A: ああ、ちょっと実験中の参加者の様子を動画で記録しておこうと思ってな。金がないから私物のやっすいwebカメラで撮れないかと思って。

B: へえ。skypeとかに使うアレですよね。そんなこと出来るんですか。

A: うーむ。微妙なところだな。正直期待していた性能は出なかったけど、まあ用途によっては使えなくもない。

B: はあ、中途半端ですな。

A: 実際に動いているところを見てみるか?

B: あ、はい。見られるんならぜひ。

A: んじゃ、書き散らしたプログラムを単純な物から順番に見せるか。まずは単にカメラ画像を表示するだけのプログラム。

B: あれ、これPythonのスクリプトじゃないですか。Pythonからそんなこと出来るんですか。

A: 出来る出来る。今回のサンプルではVideoCapture( http://videocapture.sourceforge.net/ )というモジュールを使う。Win32専用なので、今回のサンプルはLinuxなどでは動かないのでご注意あれ。

B: へええ、本当にPythonはいろんなモジュールがありますねえ。Linuxではwebカメラは使えないんですか?

A: えーと、例えばOpenCVという超有名な画像処理ライブラリがあるが、こいつにはWebカメラから画像を取り込める機能がある。んだがなぁ。

B: だが?

A: なんだか不安定で動かないカメラが多いんだよね。UVC(USB Video Class)に対応していりゃ動くはずなんだが、実際動かないものが多い( 2012/9/26追記:2012年9月現在の安定バージョンのver2.4では安定して使用できます )。うちの研究室でかき集めた分で使えたのはLogicoolのwebカメラだけだったね。その点今回使っているVideoCaptureは私が試した範囲ではすべて使えている。

B: ほう、そりゃ優秀。

A: まあ複数のプラットフォーム、いくつあるのかわかんないメーカーのハードウェアをサポートし尽くすというのは無茶な話なわけで。VideoCaptureはWin32しかサポートしていない分対応しているカメラが多いということだと私は 勝手に 判断している。

B: 勝手にですか。

A: そんなもんいちいち裏取っていられるかっての。ちなみにpygameでも1.9から試験的にカメラのサポートが始まっている。まあウチの環境(Win32+Python2.7+pygame1.9.2pre)で試したらまだまだ全く駄目だったが。

B: まったくだめ?

A: 初期化が出来なかった。まあまだExperimentalだって言ってるから仕方ない。今後OpenCVやpygameの改善が進めばWin32以外でもちょっと書き換えるだけで今回のサンプルプログラムを使えると思う。とにかくサンプルを見てみよう。動作確認してみる人はUVC対応のwebカメラを使える状態にしておいて、VideoCaptureをインストールしておいてください。VideoCaptureは今までのパッケージとちょっとインストール方法が異なっていて、ZIPファイルをダウンロードしたら自分が使っているバージョンのディレクトリをPythonのインストールディレクトリに上書きしてください。例えばPython2.5を使っているのならPython25というディレクトリを展開してインストールディレクトリに上書きするわけです。DLLsやLibというディレクトリを結合するかと聞いてくるので、それはOKで。

B: 面倒くさいですね。

A: そうそう、Python2.7の人は64bit PythonのためのDLLも入っているので適切な方を展開して上書きしてくださいね。Windowsが64bitでもPythonが32bitならインストールするのは32bitの方なのでご注意あれ。

  • 行番号なしのソースファイルをダウンロード→ 17-1.py

 1import time
 2import VideoCapture
 3import pygame
 4from pygame.locals import *
 5
 6cam = VideoCapture.Device()
 7
 8pygame.init()
 9screen = pygame.display.set_mode((640,480))
10screen.fill((0,0,0))
11
12flg = True
13while flg:
14    img = cam.getImage()
15    pimg = pygame.image.fromstring(img.tostring(), img.size, "RGB")
16    screen.blit(pimg, (0, 0))
17    pygame.display.flip() 
18    
19    for event in pygame.event.get():
20        if event.type == QUIT:
21            flg = False
22        elif event.type == KEYDOWN and event.key == K_ESCAPE:
23            flg = False
24
25

B: …へ? これだけ?

A: そう、これだけ。実行してみて。

B: どれどれ…、おお、撮れる撮れる。すごい。

A: こらこら、こんな狭いところで暴れるな。

B: あ、すんません。つい興奮して。

A: こんなもんで興奮すんな。

B: これ、VisionEggじゃなくてpygameで書いてあるんですね。珍しい。

A: 単に最初にwebカメラを使えないかな?と思った時に使ってたPCが出張用のノートPCでVisionEggを入れてなかっただけの話だ。とりあえず簡単に解説するぞ。1行目はお約束のimport。6行目でVideoCapture.Deviceのインスタンスを生成している。引数を指定しなければ適当なカメラが選択される。複数台のカメラが接続されている時は数値でどのカメラを使うか指定できる。

B: へえ。複数台。それは面白そう。

A: で、14行目。VideoCapture.DeviceのgetImageメソッドを実行すると、カメラ画像がPILのImageとして得られる。後は煮るなり焼くなり。以上。おしまい。

B: もう終わり? 15行目から17行目は?

A: PILのImageをpygameのsurfaceへ変換して画面に表示する作業だな。15行目のimg.tostring()はImageの内容を書き出すメソッド、それを引数として受け取っているpygame.image.fromstringはtostringで書き出されたデータをpygameのsurfaceに読み込むメソッド。この辺りは定番の書き方。

B: はあ、初めて見たような気がしますが定番なんですか。

A: 続いて16行目はVisionEgg.Core.Viewport.drawみたいなもんだな。pygameのsurfaceをscreenに描画する。VisionEggではViewport側のメソッドになっていてpygameではsurface側のメソッドになっているのが設計思想の違いが表れていて面白いところだな。

B: ???

A: で、17行目のflipはVisionEggのswap_buffersに相当するものだと思えばいい。pygameの解説はこのくらいにしておいて、VideoCapture.Deviceのメソッドをもう少し説明しておくか。

displayCaptureFilterProperties()
displayCapturePinProperties()

カメラの解像度やフレームレート、各種フィルタ等の機能を設定するダイアログを表示する。

getImage()

カメラ画像をPILのImageで返す。引数timestampを指定してタイムスタンプ文字列を画像に入れることが出来る。(help参照)

saveSnapshot(filename)

カメラ画像をファイルに保存する。引数timestampを指定してタイムスタンプ文字列を画像に入れることが出来る。(help参照)

setResolution(width, height)

カメラの解像度を指定する。displayCapturePinProperties()と異なりダイアログを表示しない。

B: ええと、得られるのは画像なんですか。動画じゃなくて。

A: 動画ってのは要するに次々と撮影した静止画を次々と表示しているだけだからな。静止画が戻ってくればそれで必要十分である。ご丁寧にもPILのImageで渡してくれるからPILの機能を使ってやりたい放題だ。

B: いや、でもカメラってのは動画を撮るものですよね。動画が保存できなかったら意味ないと思うんですが。

A: 「カメラは動画を撮るもの」って認識はどうかとおもうが、まあ動画で保存できると便利なことはあるわな。実はそれがかなり厄介でな。実験室で没頭してしまった理由の一つだ。最初はさっき言ったOpenCVに動画保存機能があるんでそいつを利用しようと思っていたんだが、これがまたどうもうまく動かない。webで検索するとソースからビルドしなおせとか出てくるけど、そんなの入門編としてはあり得ない。

B: 入門編って、最初からPythonで心理実験のネタにするつもりだったんですか?

A: 当然である。どうせなら皆で共有できる方がいいだろ?

B: 共有とか言うならビルドしなおしたものを公開してくれたほうが…

A: …。

B: あ、なんでもありません。ごにょごにょ。

A: ふん。で、検索していたらffmpegに放りこんじゃうなんて素敵な方法を書いていてくれた方がいて、その方法を使わせていただくことにした。動画は圧縮しないと短時間のデータでもびっくりするようなファイルサイズになるからな。ffmpegが使えるとなると心強い。

B: ffmpeg。例題16-5でも出てきましたね。今度こそ本格的な解説を期待できますか?

A: いや、それはパス。

B: ええーっ

A: ffmpegは本当に呆れるくらいどんどんコマンドラインオプションが変わるんだよな。そんなのここで解説しても結局みなさんが使う頃には各自で検索してもらわないといけないだろう。だから最初から解説しない。

B: なんかそれっぽい理由でごまかされているような…

A: とにかく、次回は動画をファイルに保存するぞ。