USBカメラコンポーネント

OpenCVを使ったUSBカメラコンポーネントを使って,画像データという大きなデータを送受信する方法などを学びましょう.

OpenCVについて

OpenCVとはIntelの技術者が開発した画像処理に関するプログラムです.opencv.jpに詳しいので参照してください.

概要

この章ではカメラで画像を取得するコンポーネント(USBCameraAcquire)とカメラから取得した画像を表示するコンポーネント(USBCameraMonitor)の両方を作ります.

実はこの二つはRTミドルウェアのExampleに含まれています.再配布を避けたいので,この部分に関してはソースの解説のみでプログラムの公開はしませんのであしからず.

もし,「C:\Program Files\OpenRTM-aist\」にRTミドルウェアをインストールしたのなら,「C:\Program Files\OpenRTM-aist\examples\USBCamera」にカメラのプログラム本体があります.

またOpenRTMのサイトでソースコードをダウンロードできます.ダウンロードのページのOpenRTM-aist-0.4.1-RELEASE C++が最新版です.

このZIPファイルを展開してexampleの中のUSBCameraの中にプロジェクトがあります.

USBCameraコンポーネントプログラム

USBCameraAcquireとUSBCameraMonitorは画像データのやりとりを行います.これまでのコンポーネントは単一のデータを用いていましたが,今回は画像というかなりまとまったデータのやり取りをしなくてはなりません.

この場合は,新たなデータ型を作成して送信することも可能ですが,ここでは再利用性などを考慮して,バイト型のデータをとして自前でシリアライズして送信してしまいます.現状ではこの方が再利用性や汎用性が高いといえるでしょうか.

早くOMG標準のImage型などが規定されると便利でしょうね.

USBCameraAcquire

USBCameraAcquire.h

ヘッダーファイルで重要なのは,データポートの宣言の部分.
// DataOutPort declaration
// <rtc-template block="outport_declare">
TimedOctetSeq m_out;
OutPort<TimedOctetSeq> m_outOut;
このようにTimedOctetSeqというデータ形を使っています.これはWindowsで言うByte型にあたるデータ型でSeqはおそらく配列という意味です.バイトデータの列を扱うにはこの形式を使うのが良いのでしょう.
それと最後の方にカメラ用のメモリを差すポインタが宣言されています.
private:
 int dummy;

 CvCapture* m_capture; //カメラ用メモリ
次にオーバーライドする関数ですが,以下の4つです.
  1. onFinalize
  2. onActivated
  3. onDeactivated
  4. onExecute

USBCameraAcquire.cpp

onFinalize

return RTC::RTC_OK;
正直,ここでは何もしていません.良く分かりません.誰か教えてー

onActivated

//カメラデバイスの探索
if(NULL==(m_capture = cvCreateCameraCapture(CV_CAP_ANY))){
 cout<<"カメラがみつかりません"<<endl;
 return RTC::RTC_ERROR;
}
return RTC::RTC_OK;
ここはカメラを開いています.カメラが無い場合はRTC_ERRORを返すことでError状態になります.Error状態からはリセットを行えばInactive状態に移行できます.OpenRTM-aistのページにある状態遷移図を参照してください.

onDeactivated

//カメラ用メモリの解放
cvReleaseCapture(&m_capture);
return RTC::RTC_OK;
ここではカメラを閉じています.onActivatedと対になる動作が基本です.

onExecute

ここではカメラから画像をキャプチャして送信しています.詳しく見ていきましょう.
static ACE_Time_Value tm_pre;
static int count = 0;
IplImage* cam_frame = NULL;

// ここでカメラ画像をキャプチャ
cam_frame = cvQueryFrame(m_capture);
if(NULL == cam_frame)
{
 std::cout << "画像がキャプチャできません!!" << std::endl;
 return RTC::RTC_ERROR;
}

// 送信バッファ用の画像データ領域を確保
IplImage* frame = cvCreateImage(cvGetSize(cam_frame), 8, 3);

