top of page

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での記述をおすすめします。

 

参考文献

  1. OpenCV2プログラミングブック制作チーム(2011)『OpenCV 2 プログラミングブック』マイナビ

  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に以下のコードを記述します。

以下のコードは抜粋となります。生成したテンプレートに当てはめて下さい。

  1. # Import OpenCV

  2. import cv2

  3. import cv2.cv as cv

  4.  

  5. class WebCamera(OpenRTM_aist.DataFlowComponentBase):

  6.    

  7.     def __init__(self, manager):

  8.         OpenRTM_aist.DataFlowComponentBase.__init__(self, manager)

  9.         self._d_image = RTC.CameraImage(RTC.Time(0,0), 0, 0, 0, "", 0.0, [])

  10.         """

  11.         """

  12.         self._cameraOut = OpenRTM_aist.OutPort("camera", self._d_image)

  13.  

  14.         # initialize of configuration-data.

  15.         # <rtc-template block="init_conf_param">

  16.         """

  17.         

  18.          - Name:  conf_width

  19.          - DefaultValue: 640

  20.         """

  21.         self._conf_width = [640]

  22.         """

  23.         

  24.          - Name:  conf_height

  25.          - DefaultValue: 480

  26.         """

  27.         self._conf_height = [480]

  28.         """

  29.         

  30.          - Name:  conf_bpp

  31.          - DefaultValue: 24

  32.         """

  33.         self._conf_bpp = [24]

  34.         

  35.         # </rtc-template>

  36.  

  37.     def onInitialize(self):

  38.                 # Bind variables and configuration variable

  39.                 self.bindParameter("Width", self._conf_width, "640")

  40.                 self.bindParameter("Height", self._conf_height, "480")

  41.                 self.bindParameter("Bits_Per_Pixel", self._conf_bpp, "24")

  42.         

  43.                 # Set OutPort buffers

  44.                 self.addOutPort("camera",self._cameraOut)

  45.       

  46.                 self.capture = cv2.VideoCapture(0)

  47.                         

  48.                 if self.capture.isOpened() is False:

  49.                         print('Please check the connection of a USB Camera')

  50.                         return RTC.RTC_ERROR

  51.         

  52.         

  53.                 return RTC.RTC_OK

  54.     

  55.     def onActivated(self, ec_id):

  56.                 if self.capture.isOpened() is False:

  57.                         self.capture.open(0)  

  58.  

  59.                         if self.capture.isOpened() is False:

  60.                                 print('Please check the connection of a USB Camera')

  61.                                 return RTC.RTC_ERROR

  62.                 

  63.                 self._d_image.height = self._conf_height[0]

  64.                 self._d_image.width = self._conf_width[0]

  65.                 self._d_image.bpp = self._conf_bpp[0]

  66.                 self.capture.set(cv.CV_CAP_PROP_FRAME_HEIGHT, self._d_image.height)

  67.                 self.capture.set(cv.CV_CAP_PROP_FRAME_WIDTH, self._d_image.width)

  68.     

  69.                 return RTC.RTC_OK

  70.  

  71.     def onDeactivated(self, ec_id):

  72.                 self.capture.release()

  73.     

  74.                 return RTC.RTC_OK

  75.     

  76.     def onExecute(self, ec_id):

  77.                 

  78.                 ret, image = self.capture.read()

  79.                 

  80.                 if ret is False:

  81.                         print('Please check the connection of a USB Camera')

  82.                         return RTC.RTC_ERROR

  83.  

  84.                 if self._d_image.bpp == 8:

  85.                         gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

  86.                         self._d_image.pixels = "".join([chr(value) for value in gray.flat])

  87.                         self._cameraOut.write()

  88.  

  89.                 elif self._d_image.bpp == 24:

  90.                         self._d_image.pixels = "".join([chr(value) for value in image.flat])

  91.                         self._cameraOut.write()

  92.  

  93.                 else:

  94.                         print('Please check Bits_Per_Pixel(8 or 24)')

  95.                         return RTC.RTC_ERROR

  96.                

  97.                 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に以下のコードを記述します。

以下のコードは抜粋となります。生成したテンプレートに当てはめて下さい。

  1. # Import OpenCV

  2. import cv2

  3. import cv2.cv as cv

  4. import numpy

  5.  

  6.  

  7. class CameraViewer(OpenRTM_aist.DataFlowComponentBase):

  8.     

  9.     def __init__(self, manager):

  10.         OpenRTM_aist.DataFlowComponentBase.__init__(self, manager)

  11.         self._d_image = RTC.CameraImage(RTC.Time(0,0), 0, 0, 0, "", 0.0, [])

  12.         """

  13.         """

  14.         self._cameraIn = OpenRTM_aist.InPort("camera", self._d_image)

  15.    

  16.     def onActivated(self, ec_id):

  17.                 print('CreateWindow')

  18.                 cv2.namedWindow("ReceiveImage", cv.CV_WINDOW_AUTOSIZE)

  19.     

  20.                 return RTC.RTC_OK

  21.     

  22.     def onDeactivated(self, ec_id):

  23.                 print('DestoryWindow ')

  24.                 cv2.destroyAllWindows()

  25.  

  26.                 return RTC.RTC_OK

  27.     

  28.     def onExecute(self, ec_id):

  29.                 if self._cameraIn.isNew():

  30.                         self._d_image = self._cameraIn.read()

  31.                         

  32.                         pixels = numpy.array([ord(i) for i in self._d_image.pixels], dtype = numpy.uint8)

  33.                                                        

  34.                         cv2.imshow("ReceiveImage",

  35.                                    pixels.reshape(self._d_image.height, self._d_image.width, self._d_image.bpp / 8))

  36.                         key = cv2.waitKey(1)

  37.             

  38.                 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で処理を分割する場合

bottom of page