RealSenseをcv2.VideoCaptureライクに使う

## RealSenseとは
Intelが作っているDepthカメラシリーズです。
今回D415を使います。
このRealSenseをOpenCVで使うとき、通常のWebカメラと同じように `cv2.VideoCapture(0)` とできなくて面倒だったので、VideoCaptureと同じように使えるクラスを作成しました。

## 依存関係
- pyrealsense2
- numpy

どちらもpipでインストールできます。

#### 2019/10/11 追記
MacやJetson Nanoなど，ArmチップのCPUではpipでpyrealsense2がインストールできないようです．sourceからbuildする必要があります．

## コード
下記のクラスが `cv2.VideoCapture` の代わりになるものです。

```python:realsensecv.py
import pyrealsense2 as rs
import numpy as np


class RealsenseCapture:

    def __init__(self):
        self.WIDTH = 640
        self.HEGIHT = 480
        self.FPS = 30
        # Configure depth and color streams
        self.config = rs.config()
        self.config.enable_stream(rs.stream.color, self.WIDTH, self.HEGIHT, rs.format.bgr8, self.FPS)
        self.config.enable_stream(rs.stream.depth, self.WIDTH, self.HEGIHT, rs.format.z16, self.FPS)

    def start(self):
        # Start streaming
        self.pipeline = rs.pipeline()
        self.pipeline.start(self.config)
        print('pipline start')

    def read(self, is_array=True):
        # Flag capture available
        ret = True
        # get frames
        frames = self.pipeline.wait_for_frames()
        # separate RGB and Depth image
        self.color_frame = frames.get_color_frame()  # RGB
        self.depth_frame = frames.get_depth_frame()  # Depth

        if not self.color_frame or not self.depth_frame:
            ret = False
            return ret, (None, None)
        elif is_array:
            # Convert images to numpy arrays
            color_image = np.array(self.color_frame.get_data())
            depth_image = np.array(self.depth_frame.get_data())
            return ret, (color_image, depth_image)
        else:
            return ret, (self.color_frame, self.depth_frame)

    def release(self):
        # Stop streaming
        self.pipeline.stop()
```
今回 `realsensecv.py` という名前で保存して、他のファイルからimportします。


## Example
上記のクラスを用いてリアルタイムに映像を出力してみます。

```python:rs_streaming.py
# coding: utf-8
import cv2
import numpy as np
from realsensecv import RealsenseCapture


cap = RealsenseCapture()
# プロパティの設定
cap.WIDTH = 640
cap.HEIGHT = 480
cap.FPS = 30
# cv2.VideoCapture()と違ってcap.start()を忘れずに
cap.start()

while True:
    ret, frames = cap.read()  # frames[0]にRGB、frames[1]にDepthの画像がndarrayが入っている
    color_frame = frames[0]
    depth_frame = frames[1]
    # ヒートマップに変換
    depth_colormap = cv2.applyColorMap(cv2.convertScaleAbs(
        depth_frame, alpha=0.08), cv2.COLORMAP_JET)
    # レンダリング
    images = np.hstack((color_frame, depth_colormap))  # RGBとDepthを横に並べて表示
    cv2.imshow('RealSense', images)

    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

# ストリーミング停止
cap.release()
cv2.destroyAllWindows()
```

pyrealsense2ではdepth_frameから距離を取得する `get_distance` メソッドなどがありますが、RealsenseCaptureで出力されるdepth_frameにはそのメソッドはありません。
`get_distance` などを使いたい場合は、インスタンス変数にアクセスしてください。

下記にクリックしたピクセルの距離を取得する例を載せます。

```python:get_distance.py
# coding: utf-8

import cv2
import numpy as np
from realsensecv import RealsenseCapture


# マウスイベント時に処理を行う
def mouse_event(event, x, y, flags, param):
    depth_frame = param
    distance = depth_frame.get_distance(x, y)
    # 左クリックで座標を返す
    if event == cv2.EVENT_LBUTTONUP:
        return print(distance)

cap = RealsenseCapture()
cap.start()

while True:
    ret, frames = cap.read()
    color_frame = frames[0]
    depth_frame = frames[1]
    # ヒートマップに変換
    depth_colormap = cv2.applyColorMap(cv2.convertScaleAbs(
        depth_frame, alpha=0.08), cv2.COLORMAP_JET)
    # レンダリング
    images = np.hstack((color_frame, depth_colormap))
    cv2.namedWindow('RealSense', cv2.WINDOW_AUTOSIZE)
    cv2.imshow('RealSense', images)

    # マウスイベント時に関数mouse_eventの処理を行う
    cv2.setMouseCallback('RealSense', mouse_event, cap.depth_frame)

    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

# ストリーミング停止
cap.release()
cv2.destroyAllWindows()
```

---
これでVideoCaptureのように使えて、既存のコードを簡単に使いまわせるようになりました！
ミスや改善点などあればコメントお願いしますm(_ _)m