// キャプチャした画像の原点が左上の場合は
if(cam_frame ->origin == IPL_ORIGIN_TL)
// 送信バッファ用画像データにカメラからキャプチャした画像をコピー
 cvCopy(cam_frame, frame);
else
// キャプチャした画像の原点が左上でない(左下)の場合は
// 左右反転して送信バッファ用画像データにカメラからキャプチャした画像をコピー

 cvFlip(cam_frame, frame);

// 画像データの総サイズを計算(色データのサイズ×フレームの幅×フレームの高さ)
int len = frame->nChannels * frame->width * frame->height;

// 画像データを送信
m_out.data.length(len);
memcpy((void *)&(m_out.data[0]),frame->imageData,len);
cvReleaseImage(&frame);

m_outOut.write();

// 120回送信したら,フレームレートをコンソールに表示します.
if (count > 120)
{
 count = 0;
 ACE_Time_Value tm;
 tm = ACE_OS::gettimeofday();
 std::cout << 120*1000/(tm - tm_pre).msec() << " [FPS]" << std::endl;
 tm_pre = tm;
}
++count;

return RTC::RTC_OK;
コンナ感じ.

USBCameraMonitor

こちらもヘッダーファイルから.

USBCameraMonitor.h

注目しなくてはならないのはデータポート.
protected:
 // DataInPort declaration
 // <rtc-template block="inport_declare">
 TimedOctetSeq m_in;
 InPort<TimedOctetSeq, RTC::RingBuffer> m_inIn;
 // </rtc-template>
TimedOctetSeq型の入力データポートが追加されています.また画像バッファ用のポインタが最後に宣言されています.
private:
 int dummy;
 IplImage* m_img;
オーバーライドされている関数は
  1. onActivated
  2. onDeactivated
  3. onExecute
の3つです.

USBCameraMonitor.cpp

onActivated

m_img=cvCreateImage(cvSize(320,240),IPL_DEPTH_8U,3);

//画像表示用ウィンドウの作成
cvNamedWindow("CaptureImage", CV_WINDOW_AUTOSIZE);


std::cout << "m_img->nChannels :" << m_img->nChannels << std::endl;
std::cout << "m_img->width :" << m_img->width << std::endl;
std::cout << "m_img->height :" << m_img->height << std::endl;

return RTC::RTC_OK;

onDeactivated

//表示ウィンドウの消去
cvDestroyWindow("CaptureImage");
return RTC::RTC_OK;

onExecute

static ACE_Time_Value tm_pre;
static int count = 0;

// 入力ポートにデータがある場合
if (m_inIn.isNew())
{
 // データの読み込み
 m_inIn.read();
 // 読み込んだデータをバッファにコピー
 if (m_in.data.length() > 0) {
  memcpy(m_img->imageData,(void *)&(m_in.data[0]),m_in.data.length());
 }

 //画像表示
 cvShowImage("CaptureImage", m_img);

 cvWaitKey(1);

 // 120回ごとにフレームレートを表示
 if (count > 120)
 {
  count = 0;
  ACE_Time_Value tm;
  tm = ACE_OS::gettimeofday();
  std::cout << 120*1000/(tm - tm_pre).msec() << " [FPS]" << std::endl;
  tm_pre = tm;
 }
 ++count;
}


return RTC::RTC_OK;

rtc.confの設定

これもrtc.confが重要ですね.「C:\Program Files\OpenRTM-aist\examples\USBCamera」にあるrtc.confはコンナ感じ.

corba.nameservers: localhost
naming.formats: %n.rtc

ネームサーバーのURLと命名規則だけ規定しています.まぁ,これで十分でしょう.

実行

正常にインストールを行ったならスターとメニューからUSBCameraAcquireとUSBCameraMonitorを実行できます.

手元ではCreativeのWEBCAM5,SANWAのCMS-V22, CMS-V23で動作確認が取れました.

まとめ

これでTimed○○Seqを使って配列データをまとめて送れることが確認できたと思います.

折角画像データを使用できるので,次はOpenCVを使って画像処理コンポーネントを作るとどうなるかを体験してみましょう.


RTミドルウェア入門