WebCamera RTCs (Python CV)
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
-
言語
Python
プロジェクトの設定が終わったら、WebCamera.pyに以下のコードを記述します。
以下のコードは抜粋となります。生成したテンプレートに当てはめて下さい。
-
# Import OpenCV
-
import cv2.cv as cv
-
-
-
class WebCamera(OpenRTM_aist.DataFlowComponentBase):
-
-
def __init__(self, manager):
-
OpenRTM_aist.DataFlowComponentBase.__init__(self, manager)
-
self._d_image = RTC.CameraImage(RTC.Time(0,0), 0, 0, 0, "", 0.0, [])
-
"""
-
"""
-
self._cameraOut = OpenRTM_aist.OutPort("camera", self._d_image)
-
-
-
# initialize of configuration-data.
-
# <rtc-template block="init_conf_param">
-
"""
-
-
- Name: conf_width
-
- DefaultValue: 640
-
"""
-
self._conf_width = [640]
-
"""
-
-
- Name: conf_height
-
- DefaultValue: 480
-
"""
-
self._conf_height = [480]
-
"""
-
-
- Name: conf_bpp
-
- DefaultValue: 24
-
"""
-
self._conf_bpp = [24]
-
-
# </rtc-template>
-
-
def onInitialize(self):
-
# Bind variables and configuration variable
-
self.bindParameter("Width", self._conf_width, "640")
-
self.bindParameter("Height", self._conf_height, "480")
-
self.bindParameter("Bits_Per_Pixel", self._conf_bpp, "24")
-
-
# Set OutPort buffers
-
self.addOutPort("camera",self._cameraOut)
-
-
try:
-
self.cam = cv.CaptureFromCAM(0)
-
-
except IOError:
-
print('Please check the connection of a USB Camera')
-
return RTC.RTC_ERROR
-
-
return RTC.RTC_OK
-
-
def onActivated(self, ec_id):
-
self._d_image.height = self._conf_height[0]
-
self._d_image.width = self._conf_width[0]
-
self._d_image.bpp = self._conf_bpp[0]
-
-
cv.SetCaptureProperty(self.cam,cv.CV_CAP_PROP_FRAME_HEIGHT, self._d_image.height)
-
cv.SetCaptureProperty(self.cam,cv.CV_CAP_PROP_FRAME_WIDTH, self._d_image.width)
-
-
if self._d_image.bpp == 8:
-
self.src = cv.CreateImage((self._d_image.width, self._d_image.height), 8, 1)
-
-
elif self._d_image.bpp == 24:
-
self.src = cv.CreateImage((self._d_image.width, self._d_image.height), 8, 3)
-
-
else:
-
print('Please check Bits_Per_Pixel(8 or 24)')
-
return RTC.RTC_ERROR
-
-
return RTC.RTC_OK
-
-
def onExecute(self, ec_id):
-
try:
-
self.src = cv.QueryFrame(self.cam)
-
-
except IOError:
-
print('Please check the connection of a USB Camera')
-
return RTC.RTC_ERROR
-
-
pixel = []
-
-
if self._d_image.bpp == 8:
-
gray = cv.CreateImage(cv.GetSize(self.src), 8, 1)
-
cv.CvtColor(self.src, gray, cv2.COLOR_BGR2GRAY)
-
-
for v in xrange(0, self._d_image.height):
-
for u in xrange(0, self._d_image.width):
-
bgr = cv.Get2D(self.src, v, u)
-
pixel.append(chr(int(bgr[0])))
-
-
self._d_image.pixels = "".join(pixel)
-
self._cameraOut.write()
-
-
elif self._d_image.bpp == 24:
-
for v in xrange(0, self._d_image.height):
-
for u in xrange(0, self._d_image.width):
-
bgr = cv.Get2D(self.src, v, u)
-
pixel.append(chr(int(bgr[0])))
-
pixel.append(chr(int(bgr[1])))
-
pixel.append(chr(int(bgr[2])))
-
-
self._d_image.pixels = "".join(pixel)
-
self._cameraOut.write()
-
-
else:
-
print('Please check Bits_Per_Pixel(8 or 24)')
-
return RTC.RTC_ERROR
-
-
return RTC.RTC_OK
それではソースコードを解説します。
-
1-2行目
OpenCV用のImportを行います。
-
9行目
デフォルトのテンプレートでは、CameraImage型の初期化が間違っているので修正します。
-
17-34・40-42行目
RTC Builderで設定したコンフィギュレーションのパラメータが記述されています。
設定した名前と変数名、初期値が記されているのがわかります。
値がリスト形式であることに注意して下さい。
-
47-52行目
CaptureFromCAMクラスのコンストラクタ(初期化)を呼びます。
Webカメラに接続できるか確認し、接続できない場合はエラーを返します。
-
57-59行目
コンフィギュレーションのパラメータを CameraImage 型の変数に格納します。
CameraImage型のアクセス方法は以下のようになります。
・self._d_変数名.メンバ名
各メンバ名の意味は以下のようになります。
・tm
タイムスタンプ
・width
画像の横幅
・height
画像の高さ
・bpp
ビット深度
・format
画像形式
・fDiv
画像の横幅
・pixels
画素値
今回は上記の中からwidth、height、bpp、pixelsを使用します。
次に、コンフィギュレーションのパラメータは以下のようにアクセスします。
・self._変数名
57-59行は、このコンフィギュレーションのパラメータをCameraImage型のメンバに代入しています。
代入しているパラメータは画像の縦横サイズとビット深度*1です。
*1 ビット深度は1画素あたりのビット数です。グレースケールだと8bit、カラー画像だと24bitになります。
Activateする度にコンフィギュレーションで変更された値をプログラム上に反映することになります。
-
61-62行目
Web カメラからキャプチャする画像サイズも指定します。
コンフィギュレーションでwidth、heightを変更すると、Activate時にキャプチャする画像サイズを変更します。
-
64-72行目
ビット深度によって、以下の2つの処理をスイッチしています。
■ビット深度が8 bitのとき
8bit1チャンネルの画像データを作成します。
■ビット深度が24 bitのとき
8bit3チャンネルの画像データを作成します。
■それ以外
エラーを返します。
-
64-65行目
グレースケール画像用に8bit1チャンネルの画像データを作成します。
-
67-68行目
カラー画像用に8bit3チャンネルの画像データを作成します。
-
70-72行目
指定されたビット深度が不正なので、エラーを返します。
-
77-82行目
Webカメラから取得した画像を格納します。
失敗した場合はエラーを返します。
-
84行目
OutPortから送信するようの画素値を格納するリストpixelを定義します。
-
86-111行目
ビット深度によって、以下の2つの処理をスイッチしています。
・ビット深度が8 bitのとき
グレースケール画像を送信します。
・ビット深度が24 bitのとき
カラー画像を送信します。
-
86-96行目
gray画像データを用意し、そこにカラー画像からグレースケール画像に変換した画像を格納します。
CV_BGR2GRAYの2はtoと読みます。
つまりBGRからグレーと読め、処理の内容がわかります。このような読み方に慣れておくと役に立ちます。
次に、画素値を格納するリストpixelに画像データから取り出した画素値を格納してます。
ここで画素値のアクセスは以下のメソッドを使用します。
cv.Get2D(画像, v座標, u座標)
このメソッドで帰ってくる値は、[B, G, R]のリスト形式の画素値になります。
グレースケール画像では最初の値を読み出せば良いので、0番目にアクセスしています。
この処理を全ての画素に対して行うために、2重のforループを回しています。
画素値を格納したら、Octet型のデータにするためにリストを連結します。
その後、画像データをポートから出力します。
-
98-107行目
画素値を格納するリストpixelに画像データから取り出した画素値を格納してます。
先ほどとは異なり、カラー画像は3チャンネルあるので、[B, G, R]の値を順次追加しています。
下図のように、BGRの順で画素値がシリアライズされていきます*1。
これはOpenCVでCppを使用したデータ構造を模したものとなっています。
*1 図では行があるように見えますが、実際にはBGRBGR...行と列が一本化されたデータ構造になっています。
4. 動作確認
作成したRTCを実行して下さい。
まずは、普通につなげてActivateしてみてください。
そのときに、640 × 480 pixelのカラー画像が表示されれば成功です。