今回のプロジェクトでは(机の引き出しで眠っていた)Beagle Bone Black(以下BBB)とUSBカメラLogitec C310を使ってOpenCVでフェイストラッキングをやってみることにします。このプロジェクトのできしだいでは、今後のロボットプロジェクトなどで活用したいと考えています。
レシピ
- Beagle Bone Black(以下BBB) Rev. ?
- ウェブカメラ Logitech(Logicool) C310
- OpenCV 2.4.8
環境を構築する
今回のプロジェクトでは、OSにはUbuntu(14.04.2 LTS)を使用、外部電源とUSBカメラ(Logitech C310)、ネットワークケーブルをBBBに指しています。ちなみにこの写真のケースは3Dプリンタで作成したものです。
BBBには、オープンソースのコンピュータビジョンライブラリ「OpenCV」をインストールします。OpenCVは2.4.8をクロスコンパイルでビルドしました。OpenCVの環境構築はネットで広く紹介されていますので今回は割愛しますが、[0]を参考にして構築しています。
あれっ?640x480サイズの画像がキャプチャできないぞ!?
早速OpenCVのVideoCaptureクラスを使ってWebカメラのキャプチャープログラムを実行します。しかし以下のようなエラーメッセージが表示されるばかりで、全く画像をキャプチャーすることができません。あれ、こいつはオカシイぞ・・・壊れたかな?(汗)
select timeout select timeout
気を取り直して、今度はOpenCVを使用しないプログラムでWebカメラから画像を取得できるか試してみます。UVCカメラ用のビューワープログラム「luvcview」で試したところ、Webカメラから画像を取得して画面に表示できることが確認できました。ふーむ、どうやらC310は正常に動作しているようです。
うーむ。これはいったい、どういうことだ??
いろいろとネットで情報を集めた結果、BBBでウェブカメラを使用する場合、以下の問題があることがわかりました[0][1]。
- Beagle Bone BlackではUSB経由でWebカメラを使用する際にデータ転送(バルク転送)に制限がある
- この理由により、無圧縮の画像フォーマットを使用し、かつ高フレームレートで画像をキャプチャしようとするとエラーが生ずる
- したがって、Webカメラから高フレームレートで画像を取得したい場合には、圧縮フォーマットを使用し、Webカメラから送信されるデータ容量を削減し、そのうえで画像をキャプチャする必要がある
高さ x 幅 x 3 x 8 x フレーム数 (bps)
BBBではこの上限がおおよそ13MB程度であるため、これ以上のデータを転送しようとすると先ほどのようにエラーが発生します(640x480では約26MB)。
さて、なるほど、BBBの事情は大体分かりました。ではC310はどうなのでしょうか?対応する画像フォーマットを調べてみましょう。v4l2-cntlで情報を取得すると以下のような情報を得ることができます。
ubuntu@arm:~$ v4l2-ctl -d 0 --list-formats ioctl: VIDIOC_ENUM_FMT Index : 0 Type : Video Capture Pixel Format: 'YUYV' Name : YUV 4:2:2 (YUYV) Index : 1 Type : Video Capture Pixel Format: 'MJPG' (compressed) Name : MJPEG
C310は画像フォーマットとしてYUYVとMJPEGに対応していることがわかります。YUYVは無圧縮、MJPEGはJPEG圧縮を行うフォーマットになります。OpenCVのソースコードを斜め読みした限りでは、OpenCVが画像フォーマットを自動的に判別するんですが、C310の場合はYUYVが優先的に設定されているようです。UYUVフォーマットですと無圧縮なので、転送データ量が前述の上限を超え、timeoutエラーが生じてたようです。
ためしに画像サイズを320x240に縮小するか、フレームレートを10以下に設定するとOpenCVのVideoCaptureでもtimeoutが発生せず画像が取得できることがわかりました。
でも、せっかくC310はHD720p対応のWebカメラですがから、もっと大きな画像サイズ、高フレームレートで使いたいですよねえ。
C310をめぐる冒険
[0] でも触れられていますが、C310の課題の一つはjpegデータにハフマン符号テーブルが欠落していることです。一部のWebカメラでは、どうもそのような仕様になっているようです。毎フレームに同じデータを埋め込むわけなので、無駄なデータ容量を削減するための合理的な判断、といったところでしょう。luvcviewのソースでも確認しましたが、この問題に対処するためには、v4l2でバッファメモリをコピーした後に、省略されれているハフマンテーブルを適切な位置に埋め込むことが必要です。幸い、OpenCVのimdecodeでは自動的にハフマンテーブルの欠落を認識し、これを修正してくれるので助かりました。というか、当初はそれを知らなかったので自力でハフマンテーブルを埋め込む処理を実装していたんですが・・・無駄な苦労?・・・いやいや、これも修行です。MJPEG専用OpenCVキャプチャーの作成
BBB上でWebカメラから高FPSで画像をキャプチャするには、圧縮フォーマットを使用して、転送データを上限以内に抑える必要があります[0]。そこでOpenCV互換のMJPEGフォーマット専用キャプチャークラスMJPEGCaptureを作成しました。さきほど参照したブログ[0]と[3]で公開されているプログラムをベースに改良したものです。v4l2を使用してMJPEGフォーマットで画像をキャプチャします。
githubで公開していますので興味のある方は試してみてください。
https://github.com/yamsam/MJPEGCapture
なおこのプログラムはcmakeを使用しているので以下のように依存ライブラリ(OpenCVとV4L2)の設定が必要になります。以下はccmakeでの設定例になります。
CMAKE_BUILD_TYPE
CMAKE_INSTALL_PREFIX /usr/local
OPENCV_FOUND ON
OpenCV_DIR /usr/local/share/OpenCV
OpenCV_FOUND ON
V4L_INCLUDE_DIR /usr/include
V4L_LIB /usr/lib/arm-linux-gnueabihf/libv4l2.so
測定実験
キャプチャー性能の測定
githubで公開しているソースコードから、純粋にキャプチャ性能だけを測定するプログラム「bench」を実行した結果です。
Capture 320 x 240 pixels at 30 fps mjpegcapture time=6.88099 fps=14.5328 Capture 640 x 480 pixels at 30 fps mjpegcapture time=6.93616 fps=14.4172 Capture 1280 x 960 pixels at 30 fps mjpegcapture time=6.97647 fps=14.3339
カメラ設定がデフォルトの状態だと、320x240~1280x960までの画像サイズで、大体15FPSの性能が得られました。カメラ側の設定は30FPSとなっているので、この測定値ではFPSが設定値の半分しか得られていません。MJPEGの圧縮率から考えると、30FPSでもBBBの上限には達しないと思われるため、もっと高い値であってもよいはずです。
これはカメラ側の自動露出が原因のようです。
Webカメラの設定を変更するにはv4l2-ctrlコマンドを使用します。今回はv4l2-ctrlで設定を以下のように変更し、自動露出を停止、露出をマニュアル指定します。設定可能なカメラパラメータはv4l2-ctl --all コマンドで取得が可能です。C310はexposure_autoで自動露出、exposure_absoluteでマニュアル露出の設定が可能になっています。今回は以下の値を設定してみました。
v4l2-ctl -d 0 -c exposure_auto=1 -c exposure_absolute=300
マニュアル露出を設定した場合の測定結果は以下の様になります。
Capture 320 x 240 pixels at 30 fps mjpegcapture time=3.35413 fps=29.814 Capture 640 x 480 pixels at 30 fps mjpegcapture time=3.40226 fps=29.3923 Capture 1280 x 960 pixels at 30 fps mjpegcapture time=6.35601 fps=15.7331
このように自動露出を停止すれば、画像サイズ1280x960以外では、C310のスペック上の最大値である30FPSが得られることが確認できました。ただしマニュアルで最適な露出を指定するのは一般的に難しいので、プロジェクトによっては、FPSは半減するけれども安定した画質を優先し自動露出を有効にしてもよいでしょう。一方で、FPSを重視するプロジェクトでは自動露出をOFFにし、マニュアル露出を適切に設定するとよいのではないでしょうか。
さて、こうして、ついに、ようやく、やっと、C310を本格的に活用できる体制が整いました。
ここまでが長かった・・・
ここまでが長かった・・・
ではOpenCVを使って顔検出を行ってみます。
今回、顔検出には、OpenCVのカスケード型分類器を使用、特徴量には高速なLBP特徴量(lbpcascade_frontalface.xml)を使用します。
- 画像サイズ640x480
fps=2.27589
- 画像サイズ320x240
fps=9.01146
320x240であれば10FPS近く出ました。以下は、実験中に撮影した顔認識の結果画像です(640x480)。、最近、超過酷な、娘のおままごとタスクフォースに派遣されている英国陸軍特殊部隊の方(対テロ装備)に協力していただきました。
まとめ
今回のプロジェクトをまとめますと、やはりシングルボードLinuxでリアルタイムのコンピュータビジョンを行うのは、厳しいと言わざるをえません。幸い今回の性能は、筆者のようなアマチュア向けの用途であれば、ギリOK、というところでしょうか。組み込みLinuxで本格的なリアルタイムコンピュータビジョンを実現するには、アクセラレーションが欠かせないでしょう。
今回のプロジェクトの教訓
GPUでOpenCVを高速化ができる、NvidiaのJetson TK1がスゴーク欲しい!
残念ながら当ブログは慢性的なリソース不足なんで、当分、購入見込みはありませんが(泣)
今後の予定
[2]ではドローンにBBB搭載して、OpenCVの画像認識によって、ドローンを自動着陸させる研究が報告されています。これを読むと、思ったよりもNEONでは高速化されないことや、libjpegの代わりTurboJPEGライブラリを使うことで若干高速化することなど、BBB上でOpenCVを利用する上での、貴重な知見を得ることできました。
この文献から得られた重要なトピックスのもう一つは、制御用ミドルウェアとしてROS(Robot Operation System)を使用していることです。今回作成したプログラムで、まあまあな速度のフェイストラッキングが実現できましたから、今度はBBB上にROSプラットフォームに乗っけて、OpenCVとROSを連携させたいと思います。
というわけで、次回はBBBでROSにチャレンジします!こうご期待!
その他、課題
デスクトップPCで問題ないんですが、BBBでMJPEGCaptureを実行すると以下のエラーメッセージが表示される問題が未解決です。
Corrupt JPEG data: 2 extraneous bytes before marker 0xd4
画像は正しく取得できているようなのですが、デコーダー周りになにか問題があるのか?BBB上でlucvviewを実行してもエラーは表示されないので、lucviewのjpegデコーダーを移植してみるとよいかもしれません。
あとOpenCV-3.0.0-rc1でも測定するべきだな。
参考文献
[0]How to Achieve 30 fps with BeagleBone Black, OpenCV, and Logitech C920 Webcam ,http://blog.lemoneerlabs.com/3rdParty/Darling_BBB_30fps_DRAFT.htm
[1]http://blog.lemoneerlabs.com/post/BBB-webcams
[2]Real-time Image Processing on Low Cost Embedded Computers, Sunil Shah ,
http://www.eecs.berkeley.edu/Pubs/TechRpts/2014/EECS-2014-117.pdfhttp://www.eecs.berkeley.edu/Pubs/TechRpts/2014/EECS-2014-117.pdf
[3]https://bitbucket.org/beldenfox/cvcapture/src