top of page

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を作るときにぜひ応用してみてください。

 

参考文献

  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
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.cv as cv

  3.  

  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.         

  15.         # initialize of configuration-data.

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

  17.         """

  18.         

  19.          - Name:  conf_width

  20.          - DefaultValue: 640

  21.         """

  22.         self._conf_width = [640]

  23.         """

  24.         

  25.          - Name:  conf_height

  26.          - DefaultValue: 480

  27.         """

  28.         self._conf_height = [480]

  29.         """

  30.         

  31.          - Name:  conf_bpp

  32.          - DefaultValue: 24

  33.         """

  34.         self._conf_bpp = [24]

  35.         

  36.         # </rtc-template>

  37.  

  38.     def onInitialize(self):

  39.         # Bind variables and configuration variable

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

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

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

  43.         

  44.         # Set OutPort buffers

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

  46.  

  47.         try:

  48.                         self.cam = cv.CaptureFromCAM(0)

  49.                         

  50.         except IOError:

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

  52.                         return RTC.RTC_ERROR

  53.         

  54.         return RTC.RTC_OK

  55.     

  56.     def onActivated(self, ec_id):

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

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

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

  60.  

  61.                 cv.SetCaptureProperty(self.cam,cv.CV_CAP_PROP_FRAME_HEIGHT, self._d_image.height)

  62.                 cv.SetCaptureProperty(self.cam,cv.CV_CAP_PROP_FRAME_WIDTH, self._d_image.width)

  63.  

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

  65.                         self.src = cv.CreateImage((self._d_image.width, self._d_image.height), 8, 1)

  66.  

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

  68.                         self.src = cv.CreateImage((self._d_image.width, self._d_image.height), 8, 3)

  69.  

  70.                 else:

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

  72.                         return RTC.RTC_ERROR

  73.     

  74.         return RTC.RTC_OK

  75.     

  76.     def onExecute(self, ec_id):

  77.                 try:

  78.                         self.src = cv.QueryFrame(self.cam)

  79.  

  80.                 except IOError:

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

  82.                         return RTC.RTC_ERROR

  83.  

  84.                 pixel = []

  85.  

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

  87.                         gray = cv.CreateImage(cv.GetSize(self.src), 8, 1)

  88.                         cv.CvtColor(self.src, gray, cv2.COLOR_BGR2GRAY)

  89.  

  90.                         for v in xrange(0, self._d_image.height):

  91.                                 for u in xrange(0, self._d_image.width):

  92.                                         bgr = cv.Get2D(self.src, v, u)

  93.                                         pixel.append(chr(int(bgr[0])))

  94.  

  95.                         self._d_image.pixels = "".join(pixel)

  96.                         self._cameraOut.write()

  97.                                         

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

  99.                         for v in xrange(0, self._d_image.height):

  100.                                 for u in xrange(0, self._d_image.width):

  101.                                         bgr = cv.Get2D(self.src, v, u)

  102.                                         pixel.append(chr(int(bgr[0])))

  103.                                         pixel.append(chr(int(bgr[1])))

  104.                                         pixel.append(chr(int(bgr[2])))

  105.  

  106.                         self._d_image.pixels = "".join(pixel)

  107.                         self._cameraOut.write()

  108.                                         

  109.                 else:

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

  111.                         return RTC.RTC_ERROR

  112.                 

  113.         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のカラー画像が表示されれば成功です。

ロボコ�ンマガジン

 

画素値を格納したら、Octet型のデータにするためにリストを連結します。

その後、画像データをポートから出力します。

 

  • 109-111行目

