以前にこのところ大変リーズナブルになったWEBカメラを複数同時使用できるのかについて記事にした事があります。
先日、この時のソースを下さいってご連絡を頂きました。カメラ制御ライブラリはその後libwrpに統合して管理する事にしたので、リポジトリからlibwrpを入手して頂き、libwrpライブラリ(C++専用、コンパイル不要)にパスが通る様にし、下記の様にすると複数台のカメラをわりとお手軽に使えます。ライブラリ内部では、WindowsではDirectShow、LinuxではV4L2を使っています。なお、libwrpソースコードのライセンスはCC-BYです。
以下のコードは2台のカメラがDirectShowで利用可能な状態を前提とした各種チェックやエラー対策無しの、libwrpを使った簡単なコードの例です。(テスト環境: Windows 7 x64, VS2010, libwrp rev.10, Elecom UCAM-DLT30HWH)
C++, using GeSHi 1.0.8.8
#include <cstdint>
#include <iostream>
#include <numeric>
#include <vector>
#define LIBWRP_USE_DIRECTSHOW
#include <wrp/media/camera.hxx>
using namespace std;
using namespace wrp::media;
int main()
{
auto c0 = CameraDS(0);
auto c1 = CameraDS(1);
auto b0 = vector<uint8_t>();
auto b1 = vector<uint8_t>();
for(char i = '\0'; i != 'x'; cin.get(i))
{
c0.capture(b0);
c1.capture(b1);
auto v0 = accumulate(b0.begin(), b0.end(), 0.) / b0.size();
auto v1 = accumulate(b1.begin(), b1.end(), 0.) / b1.size();
cout<<v0<<" "<<v1<<endl;
}
}
Parsed in 0.022 seconds at 28.37 KB/s
6行目の#defineによって、7行目の#includeでlibwrpのカメラライブラリはDirectShow実装版のCameraDSクラスをtypedefします。これが無い場合は、Camera<IMPL>テンプレートクラスに任意の実装を入れて使う感じになります。また、Linuxで使う場合は6行目を#define LIBWRP_USE_V4L2に置き換えると、V4L2実装版のCameraV4L2クラスがtypedefされます(V4L2版は動くかも程度の実装しかしていません!)。
14行目、15行目でCameraDSのコンストラクタの第1パラメータにシステムに接続されたカメラのデバイス番号(システムに認識されているN番目、という意味の)を指示、省略可能な第2パラメータには画像フォーマットを指示できます(デフォルト値はwrp::media::VIDEO_FORMAT::RGB24)。
カメラからの画像データを受け取るバッファはstd::vectorを渡せば、ライブラリ側で適切なリサイズを行いキャプチャデータを放り込みます。17行目、18行目でバッファを定義し、22行目、23行目でカメラからバッファにキャプチャしています。
25行目、26行目はキャプチャデータの相加平均(RGB24に対して処理すれば画像全体の平均輝度)を求め、28行目で標準出力に書き出しています。
20行目で定義しているループでは標準入力から1文字読み取り、'x'でなければループを繰り返します。
C++なので、当然の事ながら解放処理などはlibwrpライブラリ内部でよしなにやっています。また、取得される画像はカメラデバイスのドライバとAPIに依存しますので、RGB24で取得したらY座標反転したBGR24が入ってた(DirectShowはBitmapのデフォルトでデータくれます)とかあるかと思います。オーバーヘッドになるので自動的に変換はしません。
と、以上は簡単に使う方法でしたが、実際にはシステムに接続されているカメラをチェックしたり、動的に切り替えたりしたい事もあるかと思います。
C++, using GeSHi 1.0.8.8
auto c = CameraDS();
auto dss = vector<TSTRING>();
c.device_enum(dss);
BOOST_FOREACH(const auto& d, dss)
TCOUT<<d<<endl;
Parsed in 0.013 seconds at 9.54 KB/s
カメラクラスのコンストラクタをパラメータ無しで定義すると、特定のカメラデバイスへの初期化処理を行いません。device_enumメンバ関数に結果を受け取りたい文字列のベクタを渡すと、使用可能と思われるカメラのデバイス名の列挙がベクタで返されます。これで何台のカメラが利用可能らしいのか、それぞれのカメラのお名前は何なのかわかります。
TSTRINGはlibwrp/string.hxxで#defineされたUNICODE定義に応じてwstringとstrginを切り替えるマクロです。TCOUTも同様にwcoutとcoutを切り替えるマクロ、BOOST_FOREACHは手っ取り早いので使ったboostのお手軽foreachです。
なお、device_enumメンバ関数はboolを返します。使用可能と思われるデバイスの列挙に失敗した場合はfalseを返すので、実用上は適宜にエラー処理して下さい。
また、コンストラクタにパラメータを渡してデバイスを使用可能な状態に初期化しなかった場合はdevice_initメンバ関数でデバイスを使用可能な状態に初期化できます。やっぱりコレもboolを返すので、適宜にエラー処理してください。
初期化済みのカメラクラスインスタンスは、get_fps、get_image_height、get_image_width、get_video_format、メンバ関数でなにやらそういったものを取得可能です。
なお、カメラデバイスのコンフィグレーションは、show_configメンバ関数で呼び出せます。このメンバ関数の第1パラメータはnum_of_configsメンバ関数により、最大いくつのコンフィグがカメラデバイスに用意されているのか取得できるのでそれに応じて指示します。たいていはお馴染みの解像度や画像補正のコンフィグGUIが出現しますが、この部分はデバイスドライバ依存なので実際どんなコンフィグが可能かはカメラのドライバ次第です。
最後になりましたが、はじめに列挙した過去の記事では、複数台の安価なWEBカメラ等々について同時利用する為にはハードウェア、OS、ドライバ、などソフトウェア以前に幾つか壁があり、特に3台以上を同時使用する場合には画像フォーマットによっては転送帯域も考慮しなければならない事などを書いてあったかと思います。その点はこのライブラリを使用する際にも大前提になりますので、複数のWEBカメラを使用したい場合にはどういう組み合わせでどういう接続にしなければならなさそうなのか、ある程度勉強されてからカメラを購入すべきです。面倒ならUVCをチョイスしてドライバレベルの複数同時認識に任せるという手もありますが、用途によってはカメラドライバレベルの画質調整機能に不満が生じる事もあるかと思います。安価になったとは言え、ハードウェアの選択は慎重にどうぞ!
※リポジトリからlibwrpを取得する際は、SVNにてhttp://repos.wonderrabbitproject.net/libwrp/trunk/をチェックアウトして下さい。カメラまわりは単一のファイル構成ではないので、ちまちま拾うと何時までも動かず面倒かと思います。
<追記: CameraDSBuffered について>
うっかり書き忘れるところでしたが、#define LIBWRP_USE_DIRECTSHOW すると、CameraDSBuffered てなもんが typedef されます。中身は Camera<Camera_DirectShow_Buffered<30> > で、バッファ付きの CameraDS です。
CameraDSBuffered はお手軽使用向けで、バッファサイズが30フレームに固定されています。任意のバッファサイズを扱いたい場合は、Camera_DirectShow_Buffered<unsigned int N_MAX_BUFFER_SIZE> のテンプレートパラメータ N_MAX_BUFFER_SIZE をお好みにセットしてください。
この CameraDSBuffered クラスは、std::queue型のバッファを内蔵し、DirectShowのコールバックを用いて、キャプチャが開始されると自動的にバッファに溢れない様に画像を溜め込みます。使用方法は CameraDS と同様ですが、capture メンバ関数の挙動は少し変わります。
CameraDSBuffered クラスのインスタンスは、初期化を終えると常に内蔵バッファにカメラからの画像を、カメラデバイスドライバのフレームレートと実際のキャプチャ状況によって溜め込んでいます。capture メンバ関数を呼び出すと、内蔵バッファのキューから1枚画像データのポップを試みます。バッファにデータが無ければ capture メンバ関数はfalseを返します。
なお、内蔵バッファへのアクセスはCRITICAL_SECTIONを用いた排他処理で実装してあります。
8c7a729c-1c39-43c0-81c9-09229a4a3302|0|.0