WebCamera RTCs (Python CV2)
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を作るときにぜひ応用してみてください。
余談ですが、PythonでOpenCVを扱う場合は、CV2系列の書き方で書くほうがシンプルかつ高速です。
CV2ではnumpyのarrayとしてデータを扱うので、listよりも高速に処理することができます。
今回のように巨大なデータを扱う場合には、CV2での記述をおすすめします。
参考文献
-
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
onDeactivated
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
-
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)
-
-
self.capture = cv2.VideoCapture(0)
-
-
if self.capture.isOpened() is False:
-
print('Please check the connection of a USB Camera')
-
return RTC.RTC_ERROR
-
-
-
return RTC.RTC_OK
-
-
def onActivated(self, ec_id):
-
if self.capture.isOpened() is False:
-
self.capture.open(0)
-
-
if self.capture.isOpened() is False:
-
print('Please check the connection of a USB Camera')
-
return RTC.RTC_ERROR
-
-
self._d_image.height = self._conf_height[0]
-
self._d_image.width = self._conf_width[0]
-
self._d_image.bpp = self._conf_bpp[0]
-
self.capture.set(cv.CV_CAP_PROP_FRAME_HEIGHT, self._d_image.height)
-
self.capture.set(cv.CV_CAP_PROP_FRAME_WIDTH, self._d_image.width)
-
-
return RTC.RTC_OK
-
-
def onDeactivated(self, ec_id):
-
self.capture.release()
-
-
return RTC.RTC_OK
-
-
def onExecute(self, ec_id):
-
-
ret, image = self.capture.read()
-
-
if ret is False:
-
print('Please check the connection of a USB Camera')
-
return RTC.RTC_ERROR
-
-
if self._d_image.bpp == 8:
-
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
-
self._d_image.pixels = "".join([chr(value) for value in gray.flat])
-
self._cameraOut.write()
-
-
elif self._d_image.bpp == 24:
-
self._d_image.pixels = "".join([chr(value) for value in image.flat])
-
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を行います。
cv2系列とcv系列のimportを明確に分けています。
-
9行目
デフォルトのテンプレートでは、CameraImage型の初期化が間違っているので修正します。
-
15-35・38-41行目
RTC Builderで設定したコンフィギュレーションのパラメータが記述されています。
設定した名前と変数名、初期値が記されているのがわかります。
値がリスト形式であることに注意して下さい。
-
47-52行目
VideoCaptureクラスのコンストラクタ(初期化)を呼びます。
Webカメラに接続できるか確認し、接続できない場合はエラーを返します。
-
56-61行目
Webカメラが接続されているか確認し、接続されていない場合は接続を試みます。
再度Webカメラに接続できるか確認し、接続できない場合はエラーを返します。
-
63-65行目
コンフィギュレーションのパラメータを 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する度にコンフィギュレーションで変更された値をプログラム上に反映することになります。
-
66-67行目
Web カメラからキャプチャする画像サイズも指定します。
コンフィギュレーションでwidth、heightを変更すると、Activate時にキャプチャする画像サイズを変更します。
-
72行目
DeactivateするとWeb カメラを開放します。
-
78-82行目
Webカメラが画像を取得します。
取得に失敗した場合はエラーを返します。
-
84-95行目
ビット深度によって、以下の2つの処理をスイッチしています。
■ビット深度が8 bitのとき
8bit1チャンネルの画像データをポートから出力します。
■ビット深度が24 bitのとき
8bit3チャンネルの画像データをポートから出力します。
■それ以外
エラーを返します。
-
85行目
カラー画像をグレースケール画像に変換します。
-
86・90行目
array型のメソッドであるflatで画素値を順次取り出し、str型に変換してリストに格納します。
その後区切り文字なしで、リストを結合します。
これでOctet型のデータとして画像を書き出すことができます。
-
87・91行目
画像データをポートから出力します。
-
93-95行目
指定されたビット深度が不正なので、エラーを返します。
以上で、WebCameraのコーディングは終了です。
4. 動作確認
作成したRTCを実行して下さい。
まずは、普通につなげてActivateしてみてください。
そのときに、640 × 480 pixelのカラー画像が表示されれば成功です。
3. CameraViewer RTC
CameraViewer RTCを作成します。
テンプレートを以下に従って設定てください。
-
モジュール名
CameraViewer
-
ベンダ名
あなたの名前
-
モジュールカテゴリ
CAMERA
-
実行周期
30
-
アクションコールバック
onInitialize
onActivated
onDeactivated
onExecute
-
データポート
・cameraポート
ポート名(InPort) camera
データ型 CameraImage
変数名 image
表示位置 LEFT
-
言語
Python
プロジェクトの設定が終わったら、CameraViewer.pyに以下のコードを記述します。
以下のコードは抜粋となります。生成したテンプレートに当てはめて下さい。
-
# Import OpenCV
-
import cv2
-
import cv2.cv as cv
-
import numpy
-
-
-
class CameraViewer(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._cameraIn = OpenRTM_aist.InPort("camera", self._d_image)
-
-
def onActivated(self, ec_id):
-
print('CreateWindow')
-
cv2.namedWindow("ReceiveImage", cv.CV_WINDOW_AUTOSIZE)
-
-
return RTC.RTC_OK
-
-
def onDeactivated(self, ec_id):
-
print('DestoryWindow ')
-
cv2.destroyAllWindows()
-
-
return RTC.RTC_OK
-
-
def onExecute(self, ec_id):
-
if self._cameraIn.isNew():
-
self._d_image = self._cameraIn.read()
-
-
pixels = numpy.array([ord(i) for i in self._d_image.pixels], dtype = numpy.uint8)
-
-
cv2.imshow("ReceiveImage",
-
pixels.reshape(self._d_image.height, self._d_image.width, self._d_image.bpp / 8))
-
key = cv2.waitKey(1)
-
-
return RTC.RTC_OK
コードの解説をします。
-
1-4行目
OpenCV用のImportを行います。
cv2系列とcv系列のimportを明確に分けています。
また、cv2系列では画像データはnumpyのarray型で扱うので、numpyをimportしています。
-
11行目
デフォルトのテンプレートでは、CameraImage型の初期化が間違っているので修正します。
-
17-18行目
Acitivate時に画像を表示するウィンドウを作成します。
-
23-24行目
Deacitivate時にウィンドウを破棄します。
-
29-38行目
データが取得できた場合にこのあとの処理を行います。
-
30行目
InPortからデータを読み込みます。
-
32行目
取得した画素値はstr型の結合した長い文字列になっています。
そこで、全てバイト値に変換し、uint8型のarray型に変換します。
CV2では画像データはarray型である必要があります。
-
34-36行目
array pixelsはCV2が求める形状に合わないので、array型のメソッドreshapeを使用して形状を変更します。
reshape()の引数の意味は以下のようになります。
reshape(画像の高さ, 画像の横幅, チャンネル数)
このデータ形状を変更したpixelsをimshow()の引数に与え画像を表示します。
また、忘れずに必ずwatiKey()を実行します。
以上で、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で処理を分割する場合