cv_bridgeのRGB⇔BGR変換について
はじめに
ROS の Image メッセージと OpenCV の画像データ (C++ では cv::Mat、Python では ndarray) の相互変換をしてくれる cv_bridge ですが、RGB ⇔ BGR 変換の仕様が思っていたよりややこしかったので、忘れないようにまとめました。
RGB と BGR
一般的にカラー画像は、光の三原色である赤 (R)、緑 (G)、青 (B) の3色の重ね合わせで表現されます。1色1チャンネルとして扱われるのでカラー画像には3チャンネルあるわけですが、この並び順には RGB と BGR の2つの流派があります。OpenCV では BGR、それ以外の画像処理ライブラリでは主に RGB が用いられています。そのため、カラー画像を扱うときは赤 (R) と青 (B) を取り間違えないよう、適切に RGB ⇔ BGR 変換を実施する必要があります。
ROS の Image メッセージ
ROS ノード間で画像データを送受信するときは、Image メッセージ を使います。このメッセージには encoding というフィールドがあり、include/sensor_msgs/image_encodings.h で定義される文字列を設定することになっています。例えば、以下のようなものがあります。
-
rgb8
:RGB のカラー画像で、各画素値のサイズは 8 bit -
bgr8
:BGR のカラー画像で、各画素値のサイズは 8 bit -
mono16
:グレースケール画像で、各画素値のサイズは 16 bit -
8UC3
:3チャンネルのカラー画像 (RGB か BGR かは不定) で、各画素値のサイズは 8 bit
送信側が Image メッセージの encoding を設定することで、受信側は送られてきた画像データが RGB か BGR かなどの情報を得ることができます。
cv_bridge の encoding の仕様
Image メッセージから OpenCV へ変換
Python の場合、Image メッセージから OpenCV への変換には imgmsg_to_cv2()
を使用します。
引数の desired_encoding に以下のいずれかが指定された場合、入力された Image メッセージの encoding と照らし合わせ、必要に応じてチャンネルの並び順や各画素値のサイズが変換されます。そして変換後の画像データが ndarray として出力されます。
mono8
mono16
bgr8
rgb8
bgra8
rgba8
ただし、passthrough
が指定された場合、通常はデータの内容自体は特に変換されず ndarray として出力されます。
OpenCV から Image メッセージへ変換
OpenCV から Image メッセージへの変換には cv2_to_imgmsg()
を使用します。この関数は画像データを変換しません。
ここで渡す引数 encoding は先程の desired_encoding とは異なり、関数に渡す画像データのエンコーディングです。例えば、通常の OpenCV のカラー画像であれば bgr8
、グレースケール画像であれば mono8
です。出力される Image メッセージの encoding フィールドにはこの値が設定されます。
画像データ自体は BGR なのに rgb8
が指定された場合、受信側が RGB の画像データであると判断してしまうため、結果的に色が反転するという事象が発生します。(rqt_image_view なども自動的にチャンネル順を変換するため同様の結果になります。)
なお、passthrough
が指定された場合、Image メッセージの encoding フィールドには 8UC3
などが設定されるようです。
まとめ
- Image メッセージには encoding というフィールドがあり、 RGB か BGR かなどの情報を保持する。
-
imgmsg_to_cv2()
は必要に応じて RGB ⇔ BGR 変換する。desired_encoding には出力したい画像データのエンコーディングを指定する。 -
cv2_to_imgmsg()
は RGB ⇔ BGR 変換しない。encoding には入力する画像データのエンコーディングを指定する。
Discussion