2
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

RealSenseをLiDARのように使い、インタラクティブな壁を作った

Last updated at Posted at 2019-12-27

インタラクティブな壁を作りました。

実験環境

ハードウェア

ソフトウェア

  • Windows 10
  • Anaconda
  • Python 3.6.8
  • pyrealsense, opencv-python, numpy
  • RealSense SDK

環境設定

RealSense D435を床に置きます。設置位置からプロジェクションのスクリーンの底辺までの距離をZ_MINとします。
D435の水平視野角は91.2°なので、黄色い点線から内側が見える感じですね。プロジェクションのスクリーン幅(SC_WIDTH)の最大は、2Z_MINtan(91.2°/2)。

D435:深度センサ視野角(水平 x 垂直 x 斜め)91.2° x 65.5° x 100.6°(±3°)
D415:深度センサ視野角(水平 x 垂直 x 斜め)69.4° x 42.5° x 77°(±3°)

01.PNG

プロジェクタを設置します。赤い枠に映像が映るようにしています。
80803202_1593190520830448_1336100068779884544_n.jpg

適宜、壁をタッチしたい場所や環境に合わせて、パラメータを調整してください。

プログラム

RealSenseのDepth画像から真ん中のライン(Y=0)をスキャンします。XとZの値を保持して、それを表示しています。

import pyrealsense2 as rs
import numpy as np
import cv2

# D435 Settings
RS_WIDTH = 848
RS_HEIGHT = 480
TARGET_DISTANCE = 10
align = rs.align(rs.stream.color)
pipe = rs.pipeline()
cfg = rs.config()
cfg.enable_stream(rs.stream.depth, RS_WIDTH, RS_HEIGHT,  rs.format.z16, 90)
cfg.enable_stream(rs.stream.color, RS_WIDTH, RS_HEIGHT, rs.format.bgr8, 60)
profile = pipe.start(cfg)
intr = profile.get_stream(rs.stream.color).as_video_stream_profile().get_intrinsics()
depth_sensor = profile.get_device().first_depth_sensor()
depth_scale = depth_sensor.get_depth_scale()
distance_max = TARGET_DISTANCE/depth_scale

# Screen Settings
cv2.namedWindow("screen", cv2.WINDOW_NORMAL)
cv2.setWindowProperty("screen", cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN)
screen = cv2.imread("screen.png", -1)
height, width, channels = screen.shape[:3]
SC_WIDTH = 1530 # mm
SC_HEIGHT = 870 # mm
SC_CENTER_X = int(SC_WIDTH/2)
SC_CENTER_Y = int(SC_HEIGHT/2)

SC_IMAGE_WIDTH = 854 # pix
SC_IMAGE_HEIGHT = 480 # pix
SC_IMAGE_CENTER_X = int(SC_IMAGE_WIDTH/2)
SC_IMAGE_CENTER_Y = int(SC_IMAGE_HEIGHT/2)

X_MIN = int(-SC_WIDTH/2)
X_MAX = int(SC_WIDTH/2)
Z_MIN = 1280
Z_MAX = Z_MIN + SC_HEIGHT
OFFSET_X = 20
OFFSET_Z = 110

while True:
    frames = pipe.wait_for_frames()
    aligned_frames = align.process(frames)
    color_frame = aligned_frames.get_color_frame()
    depth_frame = aligned_frames.get_depth_frame()
    if not depth_frame or not color_frame:
        continue

    color_image = np.asanyarray(color_frame.get_data())
    depth_image = np.asanyarray(depth_frame.get_data())

    scan_pts = []
    for uy in range(RS_HEIGHT):
        if uy == int(RS_HEIGHT/2) - 7:
            for ux in range(RS_WIDTH):
                z = depth_frame.get_distance(ux, uy)
                x = (float(ux) - intr.ppx) * z / intr.fx
                y = (float(uy) - intr.ppy) * z / intr.fy
                scan_pts.append([x, y, z])

    sc_image = np.zeros((SC_IMAGE_HEIGHT, SC_IMAGE_WIDTH, 3), np.uint8)
    for p in scan_pts:
        y = p[1]*1000
        if abs(y) < 1:
            x = -p[0]*1000 + OFFSET_X
            z =  p[2]*1000 + OFFSET_Z
            if abs(x) < X_MAX:
                if z > Z_MIN and z < Z_MAX:
                    ux = int((x + X_MAX) / SC_WIDTH * SC_IMAGE_WIDTH)
                    uy = SC_IMAGE_HEIGHT - int((z - Z_MIN) / SC_HEIGHT * SC_IMAGE_HEIGHT)
                    if ux >= 0 and ux <= SC_IMAGE_WIDTH and uy >= 0 and uy < SC_IMAGE_HEIGHT:
                        sc_image[uy, ux, 0] = 255 # B
                        sc_image[uy, ux, 1] = 255 # G
                        sc_image[uy, ux, 2] = 255 # R

    cv2.imshow("screen", sc_image)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

まとめ

隣同士の点群の距離が短いものをひとまとまりにクラスタリングして、そのクラスタの大きさから手くらいの大きさだけに反応するようにすれば、壁にタッチしたらなんかするとか、文字書いたりもできますね。

2
4
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?