このケーススタディでは、簡単な画像処理をコンポーネント化する方法を紹介します。既存のカメラコンポーネントと画像表示コンポーネントを利用し、カメラからの画像を左右(または上下)に反転させる処理部分をコンポーネントとして作成してカメラの画像を反転させ表示するシステムを作成します。
画像を反転する処理は簡単に実装することができますが、ここではより簡単に実装するために OpenCV ライブラリを利用し、汎用性の高い RTコンポーネントを作成します。
OpenCV (オープンシーブイ) とはかつてインテルが、現在はItseezが開発・公開しているオープンソースのコンピュータービジョン向けライブラリです。
Wikipediaより抜粋。
入力された画像を左右または上下に反転し出力する RTコンポーネントを、OpenCV ライブラリの cv::flip 関数を利用して作成します。作成および実行環境は Windows上 の Visual Studio を想定しています。対象 OpenRTM-aist のバージョンは 1.1.2 です。
作成手順はおおよそ以下のようになります。
cv::flip 関数は、OpenCV で標準的に用いられている cv::Mat 型の画像データを垂直軸 (左右反転)、水平軸 (上下反転)、または両軸 (上下左右反転)に対して反転させます。関数プロトタイプと入出力の引数の意味は以下の通りです。
void flip(const Mat& src, Mat& dst, int flipMode)
src | 入力配列 |
dst | 出力配 |
flipMode | 配列の反転方法の指定内容: flipMode = 0: X軸周りでの反転(上下反転) flipMode > 0: Y軸周りでの反転(左右反転) flipMode < 0: 両軸周りでの反転(上下左右反転) |
これから作成するコンポーネントを Flip コンポーネントと呼ぶことにします。
このコンポーネントは画像データ型の入力ポート (InPort) と、反転処理した画像を出力するための出力ポート (OutPort) を持ちます。それぞれのポートの名前を 入力ポート(InPort)名: originalImage、出力ポート(OutPort)名: flippedImage とします。
OpenRTM-aist には OpenCV を使用したビジョン関連のコンポーネントがサンプルとして付属しています。これらのコンポーネントのデータポートは画像の入出力に以下のような CameraImage 型を使用しています。
struct CameraImage { /// Time stamp. Time tm; /// Image pixel width. unsigned short width; /// Image pixel height. unsigned short height; /// Bits per pixel. unsigned short bpp; /// Image format (e.g. bitmap, jpeg, etc.). string format; /// Scale factor for images, such as disparity maps, /// where the integer pixel value should be divided /// by this factor to get the real pixel value. double fDiv; /// Raw pixel data. sequence<octet> pixels; };
このFlipコンポーネントではこれらのサンプルコンポーネントとデータのやり取りができるよう同じく CameraImage型 を InPort と OutPort に使用することにします。
CameraImage型 は InterfaceDataTypes.idl で定義されており、C++であれば、InterfaceDataTypesSkel.h をインクルードすると使えるようになります。
また、画像を反転させる方向は、左右反転、上下反転、上下左右反転の3通りがあります。これを実行時に指定できるように、RTコンポーネントのコンフィギュレーション機能を使用して指定できるようにします。パラメーター名は flipMode という名前にします。
flipMode は cv::flip 関数の仕様に合わせて、型は int 型とし 上下反転、左右反転、上下左右反転それぞれに 0、1、-1 を割り当てることにします。
flipMode の各値での画像処理のイメージを下図に示します。
以上から Flip コンポーネントの仕様をまとめます。
コンポーネント名称 | Flip |
InPort | |
ポート名 | originalImage |
型 | RTC::CameraImage |
説明 | 入力画像 |
OutPort | |
ポート名 | flippedImage |
型 | RTC::CameraImage |
説明 | 反転された画像 |
Configuration | |
パラメーター名 | flipMode |
型 | int |
デフォルト値 | 0 |
制約 | (0,-1, 1) |
Widget | radio |
説明 | 反転モード 上下反転: 0 左右反転: 1 上下左右反転: -1 |
ここで動作環境および開発環境を確認しておきます。
OpenRTM-aist-1.1 以降では、コンポーネントのビルドに CMake を使用します。
Flipコンポーネントの雛型の生成は、RTCBuilder を用いて行います。
Eclipse では、各種作業を行うフォルダーを「ワークスペース」(Work Space)とよび、原則としてすべての生成物はこのフォルダの下に保存されます。 ワークスペースはアクセスできるフォルダーであれば、どこに作っても構いませんが、このチュートリアルでは以下のワークスペースを仮定します。
まずは Eclipse を起動します。 Windows 10の場合はデスクトップの以下のショートカットをダブルクリックして起動します。
最初にワークスペースの場所を尋ねられますので、上記のワークスペースを指定して [OK] をクリックしてください。
すると、以下のようなWelcomeページが表示されます。
Welcomeページは必要ないので左上の [×] ボタンをクリックして閉じてください。
右上の [Open Perspective] ボタンをクリックしてください。
「RTC Builder」を選択し、[OK] ボタンをクリックします。
RTCBuilderが起動します。
Flipコンポーネントを作成するために、RTCBuilder で新規プロジェクトを作成する必要があります。
左上の [Open New RTCBuilder Editor] のアイコンをクリックしてください。
「プロジェクト名」欄に作成するプロジェクト名 (ここでは Flip) を入力して [終了] をクリックします。
指定した名称のプロジェクトが生成され、パッケージエクスプローラ内に追加されます。
生成したプロジェクト内には、デフォルト値が設定された RTC プロファイル XML(RTC.xml) が自動的に生成されます。
RTC.xmlが生成された時点で、このプロジェクトに関連付けられているワークスペースとして RTCBuilder のエディタが開くはずです。
まず、いちばん左の「基本」タブを選択し、基本情報を入力します。先ほど決めた Flip コンポーネントの仕様(名前)の他に、概要やバージョン等を入力してください。ラベルが赤字の項目は必須項目です。その他はデフォルトで構いません。
次に、「アクティビティ」タブを選択し、使用するアクションコールバックを指定します。
Flipコンポーネントでは、onActivated()、onDeactivated()、onExecute()コールバックを使用します。下図のように①の onAtivated をクリック後に ②のラジオボタンにて [ON] にチェックを入れます。onDeactivated、onExecute についても同様の操作を行います。
さらに、「データポート」タブを選択し、データポートの情報を入力します。 先ほど決めた仕様を元に以下のように入力します。なお、変数名や表示位置はオプションなので、変更しなくて結構です。
次に、「コンフィギュレーション」タブを選択し、先ほど決めた仕様を元に、Configuration の情報を入力します。制約条件および Widget とは、RTSystemEditor でコンポーネントのコンフィギュレーションパラメーターを表示する際に、スライダー、スピンボタン、ラジオボタンなど、GUIで値の変更を行うためのものです。
ここでは、flipMode が取りうる値は先ほど仕様を決めたときに、-1、0、1 の3つの値のみ取ることにしたので、ラジオボタンを使用することにします。
次にプログラミング言語の選択とコードの生成を行いますが、OpenRTM-aist 2.0と1.2でツールの仕様が変わっています。
まずは1.2.2以前のバージョンの手順について説明します。
「言語・環境」タブを選択し、プログラミング言語を選択します。ここでは、C++(言語)を選択します。なお、言語・環境はデフォルト等が設定されておらず、指定し忘れるとコード生成時にエラーになりますので、必ず言語の指定を行うようにしてください。
最後に、「基本」タブにある"コード生成"ボタンをクリックし、コンポーネントの雛型を生成します。
次に2.0以降のバージョンの手順について説明します。
「基本」タブを選択して、下にスクロールすると見える「言語」の項目でC++を選択します。
最後にコード生成ボタンをクリックし、コンポーネントの雛型を生成します。
コード生成が完了したら、以下のようにFlipプロジェクトを右クリックして、「表示方法」->「システム・エクスプローラー」をクリックすることで、エクスプローラーでワークスペースのフォルダを開いてください。 Flipフォルダに各種ファイルが生成されているか確認してください。
RTC Builder で生成したコードの中には CMake でビルドに必要な各種ファイルを生成するための CMakeLists.txt が含まれています。 CMake を利用することにより CMakeLists.txt から Visual Studio のプロジェクトファイル、ソリューションファイル、もしくは Makefile 等を自動生成できます。
Flipフォルダのsrc/CMakeLists.txtをメモ帳などで開いて編集します。
このコンポーネントでは OpenCV を利用していますので、OpenCV のヘッダのインクルードパス、ライブラリやライブラリサーチパスを設定する必要があります。OpenCV は CMake に対応しており、以下の2行を追加・変更するだけで OpenCV のライブラリがリンクされ使えるようになります。
set(comp_srcs Flip.cpp ) set(standalone_srcs FlipComp.cpp) find_package(OpenCV REQUIRED) # <- この行を追加 :中略 add_dependencies(${PROJECT_NAME} ALL_IDL_TGT) target_link_libraries(${PROJECT_NAME} ${OPENRTM_LIBRARIES} ${OpenCV_LIBS}) # <- OepnCV_LIBSを追加 :中略 add_executable(${PROJECT_NAME}Comp ${standalone_srcs} ${comp_srcs} ${comp_headers} ${ALL_IDL_SRCS}) add_dependencies(${PROJECT_NAME}Comp ALL_IDL_TGT) target_link_libraries(${PROJECT_NAME}Comp ${OPENRTM_LIBRARIES} ${OpenCV_LIBS}) # <- OepnCV_LIBSを追加
CMakeを利用してビルド環境のConfigureを行います。 まずはCMake(cmake-gui)を起動してください。Windows 10の場合は画面左下の「ここに入力して検索」にCMakeと入力して検索してください。
画面上部に以下のようなテキストボックスがありますので、それぞれソースコードの場所( CMakeList.txt がある場所) と、ビルドディレクトリーを指定します。
ソースコードの場所は Flip コンポーネントのソースが生成された場所で CMakeList.txt が存在するディレクトリーです。デフォルトでは <ワークスペースディレクトリー>/Flip になります。
FlipフォルダのCMakeLists.txtをcmake-guiにドラッグアンドドロップすると、Flipフォルダのパスが自動的に入力されます。
ビルドディレクトリーとは、ビルドするためのプロジェクトファイルやオブジェクトファイル、バイナリを格納する場所のことです。場所は任意ですが、この場合 <ワークスペースディレクトリー>/Flip/build のように分かりやすい名前をつけたFlipのサブディレクトリーを指定することをお勧めします。
Where is the soruce code | C:\workspace\Flip |
Where to build the binaries | C:\workspace\Flip\build |
指定したら、下のConfigureボタンを押します。Create Directoryの画面が出たらYESをクリックしてください。
すると下図のようなダイアログが表示されますので、生成したいプロジェクトの種類を指定します。 今回は Visual Studio 16 2019 とします。VS2017やVS2022 を利用している方はそれぞれ読み替えてください。項目にVisual Studio 2022が無い場合は、cmake-guiのバージョンが古いため最新のバージョンをインストールし直してください。
ダイアログで [Finish] をクリックすると Configure が始まります。問題がなければ下部のログウインドウに「Configuring done」と表示されますので、続けて [Generate] ボタンをクリックします。「Generating done」と表示されればプロジェクトファイル・ソリューションファイル等の出力が完了します。
「Open Project」ボタンを押すと、Visual Studio 2019が起動して先ほど指定した build ディレクトリーの中の Flip.sln を 開きます。
なお、CMake は Configure の段階でキャッシュファイルを生成しますので、トラブルなどで設定を変更したり環境を変更した場合は [File] > [Delete Cache] でキャッシュを削除して Configure からやり直してください。
ヘッダ (include/Flip/Flip.h) およびソースコード (src/Flip.cpp) をそれぞれ編集します。 Visual Studio のソリューションエクスプローラから Flip.h、Flip.cpp をクリックすることで編集画面が開きます。
Flip コンポーネントでは、InPort から受け取った画像を画像保存用バッファに保存し、その保存した画像を OpenCVのcv::flip() 関数にて変換します。その後、変換された画像を OutPort から送信します。
onActivated()、onExecute()、onDeactivated()での処理内容を下図に示します。
onExecute() での処理を下図に示します。
OpenCV のライブラリを使用するため、OpenCV のインクルードファイルをインクルードします。
#include <rtm/DataInPort.h> #include <rtm/DataOutPort.h> #include <opencv2/opencv.hpp> #この行を追加 // <rtc-template block="component_description">
反転した画像の保存用にメンバー変数を追加します。
private: // <rtc-template block="private_attribute"> // </rtc-template> // <rtc-template block="private_operation"> // </rtc-template> cv::Mat m_imageBuff; #この行を追加 cv::Mat m_flipImageBuff; #この行を追加
下記のように、onActivated()、onDeactivated()、onExecute() を実装します。
RTC::ReturnCode_t Flip::onActivated(RTC::UniqueId ec_id) { // OutPortの画面サイズを0に設定 m_flippedImage.width = 0; m_flippedImage.height = 0; return RTC::RTC_OK; }
RTC::ReturnCode_t Flip::onDeactivated(RTC::UniqueId ec_id) { if (!m_imageBuff.empty()) { // 画像用メモリの解放 m_imageBuff.release(); m_flipImageBuff.release(); } return RTC::RTC_OK; }
RTC::ReturnCode_t Flip::onExecute(RTC::UniqueId ec_id) { // 新しいデータのチェック if (m_originalImageIn.isNew()) { // InPortデータの読み込み m_originalImageIn.read(); // InPortとOutPortの画面サイズ処理およびイメージ用メモリの確保 if (m_originalImage.width != m_flippedImage.width || m_originalImage.height != m_flippedImage.height) { m_flippedImage.width = m_originalImage.width; m_flippedImage.height = m_originalImage.height; m_imageBuff.create(cv::Size(m_originalImage.width, m_originalImage.height), CV_8UC3); m_flipImageBuff.create(cv::Size(m_originalImage.width, m_originalImage.height), CV_8UC3); } // InPortの画像データをm_imageBuffにコピー memcpy(m_imageBuff.data, (void *)&(m_originalImage.pixels[0]), m_originalImage.pixels.length()); // InPortからの画像データを反転する。 m_flipMode 0: X軸周り、1: Y軸周り、-1: 両方の軸周り cv::flip(m_imageBuff, m_flipImageBuff, m_flipMode); // 画像データのサイズ取得 int len = m_flipImageBuff.channels() * m_flipImageBuff.cols * m_flipImageBuff.rows; m_flippedImage.pixels.length(len); // 反転した画像データをOutPortにコピー memcpy((void *)&(m_flippedImage.pixels[0]), m_flipImageBuff.data, len); // 反転した画像データをOutPortから出力する。 m_flippedImageOut.write(); } return RTC::RTC_OK; }
Visual Studioの「ビルド」→「ソリューションのビルド」を選択してビルドを行います。
ここでは、OpenRTM-aist-1.1 以降で同梱されるようになったカメラコンポーネント (OpenCVCameraComp)とビューアコンポーネント (CameraViewerComp)を接続し動作確認を行います。
まずはRTSystemEditorを起動します。 OpenRTPのパースペクティブを開く画面からRTSystemEditorを選択して開いてください。
コンポーネントの参照を登録するためのネームサービスを起動します。
RTSystemEditorのネームサービスビュー上部のネームサービス起動ボタンを押してください。
Flip コンポーネントを起動します。
build/src/Debug (もしくはbuild/src/Release)フォルダの FlipComp.exe ファイルを実行して下さい。
USBカメラのキャプチャ画像を OutPort から出力する OpenCVCameraComp と、InPort で受け取った画像を画面に表示する CameraViewerComp を起動します。
これら2つのコンポーネントは、下記の手順にて起動できます。
コンポーネントが起動すると、以下のようにネームサービスビューにFlip、CameraViewer、OpenCVCameraコンポーネントが表示されるため、左上の「Open New System Editor」ボタン (ONのボタン) をクリックしてエディタを起動後に、エディタにコンポーネントをドラッグアンドドロップします。
下図のように、RTSystemEditorにて OpenCVCameraComp と Flip、CameraviewerComp コンポーネントを接続します。
ポートの接続は、片側のポートからもう片側のポートにドラッグアンドドロップすることでコネクタが生成されます。
RTSystemEditor の上部にあります「All Activate」というアイコンをクリックし、全てのコンポーネントをアクティブ化します。正常にアクティベートされた場合、下図のように黄緑色でコンポーネントが表示されます。
下図のようにコンフィギュレーションビューにてコンフィギュレーションを変更することができます。
エディタ上のFlipコンポーネントをクリックして編集ボタンを押してください。
Flip コンポーネントのコンフィギュレーションパラメーター「flipMode」を「0」や「-1」などに変更し、画像の反転が行われるかを確認してください。
コンポーネントを終了する場合は、「All Exit」ボタンを押すとエディタ上のコンポーネントが全て終了します。
このケーススタディでは、簡単な画像処理をコンポーネント化する方法を紹介します。既存のカメラコンポーネントと画像表示コンポーネントを利用し、カメラからの画像を左右(または上下)に反転させる処理部分をコンポーネントとして作成してカメラの画像を反転させ表示するシステムを作成します。
画像を反転する処理は簡単に実装することができますが、ここではより簡単に実装するために OpenCV ライブラリを利用し、汎用性の高い RTコンポーネントを作成します。
OpenCV (オープンシーブイ) とはかつてインテルが、現在はItseezが開発・公開しているオープンソースのコンピュータービジョン向けライブラリです。
Wikipediaより抜粋。
入力された画像を左右または上下に反転し出力する RTコンポーネントを、OpenCV ライブラリの cv::flip 関数を利用して作成します。作成および実行環境は Windows上 の Visual Studio を想定しています。対象 OpenRTM-aist のバージョンは 1.1.2 です。
作成手順はおおよそ以下のようになります。
cv::flip 関数は、OpenCV で標準的に用いられている cv::Mat 型の画像データを垂直軸 (左右反転)、水平軸 (上下反転)、または両軸 (上下左右反転)に対して反転させます。関数プロトタイプと入出力の引数の意味は以下の通りです。
void flip(const Mat& src, Mat& dst, int flipMode) src 入力配列 dst 出力配列。もしdst=NULLであれば、srcが上書きされます。 flipMode 配列の反転方法の指定内容: flipMode = 0: X軸周りでの反転(上下反転) flipMode > 0: Y軸周りでの反転(左右反転) flipMode < 0: 両軸周りでの反転(上下左右反転)
これから作成するコンポーネントを Flip コンポーネントと呼ぶことにします。
このコンポーネントは画像データ型の入力ポート (InPort) と、反転処理した画像を出力するための出力ポート (OutPort) を持ちます。それぞれのポートの名前を 入力ポート(InPort)名: originalImage、出力ポート(OutPort)名: flippedImage とします。
OpenRTM-aist には OpenCV を使用したビジョン関連のコンポーネントがサンプルとして付属しています。これらのコンポーネントのデータポートは画像の入出力に以下のような CameraImage 型を使用しています。
struct CameraImage { /// Time stamp. Time tm; /// Image pixel width. unsigned short width; /// Image pixel height. unsigned short height; /// Bits per pixel. unsigned short bpp; /// Image format (e.g. bitmap, jpeg, etc.). string format; /// Scale factor for images, such as disparity maps, /// where the integer pixel value should be divided /// by this factor to get the real pixel value. double fDiv; /// Raw pixel data. sequence<octet> pixels; };
このFlipコンポーネントではこれらのサンプルコンポーネントとデータのやり取りができるよう同じく CameraImage型 を InPort と OutPort に使用することにします。
CameraImage型 は InterfaceDataTypes.idl で定義されており、C++であれば、InterfaceDataTypesSkel.h をインクルードすると使えるようになります。
また、画像を反転させる方向は、左右反転、上下反転、上下左右反転の3通りがあります。これを実行時に指定できるように、RTコンポーネントのコンフィギュレーション機能を使用して指定できるようにします。パラメーター名は flipMode という名前にします。
flipMode は cv::flip 関数の仕様に合わせて、型は int 型とし 上下反転、左右反転、上下左右反転それぞれに 0、1、-1 を割り当てることにします。
flipMode の各値での画像処理のイメージを下図に示します。
以上から Flip コンポーネントの仕様をまとめます。
コンポーネント名称 | Flip |
InPort | |
ポート名 | originalImage |
型 | CameraImage |
説明 | 入力画像 |
OutPort | |
ポート名 | flippedImage |
型 | CameraImage |
説明 | 反転された画像 |
Configuration | |
パラメーター名 | flipMode |
型 | int |
デフォルト値 | 0 |
制約 | (0,-1, 1) |
Widget | radio |
説明 | 反転モード 上下反転: 0 左右反転: 1 上下左右反転: -1 |
ここで動作環境および開発環境を確認しておきます。
OpenRTM-aist-1.1 以降では、コンポーネントのビルドに CMake を使用します。 また、RTC のひな形生成ツール RTCBuilder では、ドキュメントを入力してこれを Doxygen に処理させることで、コンポーネントのマニュアルも自動で生成することができるようになっており、CMake で Configure を行うときに Doxygen が要求されるため予めインストールしておく必要があります。
Flipコンポーネントの雛型の生成は、RTCBuilder を用いて行います。
Eclipse では、各種作業を行うフォルダーを「ワークスペース」(Work Space)とよび、原則としてすべての生成物はこのフォルダの下に保存されます。 ワークスペースはアクセスできるフォルダーであれば、どこに作っても構いませんが、このチュートリアルでは以下のワークスペースを仮定します。
まずは Eclipse を起動します。 Windows 8.1の場合は「スタート」→「アプリビュー(右下矢印)」→「OpenRTM-aist 1.1.2」→「OpenRTP」をクリックすると起動できます。
最初にワークスペースの場所を尋ねられますので、上記のワークスペースを指定して [OK] をクリックしてください。
すると、以下のような「ようこそ」画面が表示されます。
「ようこそ」画面は必要ないので左上の [×] ボタンをクリックして閉じてください。
右上の [Open Perspective] ボタンをクリックしてください。
「RTC Builder」を選択し、[OK] ボタンをクリックします。
RTCBuilderが起動します。
Flipコンポーネントを作成するために、RTCBuilder で新規プロジェクトを作成する必要があります。
左上の [Open New RTCBuilder Editor] のアイコンをクリックしてください。
「プロジェクト名」欄に作成するプロジェクト名 (ここでは Flip) を入力して [終了] をクリックします。
指定した名称のプロジェクトが生成され、パッケージエクスプローラ内に追加されます。
生成したプロジェクト内には、デフォルト値が設定された RTC プロファイル XML(RTC.xml) が自動的に生成されます。
RTC.xmlが生成された時点で、このプロジェクトに関連付けられているワークスペースとして RTCBuilder のエディタが開くはずです。 もし起動しない場合はパッケージエクスプローラーの RTC.xml をダブルクリックしてください。
まず、いちばん左の「基本」タブを選択し、基本情報を入力します。先ほど決めた Flip コンポーネントの仕様(名前)の他に、概要やバージョン等を入力してください。ラベルが赤字の項目は必須項目です。その他はデフォルトで構いません。
次に、「アクティビティ」タブを選択し、使用するアクションコールバックを指定します。
Flipコンポーネントでは、onActivated()、onDeactivated()、onExecute()コールバックを使用します。下図のように①の onAtivated をクリック後に ②のラジオボタンにて [ON] にチェックを入れます。onDeactivated、onExecute についても同様の操作を行います。
さらに、「データポート」タブを選択し、データポートの情報を入力します。 先ほど決めた仕様を元に以下のように入力します。なお、変数名や表示位置はオプションなので、変更しなくて結構です。
次に、「コンフィギュレーション」タブを選択し、先ほど決めた仕様を元に、Configuration の情報を入力します。制約条件および Widget とは、RTSystemEditor でコンポーネントのコンフィギュレーションパラメーターを表示する際に、スライダー、スピンボタン、ラジオボタンなど、GUIで値の変更を行うためのものです。
ここでは、flipMode が取りうる値は先ほど仕様を決めたときに、-1、0、1 の3つの値のみ取ることにしたので、ラジオボタンを使用することにします。
次に、「言語・環境」タブを選択し、プログラミング言語を選択します。ここでは、C++(言語)を選択します。なお、言語・環境はデフォルト等が設定されておらず、指定し忘れるとコード生成時にエラーになりますので、必ず言語の指定を行うようにしてください。
また、C++の場合デフォルトでは CMake を利用してビルドすることになっていますが、旧式のVCのプロジェクトやソリューションを直接 RTCBuilder が生成する方法を利用したい場合は [Use old build environment] をチェックしてください。
最後に、「基本」タブにある"コード生成"ボタンをクリックし、コンポーネントの雛型を生成します。
※ 生成されるコード群は、Eclipse 起動時に指定したワークスペースフォルダの中に生成されます。現在のワークスペースは、[ファイル] > [ワークスペースの切り替え] で確認することができます。
RTC Builder で生成したコードの中には CMake でビルドに必要な各種ファイルを生成するための CMakeLists.txt が含まれています。 CMake を利用することにより CMakeLists.txt から Visual Studio のプロジェクトファイル、ソリューションファイル、もしくは Makefile 等を自動生成できます。
src/CMakeLists.txtをメモ帳などで開いて編集します。 もしくは Eclipse 画面左のパッケージエクスプローラで src/CMakeLists.txt をダブルクリックもしくはエディタへドラッグアンドドロップしても編集できます。
このコンポーネントでは OpenCV を利用していますので、OpenCV のヘッダのインクルードパス、ライブラリやライブラリサーチパスを与えてやる必要があります。幸い OpenCV は CMake に対応しており、以下の2行を追加・変更するだけで OpenCV のライブラリがリンクされ使えるようになります。
set(comp_srcs Flip.cpp ) set(standalone_srcs FlipComp.cpp) find_package(OpenCV REQUIRED) ←この行を追加 :中略 add_dependencies(${PROJECT_NAME} ALL_IDL_TGT) target_link_libraries(${PROJECT_NAME} ${OPENRTM_LIBRARIES} ${OpenCV_LIBS}) ← OepnCV_LIBSを追加 :中略 add_executable(${PROJECT_NAME}Comp ${standalone_srcs} ${comp_srcs} ${comp_headers} ${ALL_IDL_SRCS}) target_link_libraries(${PROJECT_NAME}Comp ${OPENRTM_LIBRARIES} ${OpenCV_LIBS}) ← OepnCV_LIBSを追加
CMakeを利用してビルド環境のConfigureを行います。 まずはCMake(cmake-gui)を起動してください。「スタート」→「アプリビュー(右下矢印)」→「CMake 3.2.1」→「CMake (cmake-gui)」をクリックすると起動できます。
画面上部に以下のようなテキストボックスがありますので、それぞれソースコードの場所( CMakeList.txt がある場所) と、ビルドディレクトリーを指定します。
ソースコードの場所は Flip コンポーネントのソースが生成された場所で CMakeList.txt が存在するディレクトリーです。デフォルトでは <ワークスペースディレクトリー>/Flip になります。
ビルドディレクトリーとは、ビルドするためのプロジェクトファイルやオブジェクトファイル、バイナリを格納する場所のことです。場所は任意ですが、この場合 <ワークスペースディレクトリー>/Flip/build のように分かりやすい名前をつけたFlipのサブディレクトリーを指定することをお勧めします。
Where is the soruce code | C:\workspace\Flip |
Where to build the binaries | C:\workspace\Flip\build |
指定したら、下のConfigureボタンを押します。すると下図のようなダイアログが表示されますので、生成したいプロジェクトの種類を指定します。 今回は Visual Studio 12 2013 とします。VS10 や VS11 を利用している方はそれぞれ読み替えてください。また、プロジェクトのタイプには32bitと64bitも選択できる場合がありますので、自分がインストールしている OpenRTM-aist に合わせて選択してください。
ダイアログで [Finish] をクリックすると Configure が始まります。問題がなければ下部のログウインドウに「Configuring done」と表示されますので、続けて [Generate] ボタンをクリックします。「Generating done」と表示されればプロジェクトファイル・ソリューションファイル等の出力が完了します。
なお、CMake は Configure の段階でキャッシュファイルを生成しますので、トラブルなどで設定を変更したり環境を変更した場合は [File] > [Delete Cache] でキャッシュを削除して Configure からやり直してください。
次に先ほど指定した build ディレクトリーの中の Flip.sln をダブルクリックして Visual Studio 2013 を起動します。
ヘッダ (include/Flip/Flip.h) およびソースコード (src/Flip.cpp) をそれぞれ編集します。 Visual Studio のソリューションエクスプローラから Flip.h、Flip.cpp をクリックすることで編集画面が開きます。
Flip コンポーネントでは、InPort から受け取った画像を画像保存用バッファに保存し、その保存した画像を OpenCVのcv::flip() 関数にて変換します。その後、変換された画像を OutPort から送信します。
onActivated()、onExecute()、onDeactivated()での処理内容を下図に示します。
onExecute() での処理を下図に示します。
OpenCV のライブラリを使用するため、OpenCV のインクルードファイルをインクルードします。
//OpenCV用インクルードファイルのインクルード #include <opencv2/opencv.hpp>
反転した画像の保存用にメンバー変数を追加します。
private: // <rtc-template block="private_attribute"> // </rtc-template> // <rtc-template block="private_operation"> // </rtc-template> cv::Mat m_imageBuff; cv::Mat m_flipImageBuff;
下記のように、onActivated()、onDeactivated()、onExecute() を実装します。
RTC::ReturnCode_t Flip::onActivated(RTC::UniqueId ec_id) { // OutPortの画面サイズの初期化 m_flippedImage.width = 0; m_flippedImage.height = 0; return RTC::RTC_OK; }
RTC::ReturnCode_t Flip::onDeactivated(RTC::UniqueId ec_id) { if (!m_imageBuff.empty()) { // 画像用メモリの解放 m_imageBuff.release(); m_flipImageBuff.release(); } return RTC::RTC_OK; }
RTC::ReturnCode_t Flip::onExecute(RTC::UniqueId ec_id) { // 新しいデータのチェック if (m_originalImageIn.isNew()) { // InPortデータの読み込み m_originalImageIn.read(); // InPortとOutPortの画面サイズ処理およびイメージ用メモリの確保 if (m_originalImage.width != m_flippedImage.width || m_originalImage.height != m_flippedImage.height) { m_flippedImage.width = m_originalImage.width; m_flippedImage.height = m_originalImage.height; m_imageBuff.create(cv::Size(m_originalImage.width, m_originalImage.height), CV_8UC3); m_flipImageBuff.create(cv::Size(m_originalImage.width, m_originalImage.height), CV_8UC3); } // InPortの画像データをm_imageBuffにコピー memcpy(m_imageBuff.data, (void *)&(m_originalImage.pixels[0]), m_originalImage.pixels.length()); // InPortからの画像データを反転する。 m_flipMode 0: X軸周り、1: Y軸周り、-1: 両方の軸周り cv::flip(m_imageBuff, m_flipImageBuff, m_flipMode); // 画像データのサイズ取得 int len = m_flipImageBuff.channels() * m_flipImageBuff.cols * m_flipImageBuff.rows; m_flippedImage.pixels.length(len); // 反転した画像データをOutPortにコピー memcpy((void *)&(m_flippedImage.pixels[0]), m_flipImageBuff.data, len); // 反転した画像データをOutPortから出力する。 m_flippedImageOut.write(); } return RTC::RTC_OK; }
Visual Studioの「ビルド」→「ソリューションのビルド」を選択してビルドを行います。
ここでは、OpenRTM-aist-1.1 以降で同梱されるようになったカメラコンポーネント (OpenCVCameraComp)とビューアコンポーネント (CameraViewerComp)を接続し動作確認を行います。
コンポーネントの参照を登録するためのネームサービスを起動します。
「スタート」→「アプリビュー(右下矢印)」→「OpenRTM-aist 1.1.2」の順に辿り、「Start Naming Service」をクリックして下さい。
&color(red){※ 「Start Naming Service」をクリックしても omniNames が起動されない場合は、フルコンピューター名が14文字以内に設定されているかを確認してください。
RTコンポーネントでは、ネームサーバーのアドレスやネームサーバーへの登録フォーマットなどの情報を rtc.conf というファイルで指定する必要があります。
下記の内容を rtc.conf というファイル名で保存し、 Flip\build\src\Debug(もしくは、Release)フォルダーに置いてください。
※ Eclipse起動時にworkspaceをデフォルトのままにしていた場合、Flipフォルダのパスは、 C:\Documents and Settings\<ログインユーザー名>\workspace となります。
corba.nameservers: localhost naming.formats: %n.rtc
Flip コンポーネントを起動します。
先程 rtc.conf ファイルを置いたフォルダにある、FlipComp.exe ファイルを実行して下さい。
USBカメラのキャプチャ画像を OutPort から出力する OpenCVCameraComp と、InPort で受け取った画像を画面に表示する CameraViewerComp を起動します。
これら2つのコンポーネントは、下記の手順にて起動できます。
「スタート」→「アプリビュー(右下矢印)」→「OpenRTM-aist 1.1.2」の順に辿り、 「OpenCVCameraComp」と「CameraViewerComp」をそれぞれクリックして実行します。
下図のように、RTSystemEditorにて OpenCVCameraComp と Flip、CameraviewerComp コンポーネントを接続します。
RTSystemEditor の上部にあります「All Activate」というアイコンをクリックし、全てのコンポーネントをアクティブ化します。正常にアクティベートされた場合、下図のように黄緑色でコンポーネントが表示されます。
下図のようにコンフィギュレーションビューにてコンフィギュレーションを変更することができます。
Flip コンポーネントのコンフィギュレーションパラメーター「flipMode」を「0」や「-1」などに変更し、画像の反転が行われるかを確認してください。
このケーススタディーでは、簡単な画像処理をコンポーネント化する方法を紹介します。 既存のカメラコンポーネントと画像表示コンポーネントを利用し、カメラからの画像をj左右(または上下)に反転させる処理部分をコンポーネントとして作成してカメラの画像を反転させ表示するシステムを作成します。
画像を反転する処理は簡単に実装することができますが、ここではさらに簡単のために OpenCV ライブラリを利用しより汎用性の高い RTコンポーネントを作成します。
OpenCV (オープンシーブイ) とはかつてインテルが、現在は Willow Garage が開発・公開しているオープンソースのコンピュータビジョン向けライブラリです。
Wikipediaより抜粋。
入力された画像を左右または上下に反転し出力するRTコンポーネントを、OpenCV ライブラリの cvFlip 関数を利用して作成します。 作成および実行環境は Windows上 のVisual C++ を想定しています。対象 OpenRTM-aist のバージョンは 1.1.0 です。
作成手順はおおよそ以下のようになります。
cvFlip 関数は、OpenCV で標準的に用いられている IplImage型の画像データを垂直軸 (左右反転)、水平軸 (上下反転)、または両軸 (上下左右反転)に対して反転させます。 関数プロトタイプと入出力の引数の意味は以下の通りです。
void cvFlip(IplImage* src, IplImage* dst=NULL, int flipMode=0); #define cvMirror cvFlip src 入力配列 dst 出力配列。もし dst=NULL であれば、src が上書きされます。 flipMode 配列の反転方法の指定内容: flipMode = 0: X軸周りでの反転(上下反転) flipMode > 0: Y軸周りでの反転(左右反転) flipMode < 0: 両軸周りでの反転(上下左右反転)
これから作成するコンポーネントを Flip コンポーネントと呼ぶことにします。
このコンポーネントは画像データ型の入力ポート (InPort) と、反転処理した画像を出力するための出力ポート (OutPort) を持ちます。 それぞれのポートの名前を 入力ポート(InPort)名: originalImage、出力ポート(OutPort)名: flippedImage とします。
OpenRTM-aist には OpenCV を使用したビジョン関連のコンポーネントがサンプルとして付属しています。 これらのコンポーネントのデータポートは画像の入出力に以下のような CameraImage 型を使用しています。
struct CameraImage { /// Time stamp. Time tm; /// Image pixel width. unsigned short width; /// Image pixel height. unsigned short height; /// Bits per pixel. unsigned short bpp; /// Image format (e.g. bitmap, jpeg, etc.). string format; /// Scale factor for images, such as disparity maps, /// where the integer pixel value should be divided /// by this factor to get the real pixel value. double fDiv; /// Raw pixel data. sequence<octet> pixels; };
この Flip コンポーネントではこれらのサンプルコンポーネントとデータのやり取りができるよう同じく CameraImage型を InPort と OutPort に使用することにします。 CameraImage型は InterfaceDataTypes.idl で定義されており、C++であれば、InterfaceDataTypesSkel.h をインクルードすると使えるようになります。
また、画像を反転させる方向は、左右反転、上下反転、上下左右反転の3通りがあります。 これを実行時に指定できるように、RTコンポーネントのコンフィギュレーション機能を使用して指定できるようにします。 パラメーター名は flipMode という名前にします。
flipMode は cvFlip 関数の仕様に合わせて、型は int 型とし 上下反転、左右反転、上下左右反転それぞれに 0、1、-1 を割り当てることにします。
flipMode の各値での画像処理のイメージを下図に示します。
以上からFlipコンポーネントの仕様をまとめます。
コンポーネント名称 | Flip |
InPort | |
ポート名 | originalImage |
型 | CameraImage |
意味 | 入力画像 |
OutPort | |
ポート名 | flippedImage |
型 | CameraImage |
意味 | 反転された画像 |
Configuration | |
パラメーター名 | flipMode |
型 | int |
意味 | 反転モード 上下反転: 0 左右反転: 1 上下左右反転: -1 |
ここで動作環境および開発環境を確認しておきます。
OpenRTM-aist-1.1以降では、コンポーネントのビルドにCMakeを使用します。 また、RTC のひな形生成ツール RTCBuilder では、ドキュメントを入力してこれを Doxygen に処理させることで、コンポーネントのマニュアルも自動で生成することができるようになっており、このため CMake で Configureを行うときに Doxygen が要求されるため、予めインストールしておく必要があります。
Flip コンポーネントの雛型の生成は、RTCBuilder を用いて行います。
Eclipse では、各種作業を行うフォルダーを「ワークスペース」(Work Space)とよび、原則としてすべての生成物はこのフォルダーの下に保存されます。 ワークスペースはアクセスできるフォルダーであれば、どこに作っても構いませんが、このチュートリアルでは以下のワークスペースを仮定します。
eclipse.exe をダブルクリックするとまず、ワークスペースの場所を尋ねられますので、上記のワークスペースを指定してください。 すると、以下のような Welcome ページが表示されます。
Welcome ページはいまは必要ないので左上の「×」ボタンをクリックして閉じてください。
右上の [Open Perspective] ボタンをクリックし、プルダウンの「Other…」を選択します。
「RTC Builder」を選択することで、RTCBuilder が起動します。メニューバーに「カナヅチとRT」の RTCBuilder のアイコンが現れます。
Flip コンポーネントを作成するために、RTCBuilder で新規プロジェクトを作成する必要があります。プロジェクトを作成する方法は2種類あります。
どちらの方法でも、次ようなプロジェクト作成ウィザードが開始されます。 「プロジェクト名」欄に作成するプロジェクト名 (ここでは Flip) を入力して [終了] ボタンをクリックします。
指定した名称のプロジェクトが生成され、パッケージエクスプローラ内に追加されます。
生成したプロジェクト内には、デフォルト値が設定された RTC プロファイル XML(RTC.xml) が自動的に生成されます。
RTC.xml が生成された時点で、このプロジェクトに関連付けられているワークスペースとして RTCBuilder のエディタが開くはずです。もし開かない場合は、ツールバーの [Open New RtcBuilder Editor] ボタンをクリックするか、メニューバーの [File] > [Open New Builder Editor] を選択します。
まず、いちばん左の「基本」タブを選択し、基本情報を入力します。先ほど決めた Flip コンポーネントの仕様(名前)の他に、概要やバージョン等を入力してください。 ラベルが赤字の項目は必須項目です。その他はデフォルトで構いません。
次に、「アクティビティ」タブを選択し、使用するアクションコールバックを指定します。
Flip コンポーネントでは、onActivated()、onDeactivated()、onExecute() コールバックを使用します。 下図のように①の onAtivated をクリック後に②のラジオボタンにて [ON] チェックを入れます。onDeactivated、onExecuteについても同様の手順を行います。
さらに、「データポート」タブを選択し、データポートの情報を入力します。 先ほど決めた仕様を元に以下のように入力します。なお、変数名や表示位置はオプションで、そのままで結構です。
次に、「コンフィギュレーション」タブを選択し、先ほど決めた仕様を元に、Configuration の情報を入力します。 制約条件および Widget とは、RTSystemEditor でコンポーネントのコンフィギュレーションパラメーターを表示する際に、スライダー、スピンボタン、ラジオボタンなど、GUI で値の変更を行うためのものです。
ここでは、flipMode が取りうる値は先ほど仕様を決めたときに、-1、0、1 の3つの値のみ取ることにしたので、ラジオボタンを使用することにします。
次に、「言語・環境」タブを選択し、プログラミング言語を選択します。 ここでは、C++(言語)を選択します。なお、言語・環境はデフォルト等が設定されておらず、指定し忘れるとコード生成時にエラーになりますので、必ず言語の指定を行うようにしてください。
また、C++の場合デフォルトでは CMake を利用してビルドすることになっていますが、旧式のVCのプロジェクトやソリューションを直接 RTCBuilder が生成する方法を利用したい場合は [Use old build environment] を チェックしてください。
最後に、「基本」タブにあ [コード生成] タンをクリックし、コンポーネントの雛型を生成します。
※ 生成されるコード群は、eclipse 起動時に指定したワークスペースフォルダーの中に生成されます。現在のワークスペースは、「ファイル」 > 「ワークスペースの切り替え...」で確認することができます。
さて、ここまでで Flip コンポーネントのソースコードが生成されました。 処理の中身は実装されていないので、InPort に画像を入力しても何も出力されませんが、生成直後のソースコードだけでもコンパイルおよび実行はできます。
※サービスポートとプロバイダを持つコンポーネントの場合、実装を行わないとビルドが通らないものもあります。
では、まず CMake を利用してビルド環境の Configure を行います。Linuxであれば、Flip コンポーネントのソースが生成されたディレクトリーで
$ cmake . $ make
とすれば、Configure およびビルドが完了するはずです。
Windows の場合は GUI を利用してConfigure してみます。 スタートメニューなどから CMake (cmake-gui) を起動します。
画面上部に以下のようなテキストボックスがありますので、それぞれソースコードの場所(CMakeList.txtがある場所) と、ビルドディレクトリーを指定します。
ソースコードの場所は Flip コンポーネントのソースが生成された場所で CMakeList.txt が存在するディレクトリーです。デフォルトでは <ワークスペースディレクトリー>/Flip になります。
Eclipse の画面左側、パッケージエクスプローラの Flip 直下の CMakeLists.txt を CMake GUI の Where is the soruce code のテキストボックスにドラッグアンドドロップするのが一番手っ取り早いでしょう。
ビルドディレクトリーとは、ビルドするためのプロジェクトファイルやオブジェクトファイル、バイナリを格納する場所のことです。 場所は任意ですが、この場合 <ワークスペースディレクトリー>/Flip/build のように分かりやすい名前をつけた Flip のサブディレクトリーを指定することをお勧めします。
Where is the soruce code | C:\rtcws\Flip |
Where to build the binaries | C:\rtcws\Flip\build |
指定したら、下の [Configure] ボタンをクリックします。すると下図のようなダイアログが表示されますので、生成したいプロジェクトの種類を指定します。 今回はVisual Studio 10 とします。VS8やVS9を利用している方はそれぞれ読み替えてください。 また、プロジェクトのタイプには 32bit と 64bit も選択できる場合がありますので、自分がインストールしている OpenRTM-aist に合わせて選択してください。
ダイアログで [Finish] をクリックすると Configure が始まります。問題がなければ下部のログウインドウに「Configuring done」と出力されますので、続けて「Generate」ボタンをクリックします。 「Generating done」と出ればプロジェクトファイル・ソリューションファイル等の出力が完了します。
なお、CMake は Configure の段階でキャッシュファイルを生成しますので、トラブルなどで設定を変更したり環境を変更した場合は [File] > [Delete Cache] でキャッシュを削除して Configure からやり直してください。
次に先ほど指定した build ディレクトリーの中の Flip.sln をダブルクリックして Visual Studio 2010 を起動します。
起動後、ソリューションエクスプローラーの「ALL_BUILD」を右クリックし「ビルド」を選択してビルドします。 特に問題がなければ正常にビルドが終了します。
ヘッダ (include/Flip/Flip.h) およびソースコード (src/Flip.cpp) をそれぞれ編集します。 Eclipse 画面左のパッケージエクスプローラでそれぞれのファイルをダブルクリックすると、通常は Visual C++ の編集画面が開きますので、そこで編集します。 Eclipse の中央のエディタにドラッグアンドドロップしても編集できます。
Flip コンポーネントでは、InPort から受け取った画像を画像保存用バッファに保存し、その保存した画像を OpenCV の cvFlip() 関数にて変換します。 その後、変換された画像を OutPort から送信します。
onActivated()、onExecute()、onDeactivated() での処理内容を下図に示します。
onExecute() での処理を下図に示します。
OpenCV のライブラリを使用するため、OpenCV のインクルードファイルをインクルードします。
//OpenCV用インクルードファイルのインクルード #include<cv.h> #include<cxcore.h> #include<highgui.h>
この cvFlip コンポーネントでは、画像領域の確保、Flip 処理、確保した画像領域の解放のそれぞれの処理を行います。 これらの処理は、それぞれonActivated()、onDeactivated()、onExecute() のコールバック関数にて行います。
/*** * * 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_imageBuff; IplImage* m_flipImageBuff;
下記のように、onActivated()、onDeactivated()、onExecute() を実装します。
RTC::ReturnCode_t Flip::onActivated(RTC::UniqueId ec_id) { // イメージ用メモリの初期化 m_imageBuff = NULL; m_flipImageBuff = NULL; // OutPortの画面サイズの初期化 m_flippedImage.width = 0; m_flippedImage.height = 0; return RTC::RTC_OK; } RTC::ReturnCode_t Flip::onDeactivated(RTC::UniqueId ec_id) { if(m_imageBuff != NULL) { // イメージ用メモリの解放 cvReleaseImage(&m_imageBuff); cvReleaseImage(&m_flipImageBuff); } return RTC::RTC_OK; } RTC::ReturnCode_t Flip::onExecute(RTC::UniqueId ec_id) { // 新しいデータのチェック if (m_originalImageIn.isNew()) { // InPortデータの読み込み m_originalImageIn.read(); // InPortとOutPortの画面サイズ処理およびイメージ用メモリの確保 if( m_originalImage.width != m_flippedImage.width || m_originalImage.height != m_flippedImage.height) { m_flippedImage.width = m_originalImage.width; m_flippedImage.height = m_originalImage.height; // InPortのイメージサイズが変更された場合 if(m_imageBuff != NULL) { cvReleaseImage(&m_imageBuff); cvReleaseImage(&m_flipImageBuff); } // イメージ用メモリの確保 m_imageBuff = cvCreateImage(cvSize(m_originalImage.width, m_originalImage.height), IPL_DEPTH_8U, 3); m_flipImageBuff = cvCreateImage(cvSize(m_originalImage.width, m_originalImage.height), IPL_DEPTH_8U, 3); } // InPortの画像データをIplImageのimageDataにコピー memcpy(m_imageBuff->imageData,(void *)&(m_originalImage.pixels[0]),m_originalImage.pixels.length()); // InPortからの画像データを反転する。 m_flipMode 0: X軸周り, 1: Y軸周り, -1: 両方の軸周り cvFlip(m_imageBuff, m_flipImageBuff, m_flipMode); // 画像データのサイズ取得 int len = m_flipImageBuff->nChannels * m_flipImageBuff->width * m_flipImageBuff->height; m_flippedImage.pixels.length(len); // 反転した画像データをOutPortにコピー memcpy((void *)&(m_flippedImage.pixels[0]),m_flipImageBuff->imageData,len); // 反転した画像データをOutPortから出力する。 m_flippedImageOut.write(); } return RTC::RTC_OK; }
Eclipse 画面左のパッケージエクスプローラで src/CMakeLists.txt をダブルクリックもしくはエディタへドラッグアンドドロップして編集します。
このコンポーネントでは OpenCV を利用していますので、OpenCV のヘッダのインクルードパス、ライブラリやライブラリサーチパスを与えてやる必要があります。 幸いOpenCV は CMake に対応しており、以下の2行を追加・変更するだけで OpenCV のライブラリがリンクされ使えるようになります。
set(comp_srcs Flip.cpp ) set(standalone_srcs FlipComp.cpp) find_package(OpenCV REQUIRED) ←この行を追加 :中略 add_dependencies(${PROJECT_NAME} ALL_IDL_TGT) target_link_libraries(${PROJECT_NAME} ${OPENRTM_LIBRARIES} ${OpenCV_LIBS}) ← OepnCV_LIBSを追加 :中略 add_executable(${PROJECT_NAME}Comp ${standalone_srcs} ${comp_srcs} ${comp_headers} ${ALL_IDL_SRCS}) target_link_libraries(${PROJECT_NAME}Comp ${OPENRTM_LIBRARIES} ${OpenCV_LIBS}) ← OepnCV_LIBSを追加
CMakeList.txt を編集したので、再度 CMake GUI で Configure および Generate を行います。 CMake の Generate が正常に終了した事を確認し、Flip.slnファイルをダブルクリックし、Visual C++ 2010 を起動します。
Visual C++ 2010の起動後、下図のようにし、コンポーネントのビルドを行います。
ここでは、OpenRTM-aist-1.1 以降で同梱されるようになったカメラコンポーネント (OpenCVCameraComp、または DirectShowCamComp)とビューアコンポーネント (CameraViewerComp)を接続し動作確認を行います。
コンポーネントの参照を登録するためのネームサービスを起動します。
[スタート] > [すべてのプログラム] > [OpenRTM-aist] > [C++] > [tools] の順に辿り、「Start Naming Service」をクリックしてください。
&color(RED){※ 「Start Naming Service」をクリックしても omniNames が起動されない場合は、フルコンピューター名が14文字以内に設定されているかを確認してください。
RTコンポーネントでは、ネームサーバーのアドレスやネームサーバーへの登録フォーマットなどの情報を rtc.conf というファイルで指定する必要があります。
下記の内容を rtc.conf というファイル名で保存し、Flip\FlipComp\Debug(もしくは、Release)フォルダーに置いてください。
※ Eclipse 起動時に workspace をデフォルトのままにしていた場合、Flipフォルダーのパスは、
C:\Documents and Settings\<ログインユーザー名>\workspace となります。
corba.nameservers: localhost naming.formats: %n.rtc
Flip コンポーネントを起動します。
先程 rtc.conf ファイルを置いたフォルダにある、FlipComp.exe ファイルを実行してください。
USBカメラのキャプチャ画像を OutPort から出力する OpenCVCameraComp、または DirectShowCamComp コンポーネントと、InPort で受け取った画像を画面に表示する CameraViewerComp を起動します。
これら2つのコンポーネントは、下記の手順にて起動できます。
[スタート] > [すべてのプログラム] > [OpenRTM-aist] > [components] > [C++] > [examples] > [opencv-rtcs] の順に辿り、「OpenCVCameraComp」と「CameraViewerComp」をそれぞれクリックして実行します。
OpenCVCameraComp ではうまくカメラを認識しない場合があります。その場合は DirectShowCamComp を使用してみてください。
下図のように、RTSystemEditor にて OpenCVCameraComp (またはDirectShowcomp) と Flip、CameraviewerComp コンポーネントを接続します。
RTSystemEditor の上部にあります「ALL」というアイコンをクリックし、全てのコンポーネントをアクティブ化します。 正常にアクティベートされた場合、下図のように黄緑色でコンポーネントが表示されます。
下図のようにコンフィギュレーションビューにてコンフィギュレーションを変更することができます。
Flip コンポーネントのコンフィギュレーションパラメーター「flipMode」を「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", // Configuration variables "conf.default.flipMode", "1", // Widget "conf.__widget__.flipMode", "radio", // Constraints "conf.__constraints__.flip_mode", "(-1,0,1)", "" }; /*! * @brief constructor * @param manager Maneger Object */ Flip::Flip(RTC::Manager* manager) : RTC::DataFlowComponentBase(manager), m_originalImageIn("originalImage", m_originalImage), m_flippedImageOut("flippedImage", m_flippedImage) { } /*! * @brief destructor */ Flip::~Flip() { } RTC::ReturnCode_t Flip::onInitialize() { // Registration: InPort/OutPort/Service // Set InPort buffers addInPort("originalImage", m_originalImageIn); // Set OutPort buffer addOutPort("flippedImage", m_flippedImageOut); // Bind variables and configuration variable bindParameter("flipMode", m_flipMode, "1"); return RTC::RTC_OK; } RTC::ReturnCode_t Flip::onActivated(RTC::UniqueId ec_id) { // イメージ用メモリの初期化 m_imageBuff = NULL; m_flipImageBuff = NULL; // OutPortの画面サイズの初期化 m_flippedImage.width = 0; m_flippedImage.height = 0; return RTC::RTC_OK; } RTC::ReturnCode_t Flip::onDeactivated(RTC::UniqueId ec_id) { if(m_imageBuff != NULL) { // イメージ用メモリの解放 cvReleaseImage(&m_imageBuff); cvReleaseImage(&m_flipImageBuff); } return RTC::RTC_OK; } RTC::ReturnCode_t Flip::onExecute(RTC::UniqueId ec_id) { // 新しいデータのチェック if (m_originalImageIn.isNew()) { // InPortデータの読み込み m_originalImageIn.read(); // InPortとOutPortの画面サイズ処理およびイメージ用メモリの確保 if( m_originalImage.width != m_flippedImage.width || m_originalImage.height != m_flippedImage.height) { m_flippedImage.width = m_originalImage.width; m_flippedImage.height = m_originalImage.height; // InPortのイメージサイズが変更された場合 if(m_imageBuff != NULL) { cvReleaseImage(&m_imageBuff); cvReleaseImage(&m_flipImageBuff); } // イメージ用メモリの確保 m_imageBuff = cvCreateImage(cvSize(m_originalImage.width, m_originalImage.height), IPL_DEPTH_8U, 3); m_flipImageBuff = cvCreateImage(cvSize(m_originalImage.width, m_originalImage.height), IPL_DEPTH_8U, 3); } // InPortの画像データをIplImageのimageDataにコピー memcpy(m_imageBuff->imageData,(void *)&(m_originalImage.pixels[0]),m_originalImage.pixels.length()); // InPortからの画像データを反転する。 m_flipMode 0: X軸周り, 1: Y軸周り, -1: 両方の軸周り cvFlip(m_imageBuff, m_flipImageBuff, m_flipMode); // 画像データのサイズ取得 int len = m_flipImageBuff->nChannels * m_flipImageBuff->width * m_flipImageBuff->height; m_flippedImage.pixels.length(len); // 反転した画像データをOutPortにコピー memcpy((void *)&(m_flippedImage.pixels[0]),m_flipImageBuff->imageData,len); // 反転した画像データをOutPortから出力する。 m_flippedImageOut.write(); } return RTC::RTC_OK; } extern "C" { void FlipInit(RTC::Manager* manager) { coil::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> #include <rtm/idl/ExtendedDataTypesSkel.h> #include <rtm/idl/InterfaceDataTypesSkel.h> //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 /*! * * - Name: flipMode * - DefaultValue: 1 */ int m_flipMode; // DataInPort declaration CameraImage m_originalImage; /*! */ InPort<CameraImage> m_originalImageIn; // DataOutPort declaration CameraImage m_flippedImage; /*! */ OutPort<CameraImage> m_flippedImageOut; private: // 処理画像用バッファ IplImage* m_imageBuff; IplImage* m_flipImageBuff; }; extern "C" { DLL_EXPORT void FlipInit(RTC::Manager* manager); }; #endif // FLIP_H
Flip コンポーネントの全ソースコードを以下に添付します。
このケーススタディでは、簡単な画像処理をコンポーネント化する方法を紹介します。 既存のカメラコンポーネントと画像表示コンポーネントを利用し、カメラからの画像を左右(または上下)に反転させる処理部分をコンポーネントとして作成してカメラの画像を反転させ表示するシステムを作成します。
画像を反転する処理は簡単に実装することができますが、ここでは、より簡単に実装するのため OpenCV ライブラリを利用して汎用性の高い RTコンポーネントを作成します。
OpenCV (オープンシーブイ) とはかつてインテルが、現在は Willow Garage が開発・公開しているオープンソースのコンピュータービジョン向けライブラリです。
Wikipediaより抜粋。
入力された画像を左右または上下に反転し出力する RTコンポーネントを、OpenCV ライブラリの cvFlip 関数を利用して作成します。 作成および実行環境は Windows 上の Visual C++ を想定しています。対象 OpenRTM-aist のバージョンは 1.1.0 です。
作成手順はおおよそ以下のようになります。
cvFlip 関数は、OpenCV で標準的に用いられている IplImage 型の画像データを垂直軸 (左右反転)、水平軸 (上下反転)、または両軸 (上下左右反転)に対して反転させます。 関数プロトタイプと入出力の引数の意味は以下のとおりです。
void cvFlip(IplImage* src, IplImage* dst=NULL, int flipMode=0); #define cvMirror cvFlip src 入力配列 dst 出力配列。もし dst=NULL であれば、src が上書きされます。 flipMode 配列の反転方法の指定内容: flipMode = 0: X軸周りでの反転(上下反転) flipMode > 0: Y軸周りでの反転(左右反転) flipMode < 0: 両軸周りでの反転(上下左右反転)
これから作成するコンポーネントを Flip コンポーネントと呼ぶことにします。
このコンポーネントは画像データ型の入力ポート (InPort) と、反転処理した画像を出力するための出力ポート (OutPort) を持ちます。 それぞれのポートの名前を 入力ポート(InPort)名: originalImage、出力ポート(OutPort)名: flippedImage とします。
OpenRTM-aist には OpenCVを使用したビジョン関連のコンポーネントがサンプルとして付属しています。 これらのコンポーネントのデータポートは画像の入出力に以下のような CameraImage 型を使用しています。
struct CameraImage { /// Time stamp. Time tm; /// Image pixel width. unsigned short width; /// Image pixel height. unsigned short height; /// Bits per pixel. unsigned short bpp; /// Image format (e.g. bitmap, jpeg, etc.). string format; /// Scale factor for images, such as disparity maps, /// where the integer pixel value should be divided /// by this factor to get the real pixel value. double fDiv; /// Raw pixel data. sequence<octet> pixels; };
この Flip コンポーネントではこれらのサンプルコンポーネントとデータのやり取りができるよう同じく CameraImage 型を InPort と OutPort に使用することにします。 CameraImage 型は InterfaceDataTypes.idl で定義されており、C++であれば、InterfaceDataTypesSkel.h をインクルードすると使えるようになります。
また、画像を反転させる方向は、左右反転、上下反転、上下左右反転の3通りがあります。これを実行時に指定できるように、RTコンポーネントのコンフィギュレーション機能を使用して指定できるようにします。 パラメーター名は flipMode という名前にします。
flipMode は cvFlip 関数の仕様に合わせて、型は int 型とし、上下反転、左右反転、上下左右反転それぞれに 0、1、-1 を割り当てることにします。
flipMode の各値での画像処理のイメージを図1に示します。
以上から Flip コンポーネントの仕様をまとめます。
コンポーネント名称 | Flip |
InPort | |
ポート名 | originalImage |
型 | CameraImage |
意味 | 入力画像 |
OutPort | |
ポート名 | flippedImage |
型 | CameraImage |
意味 | 反転された画像 |
Configuration | |
パラメーター名 | flipMode |
型 | int |
意味 | 反転モード 上下反転: 0 左右反転: 1 上下左右反転: -1 |
ここで動作環境および開発環境を確認しておきます。
OpenRTM-aist-1.1 以降では、コンポーネントのビルドに CMake を使用します。 また、RTC のひな形生成ツール RTCBuilder では、ドキュメントを入力して Doxygen に処理させることで、コンポーネントのマニュアルも自動で生成することができるようになっています。 このため CMake で Configure を行うときに Doxygen が要求されるので予めインストールしておく必要があります。
Flip コンポーネントの雛型の生成は、RTCBuilder を用いて行います。
新規ワークスペースを指定して Eclipse を起動すると、以下のような「ようこそ」画面が表示されます。
「ようこそ」画面は必要ないので左上の [×] ボタンをクリックして閉じてください。
右上の [Open Perspective] ボタンをクリックし、プルダウンの「Other…」を選択します。
「RTC Builder」を選択し、[OK] ボタンをクリックします。
RTCBuilder が起動します。
Flip コンポーネントを作成するために、RTCBuilder で新規プロジェクトを作成する必要があります。メニューから [ファイル] > [新規] > [プロジェクト] を選択します。
表示された 「新規プロジェクト」 画面において,[その他] > [RTCビルダ] を選択し、[次へ] をクリックします。
「プロジェクト名」欄に作成するプロジェクト名 (ここでは Flip) を入力して [完了] をクリックします。
指定した名称のプロジェクトが生成され、パッケージエクスプローラー内に追加されます。
生成したプロジェクト内には、デフォルト値が設定された RTC プロファイル XML(RTC.xml) が自動的に生成されます。
RTC.xml が生成された時点で、このプロジェクトに関連付けられているワークスペースとして RTCBuilder のエディタが開くはずです。 もし開かない場合は、ツールバーの [Open New RtcBuilder Editor] ボタンをクリックするか、メニューから [ファイル] > [Open New Builder Editor] を選択します。
まず、いちばん左の「基本」タブを選択し、基本情報を入力します。先ほど決めた Flip コンポーネントの仕様(名前)の他に、概要やバージョン等を入力してください。 ラベルが赤字の項目は必須項目です。その他はデフォルトで構いません。
次に、「アクティビティ」タブを選択し、使用するアクションコールバックを指定します。
Flipコンポーネントでは、onActivated()、onDeactivated()、onExecute()コールバックを使用します。 図14のように①の onAtivated をクリック後に ②のラジオボタンにて [ON] にチェックを入れます。onDeactivated、onExecute についても同様の操作を行います。
次に、「データポート」タブを選択し、データポートの情報を入力します。先ほど決めた仕様を元に以下のように入力します。 なお、変数名や表示位置はオプションのため、変更しないで結構です。
次に、「コンフィギュレーション」タブを選択し、先ほど決めた仕様を元に、Configuration の情報を入力します。 制約条件および Widget とは、RTSystemEditor でコンポーネントのコンフィギュレーションパラメーターを表示する際に、スライダー、スピンボタン、ラジオボタンなど、GUIで値の変更を行うためのものです。
ここでは、flipMode が取りうる値は先ほど仕様を決めたときに、-1、0、1 の3つの値のみ取ることにしたので、ラジオボタンを使用することにします。
次に、「言語・環境」タブを選択し、プログラミング言語を選択します。ここでは、C++(言語)を選択します。 なお、言語・環境はデフォルト等が設定されておらず、指定し忘れるとコード生成時にエラーになりますので、必ず言語の指定を行うようにしてください。
また、C++ の場合デフォルトでは CMake を利用してビルドすることになっていますが、旧式の VC のプロジェクトやソリューションを直接 RTCBuilder が生成する方法を利用したい場合は Use old build environment をチェックしてください。
最後に、「基本」タブにある [コード生成] ボタンをクリックし、コンポーネントの雛型を生成します。
※ 生成されるコード群は、Eclipse 起動時に指定したワークスペースフォルダーの中に生成されます。 現在のワークスペースは、[ファイル] > [ワークスペースの切り替え] で確認することができます。
さて、ここまでで Flip コンポーネントのソースコードが生成されました。 処理の中身は実装されていないので、InPort に画像を入力しても何も出力されませんが、生成直後のソースコードだけでもコンパイルおよび実行はできます。
※サービスポートとプロバイダーを持つコンポーネントの場合、実装を行わないとビルドが通らないものもあります。
では、まず CMake を利用してビルド環境の Configure を行います。Linux であれば、Flip コンポーネントのソースが生成されたディレクトリーで
$ cmake . $ make
とすれば、Configure およびビルドが完了するはずです。
Windows の場合は GUI を利用して Configure してみます。 スタートメニューなどから CMake (cmake-gui) を起動します。
画面上部に以下のようなテキストボックスがありますので、それぞれソースコードの場所(CMakeList.txtがある場所) と、ビルドディレクトリーを指定します。
ソースコードの場所は Flip コンポーネントのソースが生成された場所で、CMakeList.txt が存在するディレクトリーです。 デフォルトでは <ワークスペースディレクトリー>/Flip になります。
また、ビルドディレクトリーとは、ビルドするためのプロジェクトファイルやオブジェクトファイル、バイナリを格納する場所のことです。 場所は任意ですが、この場合 <ワークスペースディレクトリー>/Flip/build のように分かりやすい名前をつけた Flip のサブディレクトリーを指定することをお勧めします。
指定したら、[Configure] ボタンをクリックします。すると図20のようなダイアログが表示されますので、生成したいプロジェクトの種類を指定します。 今回は Visual Studio 10 とします。VS8 や VS9 を利用している方はそれぞれ読み替えてください。
ダイアログで [Finish] をクリックすると Configure が始まります。問題がなければ下部のログウインドウに「Configuring done」と出力されますので、続けて [Generate] ボタンをクリックします。 「Generating done」と出ればプロジェクトファイル・ソリューションファイル等の出力が完了します。
なお、CMake は Configure の段階でキャッシュファイルを生成しますので、トラブルなどで設定を変更したり環境を変更した場合は [File] > [Delete Cache] でキャッシュを削除して Configure からやり直してください。
次に先ほど指定したbuild ディレクトリーの中の Flip.sln をダブルクリックして Visual Studio 2010 を起動します。
起動後、ソリューションエクスプローラーの「ALL_BUILD」を右クリックし「ビルド」を選択します。特に問題がなければ、正常にビルドが終了します。
Flip コンポーネントでは、InPort から受け取った画像を画像保存用バッファに保存し、その保存した画像を OpenCV の cvFlip() 関数にて変換します。 その後、変換された画像を OutPort から送信します。
onActivated()、onExecute()、onDeactivated() での処理内容を図22に示します。
onExecute() での処理を図23に示します。
OpenCV のライブラリを使用するため、OpenCV のインクルードファイルをインクルードします。
//OpenCV用インクルードファイルのインクルード #include<cv.h> #include<cxcore.h> #include<highgui.h>
この cvFlip コンポーネントでは、画像領域の確保、Flip 処理、確保した画像領域の解放のそれぞれの処理を行います。これらの処理は、それぞれ onActivated()、onDeactivated()、onExecute() のコールバック関数にて行います。
/*** * * 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_imageBuff; IplImage* m_flipImageBuff;
下記のように、onActivated()、onDeactivated()、onExecute()を実装します。
RTC::ReturnCode_t Flip::onActivated(RTC::UniqueId ec_id) { // イメージ用メモリーの初期化 m_imageBuff = NULL; m_flipImageBuff = NULL; // OutPort の画面サイズの初期化 m_flippedImage.width = 0; m_flippedImage.height = 0; return RTC::RTC_OK; } RTC::ReturnCode_t Flip::onDeactivated(RTC::UniqueId ec_id) { if(m_imageBuff != NULL) { // イメージ用メモリーの解放 cvReleaseImage(&m_imageBuff); cvReleaseImage(&m_flipImageBuff); } return RTC::RTC_OK; } RTC::ReturnCode_t Flip::onExecute(RTC::UniqueId ec_id) { // 新しいデータのチェック if (m_originalImageIn.isNew()) { // InPortデータの読み込み m_originalImageIn.read(); // InPort と OutPort の画面サイズ処理およびイメージ用メモリーの確保 if( m_originalImage.width != m_flippedImage.width || m_originalImage.height != m_flippedImage.height) { m_flippedImage.width = m_originalImage.width; m_flippedImage.height = m_originalImage.height; // InPort のイメージサイズが変更された場合 if(m_imageBuff != NULL) { cvReleaseImage(&m_imageBuff); cvReleaseImage(&m_flipImageBuff); } // イメージ用メモリーの確保 m_imageBuff = cvCreateImage(cvSize(m_originalImage.width, m_originalImage.height), IPL_DEPTH_8U, 3); m_flipImageBuff = cvCreateImage(cvSize(m_originalImage.width, m_originalImage.height), IPL_DEPTH_8U, 3); } // InPort の画像データを IplImageの imageData にコピー memcpy(m_imageBuff->imageData,(void *)&(m_originalImage.pixels[0]),m_originalImage.pixels.length()); // InPort からの画像データを反転する。 m_flipMode 0: X軸周り, 1: Y軸周り, -1: 両方の軸周り cvFlip(m_imageBuff, m_flipImageBuff, m_flipMode); // 画像データのサイズ取得 int len = m_flipImageBuff->nChannels * m_flipImageBuff->width * m_flipImageBuff->height; m_flippedImage.pixels.length(len); // 反転した画像データを OutPort にコピー memcpy((void *)&(m_flippedImage.pixels[0]),m_flipImageBuff->imageData,len); // 反転した画像データを OutPort から出力する。 m_flippedImageOut.write(); } return RTC::RTC_OK; }
このコンポーネントでは OpenCV を利用していますので、OpenCV のヘッダのインクルードパス、ライブラリーやライブラリサーチパスを与えてやる必要があります。幸い OpenCV は CMake に対応しており、以下の4行を追加するだけで OpenCV のライブラリーがリンクされ使えるようになります。
# check doxygen installed find_package(Doxygen) if(DOXYGEN_FOUND STREQUAL "NO") message(FATAL_ERROR "Doxygen not found.") endif()
# 以下の5行をCMakeList.txt に加える cmake_policy(SET CMP0015 NEW) find_package(OpenCV REQUIRED) list(APPEND INCLUDE_DIRS ${OpenCV_INCLUDE_DIRS}) list(APPEND LIBRARY_DIRS ${OpenCV_LIB_DIR}) list(APPEND LIBRARIES ${OpenCV_LIBS})
CMakeList.txt を編集したので、再度 CMake GUI で ConfigureおよびGenerate を行います。 CMakeのGenerate が正常に終了した事を確認し、Flip.sln ファイルをダブルクリックし、Visual C++ 2010 を起動します。
Visual C++ 2010 の起動後、図24のようにし、コンポーネントのビルドを行います。
ここでは、OpenRTM-aist-1.1 以降で同梱されるようになったカメラコンポーネント (OpenCVCameraComp、または DirectShowCamComp)とビューアコンポーネント (CameraViewerComp)を接続し動作確認を行います。
コンポーネントの参照を登録するためのネームサービスを起動します。
[スタート] > [すべてのプログラム] > [OpenRTM-aist] > [C++] > [tools] を選択し、「Start Naming Service」をクリックしてください。
&color(RED){※ 「Star Naming Service」をクリックしても omniNames が起動しない場合は、フルコンピューター名が14文字以内に設定されているかを確認してください。
RTコンポーネントでは、ネームサーバーのアドレスやネームサーバーへの登録フォーマットなどの情報を rtc.conf というファイルで指定する必要があります。
下記の内容を rtc.conf というファイル名で保存し、Flip\FlipComp\Debug(もしくは、Release)フォルダーに置いてください。
※ Eclipse 起動時に workspace をデフォルトのままにしていた場合、Flip フォルダーのパスは、
C:\Documents and Settings\<ログインユーザー名>\workspace となります。
corba.nameservers: localhost naming.formats: %n.rtc
Flip コンポーネントを起動します。
先程 rtc.conf ファイルを置いたフォルダーにある、FlipComp.exe ファイルを実行してください。 FlipComp.exe 画面が表示されます。
USB カメラのキャプチャー画像を OutPort から出力する OpenCVCameraComp または DirectShowCamComp コンポーネントと、InPort で受け取った画像を画面に表示する CameraViewerComp を起動します。
これら2つのコンポーネントは、下記の手順にて起動できます。
[スタート] > [すべてのプログラム] > [OpenRTM-aist] > [components] > [C++] > [examples] > [opencv-rtcs] を選択し、「OpenCVCameraComp」と「CameraViewerComp」をそれぞれクリックして実行します。 「OpenCVCameraComp」と「CameraViewerComp」画面が表示されます。
OpenCVCameraComp ではうまくカメラを認識しない場合があります。その場合は DirectShowCamComp を使用してみてください。
図25のように、RTSystemEditor にて USBCameraAqcuireComp、Flip、USBCameraMonitorComp コンポーネントを接続します。
RTSystemEditor の上部にあります「ALL」というアイコンをクリックし、全てのコンポーネントをアクティブ化します。正常にアクティベートされた場合、図26のように黄緑色でコンポーネントが表示されます。
図27のようにコンフィギュレーションビューにてコンフィギュレーションを変更することができます。
Flip コンポーネントのコンフィギュレーションパラメーター「flipMode」を「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", // Configuration variables "conf.default.flipMode", "1", // Widget "conf.__widget__.flipMode", "radio", // Constraints "conf.__constraints__.flip_mode", "(-1,0,1)", "" }; /*! * @brief constructor * @param manager Maneger Object */ Flip::Flip(RTC::Manager* manager) : RTC::DataFlowComponentBase(manager), m_originalImageIn("originalImage", m_originalImage), m_flippedImageOut("flippedImage", m_flippedImage) { } /*! * @brief destructor */ Flip::~Flip() { } RTC::ReturnCode_t Flip::onInitialize() { // Registration: InPort/OutPort/Service // Set InPort buffers addInPort("originalImage", m_originalImageIn); // Set OutPort buffer addOutPort("flippedImage", m_flippedImageOut); // Bind variables and configuration variable bindParameter("flipMode", m_flipMode, "1"); return RTC::RTC_OK; } RTC::ReturnCode_t Flip::onActivated(RTC::UniqueId ec_id) { // イメージ用メモリーの初期化 m_imageBuff = NULL; m_flipImageBuff = NULL; // OutPortの画面サイズの初期化 m_flippedImage.width = 0; m_flippedImage.height = 0; return RTC::RTC_OK; } RTC::ReturnCode_t Flip::onDeactivated(RTC::UniqueId ec_id) { if(m_imageBuff != NULL) { // イメージ用メモリーの解放 cvReleaseImage(&m_imageBuff); cvReleaseImage(&m_flipImageBuff); } return RTC::RTC_OK; } RTC::ReturnCode_t Flip::onExecute(RTC::UniqueId ec_id) { // 新しいデータのチェック if (m_originalImageIn.isNew()) { // InPortデータの読み込み m_originalImageIn.read(); // InPort と OutPort の画面サイズ処理およびイメージ用メモリーの確保 if( m_originalImage.width != m_flippedImage.width || m_originalImage.height != m_flippedImage.height) { m_flippedImage.width = m_originalImage.width; m_flippedImage.height = m_originalImage.height; // InPort のイメージサイズが変更された場合 if(m_imageBuff != NULL) { cvReleaseImage(&m_imageBuff); cvReleaseImage(&m_flipImageBuff); } // イメージ用メモリーの確保 m_imageBuff = cvCreateImage(cvSize(m_originalImage.width, m_originalImage.height), IPL_DEPTH_8U, 3); m_flipImageBuff = cvCreateImage(cvSize(m_originalImage.width, m_originalImage.height), IPL_DEPTH_8U, 3); } // InPortの画像データを IplImage の imageData にコピー memcpy(m_imageBuff->imageData,(void *)&(m_originalImage.pixels[0]),m_originalImage.pixels.length()); // InPort からの画像データを反転する。 m_flipMode 0: X軸周り, 1: Y軸周り, -1: 両方の軸周り cvFlip(m_imageBuff, m_flipImageBuff, m_flipMode); // 画像データのサイズ取得 int len = m_flipImageBuff->nChannels * m_flipImageBuff->width * m_flipImageBuff->height; m_flippedImage.pixels.length(len); // 反転した画像データをOutPortにコピー memcpy((void *)&(m_flippedImage.pixels[0]),m_flipImageBuff->imageData,len); // 反転した画像データをOutPortから出力する。 m_flippedImageOut.write(); } return RTC::RTC_OK; } extern "C" { void FlipInit(RTC::Manager* manager) { coil::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> #include <rtm/idl/ExtendedDataTypesSkel.h> #include <rtm/idl/InterfaceDataTypesSkel.h> //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 /*! * * - Name: flipMode * - DefaultValue: 1 */ int m_flipMode; // DataInPort declaration CameraImage m_originalImage; /*! */ InPort<CameraImage> m_originalImageIn; // DataOutPort declaration CameraImage m_flippedImage; /*! */ OutPort<CameraImage> m_flippedImageOut; private: // 処理画像用バッファ IplImage* m_imageBuff; IplImage* m_flipImageBuff; }; extern "C" { DLL_EXPORT void FlipInit(RTC::Manager* manager); }; #endif // FLIP_H
Flip コンポーネントの全ソースコードを以下に添付します。
このケーススタディでは、簡単な画像処理をコンポーネント化する方法を紹介します。 既存のカメラコンポーネントと画像表示コンポーネントを利用し、カメラからの画像を左右(または上下)に反転させる処理部分をコンポーネントとして作成してカメラの画像を反転させ表示するシステムを作成します。
画像を反転する処理は簡単に実装することができますが、ここではより簡単に実装するために OpenCV ライブラリを利用し、より汎用性の高い RTコンポーネントを作成します。
OpenCV (オープンシーブイ) とはかつてインテルが、現在は Itseez が開発・公開しているオープンソースのコンピュータービジョン向けライブラリです。
Wikipediaより抜粋。
入力された画像を左右または上下に反転し出力する RTコンポーネントを、OpenCV ライブラリの cv::flip 関数を利用して作成します。作成および実行環境は Ubuntu上 の Code::Blocks (gccコンパイラ) を想定しています。 対象 OpenRTM-aist のバージョンは 1.1.2 です。
作成手順はおおよそ以下のようになります。
cv::flip 関数は、OpenCV で標準的に用いられている cv::Mat型の画像データを垂直軸 (左右反転)、水平軸 (上下反転)、または両軸 (上下左右反転)に対して反転させます。 関数プロトタイプと入出力の引数の意味は以下の通りです。
void flip(const Mat& src, Mat& dst, int flipMode) src 入力配列 dst 出力配列。もし dst=NULL であれば、src が上書きされます。 flipMode 配列の反転方法の指定内容: flipMode = 0: X軸周りでの反転(上下反転) flipMode > 0: Y軸周りでの反転(左右反転) flipMode < 0: 両軸周りでの反転(上下左右反転)
これから作成するコンポーネントを Flip コンポーネントと呼ぶことにします。
このコンポーネントは画像データ型の入力ポート (InPort) と、反転処理した画像を出力するための出力ポート (OutPort) を持ちます。 それぞれのポートの名前を 入力ポート(InPort)名: originalImage、出力ポート(OutPort)名: flippedImage とします。
OpenRTM-aist には、OpenCV を使用したビジョン関連のコンポーネントがサンプルとして付属しています。 これらのコンポーネントのデータポートは画像の入出力に以下のような CameraImage 型を使用しています。
struct CameraImage { /// Time stamp. Time tm; /// Image pixel width. unsigned short width; /// Image pixel height. unsigned short height; /// Bits per pixel. unsigned short bpp; /// Image format (e.g. bitmap, jpeg, etc.). string format; /// Scale factor for images, such as disparity maps, /// where the integer pixel value should be divided /// by this factor to get the real pixel value. double fDiv; /// Raw pixel data. sequence<octet> pixels; };
この Flip コンポーネントではこれらのサンプルコンポーネントとデータのやり取りができるよう同じく CameraImage 型を InPort と OutPort に使用することにします。 CameraImage 型は InterfaceDataTypes.idl で定義されており、C++であれば、InterfaceDataTypesSkel.h をインクルードすると使えるようになります。
また、画像を反転させる方向は、左右反転、上下反転、上下左右反転の3通りがあります。これを実行時に指定できるように、RTコンポーネントのコンフィギュレーション機能を使用して指定できるようにします。 パラメーター名は flipMode という名前にします。
flipMode は cv::flip 関数の仕様に合わせて、型は int 型とし 上下反転、左右反転、上下左右反転それぞれに 0、1、-1 を割り当てることにします。
flipMode の各値での画像処理のイメージを下図に示します。
以上から Flip コンポーネントの仕様をまとめます。
コンポーネント名称 | Flip |
InPort | |
ポート名 | originalImage |
型 | CameraImage |
説明 | 入力画像 |
OutPort | |
ポート名 | flippedImage |
型 | CameraImage |
説明 | 反転された画像 |
Configuration | |
パラメーター名 | flipMode |
型 | int |
デフォルト値 | 0 |
制約 | (0,-1,1) |
Widget | radio |
説明 | 反転モード 上下反転: 0 左右反転: 1 上下左右反転: -1 |
Linux (ここでは Ubuntu 16.04 を仮定) 上に開発環境を構築します。
インストールスクリプトでインストールします。
$ wget http://svn.openrtm.org/OpenRTM-aist/tags/RELEASE_1_1_1/OpenRTM-aist/build/pkg_install_ubuntu.sh $ sudo sh pkg_install_ubuntu.sh
こちらのURL からLinux版の OpenRTP(コンポーネント開発ツール、システム開発ツール統合環境) をダウンロード、インストールします。 OepnRTP の実行には Java も必要となりますので default-jre パッケージをインストールします。
$ apt-get install default-jre $ wget http://openrtm.org/pub/openrtp/packages/1.1.2.v20160526/eclipse442-openrtp112v20160526-ja-linux-gtk-x86_64.tar.gz $ tar xvzf eclipse442-openrtp112v20160526-ja-linux-gtk-x86_64.tar.gz
Eclipse 起動後、RTSystemEditor でネームサーバーに接続できない場合があります。その場合、/etc/hosts の localhost の行に自ホスト名を追記してください。
$ hostname ubuntu1404 ← ホスト名は ubuntu1404 $ sudo vi /etc/hosts
127.0.0.1 localhost を以下のように変更 127.0.0.1 localhost ubuntu1404
$ sudo apt-get install cmake cmake-gui
OpenCV および OpenCV のコンポーネントをインストールします。
まず、Ubuntu が提供している OpenCV のパッケージを下記のようにインストールします。
$ sudo apt-get install libopencv-dev libcv2.4 libcvaux2.4 libhighgui2.4
リポジトリからソースコードをチェックアウトして、手動でコンパイルを行います。
$ svn co http://svn.openrtm.org/ImageProcessing/trunk/ImageProcessing/opencv/ $ cd opencv $ mkdir work $ cd work $ cmake .. $ make $ sudo make install AffineComp FlipComp RockPaperScissorsComp Affine.so Flip.so RockPaperScissors.so BackGroundSubtractionSimpleComp HistogramComp RotateComp BackGroundSubtractionSimple.so Histogram.so Rotate.so BinarizationComp HoughComp ScaleComp Binarization.so Hough.so Scale.so CameraViewerComp ImageCalibrationComp SepiaComp CameraViewer.so ImageCalibration.so Sepia.so ChromakeyComp ImageSubstractionComp SubStractCaptureImageComp Chromakey.so ImageSubstraction.so SubStractCaptureImage.so DilationErosionComp ObjectTrackingComp TemplateComp DilationErosion.so ObjectTracking.so Template.so EdgeComp OpenCVCameraComp TranslateComp Edge.so OpenCVCamera.so Translate.so FindcontourComp PerspectiveComp Findcontour.so Perspective.so
Code::Blocks は C/C++ に対応した統合開発環境です。 以下のコマンドでインストールできます。
$ sudo apt-get install codeblocks
最新版を入手したい場合は以下のコマンドを入力します。
$ sudo add-apt-repository ppa:damien-moore/codeblocks-stable $ sudo apt-get update $ sudo apt-get install codeblocks
Flip コンポーネントの雛型の生成は、RTCBuilder を用いて行います。
Eclipse では、各種作業を行うフォルダーを「ワークスペース」(Work Space)とよび、原則としてすべての生成物はこのフォルダーの下に保存されます。 ワークスペースはアクセスできるフォルダーであれば、どこに作っても構いませんが、このチュートリアルでは以下のワークスペースを仮定します。
まずは Eclipse を起動します。 OpenRTP を展開したディレクトリーに移動して以下のコマンドを入力します。
$ ./openrtp
最初にワークスペースの場所を尋ねられますので、上記のワークスペースを指定してください。
すると、以下のような「ようこそ」画面が表示されます。
「ようこそ」画面は必要ないので左上の [×] ボタンをクリックして閉じてください。
右上の [Open Perspective] ボタンをクリックしてください。
「RTC Builder」を選択し、[OK] ボタンをクリックすることで、RTCBuilder が起動します。
Flip コンポーネントを作成するために、RTCBuilder で新規プロジェクトを作成する必要があります。
左上の [Open New RTCBuilder Editor] のアイコンをクリックしてください。
「プロジェクト名」欄に作成するプロジェクト名 (ここでは Flip) を入力して [終了] をクリックします。
指定した名称のプロジェクトが生成され、パッケージエクスプローラ内に追加されます。
生成したプロジェクト内には、デフォルト値が設定された RTC プロファイル XML(RTC.xml) が自動的に生成されます。
RTC.xml が生成された時点で、このプロジェクトに関連付けられているワークスペースとして RTCBuilder のエディタが開くはずです。 もし起動しない場合はパッケージエクスプローラーの RTC.xml をダブルクリックしてください。
まず、いちばん左の「基本」タブを選択し、基本情報を入力します。先ほど決めた Flip コンポーネントの仕様(名前)の他に、概要やバージョン等を入力してください。 ラベルが赤字の項目は必須項目です。その他はデフォルトで構いません。
次に、「アクティビティ」タブを選択し、使用するアクションコールバックを指定します。
Flip コンポーネントでは、onActivated()、onDeactivated()、onExecute() コールバックを使用します。下図のように①の onAtivated をクリック後に ②のラジオボタンにて [ON] にチェックを入れます。 onDeactivated、onExecute についても同様の操作を行います。
さらに、「データポート」タブを選択し、データポートの情報を入力します。 先ほど決めた仕様を元に以下のように入力します。なお、変数名や表示位置はオプションなので変更しないで結構です。
次に、「コンフィギュレーション」タブを選択し、先ほど決めた仕様を元に、Configuration の情報を入力します。制約条件および Widget とは、RTSystemEditor でコンポーネントのコンフィギュレーションパラメーターを表示する際に、スライダー、スピンボタン、ラジオボタンなど、GUIで値の変更を行うためのものです。
ここでは、flipMode が取りうる値は先ほど仕様を決めたときに、-1、0、1 の3つの値のみ取ることにしたので、ラジオボタンを使用することにします。
次に、「言語・環境」タブを選択し、プログラミング言語を選択します。 ここでは、C++(言語)を選択します。なお、言語・環境はデフォルト等が設定されておらず、指定し忘れるとコード生成時にエラーになりますので、必ず言語の指定を行うようにしてください。
また、C++ の場合デフォルトでは CMake を利用してビルドすることになっていますが、旧式の VC のプロジェクトやソリューションを直接 RTCBuilder が生成する方法を利用したい場合は [Use old build environment] をチェックしてください。
最後に、「基本」タブにある [コード生成] ボタンをクリックし、コンポーネントの雛型を生成します。
※ 生成されるコード群は、Eclipse 起動時に指定したワークスペースフォルダーの中に生成されます。 現在のワークスペースは、[ファイル] > [ワークスペースの切り替え] で確認することができます。
RTC Builder で生成したコードの中には CMake でビルドに必要な各種ファイルを生成するための CMakeLists.txt が含まれています。 CMake を利用することにより CMakeLists.txt から Visual Studio のプロジェクトファイル、ソリューションファイル、もしくは Makefile 等を自動生成できます。
src/CMakeLists.txt を gedit などで開いて編集します。 もしくは Eclipse 画面左のパッケージエクスプローラで src/CMakeLists.txt をダブルクリックもしくはエディタへドラッグアンドドロップしても編集できます。
このコンポーネントでは OpenCV を利用していますので、OpenCV のヘッダのインクルードパス、ライブラリやライブラリサーチパスを与えてやる必要があります。 幸い OpenCV は CMake に対応しており、以下の2行を追加・変更するだけで OpenCV のライブラリがリンクされ使えるようになります。
set(comp_srcs Flip.cpp ) set(standalone_srcs FlipComp.cpp) find_package(OpenCV REQUIRED) ←この行を追加 :中略 add_dependencies(${PROJECT_NAME} ALL_IDL_TGT) target_link_libraries(${PROJECT_NAME} ${OPENRTM_LIBRARIES} ${OpenCV_LIBS}) ← OepnCV_LIBSを追加 :中略 add_executable(${PROJECT_NAME}Comp ${standalone_srcs} ${comp_srcs} ${comp_headers} ${ALL_IDL_SRCS}) target_link_libraries(${PROJECT_NAME}Comp ${OPENRTM_LIBRARIES} ${OpenCV_LIBS}) ← OepnCV_LIBSを追加
CMake を利用してビルド環境の Configure を行います。 まずは CMake (cmake-gui) を起動してください。
$ cmake-gui
画面上部に以下のようなテキストボックスがありますので、それぞれソースコードの場所 (CMakeList.txtがある場所) と、ビルドディレクトリーを指定します。
ソースコードの場所は Flip コンポーネントのソースが生成された場所で CMakeList.txt が存在するディレクトリーです。 デフォルトでは <ワークスペースディレクトリー>/Flip になります。
ビルドディレクトリとは、ビルドするためのプロジェクトファイルやオブジェクトファイル、バイナリを格納する場所のことです。 場所は任意ですが、この場合 <ワークスペースディレクトリー>/Flip/build のように分かりやすい名前をつけた Flip のサブディレクトリーを指定することをお勧めします。
Where is the soruce code | /home/ユーザー名/workspace/Flip |
Where to build the binaries | /home/ユーザー名/workspace/Flip/build |
指定したら、下のConfigureボタンを押します。すると下図のようなダイアログが表示されますので、生成したいプロジェクトの種類を指定します。 今回は CodeBlocks - Unix Makefiles を指定します。 Code::Blocks を使わない場合は Unix Makefiles を使ってください。
また cmake-gui を使用しない場合は以下のコマンドでファイルを生成できます。
$ mkdir build $ cd build $ cmake .. -G "CodeBlocks - Unix Makefiles"
ダイアログで [Finish] をクリックすると Configure が始まります。問題がなければ下部のログウインドウに「Configuring done」と表示されますので、続けて [Generate] ボタンをクリックします。 「Generating done」と表示されればプロジェクトファイル(.cbp)・Makefile 等の出力が完了します。
なお、CMake は Configure の段階でキャッシュファイルを生成しますので、トラブルなどで設定を変更したり環境を変更した場合は [File] > [Delete Cache] でキャッシュを削除して Configure からやり直してください。
次に先ほど指定した build ディレクトリーの中の Flip.cbp をダブルクリックして Code::Blocks を起動します。
ヘッダ (include/Flip/Flip.h) およびソースコード (src/Flip.cpp) をそれぞれ編集します。 Code::Blocks の Projects から Flip.h、Flip.cpp をダブルクリックすることで編集画面が開きます。
64bitの環境の場合に Code::Blocks の動作が不安定になることがあります。その場合は code completion というプラグインを無効化すると動作することがあります。
「Plugins」>「Manage plugins...」を選択します。
「code completion」を選択して [Disable] をクリックします。
動作しないときはこの手順を試してください。
Flip コンポーネントでは、InPort から受け取った画像を画像保存用バッファに保存し、その保存した画像を OpenCVのcv::flip() 関数にて変換します。 その後、変換された画像を OutPort から送信します。
onActivated()、onExecute()、onDeactivated() での処理内容を下図に示します。
onExecute() での処理を下図に示します。
OpenCV のライブラリを使用するため、OpenCV のインクルードファイルをインクルードします。
//OpenCV用インクルードファイルのインクルード #include <opencv2/opencv.hpp>
反転した画像の保存用にメンバー変数を追加します。
private: // <rtc-template block="private_attribute"> // </rtc-template> // <rtc-template block="private_operation"> // </rtc-template> cv::Mat m_imageBuff; cv::Mat m_flipImageBuff;
下記のように、onActivated()、onDeactivated()、onExecute() を実装します。
RTC::ReturnCode_t Flip::onActivated(RTC::UniqueId ec_id) { // OutPortの画面サイズの初期化 m_flippedImage.width = 0; m_flippedImage.height = 0; return RTC::RTC_OK; }
RTC::ReturnCode_t Flip::onDeactivated(RTC::UniqueId ec_id) { if (!m_imageBuff.empty()) { // 画像用メモリの解放 m_imageBuff.release(); m_flipImageBuff.release(); } return RTC::RTC_OK; }
RTC::ReturnCode_t Flip::onExecute(RTC::UniqueId ec_id) { // 新しいデータのチェック if (m_originalImageIn.isNew()) { // InPortデータの読み込み m_originalImageIn.read(); // InPortとOutPortの画面サイズ処理およびイメージ用メモリの確保 if (m_originalImage.width != m_flippedImage.width || m_originalImage.height != m_flippedImage.height) { m_flippedImage.width = m_originalImage.width; m_flippedImage.height = m_originalImage.height; m_imageBuff.create(cv::Size(m_originalImage.width, m_originalImage.height), CV_8UC3); m_flipImageBuff.create(cv::Size(m_originalImage.width, m_originalImage.height), CV_8UC3); } // InPortの画像データをm_imageBuffにコピー memcpy(m_imageBuff.data, (void *)&(m_originalImage.pixels[0]), m_originalImage.pixels.length()); // InPortからの画像データを反転する。 m_flipMode 0: X軸周り, 1: Y軸周り, -1: 両方の軸周り cv::flip(m_imageBuff, m_flipImageBuff, m_flipMode); // 画像データのサイズ取得 int len = m_flipImageBuff.channels() * m_flipImageBuff.cols * m_flipImageBuff.rows; m_flippedImage.pixels.length(len); // 反転した画像データをOutPortにコピー memcpy((void *)&(m_flippedImage.pixels[0]), m_flipImageBuff.data, len); // 反転した画像データをOutPortから出力する。 m_flippedImageOut.write(); } return RTC::RTC_OK; }
Code::Blocksの [ビルド] ボタンをクリックしてビルドを行います。
ここでは、カメラコンポーネント (OpenCVCameraComp)とビューアコンポーネント (CameraViewerComp)を接続し動作確認を行います。
コンポーネントの参照を登録するためのネームサービスを起動します。
$ rtm-naming
RTコンポーネントでは、ネームサーバーのアドレスやネームサーバーへの登録フォーマットなどの情報を rtc.conf というファイルで指定する必要があります。
下記の内容を rtc.conf というファイル名で保存し、Flip\build\srcフォルダーに置いて下さい。
※ Eclipse 起動時に workspace をデフォルトのままにしていた場合、Flip フォルダーのパスは、 C:\Documents and Settings\<ログインユーザー名>\workspace となります。
corba.nameservers: localhost naming.formats: %n.rtc
Flip コンポーネントを起動します。
先程 rtc.conf ファイルを置いたフォルダにある、FlipComp ファイルを実行してください。
$ FlipComp -f rtc.conf
USBカメラのキャプチャー画像を OutPort から出力する OpenCVCameraComp と InPort で受け取った画像を画面に表示する CameraViewerComp を起動します。
これら2つのコンポーネントは、下記のコマンドにて起動できます。
$ /usr/local/share/openrtm-1.1/components/c++/opencv-rtcs/CameraViewerComp $ /usr/local/share/openrtm-1.1/components/c++/opencv-rtcs/OpenCVCameraComp
下図のように、RTSystemEditor にて OpenCVCameraComp と Flip、CameraviewerComp コンポーネントを接続します。
RTSystemEditor の上部にあります [All Activate] というアイコンをクリックし、全てのコンポーネントをアクティブ化します。 正常にアクティベートされた場合、下図のように黄緑色でコンポーネントが表示されます。
下図のようにコンフィギュレーションビューにてコンフィギュレーションを変更することができます。
Flipコンポーネントのコンフィギュレーションパラメーター「flipMode」を「0」や「-1」などに変更し、画像の反転が行われるかを確認してください。
このケーススタディでは、簡単な画像処理をコンポーネント化する方法を紹介します。既存のカメラコンポーネントと画像表示コンポーネントを利用し、カメラからの画像を左右 (または上下) に反転させる処理部分をコンポーネントとして作成してカメラの画像を反転させ表示するシステムを作成します。
画像を反転する処理は簡単に実装することができますが、ここではさらに簡単に実装するため OpenCV ライブラリを利用しより汎用性の高い RTコンポーネントを作成します。
OpenCV とはかつてインテルが、現在は Willow Garage が開発・公開しているオープンソースのコンピュータービジョン向けライブラリです。
Wikipediaより抜粋。
入力された画像を左右または上下に反転し出力する RTコンポーネントを、OpenCV ライブラリの cvFlip 関数を利用して作成します。 作成および実行環境は Windows上の Visual C++ を想定しています。対象 OpenRTM-aist のバージョンは 1.1 です。
作成手順はおおよそ以下のようになります。
Linux (ここでは Ubuntu 14.04 を仮定) 上に開発環境を構築します。
Choreonoid を利用する場合、以下のように PPA から OpenRTM-aistを インストールしてください。 一括インストールスクリプトをすでに実行している場合、/etc/apt/sources.list の下の方に追記されている openrtm.org のリポジトリをコメントアウトした上で、 apt-get update してください。
$ sudo add-apt-repository ppa:hrg/daily $ sudo apt-get update $ sudo apt-get install openrtm-aist openrtm-aist-dev doxygen
こちらのURL から Linux版の OpenRTP (コンポーネント開発ツール、システム開発ツール統合環境) をダウンロード、インストールします。 OepnRTP の実行には Java も必要となりますので default-jre パッケージをインストールします。
$ apt-get install default-jre $ wget http://openrtm.org/pub/openrtp/packages/1.1.0.rc5v20150317/eclipse381-openrtp110rc5v20150317-ja-linux-gtk-x86_64.tar.gz $ tar xvzf eclipse381-openrtp110rc5v20150317-ja-linux-gtk-x86_64.tar.gz $ cd eclipse $ ./openrtp
eclipse 起動後、RTSystemEditor でネームサーバーに接続できない場合があります。その場合、/etc/hosts の localhost の行に自ホスト名を追記してください。
$ hostname ubuntu1404 ← ホスト名は ubuntu1404 $ sudo vi /etc/hosts
127.0.0.1 localhost を以下のように変更 127.0.0.1 localhost ubuntu1404
OpenCV および OpenCV のコンポーネントをインストールします。
まず、Ubuntu が提供している OpenCV のパッケージを下記のようにインストールします。
$ sudo apt-get install libopencv-dev libcv2.4 libcvaux2.4 libhighgui2.4
OpenCV RTC のパッケージは下記 URL にありますので、手動でダウンロードして、dpkg コマンドでインストールします。
$ wget http://openrtm.org/pub/Linux/ubuntu/dists/trusty/main/binary-amd64/imageprocessing-1.1.0.deb $ sudo dpkg -i imageprocessing-1.1.0.deb $ ls /usr/share/openrtm-1.1/components/c++/opencv-rtcs/ AffineComp FlipComp RockPaperScissorsComp Affine.so Flip.so RockPaperScissors.so BackGroundSubtractionSimpleComp HistogramComp RotateComp BackGroundSubtractionSimple.so Histogram.so Rotate.so BinarizationComp HoughComp ScaleComp Binarization.so Hough.so Scale.so CameraViewerComp ImageCalibrationComp SepiaComp CameraViewer.so ImageCalibration.so Sepia.so ChromakeyComp ImageSubstractionComp SubStractCaptureImageComp Chromakey.so ImageSubstraction.so SubStractCaptureImage.so DilationErosionComp ObjectTrackingComp TemplateComp DilationErosion.so ObjectTracking.so Template.so EdgeComp OpenCVCameraComp TranslateComp Edge.so OpenCVCamera.so Translate.so FindcontourComp PerspectiveComp Findcontour.so Perspective.so
cvFlip 関数は、OpenCV で標準的に用いられている IplImage型の画像データを垂直軸 (左右反転)、水平軸 (上下反転)、または両軸 (上下左右反転) に対して反転させます。関数プロトタイプと入出力の引数の意味は以下の通りです。
void cvFlip(IplImage* src, IplImage* dst=NULL, int flipMode=0); #define cvMirror cvFlip src 入力配列 dst 出力配列。もしdst=NULLであれば、srcが上書きされます。 flipMode 配列の反転方法の指定内容: flipMode = 0: X軸周りでの反転(上下反転) flipMode > 0: Y軸周りでの反転(左右反転) flipMode < 0: 両軸周りでの反転(上下左右反転)
これから作成するコンポーネントを Flip コンポーネントと呼ぶことにします。
このコンポーネントは画像データ型の入力ポート (InPort) と、反転処理した画像を出力するための出力ポート (OutPort) を持ちます。 それぞれのポートの名前を 入力ポート(InPort)名: originalImage, 出力ポート(OutPort)名: flippedImage とします。
OpenRTM-aist には OpenCV を使用したビジョン関連のコンポーネントがサンプルとして付属しています。 これらのコンポーネントのデータポートは画像の入出力に以下のような CameraImage 型を使用しています。
struct CameraImage { /// Time stamp. Time tm; /// Image pixel width. unsigned short width; /// Image pixel height. unsigned short height; /// Bits per pixel. unsigned short bpp; /// Image format (e.g. bitmap, jpeg, etc.). string format; /// Scale factor for images, such as disparity maps, /// where the integer pixel value should be divided /// by this factor to get the real pixel value. double fDiv; /// Raw pixel data. sequence<octet> pixels; };
このFlipコンポーネントではこれらのサンプルコンポーネントとデータのやり取りができるよう同じく CameraImage型を InPort と OutPort に使用することにします。 CameraImage型は InterfaceDataTypes.idl で定義されており、C++であれば、InterfaceDataTypesSkel.h をインクルードすると使えるようになります。
また、画像を反転させる方向は、左右反転、上下反転、上下左右反転の3通りがあります。これを実行時に指定できるように、RTコンポーネントのコンフィギュレーション機能を使用して指定できるようにします。 パラメーター名は flipMode という名前にします。
flipMode は cvFlip 関数の仕様に合わせて、型は int 型とし 上下反転、左右反転、上下左右反転それぞれに 0、1、-1 を割り当てることにします。
flipModeの各値での画像処理のイメージを下図に示します。
以上から Flip コンポーネントの仕様をまとめます。
コンポーネント名称 | Flip |
InPort | |
ポート名 | originalImage |
型 | CameraImage |
意味 | 入力画像 |
OutPort | |
ポート名 | flippedImage |
型 | CameraImage |
意味 | 反転された画像 |
Configuration | |
パラメーター名 | flipMode |
型 | int |
意味 | 反転モード 上下反転: 0 左右反転: 1 上下左右反転: -1 |
ここで動作環境および開発環境を確認しておきます。
OpenRTM-aist-1.1 以降では、コンポーネントのビルドに CMake を使用します。また、RTC のひな形生成ツール RTCBuilder では、ドキュメントを入力してこれを Doxygen に処理させることで、コンポーネントのマニュアルも自動で生成することができるようになっています。 このため CMakeで Configure を行うときに Doxygen が要求されるため、予めインストールしておく必要があります。
Flip コンポーネントの雛型の生成は、RTCBuilder を用いて行います。
Eclipse では、各種作業を行うフォルダーを「ワークスペース」(Work Space)とよび、原則としてすべての生成物はこのフォルダーの下に保存されます。 ワークスペースはアクセスできるフォルダーであれば、どこに作っても構いませんが、このチュートリアルでは以下のワークスペースを仮定します。
Eclipse を起動すると、ワークスペースの場所を尋ねられます。Eclipse を最初に起動したとき、または -clean オプション付きで起動した場合は、デフォルトで上記のディレクトリーが指定されますので、そのまま [OK] ボタンをクリックしてください。以下のような「ようこそ」画面が表示されます。 「ようこそ」画面はいまは必要ないので左上の「×」ボタンをクリックして閉じてください。
右上の [Open Perspective] ボタンをクリックし、プルダウンの「Other…」を選択します。
「RTC Builder」を選択することで、RTCBuilder が起動します。メニューバーに「カナヅチとRT」の RTCBuilder のアイコンが表示されます。
Flip コンポーネントを作成するために、RTCBuilder で新規プロジェクトを作成する必要があります。プロジェクトを作成する方法は2種類あります。
どちらの方法でも、次ようなプロジェクト作成ウィザードが開始されます。 「プロジェクト名」欄に作成するプロジェクト名 (ここでは Flip) を入力して [完了] ボタンをクリックします。
指定した名称のプロジェクトが生成され、パッケージエクスプローラー内に追加されます。
生成したプロジェクト内には、デフォルト値が設定された RTC プロファイル XML(RTC.xml) が自動的に生成されます。
RTC.xml が生成された時点で、このプロジェクトに関連付けられているワークスペースとして RTCBuilder のエディタが開くはずです。 もし開かない場合は、ツールバーの「Open New RtcBuilder Editor」ボタンをクリックするか、メニューバーの [ファイル] > [Open New Builder Editor] を選択します。
まず、いちばん左の「基本」タブを選択し、基本情報を入力します。先ほど決めた Flip コンポーネントの仕様(名前)の他に、概要やバージョン等を入力してください。 ラベルが赤字の項目は必須項目です。その他はデフォルトで構いません。
次に、「アクティビティ」タブを選択し、使用するアクションコールバックを指定します。
Flip コンポーネントでは、onActivated()、onDeactivated()、onExecute() コールバックを使用します。下図のように①の onAtivated をクリック後に ②のラジオボタンにて [ON] にチェックを入れます。 onDeactivated、onExecute についても同様の操作を行います。
さらに、「データポート」タブを選択し、データポートの情報を入力します。 先ほど決めた仕様を元に以下のように入力します。なお、変数名や表示位置はオプションで、そのままで結構です。
次に、「コンフィギュレーション」タブを選択し、先ほど決めた仕様を元に、Configuration の情報を入力します。 制約条件および Widget とは、RTSystemEditor でコンポーネントのコンフィギュレーションパラメーターを表示する際に、スライダー、スピンボタン、ラジオボタンなど、GUI で値の変更を行うためのものです。
ここでは、flipMode が取りうる値は先ほど仕様を決めたときに、-1、0、1 の3つの値のみ取ることにしたので、ラジオボタンを使用することにします。
次に、「言語・環境」タブを選択し、プログラミング言語を選択します。ここでは、C++(言語)を選択します。 なお、言語・環境はデフォルト等が設定されておらず、指定し忘れるとコード生成時にエラーになりますので、必ず言語の指定を行うようにしてください。
また、C++の場合デフォルトでは CMake を利用してビルドすることになっていますが、旧式の VC のプロジェクトやソリューションを直接 RTCBuilder が生成する方法を利用したい場合は [Use old build environment] をチェックしてください。
最後に、「基本」タブにある [コード生成] ボタンをクリックし、コンポーネントの雛型を生成します。
※ 生成されるコード群は、Eclipse 起動時に指定したワークスペースフォルダーの中に生成されます。 現在のワークスペースは、[ファイル] > [ワークスペースの切り替え] で確認することができます。
さて、ここまでで Flip コンポーネントのソースコードが生成されました。処理の中身は実装されていないので、InPort に画像を入力しても何も出力されませんが、生成直後のソースコードだけでもコンパイルおよび実行はできます。
※サービスポートとプロバイダを持つコンポーネントの場合、実装を行わないとビルドが通らないものもあります。
では、まず CMake を利用してビルド環境の Configure を行います。Linux であれば、Flip コンポーネントのソースが生成されたディレクトリーで
$ cd workspace/Flip $ mkdir build $ cd build $ cmake .. $ make
とすれば、Configure およびビルドが完了するはずです。ビルド終了後、からの FlipComp を起動してみましょう。
$ cd src $ ./FlipComp
起動後、RTSystemEditor などでアクセスしてみてください。Flip というコンポーネントが表示されているはずです。 カメラコンポーネント (OpenCVCameraComp) や表示コンポーネント (CameraViewerComp) などとも接続できますが、中で何も処理を行っていないでの、表示コンポーネントには何も表示されません。
次からは、コンポーネントのソースコードを作成し、中身を実装していきます。
ヘッダ (include/Flip/Flip.h) およびソースコード (src/Flip.cpp) をそれぞれ編集します。 Eclipse 画面左のパッケージエクスプローラーでそれぞれのファイルをダブルクリックすると、通常は Visual C++ の編集画面が開きますので、そこで編集します。 Eclipse の中央のエディタにドラッグアンドドロップしても編集できます。
Flip コンポーネントでは、InPort から受け取った画像を画像保存用バッファに保存し、その保存した画像を OpenCV の cvFlip()関数にて変換します。 その後、変換された画像を OutPort から送信します。
onActivated()、onExecute()、onDeactivated() での処理内容を下図に示します。
onExecute() での処理を下図に示します。
OpenCV のライブラリを使用するため、OpenCV のインクルードファイルをインクルードします。
//OpenCV用インクルードファイルのインクルード #include<cv.h> #include<cxcore.h> #include<highgui.h>
この cvFlip コンポーネントでは、画像領域の確保、Flip 処理、確保した画像領域の解放のそれぞれの処理を行います。 これらの処理は、それぞれ onActivated()、onDeactivated()、onExecute() のコールバック関数にて行います。
/*** * * 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_imageBuff; IplImage* m_flipImageBuff;
下記のように、onActivated()、onDeactivated()、onExecute()を実装します。
RTC::ReturnCode_t Flip::onActivated(RTC::UniqueId ec_id) { // イメージ用メモリーの初期化 m_imageBuff = NULL; m_flipImageBuff = NULL; // OutPortの画面サイズの初期化 m_flippedImage.width = 0; m_flippedImage.height = 0; return RTC::RTC_OK; } RTC::ReturnCode_t Flip::onDeactivated(RTC::UniqueId ec_id) { if(m_imageBuff != NULL) { // イメージ用メモリーの解放 cvReleaseImage(&m_imageBuff); cvReleaseImage(&m_flipImageBuff); } return RTC::RTC_OK; } RTC::ReturnCode_t Flip::onExecute(RTC::UniqueId ec_id) { // 新しいデータのチェック if (m_originalImageIn.isNew()) { // InPortデータの読み込み m_originalImageIn.read(); // InPort と OutPort の画面サイズ処理およびイメージ用メモリーの確保 if( m_originalImage.width != m_flippedImage.width || m_originalImage.height != m_flippedImage.height) { m_flippedImage.width = m_originalImage.width; m_flippedImage.height = m_originalImage.height; // InPort のイメージサイズが変更された場合 if(m_imageBuff != NULL) { cvReleaseImage(&m_imageBuff); cvReleaseImage(&m_flipImageBuff); } // イメージ用メモリーの確保 m_imageBuff = cvCreateImage(cvSize(m_originalImage.width, m_originalImage.height), IPL_DEPTH_8U, 3); m_flipImageBuff = cvCreateImage(cvSize(m_originalImage.width, m_originalImage.height), IPL_DEPTH_8U, 3); } // InPort の画像データを IplImage の imageData にコピー memcpy(m_imageBuff->imageData,(void *)&(m_originalImage.pixels[0]),m_originalImage.pixels.length()); // InPort からの画像データを反転する。 m_flipMode 0: X軸周り, 1: Y軸周り, -1: 両方の軸周り cvFlip(m_imageBuff, m_flipImageBuff, m_flipMode); // 画像データのサイズ取得 int len = m_flipImageBuff->nChannels * m_flipImageBuff->width * m_flipImageBuff->height; m_flippedImage.pixels.length(len); // 反転した画像データを OutPort にコピー memcpy((void *)&(m_flippedImage.pixels[0]),m_flipImageBuff->imageData,len); // 反転した画像データを OutPort から出力する。 m_flippedImageOut.write(); } return RTC::RTC_OK; }
Eclipse 画面左のパッケージエクスプローラーで src/CMakeLists.txt をダブルクリックもしくはエディタへドラッグアンドドロップして編集します。
このコンポーネントでは OpenCV を利用していますので、OpenCV のヘッダのインクルードパス、ライブラリやライブラリサーチパスを与えてやる必要があります。 幸い OpenCV は CMake に対応しており、以下の2行を追加・変更するだけで OpenCV のライブラリがリンクされ使えるようになります。
set(comp_srcs Flip.cpp ) set(standalone_srcs FlipComp.cpp) find_package(OpenCV REQUIRED) ← この行を追加 :中略 add_dependencies(${PROJECT_NAME} ALL_IDL_TGT) target_link_libraries(${PROJECT_NAME} ${OPENRTM_LIBRARIES} ${OpenCV_LIBS}) ← OepnCV_LIBSを追加 :中略 add_executable(${PROJECT_NAME}Comp ${standalone_srcs} ${comp_srcs} ${comp_headers} ${ALL_IDL_SRCS}) target_link_libraries(${PROJECT_NAME}Comp ${OPENRTM_LIBRARIES} ${OpenCV_LIBS}) ← OepnCV_LIBSを追加
CMakeList.txt を編集したので、再度 CMake で Configure および Generate を行います。
$ cd workspace/Flip (eclipse ワークスペース下の Flip ディレクトリーへ移動) $ rm -rf build (念のため仮ビルドで作成したディレクトリーは削除) $ mkdir build (再度 build ディレクトリーを作成) $ cd build $ cmake .. $ make
ここでは、OpenRTM-aist-1.1 以降で同梱されるようになったカメラコンポーネント (OpenCVCameraComp、または DirectShowCamComp)とビューアコンポーネント (CameraViewerComp)を接続し動作確認を行います。
コンポーネントの参照を登録するためのネームサービスを起動します。
$ rtm-naming
RTコンポーネントでは、ネームサーバーのアドレスやネームサーバーへの登録フォーマットなどの情報を rtc.conf というファイルで指定する必要があります。
下記の内容を rtc.conf というファイル名で保存し、workspace/Flip/build/src/ ディレクトリーに置いてください。
corba.nameservers: localhost naming.formats: %n.rtc
Flip コンポーネントを起動します。
先程 rtc.conf ファイルを置いたフォルダーにある、FlipComp.exe ファイルを実行してください。
$ cd workspace/Flip/build/src (もし現在 build/src 以外にいる場合) $ ./FlipComp
USBカメラのキャプチャー画像を OutPort から出力する OpenCVCameraComp、InPort で受け取った画像を画面に表示する CameraViewerComp を起動します。
$ /usr/share/openrtm-1.1/components/c++/opencv-rtcs/OpenCVCameraComp $ /usr/share/openrtm-1.1/components/c++/opencv-rtcs/CameraViewerComp
下図のように、RTSystemEditor にて OpenCVCameraComp (またはDirectShowcomp) と Flip、CameraviewerComp コンポーネントを接続します。
Eclipse起動後、RTSystemEditor でネームサーバーに接続できない場合があります。その場合、/etc/hosts の localhost の行に自ホスト名を追記してください。
$ hostname ubuntu1404 ← ホスト名は ubuntu1404 $ sudo vi /etc/hosts
127.0.0.1 localhost を以下のように変更 127.0.0.1 localhost ubuntu1404
RTSystemEditor の上部にあります「ALL」というアイコンをクリックし、全てのコンポーネントをアクティブ化します。正常にアクティベートされた場合、下図のように黄緑色でコンポーネントが表示されます。
下図のようにコンフィギュレーションビューにてコンフィギュレーションを変更することができます。
Flip コンポーネントのコンフィギュレーションパラメーター「flipMode」を「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", // Configuration variables "conf.default.flipMode", "1", // Widget "conf.__widget__.flipMode", "radio", // Constraints "conf.__constraints__.flip_mode", "(-1,0,1)", "" }; /*! * @brief constructor * @param manager Maneger Object */ Flip::Flip(RTC::Manager* manager) : RTC::DataFlowComponentBase(manager), m_originalImageIn("originalImage", m_originalImage), m_flippedImageOut("flippedImage", m_flippedImage) { } /*! * @brief destructor */ Flip::~Flip() { } RTC::ReturnCode_t Flip::onInitialize() { // Registration: InPort/OutPort/Service // Set InPort buffers addInPort("originalImage", m_originalImageIn); // Set OutPort buffer addOutPort("flippedImage", m_flippedImageOut); // Bind variables and configuration variable bindParameter("flipMode", m_flipMode, "1"); return RTC::RTC_OK; } RTC::ReturnCode_t Flip::onActivated(RTC::UniqueId ec_id) { // イメージ用メモリーの初期化 m_imageBuff = NULL; m_flipImageBuff = NULL; // OutPort の画面サイズの初期化 m_flippedImage.width = 0; m_flippedImage.height = 0; return RTC::RTC_OK; } RTC::ReturnCode_t Flip::onDeactivated(RTC::UniqueId ec_id) { if(m_imageBuff != NULL) { // イメージ用メモリーの解放 cvReleaseImage(&m_imageBuff); cvReleaseImage(&m_flipImageBuff); } return RTC::RTC_OK; } RTC::ReturnCode_t Flip::onExecute(RTC::UniqueId ec_id) { // 新しいデータのチェック if (m_originalImageIn.isNew()) { // InPort データの読み込み m_originalImageIn.read(); // InPort と OutPort の画面サイズ処理およびイメージ用メモリーの確保 if( m_originalImage.width != m_flippedImage.width || m_originalImage.height != m_flippedImage.height) { m_flippedImage.width = m_originalImage.width; m_flippedImage.height = m_originalImage.height; // InPort のイメージサイズが変更された場合 if(m_imageBuff != NULL) { cvReleaseImage(&m_imageBuff); cvReleaseImage(&m_flipImageBuff); } // イメージ用メモリーの確保 m_imageBuff = cvCreateImage(cvSize(m_originalImage.width, m_originalImage.height), IPL_DEPTH_8U, 3); m_flipImageBuff = cvCreateImage(cvSize(m_originalImage.width, m_originalImage.height), IPL_DEPTH_8U, 3); } // InPort の画像データをIplImage の imageData にコピー memcpy(m_imageBuff->imageData,(void *)&(m_originalImage.pixels[0]),m_originalImage.pixels.length()); // InPort からの画像データを反転する。 m_flipMode 0: X軸周り, 1: Y軸周り, -1: 両方の軸周り cvFlip(m_imageBuff, m_flipImageBuff, m_flipMode); // 画像データのサイズ取得 int len = m_flipImageBuff->nChannels * m_flipImageBuff->width * m_flipImageBuff->height; m_flippedImage.pixels.length(len); // 反転した画像データを OutPort にコピー memcpy((void *)&(m_flippedImage.pixels[0]),m_flipImageBuff->imageData,len); // 反転した画像データを OutPort から出力する。 m_flippedImageOut.write(); } return RTC::RTC_OK; } extern "C" { void FlipInit(RTC::Manager* manager) { coil::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> #include <rtm/idl/ExtendedDataTypesSkel.h> #include <rtm/idl/InterfaceDataTypesSkel.h> //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 /*! * * - Name: flipMode * - DefaultValue: 1 */ int m_flipMode; // DataInPort declaration CameraImage m_originalImage; /*! */ InPort<CameraImage> m_originalImageIn; // DataOutPort declaration CameraImage m_flippedImage; /*! */ OutPort<CameraImage> m_flippedImageOut; private: // 処理画像用バッファ IplImage* m_imageBuff; IplImage* m_flipImageBuff; }; extern "C" { DLL_EXPORT void FlipInit(RTC::Manager* manager); }; #endif // FLIP_H
Flip コンポーネントの全ソースコードを以下に添付します。