ROSの勉強　第45弾：ROS (melodic)においてpython3系でcv_bridgeが使えないことへの解決

# #プログラミング ROS< ROS (melodic)においてpython3系でcv_bridgeが使えないことへの解決 >

## はじめに
ROS(Robot Operating System)をさらに扱えるようになることが目的である．その第45弾として，「ROS (Melodic)においてpython3系でcv_bridgeが使えないことへの解決」を扱う．
これは，著者が実際にmelodicにおいてpython3系でcv_bridgeを使おうとしたときに数か月悩み解決に至ったまでの話である．

## 環境
##### 仮想環境
|||
|:-:|:--|
|ソフト|VMware Workstation 15|
|実装RAM|3 GB|
|OS|Ubuntu 64 ビット|
|isoファイル|ubuntu-ja-18.04.3-desktop-amd64.iso|

##### コンピュータ
|||
|:-:|:--|
|デバイス|MSI|
|プロセッサ|Intel(R) Core(TM) i5-7300HQ CPU @ 2.50GHz 2.50GHz|
|実装RAM|8.00 GB (7.89 GB 使用可能)|
|OS|Windows (Windows 10 Home, バージョン：21H1)|

##### ROS
|||
|:-:|:--|
|Distribution|melodic|

## cv_bridgeとは
ROS上で配信されている画像に対して何かしらの処理をする際には，**型をopencvなどで扱える形に**しておく必要がある．それに用いられるのが**cv_bridge**というライブラリである．

## なぜmelodicでは動かないのか？
実は，第24弾では，何気にcv_bridgeをpython3系で使っていた．

https://qiita.com/Yuya-Shimizu/items/5c408fb06878471ad486

これは，noeticであったから特に問題が起きていなかった．

noeticではできて，melodicではできない. . .

どうやら，melodicにおいては，**cv_bridgeがpython2系で動いてしまっている**ようだ．noeticでは，python3系が標準となっているためにこのようなエラーが起きなかった．



## 試したこと
先人たちもこれについて解決をしようと試みていたようだ．python3系を標準に使えるようにしたり，cv_bridgeをpython3系でコンパイルすることも提案されていた．

https://qiita.com/tnjz3/items/4d64fc2d36b75e604ab1

https://qiita.com/Hiroaki-K4/items/01945ecea97c26ff8324

どれも試してみたが，違うところでエラーが出てコンパイルができなかったりとかなり労力が多い．また，私の場合，うまくいかなかった．そこで，更にいろいろと調べていく中で，GitHubのIssueの中に以下のようなものを見つけた．

https://github.com/ros-perception/vision_opencv/issues/207#issuecomment-790392413

```python
import numpy as np
im = np.frombuffer(image_data.data, dtype=np.uint8).reshape(image_data.height, image_data.width, -1)
```
ここで，ROSにおいて画像処理するとき，**変換さえできれば別にcv_bridgeを使わなくてもよい**ということを知った．これは，かなりの収獲である．numpyは特にpythonのバージョンに依存しているわけでもなく，比較的頻繁に使用されるライブラリであるから，今回のような**環境依存に関するエラーへの解決としては，非常によい策**だと思う．

ただ，もう少しIssueをのぞいて見ると以下のような美しい解決策が示されていた．


```python
import ros_numpy
np_img = ros_numpy.numpify(ros_img_msg)
```
後者の方が使いやすい形であるため，後者について見ていく．

もう少し詳しく調べるべく，そのros_numpyについて調べた．

## ros_numpyについて
https://github.com/eric-wieser/ros_numpy

#### インストール

```bash:
sudo apt-get install ros-melodic-ros-numpy
```

上記のREADMEを見れば分かるが，変換についてはいくつかの対応しているため，ここでは画像についての変換をまとめておく．

#### 変換（sensor_msgs.msg.Image -> ndarray）

```python:ROSでの画像データの型をopencvで扱える型へ
image = ros_numpy.numpify()
```

#### 変換（ndarray -> sensor_msgs.msg.Image）

```python:opencvで扱える型をROSでの画像データの型へ
image = ros_numpy.msgify()
```

### 利用の流れ
```python:変換して処理してまた変換，そして配信
#適当な画像処理関数
def add_center(image):
    img = image.copy()
    height, width = img.shape[:2]
    center_point = (width//2, height//2)
    
    cv2.circle(img,
           center = center_point,
           radius = 60,
           color = (0, 255, 0),
           thickness = -1,
           lineType = cv2.LINE_4,
           shift = 0)
    
    return img

#ROSの画像トピックを購読したときに実行するコールバック関数
def image_callback(msg):
    image = ros_numpy.numpify(msg)

    # 何かしらの画像処理を実行
    processed_img = add_center(image)

    # 処理した画像をROSでの画像データに変換
    processed_msg = ros_numpy.msgify(Image, processed_img, encoding= 'rgb8')

    # 処理した画像を配信
    pub.publish(processed_msg)
```

##解決のまとめ
cv_bridgeを使いたい局面において，ROSがpython2が標準形である場合，代わりに``ros_numpy``を使えばよい．むしろ，ros_numpyのほうが**汎用的かもしれない**．上記のros_numpyのGitHubによると，**ROSの型への変換はいくつかの型がサポートされている**ようだ．

##感想
今回は，実際に悩み続けていたものが解消されて非常に気分がよい．だんだんとROS2が進んできているが，melodicの需要もまだあるようだ．それゆえに，今回の経験は非常にためになったのではないかと思う．（かなりの労力を費やしたが．．．）これで，melodicにおいてもpython3系で画像処理ができるようになる．さらに，ros_numpyという便利なものを知ることもできた．

##参考

https://qiita.com/tnjz3/items/4d64fc2d36b75e604ab1

https://qiita.com/Hiroaki-K4/items/01945ecea97c26ff8324

https://github.com/ros-perception/vision_opencv/issues/207#issuecomment-790392413

https://github.com/eric-wieser/ros_numpy