WebCamera RTCs (C++)
Webカメラから取得した画像を送信するRTコンポーネントを作成する方法を解説します。
RTCの機能の1つであるコンフィギュレーションを使用して、送信する画像サイズとビット深度をユーザが変更できるようにします。
1. はじめに
今回は、以下のRTCを作成し、OpenCVを使いWebカメラから画像を送信し、受信する方法を学びます。
また、コンフィギュレーションというRTC内部のパラメータをユーザが外部から変更できる仕組みを利用します。
-
WebCamera
Webカメラから画像を取得し、画像を転送します。
コンフィギュレーションを使用して転送する画像のサイズやビット深度を変更します。
-
CameraViewer
CameraImage型のデータを読み込み、画像を表示します。
受信した画像の、画像サイズとビット深度に対応させます。
Simple Color Extraction RTCsでは1つのRTCで画像の取得から処理まで行いました。
これは下図(a)に相当します。
今回は、下図(b)のように、画像の取得と処理を分けます。
このようにすることで、処理ごとにRTC全体を作りなおさずに、用途に合わせて処理に合わせたRTCをつなぎ変えることができます。
今回は処理として、画像表示を選びましたが、Simple Color Extraction RTCsのように特定の色を抜き出すRTCを作成することもできます。
このように、RTCの構成は再利用性に大きく関係することを覚えておいて下さい。
5. おわりに
Webカメラから取得した画像を、コンフィギュレーションを使用して、画像サイズとビット深度を変更して送信するRTCを作成しました。
コンフィギュレーションと画像情報をうまく使うことで、ある程度汎用性のあるRTCを作ることができます。
このようなやり方は画像以外にも有効です。
自分のRTCを作るときにぜひ応用してみてください。
参考文献
-
OpenCV2プログラミングブック制作チーム(2011)『OpenCV 2 プログラミングブック』マイナビ
-
「OpenCV.jp : OpenCV逆引きリファレンス」<http://opencv.jp/cookbook/index.html>(2013/12/21アクセス)
2. WebCamera RTC
WebCamera RTCを作成します。
テンプレートを以下に従って設定てください。
-
モジュール名
WebCamera
-
ベンダ名
あなたの名前
-
モジュールカテゴリ
CAMERA
-
実行周期
30
-
アクションコールバック
onInitialize
onActivated
onExecute
-
データポート
・cameraポート
ポート名(OutPort) camera
データ型 CameraImage
変数名 image
表示位置 RIGHT
-
コンフィギュレーション
・Widthコンフィギュレーション
名称 Width
データ型 short
デフォルト値 640
変数名 conf_width
・Heightコンフィギュレーション
名称 Height
データ型 short
デフォルト値 480
変数名 conf_height
・Bits_Per_Pixelコンフィギュレーション
名称 Bits_Per_Pixel
データ型 short
デフォルト値 24
変数名 conf_bpp
-
言語
C++
use old build environment. (Linuxの場合)
■ Windows OSの場合
テンプレートを生成したらCMakeを行い、ソリューションファイルを生成して下さい。
ソリューションを開いたら、以下の2つのプロジェクトにOpenCVをビルドするための、
追加のインクルード ディレクトリと追加のライブラリ ディレクトリ、追加の依存ファイルを設定して下さい。
・WebCameraプロジェクト
・WebCameraCompプロジェクト
追加の依存ファイルは、以下の3つになります。
■ソリューションの構成がDebugの場合
opencv_core246d.lib
opencv_highgui246d.lib
opencv_imgproc246d.lib
■ソリューションの構成がReleaseの場合
opencv_core246.lib
opencv_highgui246.lib
opencv_imgproc246.lib
■ Linux OSの場合
Makefile.WebCameraを開いて、OpenCVのヘッダファイルとライブラリを指定します。
Makefileの修正方法は、OpenCVを使用したRTCの作成方法を御覧ください。
プロジェクトの設定が終わったら、WebCamera.cppに以下のコードを記述します。
以下のコードは抜粋となります。生成したテンプレートに当てはめて下さい。
-
#include "WebCamera.h"
-
-
// OpenCV
-
#include <opencv2/core/core.hpp>
-
#include <opencv2/highgui/highgui.hpp>
-
#include <opencv2/imgproc/imgproc.hpp>
-
-
cv::VideoCapture cap;
-
-
RTC::ReturnCode_t WebCamera::onInitialize()
-
{
-
addOutPort("camera", m_imageOut);
-
-
bindParameter("Width", m_conf_width, "640");
-
bindParameter("Height", m_conf_height, "480");
-
bindParameter("Bits_Per_Pixel", m_conf_bpp, "24");
-
-
std::cout << "WebCameraComp" << std::endl;
-
-
cap = cv::VideoCapture(0);
-
-
if(!cap.isOpened())
-
{
-
std::cerr << "Camera not found." << std::endl;
-
return RTC::RTC_ERROR;
-
}
-
-
return RTC::RTC_OK;
-
}
-
-
RTC::ReturnCode_t WebCamera::onActivated(RTC::UniqueId ec_id)
-
{
-
m_image.width = m_conf_width;
-
m_image.height = m_conf_height;
-
m_image.bpp = m_conf_bpp;
-
-
cap.set(CV_CAP_PROP_FRAME_WIDTH, m_image.width);
-
cap.set(CV_CAP_PROP_FRAME_HEIGHT, m_image.height);
-
-
return RTC::RTC_OK;
-
}
-
-
RTC::ReturnCode_t WebCamera::onExecute(RTC::UniqueId ec_id)
-
{
-
cv::Mat frame;
-
cap >> frame;
-
-
m_image.pixels.length(m_image.width * m_image.height * m_image.bpp / 8);
-
-
switch(m_image.bpp)
-
{
-
case 8:
-
{
-
cv::Mat gray;
-
cv::cvtColor(frame, gray, CV_BGR2GRAY);
-
-
for(int y = 0; y < m_image.height; y++)
-
{
-
memcpy((void *)&(m_image.pixels[m_image.width * m_image.bpp / 8 * y]),
-
gray.ptr<unsigned char>(y), m_image.width * m_image.bpp / 8);
-
}
-
break;
-
}
-
case 24:
-
{
-
for(int y = 0; y < m_image.height; y++)
-
{
-
memcpy((void *)&(m_image.pixels[m_image.width * m_image.bpp / 8 * y]),
-
frame.ptr<unsigned char>(y), m_image.width * m_image.bpp / 8);
-
}
-
break;
-
}
-
default:
-
std::cerr << "Bits Par Pixels is not correct." << std::endl;
-
return RTC::RTC_ERROR;
-
}
-
-
m_imageOut.write();
-
-
return RTC::RTC_OK;
-
}
それではソースコードを解説します。
-
4-6行目
OpenCV用のIncludeを行います。
-
8行目
VideoCaptureクラスをグローバルで定義します。
-
14-16行目
RTC Builderで設定したコンフィギュレーションのパラメータが記述されています。
設定した名前と変数名、初期値が記されているのがわかります。
-
20行目
VideoCaptureクラスのコンストラクタ(初期化)を呼びます。
-
22-26行目
Webカメラに接続できるか確認し、接続できない場合はエラーを返します。
-
33-35行目
コンフィギュレーションのパラメータを CameraImage 型の変数に格納します。
CameraImage型のアクセス方法は以下のようになります。
・m_変数名.メンバ名
各メンバ名の意味は以下のようになります。
・Time tm
タイムスタンプ
・unsigned short width
画像の横幅
・unsigned short height
画像の高さ
・unsigned short bpp
ビット深度
・string format
画像形式
・double fDiv
画像の横幅
・sequence<oct> pixels
画素値
今回は上記の中からwidth、height、bpp、pixelsを使用します。
次に、コンフィギュレーションのパラメータは以下のようにアクセスします。
・m_変数名
33-35行は、このコンフィギュレーションのパラメータをCameraImage型のメンバに代入しています。
代入しているパラメータは画像の縦横サイズとビット深度*1です。
*1 ビット深度は1画素あたりのビット数です。グレースケールだと8bit、カラー画像だと24bitになります。
Activateする度にコンフィギュレーションで変更された値をプログラム上に反映することになります。
-
37-38行目
Web カメラからキャプチャする画像サイズも指定します。
コンフィギュレーションでwidth、heightを変更すると、Activate時にキャプチャする画像サイズを変更します。
-
45-46行目
Mat 型の変数 frame を用意し、Webカメラから取得した画像を格納します。
-
48行目
CameraImage 型の変数 pixelsのサイズを指定します。
pixels はシーケンス型のメンバなので、長さを指定する必要があります。
長さは、以下の計算によって求めます。
縦 × 横 × ビット深度 / 8
ビット深度 / 8 は画像のチャンネル数を求めています。
全画素数(縦×横)にチャンネル数(ビット深度 / 8)をかけることで、画像データを格納するのに必要なサイズを知ることができます。
ここで使用しているCameraImage型のメンバ変数は、コンフィギュレーションのパラメータを代入されているので、
コンフィギュレーションのパラメータを変更するとActivate時に自動的に変更されます。
-
50-76行目
ビット深度によって、以下の2つの処理をスイッチしています。
■ビット深度が8 bitのとき
グレースケール画像を送信します。
■ビット深度が24 bitのとき
カラー画像を送信します。
-
54-55行目
Mat型の変数を用意し、そこにカラー画像からグレースケール画像に変換した画像を格納します。
CV_BGR2GRAYの2はtoと読みます。
つまりBGRからグレーと読め、処理の内容がわかります。このような読み方に慣れておくと役に立ちます。
-
57-61・66-70行目
Mat型の画像データをCameraImage型のメンバpixelsにコピーしています。
値のコピーは以下の関数を使用します。
memcpy(コピー先, コピー元, サイズ)
画像が640 × 480pixelのカラー画像だとすると、640 × 3列480行のデータになります。
カラー画像は1画素につき、3チャンネルのデータを持っているため、下図のようにBGRの順に値が横一列に並びます。
4. 動作確認
作成したRTCを実行して下さい。
まずは、普通につなげてActivateしてみてください。
そのときに、640 × 480 pixelのカラー画像が表示されれば成功です。
上記のように画素が並ぶため、横幅は1チャンネルのグレースケール画像に比べて3倍長くなります。
1行あたりの画像データのサイズは以下の式で求まります。
横幅 × ビット深度 / 8
画像のコピー先は、 1行取得するごとに、順に格納先をずらしています。
画像データには、以下のようにアクセスします。
Mat型変数.ptr<unsigned char>(y)
ちょっと難しい記述ですが、画像データのy行目の先頭アドレスを取得しています。
-
73-75行目
ビット深度が8、 24のいずれでもない場合は、エラーを返します。
-
78行目
画像データをポートに出力します。
以上で、WebCameraのコーディングは終了です。
コードが入力し終わったら、ビルドを行ってください。
画素値の操作が少し難いでしょうか。サンプルを丸写しでも構いませんので、頑張って最後までコーディングしてみて下さい。
今回の処理はできる限り一般化していますので、色々な場面でそのまま使えるはずです。
3. CameraViewer RTC
CameraViewer RTCを作成します。
テンプレートを以下に従って設定てください。
-
モジュール名
CameraViewer
-
ベンダ名
あなたの名前
-
モジュールカテゴリ
CAMERA
-
実行周期
30
-
アクションコールバック
onInitialize
onActivated
onDeactivated
onExecute
-
データポート
・cameraポート
ポート名(InPort) camera
データ型 CameraImage
変数名 image
表示位置 LEFT
-
言語
C++
use old build environment. (Linuxの場合)
■Windows OSの場合
テンプレートを生成したらCMakeを行い、ソリューションファイルを生成して下さい。
ソリューションを開いたら、以下の2つのプロジェクトにOpenCVをビルドするための、
追加のインクルード ディレクトリと追加のライブラリ ディレクトリ、追加の依存ファイルを設定して下さい。
・CameraViewerプロジェクト
・CameraViewerCompプロジェクト
追加の依存ファイルは、以下の3つになります。
■ソリューションの構成がDebugの場合
opencv_core246d.lib
opencv_highgui246d.lib
■ソリューションの構成がReleaseの場合
opencv_core246.lib
opencv_highgui246.lib
■Linux OSの場合
Makefile.CameraViewerを開いて、OpenCVのヘッダファイルとライブラリを指定します。
Makefileの修正方法は、OpenCVを使用したRTCの作成方法を御覧ください。
プロジェクトの設定が終わったら、CameraViewer.cppに以下のコードを記述します。
以下のコードは抜粋となります。生成したテンプレートに当てはめて下さい。
-
#include "CameraViewer.h"
-
-
// OpenCV
-
#include <opencv2/core/core.hpp>
-
#include <opencv2/highgui/highgui.hpp>
-
-
RTC::ReturnCode_t CameraViewer::onInitialize()
-
{
-
// Set InPort buffers
-
addInPort("camera", m_imageIn);
-
std::cout << "CameraViewerComp" << std::endl;
-
-
return RTC::RTC_OK;
-
}
-
-
RTC::ReturnCode_t CameraViewer::onActivated(RTC::UniqueId ec_id)
-
{
-
std::cout << "CreateWindow" << std::endl;
-
cv::namedWindow("ReceiveImage", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
-
-
return RTC::RTC_OK;
-
}
-
-
RTC::ReturnCode_t CameraViewer::onDeactivated(RTC::UniqueId ec_id)
-
{
-
std::cout << "DestroyWindow" << std::endl;
-
cv::destroyWindow("ReceiveImage");
-
-
return RTC::RTC_OK;
-
}
-
-
RTC::ReturnCode_t CameraViewer::onExecute(RTC::UniqueId ec_id)
-
{
-
if (m_imageIn.isNew()){
-
-
m_imageIn.read();
-
-
switch(m_image.bpp)
-
{
-
case 8:
-
{
-
cv::Mat receive(m_image.height, m_image.width, CV_8UC1, &m_image.pixels[0]);
-
cv::imshow("ReceiveImage", receive);
-
cv::waitKey(1);
-
break;
-
}
-
case 24:
-
{
-
cv::Mat receive(m_image.height, m_image.width, CV_8UC3, &m_image.pixels[0]);
-
cv::imshow("ReceiveImage", receive);
-
cv::waitKey(1);
-
break;
-
}
-
default:
-
std::cerr << "Bits Par Pixels is not correct." << std::endl;
-
return RTC::RTC_ERROR;
-
}
-
}
-
-
return RTC::RTC_OK;
-
}
コードの解説をします。
-
4-5行目
OpenCV用のIncludeを行います。
-
18-19行目
Acitivate時に画像を表示するウィンドウを作成します。
-
18-19行目
Acitivate時に画像を表示するウィンドウを作成します。
-
34-58行目
データが取得できた場合にこのあとの処理を行います。
-
36行目
InPortからデータを読み込みます。
-
38-57行目
CameraImage型メンバ変数bppに格納されているビット深度で処理をスイッチします。
ビット深度によって、以下の2つの処理をスイッチしています。
■ビット深度が8 bitのとき
グレースケール画像を表示します。
■ビット深度が24 bitのとき
カラー画像を表示します。
-
40-46・47-53行目
Mat型の画像データを作成します。
Mat型 変数名(行, 列, チャンネル型, アドレス)
CameraImage型に格納されている画像サイズの行列を定義します。
このようにCameraImage型が持つ情報を使うことで、様々な画像に対応することができます。
チャンネルは グレースケールで1チャンネル、カラー画像で3チャンネル用意します。
8UC1は8bit 1チャンネルで、8CU_3は8bit 3チャンネルの行列を意味します。
最後に、 CameraImage型のpixelデータが格納されているアドレスを指定すれば Mat型の画像データが作成されます。
-
54-57行目
ビット深度が8、 24のいずれでもない場合は、エラーを返します。
以上で、CameraViewerのコーディングは終了です。
コードが入力し終わったら、ビルドを行ってください。

カラー画像のデータ構造

640 × 480 pixelカラー画像

コンフィギュレーションの変更

640 × 480 pixelグレースケール画像
次に、コンフィギュレーションのパラメータを変更し、WebCameraCompが送信する画像のビット深度を変更してみます。
下図のように、①のWebCameraCompをInactivateします。
そのまま、WebCameraCompのインスタンスをクリックした状態で、System Diagramの下にConfiguration Viewというタブに注目します。
②のBits_Per_PixelのValueというテキストボックスをクリックし、24を8に書き換えてください。
書き換え終わったら、忘れずに③の適用ボタンを押します。
変更ができたら、再度WebCameraCompをActivateしてください。

1280 × 720 pixelグレースケール画像
最後に、コンフィギュレーションのパラメータを変更し、WebCameraCompが送信する画像のサイズを変更してみます。
先ほどと同じ手順で、WebCameraCompをInactivateし、HeightとWidthを書き換えます。
このとき、値を1つ変えるごとに必ず適用ボタンを押して下さい。
複数個まとめて適用ボタンを使って変更することはできませんので、注意してください。
ここでは、1280 × 720 pixelの画像を表示してみました。
皆さんがもっているWebカメラの対応している画像サイズを指定してください。

(a) 1つのRTCで処理を完結する 場合

(b) 複数のRTCで処理を分割する場合