ROS講座114 DataBaseで画像を扱いブラウザからアクセスする

# 環境
この記事は以下の環境で動いています。

| 項目 | 値 |
|:-:|:-:|
| CPU | Core i5-8250U |
| Ubuntu | 16.04 |
| ROS | Kinetic |
| python | 2.7.12 |
| mongodb | 2.6.10 |

インストールについては[ROS講座02 インストール](https://qiita.com/srs/items/e0e0a9dc3f94c2d3348e)を参照してください。
またこの記事のプログラムはgithubにアップロードされています。[ROS講座11 gitリポジトリ](https://qiita.com/srs/items/25a56a6abda41dcf396e)を参照してください。

# 概要
これまでいろいろなデータをブラウザで表示しましたが、ROSで取得した画像をブラウザで表示するのは面倒です。
ROSのimage形で保存してもブラウザでは表示できません。一度ROSで画像をファイルに書き出してブラウザで見ることもできますが、あまりROSでファイルシステムを使いたくありません。
ここでは画像のデータをBase64でエンコードすることでブラウザで表示を行います。またmongodbでこのデータを保存することで、電源を切っても情報を保存しておけます。
base64形式はバイナリーデータをテキストで表すものです。3バイトのバイナリーを4つのASCII文字に変換します。

# ソースコード
rosserviceを受けてImageをmongodbに保存する。データベースの削除をする。browserに対してデータを送信します。

## mongodbにアクセスするROSノード

```python:web_lecture/scripts/mongo_image.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import rospy
from std_srvs.srv import Empty, EmptyResponse
from sensor_msgs.msg import Image
from web_lecture.msg import StringStamp
from web_lecture.srv import StringStampList, StringStampListResponse
from mongodb_store.message_store import MessageStoreProxy
from cv_bridge import CvBridge, CvBridgeError
import base64
import cv2

class MongoImage:
    def __init__(self):
        rospy.Service('shot', Empty, self.shotCallback)
        rospy.Service('delete', Empty, self.deleteCallback)
        rospy.Service('request', StringStampList, self.requestCallback)
        rospy.Subscriber("/image_raw", Image, self.imageCallback)
        self.last_image = Image()
        self.scale = 0.5
        self.bridge = CvBridge()
        self.last_base64 = None
        self.msg_store = MessageStoreProxy(database="srs", collection="image_stamp")

    def imageCallback(self, msg):
        self.last_image = msg

    def shotCallback(self, msg):
        frame = self.bridge.imgmsg_to_cv2(self.last_image, "bgr8")
        height = frame.shape[0]
        width = frame.shape[1]
        frame2 = cv2.resize(frame , (int(width*self.scale), int(height*self.scale)))
        encode_param = [int(cv2.IMWRITE_JPEG_QUALITY), 50]
        result, frame3 = cv2.imencode(".jpg", frame2, encode_param)
        mongo_data = StringStamp()
        mongo_data.stamp = rospy.get_rostime()
        mongo_data.data = base64.b64encode(frame3)
        try:
            p_id = self.msg_store.insert(mongo_data)            
        except rospy.ServiceException, e:
            print "Service call failed: %s"%e
        return EmptyResponse()

    def deleteCallback(self, msg):
        list = self.msg_store.query(StringStamp._type)
        for item in list:
            self.msg_store.delete(str(item[1]["_id"]))
        return EmptyResponse()

    def requestCallback(self, msg):
        mongo_list = self.msg_store.query(StringStamp._type)
        output = []
        try:
            for item in mongo_list:
                output.append(item[0])
        except rospy.ServiceException, e:
            print "Service call failed: %s"%e  
        return StringStampListResponse(output)

if __name__ == '__main__':
    rospy.init_node('mongo_image')
    mongo_image = MongoImage()
    rospy.spin()
```

* `base64.b64encode(cv2.imencode(".jpg", frame, encode_param))`でopencv形式からjpg形式のデータを作り、それをbase64形式に変換します。
* mongodbでは一括で削除をするというapiはありません。一度queryで一覧を取得してからdeleteを行います。

## blowserで表示するhtml

```html:web_lecture/www/image_view.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <head>
    <meta http-equiv="content-type" content="text/html;charset=utf-8"/>
    web_lecture index.html
  </head>
  <body>
    <p>
      <a href="pub.html">pub.html</a> <br/>
      <a href="sub.html">sub.html</a> <br/>
      <a href="camera.html">camera.html</a> <br/>
      <a href="camera_full.html">camera_full.html</a> <br/>
      <a href="twist.html">twist.html</a> <br/>
      <a href="gamepad_api_test.html">gamepad_api_test.html</a> <br/>
      <a href="gamepad.html">gamepad.html</a> <br/>
      <a href="image_view.html">image_view.html</a> <br/>
    </p>
  </body>
</html>
```

# 実行

```shell:実行
mkdir ~/.ros/mongo
roslaunch web_lecture mongo_image.launch 
```

![mongo_image.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254442/226d1d84-3eba-ffe4-d275-490ef3974989.gif)

* 「shot」を押すとカメラに写っている画像を保存します。
* 「request」を押すと保存されている画像をblowserに表示します。
* 「delete」を押すと保存されている画像をすべて消去します。

# 参考
[base64で画像をhtmlに埋め込む](https://edge.sincar.jp/web/base64-inline-image/)
[pythonでbase64に変換](https://qiita.com/TsubasaSato/items/908d4f5c241091ecbf9b)

# 目次ページへのリンク
[ROS講座の目次へのリンク](https://qiita.com/srs/items/5f44440afea0eb616b4a)