指定されたビット深度が不正なので、エラーを返します。

 

 以上で、WebCameraのコーディングは終了です。

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.cv as cv

  3.  

  4.  

  5. class CameraViewer(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._cameraIn = OpenRTM_aist.InPort("camera", self._d_image)

  13.   

  14.     def onActivated(self, ec_id):

  15.                 print('CreateWindow')

  16.                 cv.NamedWindow("ReceiveImage", cv.CV_WINDOW_AUTOSIZE)

  17.     

  18.         return RTC.RTC_OK

  19.     

  20.     def onDeactivated(self, ec_id):

  21.                 print('DestoryWindow ')

  22.                 cv.DestroyAllWindows()

  23.     

  24.         return RTC.RTC_OK

  25.     

  26.     def onExecute(self, ec_id):

  27.                 

  28.                 if self._cameraIn.isNew():

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

  30.  

  31.                         pixels = [ord(i) for i in self._d_image.pixels]

  32.  

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

  34.                                 self.image = cv.CreateImage((self._d_image.width, self._d_image.height), 8, 1)

  35.  

  36.                                 for v in xrange(0, self._d_image.height):

  37.                                         for u in xrange(0, self._d_image.width):

  38.                                                 cv.Set2D(self.image, v, u,

  39.                                                          (pixels[u + v * self._d_image.width],

  40.                                                           0,

  41.                                                           0))

  42.  

  43.                                 cv.ShowImage("ReceiveImage", self.image)

  44.                                 key = cv.WaitKey(1)

  45.                                 

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

  47.                                 self.image = cv.CreateImage((self._d_image.width, self._d_image.height), 8, 3)

  48.                                 

  49.                                 for v in xrange(0, self._d_image.height):

  50.                                         for u in xrange(0, self._d_image.width):

  51.                                                 cv.Set2D(self.image, v, u,

  52.                                                          (pixels[3 * u +     v * self._d_image.width * 3],

  53.                                                           pixels[3 * u + 1 + v * self._d_image.width * 3],

  54.                                                           pixels[3 * u + 2 + v * self._d_image.width * 3]))

  55.  

  56.                                 cv.ShowImage("ReceiveImage", self.image)

  57.                                 key = cv.WaitKey(1)

  58.                         else:

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

  60.                                 return RTC.RTC_ERROR

  61.             

  62.         return RTC.RTC_OK

 コードの解説をします。

 

  • 1-2行目

OpenCV用のImportを行います。

 

  • 9行目

デフォルトのテンプレートでは、CameraImage型の初期化が間違っているので修正します。

 

  • 15-16行目

Acitivate時に画像を表示するウィンドウを作成します。

 

  • 21-22行目

Deacitivate時に画像を表示するウィンドウを破棄します。

 

  • 28-59行目

データが取得できた場合にこのあとの処理を行います。

 

  • 29行目

InPortからデータを読み込みます。

 

  • 31行目

取得した画素値はOctet型なので、int型の値を格納したリストに変換します。

 

  • 33-60行目

CameraImage型メンバ変数bppに格納されているビット深度で処理をスイッチします。

ビット深度によって、以下の2つの処理をスイッチしています。

 

■ビット深度が8 bitのとき

グレースケール画像を表示します。

 

■ビット深度が24 bitのとき

カラー画像を表示します。

 

■それ以外

受け取ったビット深度が不正なので、エラーを返します。

 

  • 33-44行目

CameraImage型に格納されている画像サイズの画像データを定義します。

このようにCameraImage型が持つ情報を使うことで、様々な画像に対応することができます。

チャンネルは グレースケールなので、1チャンネル用意します。

 

先ほど、WebCameraでシリアライズした、画素値を画像データに格納します。

Pythonでは以下のメソッドを使って、1画素ごとに格納していきます。

 

cv.Set2D(画像データ, v座標, u座標, (B, G, R))

 

画像はグレースケールなので、B以外の画素値は0を代入します。

Bに格納する値は、以下の式で画素値のリストにアクセスします。

 

u座標 + v座標 × 画像の横幅

 

最後に、 画像データをウィンドウに表示します。

 

  • 33-44行目

CameraImage型に格納されている画像サイズの画像データを定義します。

チャンネルは カラー画像なので、3チャンネル用意します。

 

先ほど、WebCameraでシリアライズした、画素値を画像データに格納します。

画像はカラー画像なので、全ての画素値に画素値リストから値を代入します。

格納する値は、以下の式で画素値のリストにアクセスします。

 

3チャンネル × u座標 + 画素値番号 + v座標  × 画像の横幅 × 3チャンネル

 

3チャンネル × u座標は1画素は3つのデータを持つので、3つごとにアクセスをするための計算です。

画素値番号とは、[B, G, R]の並びのことです。つまりBなら0番になります。

v座標  × 画像の横幅 × 3チャンネルは、行ごとにアクセスするための計算になります。

 

最後に、 画像データをウィンドウに表示します。

 

  • 54-57行目

ビット深度が8、 24のいずれでもない場合は、エラーを返します。

 

 以上で、CameraViewerのコーディングは終了です。

画素値のアクセスが少し難いでしょうか。サンプルを丸写しでも構いませんので、頑張って最後までコーディングしてみて下さい。

今回の処理はできる限り一般化していますので、色々な場面でそのまま使えるはずです。

C++でのカラー画像のデータ構造

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