ここでは、OpenCV ライブラリを VC9 にて RTコンポーネント化する手順を紹介します。
OpenCVとはインテルが開発・公開しているオープンソースのコンピュータービジョン向けライブラリです。
Wikipediaより抜粋。
ここでは、OpenCV ライブラリのうち、画像の反転を行う cvFlip() を VC9 にて RTコンポーネント化します。
以下は、作業の流れです。
cvFlip 関数は、2次元配列を垂直、水平、または両軸で反転します。
void cvFlip(IplImage* src, IplImage* dst=NULL, int flip_mode=0); #define cvMirror cvFlip src 入力配列 dst 出力配列。もし dst=NULL であれば、src が上書きされます。 flip_mode 配列の反転方法の指定内容: flip_mode = 0: X軸周りでの反転(上下反転) flip_mode > 0: Y軸周りでの反転(左右反転) flip_mode < 0: 両軸周りでの反転(上下左右反転)
InPort からの入力画像を反転し OutPort から出力するコンポーネント。 反転の対象軸は、RTCのコンフィギュレーション機能を使用して flip_mode という名前のパラメーターで指定します。
flip_mode は、反転したい方向に応じて下記のように指定してください。
作成する RTC の仕様は以下のとおりです。
※ TimedOctetSeq型は、OpenRTM-aist の BasicDataType.idl にて下記のように定義されているデータ型です。
※ octet型とは、CORBA IDL の基本型で、転送時にいかなるデータ変換も施されないことが保証されている8ビット値です。
struct Time { unsigned long sec; // sec unsigned long nsec; // nano sec }; struct TimedOctetSeq { Time tm; sequence<octet> data; };
図1は、それぞれのflip_mode での画像処理のイメージ図です。
Flip コンポーネントの雛型の生成は、RTCBuilder を用いて行います。
新規ワークスペースを指定して Eclipse を起動すると、以下の「ようこそ」画面が表示されます。 この 「ようこそ」画面の左上の「X」をクリックすると以下の画面が表示されます。
右上の [Open Perspective] ボタンをクリックし、プルダウンの「Other…」を選択します。
「RTC Builder」を選択し、[OK] ボタンをクリックします。
RTCBuilder が起動します。
まず最初に、RT コンポーネントを作成するための Eclipse プロジェクトを作成します。 画面上部のメニューから[ファイル] > [新規] > [プロジェクト] を選択します。
表示された「新規プロジェクト」画面において、[その他] > [RTC ビルダ] を選択し、[次へ] をクリックします。
「プロジェクト名」欄に作成するプロジェクト名を入力して [完了] をクリックします。
指定した名称のプロジェクトが生成され、パッケージエクスプローラー内に追加されます。
生成したプロジェクト内には、デフォルト値が設定された RTC プロファイル XML(RTC.xml) が自動的に生成されます。
データポートやサービスポートで使用するデータ型が定義された IDL ファイルが置いてある場所を予め設定しておく必要があります。 ※ ここでの設定内容は、ワークスペースを変更しない限り有効ですので、プロジェクト毎に設定する必要はありません。 下記の手順にて、データ型が定義されている IDL ファイルの在り処を設定して下さい。
1. メニューバーの [ウィンドウ] > [設定] をクリックし、設定ダイアログを表示させます。 2. [RtcBuilder] > [Data Type] をクリックし、図12の Data Type 入力画面を出します。 3. Data Type入力画面の [Add] ボタンをクリックし、"IDL File Directories"を入力します。 OpenRTM-aist で定義されているデータ型の IDL ファイルはデフォルトでは下記にインストールされます。 C:\Program Files\OpenRTM-aist\1.0\rtm\idl 4. [OK] ボタンをクリックし、設定を終了します。
RTC プロファイルエディタを開くには、ツールバーの [Open New RtcBuilder Editor] ボタンをクリックするか、メニューの [ファイル] > [Open New Builder Editor] を選択します。
1. 「基本」タブを選択し、基本情報を入力します。
2. 「アクティビティ」タブを選択し、使用するアクションコールバックを指定します。
Flipコンポーネントでは、onActivated()、onDeactivated()、onExecute() コールバックを使用しますので、 図14のようにon_activated、on_deactivated、on_executeの3つにチェックを入れます。
3. 「データポート」タブを選択し、データポートの情報を入力します。
4. 「コンフィギュレーション」タブを選択し、Configuration の変数を入力します。
5. 「言語・環境」タブを選択し、プログラミング言語を選択します。
今回は、C++(言語) を選択します。
6. 「基本」タブにある [コード生成] ボタンをクリックし、コンポーネントの雛型を生成します。
※ 生成されるコード群は、Eclipse 起動時に指定したワークスペースフォルダーの中に生成されます。現在のワークスペースは、[ファイル] > [ワークスペースの切り替え] で確認することができます。
Flip コンポーネントでは、InPort から受け取った画像を画像保存用バッファに保存し、その保存した画像を OpenCV の cvFlip() 関数にて変換します。その後、変換された画像を OutPort から送信します。 onActivated()、onExecute()、onDeactivated()での処理内容を図19に示します。
onExecute() での処理を図20に示します。
以下の内容を user_config.vsprops というファイル名で保存し、Flip フォルダーにコピーしてください。
もしくは、下記より vsprops ファイルをダウンロードし、Flip フォルダーに保存してください。
user_config.vsprops
※ 既にFlipフォルダーには user_config.vsprops ファイルが存在しておりますが、上書きして構いません。
<?xml version="1.0" encoding="shift_jis"?> <VisualStudioPropertySheet ProjectType="Visual C++" Version="8.00" Name="OpenCV" > <Tool Name="VCCLCompilerTool" AdditionalIncludeDirectories="$(cv_includes)" /> <Tool Name="VCLinkerTool" AdditionalLibraryDirectories="$(cv_libdir)" /> <UserMacro Name="user_lib" Value="$(cv_lib)" /> <UserMacro Name="user_libd" Value="$(cv_libd)" /> <UserMacro Name="cv_root" Value="C:\Program Files\OpenCV" /> <UserMacro Name="cv_includes" Value=""$(cv_root)\cv\include";"$(cv_root)\cvaux\include";"$(cv_root)\cxcore\include";"$(cv_root)\otherlibs\highgui";"$(cv_root)\otherlibs\cvcam\include"" /> <UserMacro Name="cv_libdir" Value=""$(cv_root)\lib"" /> <UserMacro Name="cv_bin" Value="$(cv_root)\bin" /> <UserMacro Name="cv_lib" Value="cv.lib cvcam.lib highgui.lib cxcore.lib" /> <UserMacro Name="cv_libd" Value="cv.lib cvcam.lib highgui.lib cxcore.lib" /> </VisualStudioPropertySheet>
copyprops.bat というファイルを実行することで、rtm_config.vsprops というファイルがコピーされます。
rtm_config.vsprops ファイルは、RTコンポーネントを VC++ でビルドするために必要なインクルードパスやリンクするライブラリ等が記述されたファイルです。
//OpenCV 用インクルードファイルのインクルード #include<cv.h> #include<cxcore.h> #include<highgui.h>
/*** * * The activated action (Active state entry action) * former rtc_active_entry() * * @param ec_id target ExecutionContext Id * * @return RTC::ReturnCode_t * * */ virtual RTC::ReturnCode_t onActivated(RTC::UniqueId ec_id); /*** * * The deactivated action (Active state exit action) * former rtc_active_exit() * * @param ec_id target ExecutionContext Id * * @return RTC::ReturnCode_t * * */ virtual RTC::ReturnCode_t onDeactivated(RTC::UniqueId ec_id); /*** * * The execution action that is invoked periodically * former rtc_active_do() * * @param ec_id target ExecutionContext Id * * @return RTC::ReturnCode_t * * */ virtual RTC::ReturnCode_t onExecute(RTC::UniqueId ec_id);
IplImage* m_image_buff; IplImage* m_flip_image_buff;
下記のように、onActivated()、onDeactivated()、onExecute()を実装します。
RTC::ReturnCode_t Flip::onActivated(RTC::UniqueId ec_id) { // イメージ用メモリーの確保 m_image_buff = cvCreateImage(cvSize(m_img_width, m_img_height), IPL_DEPTH_8U, 3); m_flip_image_buff = cvCreateImage(cvSize(m_img_width, m_img_height), IPL_DEPTH_8U, 3); return RTC::RTC_OK; } RTC::ReturnCode_t Flip::onDeactivated(RTC::UniqueId ec_id) { // イメージ用メモリーの解放 cvReleaseImage(&m_image_buff); cvReleaseImage(&m_flip_image_buff); return RTC::RTC_OK; } RTC::ReturnCode_t Flip::onExecute(RTC::UniqueId ec_id) { // 新しいデータのチェック if (m_image_origIn.isNew()) { // InPortデータの読み込み m_image_origIn.read(); // InPortの画像データをIplImageのimageDataにコピー memcpy(m_image_buff->imageData,(void *)&(m_image_orig.data[0]),m_image_orig.data.length()); // InPortからの画像データを反転する。 m_flip_mode 0: X軸周り, 1: Y軸周り, -1: 両方の軸周り cvFlip(m_image_buff, m_flip_image_buff, m_flip_mode); // 画像データのサイズ取得 int len = m_flip_image_buff->nChannels * m_flip_image_buff->width * m_flip_image_buff->height; m_image_flip.data.length(len); // 反転した画像データを OutPort にコピー memcpy((void *)&(m_image_flip.data[0]),m_flip_image_buff->imageData,len); // 反転した画像データを OutPort から出力する。 m_image_flipOut.write(); } return RTC::RTC_OK; }
図21のようにし、コンポーネントのビルドを行います。
ここでは、OpenRTM-aist のサンプルコンポーネントに含まれている USBCameraAqcuireComp コンポーネントと、USBCameraMonitorCom コンポーネント、それと、Flip コンポーネントを接続し動作確認を行います。
omniORB のネームサービスを起動します。
[スタート] > [すべてのプログラム] > [OpenRTM-aist] > [C++] > [examples] をクリックし、[Start Naming Service] をクリックしてください。
RTコンポーネントでは、ネームサーバーのアドレスやネームサーバーへの登録フォーマットなどの情報を rtc.conf というファイルで指定する必要があります。
下記の内容を rtc.conf というファイル名で保存し、Flip\FlipComp\Debug (もしくは、Release) フォルダーに置いてください。
corba.nameservers: localhost naming.formats: %n.rtc
Flip コンポーネントを起動します。
先程 rtc.conf ファイルを置いたフォルダーにある、FlipComp.exe ファイルを実行してください。
USB カメラのキャプチャ画像を OutPort から出力する USBCameraAqcuireComp コンポーネントと、InPort で受け取った画像を画面に表示する USBCameraMonitorCOmp コンポーネントを起動します。
これら2つのコンポーネントは、下記の手順にて起動できます。
[スタート] > [すべてのプログラム] > [OpenRTM-aist] > [C++] > [examples] をクリックし、「USBCameraAqcuireComp」と「USBCameraMonitorCOmp」をそれぞれクリックして実行します。
図22のように、RTSystemEditor にて USBCameraAqcuireComp,Flip、USBCameraMonitorComp コンポーネントを接続します。
図23のようにコンフィギュレーションビューにてコンフィギュレーションを変更することができます。
ELECOM製 の UCAM-DLM 130HWH (白いUSBカメラ) を使用の場合は、image_height と image_width パラメーターを下記のように変更してください。
image_height : 480 image_width : 640
また、ELECOM製 の UCAM-DLM 130HWH (白いUSBカメラ) を使用の場合は、USBCameraMonitor の image_height,image_width パラメーターも 上記のように変更する必要があります。
RTSystemEditor の上部にあります「ALL」というアイコンをクリックし、全てのコンポーネントをアクティベートします。
正常にアクティベートされた場合、図24のように黄緑色でコンポーネントが表示されます。
Flip コンポーネントのコンフィギュレーションパラメーター「flip_mode」を「0」や「-1」などに変更し、画像の反転が行われるかを確認してください。
// -*- C++ -*- /*! * @file Flip.cpp * @brief Flip image component * @date $Date$ * * $Id$ */ #include "Flip.h" // Module specification static const char* flip_spec[] = { "implementation_id", "Flip", "type_name", "Flip", "description", "Flip image component", "version", "1.0.0", "vendor", "AIST", "category", "Category", "activity_type", "PERIODIC", "kind", "DataFlowComponent", "max_instance", "1", "language", "C++", "lang_type", "compile", "exec_cxt.periodic.rate", "1.0", // Configuration variables "conf.default.flip_mode", "1", "conf.default.image_height", "240", "conf.default.image_width", "320", "" }; /*! * @brief constructor * @param manager Maneger Object */ Flip::Flip(RTC::Manager* manager) : RTC::DataFlowComponentBase(manager), m_image_origIn("original_image", m_image_orig), m_image_flipOut("fliped_image", m_image_flip), dummy(0), m_image_buff(0), m_flip_image_buff(0) { // Registration: InPort/OutPort/Service // Set InPort buffers registerInPort("original_image", m_image_origIn); // Set OutPort buffer registerOutPort("fliped_image", m_image_flipOut); } /*! * @brief destructor */ Flip::~Flip() { } RTC::ReturnCode_t Flip::onInitialize() { // Bind variables and configuration variable bindParameter("flip_mode", m_flip_mode, "1"); bindParameter("image_height", m_img_height, "240"); bindParameter("image_width", m_img_width, "320"); return RTC::RTC_OK; } RTC::ReturnCode_t Flip::onActivated(RTC::UniqueId ec_id) { // イメージ用メモリーの確保 m_image_buff = cvCreateImage(cvSize(m_img_width, m_img_height), IPL_DEPTH_8U, 3); m_flip_image_buff = cvCreateImage(cvSize(m_img_width, m_img_height), IPL_DEPTH_8U, 3); return RTC::RTC_OK; } RTC::ReturnCode_t Flip::onDeactivated(RTC::UniqueId ec_id) { // イメージ用メモリーの解放 cvReleaseImage(&m_image_buff); cvReleaseImage(&m_flip_image_buff); return RTC::RTC_OK; } RTC::ReturnCode_t Flip::onExecute(RTC::UniqueId ec_id) { // 新しいデータのチェック if (m_image_origIn.isNew()) { // InPort データの読み込み m_image_origIn.read(); // InPortの 画像データを IplImage の imageData にコピー memcpy(m_image_buff->imageData,(void *)&(m_image_orig.data[0]),m_image_orig.data.length()); // InPortからの画像データを反転する。 m_flip_mode 0: X軸周り, 1: Y軸周り, -1: 両方の軸周り cvFlip(m_image_buff, m_flip_image_buff, m_flip_mode); // 画像データのサイズ取得 int len = m_flip_image_buff->nChannels * m_flip_image_buff->width * m_flip_image_buff->height; m_image_flip.data.length(len); // 反転した画像データを OutPort にコピー memcpy((void *)&(m_image_flip.data[0]),m_flip_image_buff->imageData,len); // 反転した画像データを OutPort から出力する。 m_image_flipOut.write(); } return RTC::RTC_OK; } extern "C" { void FlipInit(RTC::Manager* manager) { RTC::Properties profile(flip_spec); manager->registerFactory(profile, RTC::Create<Flip>, RTC::Delete<Flip>); } };
// -*- C++ -*- /*! * @file Flip.h * @brief Flip image component * @date $Date$ * * $Id$ */ #ifndef FLIP_H #define FLIP_H #include <rtm/Manager.h> #include <rtm/DataFlowComponentBase.h> #include <rtm/CorbaPort.h> #include <rtm/DataInPort.h> #include <rtm/DataOutPort.h> #include <rtm/idl/BasicDataTypeSkel.h> // (1) OpenCV 用インクルードファイルのインクルード #include<cv.h> #include<cxcore.h> #include<highgui.h> using namespace RTC; /*! * @class Flip * @brief Flip image component * */ class Flip : public RTC::DataFlowComponentBase { public: /*! * @brief constructor * @param manager Maneger Object */ Flip(RTC::Manager* manager); /*! * @brief destructor */ ~Flip(); /*! * * The initialize action (on CREATED->ALIVE transition) * formaer rtc_init_entry() * * @return RTC::ReturnCode_t * * */ virtual RTC::ReturnCode_t onInitialize(); /*** * * The activated action (Active state entry action) * former rtc_active_entry() * * @param ec_id target ExecutionContext Id * * @return RTC::ReturnCode_t * * */ virtual RTC::ReturnCode_t onActivated(RTC::UniqueId ec_id); /*** * * The deactivated action (Active state exit action) * former rtc_active_exit() * * @param ec_id target ExecutionContext Id * * @return RTC::ReturnCode_t * * */ virtual RTC::ReturnCode_t onDeactivated(RTC::UniqueId ec_id); /*** * * The execution action that is invoked periodically * former rtc_active_do() * * @param ec_id target ExecutionContext Id * * @return RTC::ReturnCode_t * * */ virtual RTC::ReturnCode_t onExecute(RTC::UniqueId ec_id); protected: // Configuration variable declaration /*! * flip_mode = 0: flipping around x-axis * flip_mode > 1: flipping around y-axis * flip_mode < 0: flipping around both axises * - Name: flip_mode flip_mode * - DefaultValue: 1 */ int m_flip_mode; /*! * * - Name: m_img_height * - DefaultValue: 240 * - 画像の高さ */ int m_img_height; /*! * * - Name: m_img_width * - DefaultValue: 320 * - 画像の幅 */ int m_img_width; // DataInPort declaration TimedOctetSeq m_image_orig; InPort<TimedOctetSeq> m_image_origIn; // DataOutPort declaration TimedOctetSeq m_image_flip; OutPort<TimedOctetSeq> m_image_flipOut; private: int dummy; IplImage* m_image_buff; IplImage* m_flip_image_buff; }; extern "C" { void FlipInit(RTC::Manager* manager); }; #endif // FLIP_H
ビルド済みパッケージを下記からダウンロードできます。
拡張子を"zip_"としてますので、"zip"にリネームしてから解凍して下さい。
OpenCVのライブラリを用いて、物体追跡を行うコンポーネントです。
InPort からの画像データを表示し、マウスで選択されたオブジェクトを追跡するコンポーネント。
OutPort からは、物体追跡画像と、マウスで選択した位置からの移動量を出力する。
画像のサイズと、明度、彩度はコンフィグレーションにて変更可能。
// -*- C++ -*- /*! * @file ObjectTracking.cpp * @brief Object tracking component * @date $Date$ * * $Id$ */ #include "ObjectTracking.h" #define HIST_RANGE_MAX 180.0 #define HIST_RANGE_MIN 0.0 // 入力画像用 IplImage IplImage* g_image; // CamShift トラッキング用変数 CvPoint g_origin; CvRect g_selection; // // 初期追跡領域の設定判別フラグ値 (0: 設定なし, 1: 設定あり) int g_select_object; // トラッキングの開始/停止用フラグ値 (0: 停止, -1: 開始, 1: トラッキング中) int g_track_object; // オブジェクト選択判定用フラグ(0: 1以外 1: 選択された直後) int g_selected_flag; // 選択された範囲の中心座標 float g_selected_x; float g_selected_y; // Module specification static const char* objecttracking_spec[] = { "implementation_id", "ObjectTracking", "type_name", "ObjectTracking", "description", "Object tracking component", "version", "1.0.0", "vendor", "AIST", "category", "Category", "activity_type", "PERIODIC", "kind", "DataFlowComponent", "max_instance", "1", "language", "C++", "lang_type", "compile", "exec_cxt.periodic.rate", "1.0", // Configuration variables "conf.default.brightness_max", "256", "conf.default.brightness_min", "10", "conf.default.saturation_min", "30", "conf.default.image_height", "240", "conf.default.image_width", "320", "" }; /*! * @brief constructor * @param manager Maneger Object */ ObjectTracking::ObjectTracking(RTC::Manager* manager) : RTC::DataFlowComponentBase(manager), m_in_imageIn("in_image", m_in_image), m_tracking_imageOut("tracing_image", m_tracking_image), m_displacementOut("displacement", m_displacement), dummy(0), m_hsv(0), m_hue(0), m_mask(0), m_backproject(0), m_hist(0), m_backproject_mode(0),m_hdims(16), m_init_flag(0) { // Registration: InPort/OutPort/Service // Set InPort buffers registerInPort("in_image", m_in_imageIn); // Set OutPort buffer registerOutPort("tracing_image", m_tracking_imageOut); registerOutPort("displacement", m_displacementOut); m_hranges_arr[0] = HIST_RANGE_MIN; m_hranges_arr[1] = HIST_RANGE_MAX; m_hranges = m_hranges_arr; } /*! * @brief destructor */ ObjectTracking::~ObjectTracking() { } RTC::ReturnCode_t ObjectTracking::onInitialize() { // Bind variables and configuration variable bindParameter("brightness_max", m_b_max, "256"); bindParameter("brightness_min", m_b_min, "10"); bindParameter("saturation_min", m_s_min, "30"); bindParameter("image_height", m_img_height, "240"); bindParameter("image_width", m_img_width, "320"); m_displacement.data.length(2); return RTC::RTC_OK; } RTC::ReturnCode_t ObjectTracking::onActivated(RTC::UniqueId ec_id) { m_init_flag = 0; // ウインドウを生成する cvNamedWindow( "ObjectTracking", 1 ); // マウス操作時のコールバック処理の登録 cvSetMouseCallback( "ObjectTracking", on_mouse, 0 ); g_selected_flag = 0; g_selected_x = 0.0;; g_selected_y = 0.0;; return RTC::RTC_OK; } RTC::ReturnCode_t ObjectTracking::onDeactivated(RTC::UniqueId ec_id) { if (m_init_flag) { // メモリーを解放する cvReleaseImage(&g_image); cvReleaseImage(&m_hsv); cvReleaseImage(&m_hue); cvReleaseImage(&m_mask); cvReleaseImage(&m_backproject); } // ウインドウを破棄する cvDestroyWindow("ObjectTracking"); m_init_flag = 0; return RTC::RTC_OK; } RTC::ReturnCode_t ObjectTracking::onExecute(RTC::UniqueId ec_id) { if (m_in_imageIn.isNew()) { m_in_imageIn.read(); if(!m_init_flag) allocateBuffers(); memcpy(g_image->imageData,(void *)&(m_in_image.data[0]),m_in_image.data.length()); cvShowImage("ObjectTracking", g_image); // キャプチャされた画像を HSV 表色系に変換して hsv に格納 cvCvtColor( g_image, m_hsv, CV_BGR2HSV ); // g_track_objectフラグが0以下なら、以下の処理を行う if( g_track_object ) { cvInRangeS( m_hsv, cvScalar(HIST_RANGE_MIN,m_s_min,MIN(m_b_min,m_b_max),0), cvScalar(HIST_RANGE_MAX,256,MAX(m_b_min,m_b_max),0), m_mask ); cvSplit( m_hsv, m_hue, 0, 0, 0 ); if( g_track_object < 0 ) calcHistogram(); // バックプロジェクションを計算する cvCalcBackProject( &m_hue, m_backproject, m_hist ); // backProjectionのうち、マスクが1であるとされた部分のみ残す cvAnd( m_backproject, m_mask, m_backproject, 0 ); // CamShift法による領域追跡を実行する cvCamShift( m_backproject, m_track_window, cvTermCriteria( CV_TERMCRIT_EPS | CV_TERMCRIT_ITER, 10, 1 ), &m_track_comp, &m_track_box ); m_track_window = m_track_comp.rect; if( m_backproject_mode ) cvCvtColor( m_backproject, g_image, CV_GRAY2BGR ); if( g_image->origin ) m_track_box.angle = -m_track_box.angle; cvEllipseBox( g_image, m_track_box, CV_RGB(255,0,0), 3, CV_AA, 0 ); // マウスで選択された領域の中心座標を保存 if (g_selected_flag) { g_selected_x = m_track_box.center.x; g_selected_y = m_track_box.center.y; g_selected_flag = 0; } // マウスで選択された位置からの移動量をOutPortから出力 m_displacement.data[0] = m_track_box.center.x - g_selected_x; m_displacement.data[1] = -(m_track_box.center.y - g_selected_y); m_displacementOut.write(); } // マウスで選択中の初期追跡領域の色を反転させる if( g_select_object && g_selection.width > 0 && g_selection.height > 0 ) { cvSetImageROI( g_image, g_selection ); cvXorS( g_image, cvScalarAll(255), g_image, 0 ); cvResetImageROI( g_image ); } // 画像を表示する cvShowImage( "ObjectTracking", g_image ); // 画像をOutPortから出力する int len = g_image->nChannels * g_image->width * g_image->height; m_tracking_image.data.length(len); memcpy((void *)&(m_tracking_image.data[0]),g_image->imageData,len); m_tracking_imageOut.write(); // キー入力を待ち、押されたキーによって処理を分岐させる int c = cvWaitKey(10); // while無限ループから脱出(プログラムを終了) if( (char) c == 27 ) { this->exit(); } } return RTC::RTC_OK; } /*! * 全てのイメージ用メモリーの確保 */ void ObjectTracking::allocateBuffers() { g_image = cvCreateImage( cvSize(m_img_width,m_img_height),8, 3 ); m_hsv = cvCreateImage( cvSize(m_img_width,m_img_height),8, 3 ); m_hue = cvCreateImage( cvSize(m_img_width,m_img_height),8, 1 ); m_mask = cvCreateImage( cvSize(m_img_width,m_img_height),8, 1 ); m_backproject = cvCreateImage( cvSize(m_img_width,m_img_height),8, 1 ); m_hist = cvCreateHist( 1, &m_hdims, CV_HIST_ARRAY, &m_hranges, 1 ); m_init_flag = 1; } /*! * ヒストグラムの計算 */ void ObjectTracking::calcHistogram() { float max_val = 0.f; cvSetImageROI( m_hue, g_selection ); cvSetImageROI( m_mask, g_selection ); // ヒストグラムを計算し、最大値を求める cvCalcHist( &m_hue, m_hist, 0, m_mask ); cvGetMinMaxHistValue( m_hist, 0, &max_val, 0, 0 ); // ヒストグラムの縦軸(頻度)を0-255のダイナミックレンジに正規化 cvConvertScale( m_hist->bins, m_hist->bins, max_val ? 255. / max_val : 0., 0 ); // hue,mask画像に設定された ROI をリセット cvResetImageROI( m_hue ); cvResetImageROI( m_mask ); m_track_window = g_selection; // track_object をトラッキング中にする g_track_object = 1; } extern "C" { void ObjectTrackingInit(RTC::Manager* manager) { RTC::Properties profile(objecttracking_spec); manager->registerFactory(profile, RTC::Create<ObjectTracking>, RTC::Delete<ObjectTracking>); } // // マウスドラッグによって初期追跡領域を指定する // // 引数: // event : マウス左ボタンの状態 // x : マウスが現在ポイントしているx座標 // y : マウスが現在ポイントしているy座標 // flags : 本プログラムでは未使用 // param : 本プログラムでは未使用 // void on_mouse( int event, int x, int y, int flags, void* param ) { // 画像が取得されていなければ、処理を行わない if( !g_image ) return; // 原点の位置に応じてyの値を反転(画像の反転ではない) if( g_image->origin ) y = g_image->height - y; // マウスの左ボタンが押されていれば以下の処理を行う if( g_select_object ) { g_selection.x = MIN(x,g_origin.x); g_selection.y = MIN(y,g_origin.y); g_selection.width = g_selection.x + CV_IABS(x - g_origin.x); g_selection.height = g_selection.y + CV_IABS(y - g_origin.y); g_selection.x = MAX( g_selection.x, 0 ); g_selection.y = MAX( g_selection.y, 0 ); g_selection.width = MIN( g_selection.width, g_image->width ); g_selection.height = MIN( g_selection.height, g_image->height ); g_selection.width -= g_selection.x; g_selection.height -= g_selection.y; } // マウスの左ボタンの状態によって処理を分岐 switch( event ) { case CV_EVENT_LBUTTONDOWN: // マウスの左ボタンが押されたのであれば、 // 原点および選択された領域を設定 g_origin = cvPoint(x,y); g_selection = cvRect(x,y,0,0); g_select_object = 1; break; case CV_EVENT_LBUTTONUP: // マウスの左ボタンが離されたとき、width と height がどちらも正であれば、 // g_track_objectフラグを開始フラグにする g_select_object = 0; if( g_selection.width > 0 && g_selection.height > 0 ) { g_track_object = -1; g_selected_flag = 1; } break; } } };
// -*- C++ -*- /*! * @file ObjectTracking.h * @brief Object tracking component * @date $Date$ * * $Id$ */ #ifndef OBJECTTRACKING_H #define OBJECTTRACKING_H #include <rtm/Manager.h> #include <rtm/DataFlowComponentBase.h> #include <rtm/CorbaPort.h> #include <rtm/DataInPort.h> #include <rtm/DataOutPort.h> #include <rtm/idl/BasicDataTypeSkel.h> #include <cv.h> #include <highgui.h> #include <stdio.h> #include <ctype.h> using namespace RTC; /*! * @class ObjectTracking * @brief Object tracking component * */ class ObjectTracking : public RTC::DataFlowComponentBase { public: /*! * @brief constructor * @param manager Maneger Object */ ObjectTracking(RTC::Manager* manager); /*! * @brief destructor */ ~ObjectTracking(); /*! * * The initialize action (on CREATED->ALIVE transition) * formaer rtc_init_entry() * * @return RTC::ReturnCode_t * * */ virtual RTC::ReturnCode_t onInitialize(); /*** * * The activated action (Active state entry action) * former rtc_active_entry() * * @param ec_id target ExecutionContext Id * * @return RTC::ReturnCode_t * * */ virtual RTC::ReturnCode_t onActivated(RTC::UniqueId ec_id); /*** * * The deactivated action (Active state exit action) * former rtc_active_exit() * * @param ec_id target ExecutionContext Id * * @return RTC::ReturnCode_t * * */ virtual RTC::ReturnCode_t onDeactivated(RTC::UniqueId ec_id); /*** * * The execution action that is invoked periodically * former rtc_active_do() * * @param ec_id target ExecutionContext Id * * @return RTC::ReturnCode_t * * */ virtual RTC::ReturnCode_t onExecute(RTC::UniqueId ec_id); /*! * 入力された1つの色相値を RGB に変換する * * 引数: * hue : HSV表色系における色相値H * 戻り値: * CvScalar: RGB の色情報が BGR の順で格納されたコンテナ */ CvScalar hsv2rgb( float hue ) { int rgb[3], p, sector; static const int sector_data[][3]= {{0,2,1}, {1,2,0}, {1,0,2}, {2,0,1}, {2,1,0}, {0,1,2}}; hue *= 0.033333333333333333333333333333333f; sector = cvFloor(hue); p = cvRound(255*(hue - sector)); p ^= sector & 1 ? 255 : 0; rgb[sector_data[sector][0]] = 255; rgb[sector_data[sector][1]] = 0; rgb[sector_data[sector][2]] = p; return cvScalar(rgb[2], rgb[1], rgb[0],0); } /*! * 全てのイメージ用バッファの確保 */ void allocateBuffers(); /*! * ヒストグラムの計算 */ void calcHistogram(); protected: // Configuration variable declaration /*! * * - Name: b_max * - DefaultValue: 256 * - 明度の最大値 */ int m_b_max; /*! * * - Name: b_min * - DefaultValue: 10 * - 明度の最小値 */ int m_b_min; /*! * * - Name: s_min * - DefaultValue: 30 * - 彩度の最小値 */ int m_s_min; /*! * * - Name: m_img_height * - DefaultValue: 240 * - 画像の高さ */ int m_img_height; /*! * * - Name: m_img_width * - DefaultValue: 320 * - 画像の幅 */ int m_img_width; // DataInPort declaration TimedOctetSeq m_in_image; InPort<TimedOctetSeq> m_in_imageIn; // DataOutPort declaration TimedOctetSeq m_tracking_image; OutPort<TimedOctetSeq> m_tracking_imageOut; TimedFloatSeq m_displacement; OutPort<TimedFloatSeq> m_displacementOut; private: int dummy; IplImage* m_hsv; // HSV 表色系用 IplImage IplImage* m_hue; // HSV 表色系のHチャンネル用 IplImage IplImage* m_mask; // マスク画像用 IplImage IplImage* m_backproject; // バックプロジェクション画像用 IplImage CvHistogram * m_hist; // ヒストグラム処理用構造体 // 処理モード選択用フラグ int m_backproject_mode; // バックプロジェクション画像の表示/非表示用フラグ値 (0: 非表示, 1: 表示) // CamShiftトラッキング用変数 CvRect m_track_window; CvBox2D m_track_box; CvConnectedComp m_track_comp; // ヒストグラム用変数 int m_hdims; // ヒストグラムの次元数 float m_hranges_arr[2]; // ヒストグラムのレンジ float* m_hranges; // 初期化判定フラグ int m_init_flag; }; extern "C" { void ObjectTrackingInit(RTC::Manager* manager); void on_mouse( int event, int x, int y, int flags, void* param ); }; #endif // OBJECTTRACKING_H
図25は、USBCameraAcquire,Flip,ObjectTracking,SeqIn コンポーネントの接続例です。
まず、USBCameraAcquire コンポーネントにて USBカメラの画像を取得します。
次に、Flip コンポーネントにて左右を反転させます。
反転させている理由は、物体追跡コンポーネントの出力をジョイスティックとして使用する場合に、画像が鏡のように表示されていた方が操作しやすいためです。
次に、ObjectTracking コンポーネントで、あらかじめ選択された追跡対象物の移動量を OutPort (displacement) から出力し、SeqIn コンポーネントで移動量を表示します。
モーションエディタ/シミュレータ
動力学シミュレータ
統合開発プラットフォーム
産総研が提供するRTC集
東京オープンソースロボティクス協会
ネットワーク分散環境でデータ収集用ソフトウェアを容易に構築するためのソフトウェア・フレームワーク
はじめに
ここでは、OpenCV ライブラリを VC9 にて RTコンポーネント化する手順を紹介します。
OpenCVとは
OpenCVとはインテルが開発・公開しているオープンソースのコンピュータービジョン向けライブラリです。
Wikipediaより抜粋。
作成する RTコンポーネント
OpenCV ライブリの RTコンポーネント化 (Flipコンポーネント)
ここでは、OpenCV ライブラリのうち、画像の反転を行う cvFlip() を VC9 にて RTコンポーネント化します。
以下は、作業の流れです。
cvFlip 関数について
cvFlip 関数は、2次元配列を垂直、水平、または両軸で反転します。
コンポーネントの概要
InPort からの入力画像を反転し OutPort から出力するコンポーネント。
反転の対象軸は、RTCのコンフィギュレーション機能を使用して flip_mode という名前のパラメーターで指定します。
flip_mode は、反転したい方向に応じて下記のように指定してください。
作成する RTC の仕様は以下のとおりです。
※ TimedOctetSeq型は、OpenRTM-aist の BasicDataType.idl にて下記のように定義されているデータ型です。
※ octet型とは、CORBA IDL の基本型で、転送時にいかなるデータ変換も施されないことが保証されている8ビット値です。
図1は、それぞれのflip_mode での画像処理のイメージ図です。
動作環境・開発環境
Flip コンポーネントの雛型の生成
Flip コンポーネントの雛型の生成は、RTCBuilder を用いて行います。
RTCBuilder の起動
新規ワークスペースを指定して Eclipse を起動すると、以下の「ようこそ」画面が表示されます。 この 「ようこそ」画面の左上の「X」をクリックすると以下の画面が表示されます。
右上の [Open Perspective] ボタンをクリックし、プルダウンの「Other…」を選択します。
「RTC Builder」を選択し、[OK] ボタンをクリックします。
RTCBuilder が起動します。
RTCBuilder 用プロジェクトの作成
まず最初に、RT コンポーネントを作成するための Eclipse プロジェクトを作成します。 画面上部のメニューから[ファイル] > [新規] > [プロジェクト] を選択します。
表示された「新規プロジェクト」画面において、[その他] > [RTC ビルダ] を選択し、[次へ] をクリックします。
「プロジェクト名」欄に作成するプロジェクト名を入力して [完了] をクリックします。
指定した名称のプロジェクトが生成され、パッケージエクスプローラー内に追加されます。
生成したプロジェクト内には、デフォルト値が設定された RTC プロファイル XML(RTC.xml) が自動的に生成されます。
データポートで使用するデータタイプ定義ファイルの在り処の設定
データポートやサービスポートで使用するデータ型が定義された IDL ファイルが置いてある場所を予め設定しておく必要があります。
※ ここでの設定内容は、ワークスペースを変更しない限り有効ですので、プロジェクト毎に設定する必要はありません。
下記の手順にて、データ型が定義されている IDL ファイルの在り処を設定して下さい。
RTC プロファイルエディタの起動
RTC プロファイルエディタを開くには、ツールバーの [Open New RtcBuilder Editor] ボタンをクリックするか、メニューの [ファイル] > [Open New Builder Editor] を選択します。
コンポーネントのプロファイル情報入力とコードの生成
1. 「基本」タブを選択し、基本情報を入力します。
-Module name: Flip
-Output Project: Flip
2. 「アクティビティ」タブを選択し、使用するアクションコールバックを指定します。
Flipコンポーネントでは、onActivated()、onDeactivated()、onExecute() コールバックを使用しますので、 図14のようにon_activated、on_deactivated、on_executeの3つにチェックを入れます。
3. 「データポート」タブを選択し、データポートの情報を入力します。
4. 「コンフィギュレーション」タブを選択し、Configuration の変数を入力します。
5. 「言語・環境」タブを選択し、プログラミング言語を選択します。
今回は、C++(言語) を選択します。
6. 「基本」タブにある [コード生成] ボタンをクリックし、コンポーネントの雛型を生成します。
※ 生成されるコード群は、Eclipse 起動時に指定したワークスペースフォルダーの中に生成されます。現在のワークスペースは、[ファイル] > [ワークスペースの切り替え] で確認することができます。
アクティビティ処理の実装
Flip コンポーネントでは、InPort から受け取った画像を画像保存用バッファに保存し、その保存した画像を OpenCV の cvFlip() 関数にて変換します。その後、変換された画像を OutPort から送信します。
onActivated()、onExecute()、onDeactivated()での処理内容を図19に示します。
onExecute() での処理を図20に示します。
OpenCV 用ユーザープロパティシートのコピー
プロパティシートとは、コンパイルに必要な種々のオプション(インクルードパス、ライブラリロードバス、ライブラリ)やマクロを記述した VC の設定ファイルの一種です。 RTCBuilder や rtc-template で生成した VC 用のプロジェクトでは、VC のプロパティシートを使用して各種オプションを与えています。また、ユーザーがオプションを追加できるように、ユーザー定義のプロパティシートもインクルードするようになっています。以下の内容を user_config.vsprops というファイル名で保存し、Flip フォルダーにコピーしてください。
もしくは、下記より vsprops ファイルをダウンロードし、Flip フォルダーに保存してください。
user_config.vsprops
※ 既にFlipフォルダーには user_config.vsprops ファイルが存在しておりますが、上書きして構いません。
copyprops.bat の実行
copyprops.bat というファイルを実行することで、rtm_config.vsprops というファイルがコピーされます。
rtm_config.vsprops ファイルは、RTコンポーネントを VC++ でビルドするために必要なインクルードパスやリンクするライブラリ等が記述されたファイルです。
ヘッダファイルの編集
ソースファイルの編集
下記のように、onActivated()、onDeactivated()、onExecute()を実装します。
ビルドの実行
図21のようにし、コンポーネントのビルドを行います。
Flip コンポーネントの動作確認
ここでは、OpenRTM-aist のサンプルコンポーネントに含まれている USBCameraAqcuireComp コンポーネントと、USBCameraMonitorCom コンポーネント、それと、Flip コンポーネントを接続し動作確認を行います。
NameService の起動
omniORB のネームサービスを起動します。
[スタート] > [すべてのプログラム] > [OpenRTM-aist] > [C++] > [examples] をクリックし、[Start Naming Service] をクリックしてください。
rtc.conf の作成
RTコンポーネントでは、ネームサーバーのアドレスやネームサーバーへの登録フォーマットなどの情報を rtc.conf というファイルで指定する必要があります。
下記の内容を rtc.conf というファイル名で保存し、Flip\FlipComp\Debug (もしくは、Release) フォルダーに置いてください。
Flip コンポーネントの起動
Flip コンポーネントを起動します。
先程 rtc.conf ファイルを置いたフォルダーにある、FlipComp.exe ファイルを実行してください。
USBCameraAqcuire、USBCameraMonitor コンポーネントの起動
USB カメラのキャプチャ画像を OutPort から出力する USBCameraAqcuireComp コンポーネントと、InPort で受け取った画像を画面に表示する USBCameraMonitorCOmp コンポーネントを起動します。
これら2つのコンポーネントは、下記の手順にて起動できます。
[スタート] > [すべてのプログラム] > [OpenRTM-aist] > [C++] > [examples] をクリックし、「USBCameraAqcuireComp」と「USBCameraMonitorCOmp」をそれぞれクリックして実行します。
コンポーネントの接続
図22のように、RTSystemEditor にて USBCameraAqcuireComp,Flip、USBCameraMonitorComp コンポーネントを接続します。
Flip コンポーネントのコンフィギュレーションの変更
図23のようにコンフィギュレーションビューにてコンフィギュレーションを変更することができます。
ELECOM製 の UCAM-DLM 130HWH (白いUSBカメラ) を使用の場合は、image_height と image_width パラメーターを下記のように変更してください。
また、ELECOM製 の UCAM-DLM 130HWH (白いUSBカメラ) を使用の場合は、USBCameraMonitor の image_height,image_width パラメーターも 上記のように変更する必要があります。
コンポーネントの Activate
RTSystemEditor の上部にあります「ALL」というアイコンをクリックし、全てのコンポーネントをアクティベートします。
正常にアクティベートされた場合、図24のように黄緑色でコンポーネントが表示されます。
動作確認
Flip コンポーネントのコンフィギュレーションパラメーター「flip_mode」を「0」や「-1」などに変更し、画像の反転が行われるかを確認してください。
Flip コンポーネントのソースファイル
Flip コンポーネントのヘッダファイル
Flip コンポーネントのビルド済みパッケージ
ビルド済みパッケージを下記からダウンロードできます。
拡張子を"zip_"としてますので、"zip"にリネームしてから解凍して下さい。
おまけ(物体追跡コンポーネント)
OpenCVのライブラリを用いて、物体追跡を行うコンポーネントです。
コンポーネントの概要
InPort からの画像データを表示し、マウスで選択されたオブジェクトを追跡するコンポーネント。
OutPort からは、物体追跡画像と、マウスで選択した位置からの移動量を出力する。
画像のサイズと、明度、彩度はコンフィグレーションにて変更可能。
作成する RTC の仕様は以下のとおりです。
RTCBuilder でのプロファイル情報入力内容
基本情報
データポート
コンフィギュレーションパラメーター
ObjectTracking コンポーネントのソースファイル
ObjectTracking コンポーネントのヘッダファイル
コンポーネントの接続
図25は、USBCameraAcquire,Flip,ObjectTracking,SeqIn コンポーネントの接続例です。
まず、USBCameraAcquire コンポーネントにて USBカメラの画像を取得します。
次に、Flip コンポーネントにて左右を反転させます。
反転させている理由は、物体追跡コンポーネントの出力をジョイスティックとして使用する場合に、画像が鏡のように表示されていた方が操作しやすいためです。
次に、ObjectTracking コンポーネントで、あらかじめ選択された追跡対象物の移動量を OutPort (displacement) から出力し、SeqIn コンポーネントで移動量を表示します。
ObjectTrackingコンポーネントのビルド済みパッケージ
ビルド済みパッケージを下記からダウンロードできます。
拡張子を"zip_"としてますので、"zip"にリネームしてから解凍して下さい。