見たいところに目が届くヘッドマウントディスプレイ「遊EYE離脱」
みんなのラズパイコンテスト2016に応募するために「遊EYE離脱」を作成しました!
目次
機能
本機はヘッドマウントディスプレイと内視鏡型カメラで構成されています。
ヘッドセットのディスプレイにはRaspberry Pi 3を用いて右目用と左目用に同一の内視鏡カメラ映像2つがリアルタイムで表示されます。
カメラを様々な方向や対象に向けることで、まるで目が体から離脱したような体験ができます。
使い方
機器を起動しヘッドマウントディスプレイを装着します。カメラを手に持ち対象物に向けます。
以下のようなシチュエーションで本機の有用性がより発揮されます。
仕組み
構成
Raspberry Pi 3に7インチIGZOディスプレイと内視鏡カメラを接続し電源にモバイルバッテリを使用した。ディスプレイを市販のVRゴーグルを加工して実装しヘッドマウントディスプレイを構成している。
- Raspberry Pi 3
-
Raspberry Pi用 7インチ IGZO LCDパネル
- USB内視鏡カメラ
- モバイルバッテリー
-
VRゴーグル3D
ラズパイ設定
経緯
最初は Raspberry Pi Zero での実装予定だったのですが、IGZOパネルが高精細ということもあり、カメラ画像を遅延無く表示させるには、画像サイズを320×240 位に落とさねばなりませんでした。
それだと、IGZOパネル上では上記の様に画面が小さすぎる為、Raspberry Pi Zero での実装を断念し、高性能な Raspberry Pi 3 を使用しました。
(ちなみにRaspberry Pi 2 はこのブログを動かしているサーバーにしています。)
設定
Raspberry Pi 3 セットアップ
まずは、Raspberry Pi 3 のセットアップ、IGZOパネルの接続、固定IPの設定を行いました。
Python版OpenCVのインストール
Raspberry Pi 3 に Python版OpenCV をインストールします。
1 |
sudo apt-get install libopencv-dev python-opencv |
内視鏡カメラの接続
こちらは接続しただけで認識されました。
OpenCV
Python コード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
import cv2 import numpy as np cv2.namedWindow("bg") cv2.namedWindow("left") cv2.namedWindow("right") cv2.namedWindow("bg_mask") img = np.empty((1200,1920,3)) img.fill(0) img2 = np.empty((275,1920,3)) img2.fill(0) cv2.moveWindow("bg",0,0) cv2.moveWindow("left",156,312) cv2.moveWindow("right",996,312) cv2.moveWindow("bg_mask",0,0) src = cv2.VideoCapture(0) num = 1.2 height = int(480 * num) width = int(640 * num) cv2.imshow("bg",img) cv2.imshow("bg_mask",img2) while True: retval, frame = src.read() if frame is None: break resizedImg = cv2.resize(frame,(width,height)) # show cv2.imshow("left",resizedImg) cv2.imshow("right",resizedImg) # quit esc key key = cv2.waitKey(33) if key == 27: break cv2.destroyAllWindows() src.release() |
PYTHON コード 解説
ウィンドウの作成
1 2 3 4 |
cv2.namedWindow("bg") cv2.namedWindow("left") cv2.namedWindow("right") cv2.namedWindow("bg_mask") |
ウィンドウは、ここで宣言された順番に表示されるので、表示順に下記のウィンドウを作成します。
- 黒背景用ウィンドウ
- 左目用カメラウィンドウ
- 右目用カメラウィンドウ
- カメラウィンドウのタイトルバーを消す為のウィンドウ
4についてですが、OpenCVのウィンドウはタイトルバーを消すことは出来ない様なので、苦肉の策として、タイトルバーの上に更に黒背景のウィンドウを重ねるという手段をとっています。
塗りつぶし用配列の作成
1 2 3 4 5 |
img = np.empty((1200,1920,3)) img.fill(0) img2 = np.empty((275,1920,3)) img2.fill(0) |
- IGZOパネルのウィンドウサイズ(1920*1200) 配列を作り、黒(0)で塗りつぶします。
- 前述の、タイトルバーを消す為のウィンドウサイズ(1920*275)配列を作り、黒で塗りつぶします。高さは、実際に画面を見ながら調節しました。
ウィンドウの配置
1 2 3 4 |
cv2.moveWindow("bg",0,0) cv2.moveWindow("left",156,312) cv2.moveWindow("right",996,312) cv2.moveWindow("bg_mask",0,0) |
各ウィンドウの配置を行います。左上を0として、横、縦のピクセルを指定します。画面の配置は、最初は真ん中に置いてから画面を見ながら調整しました。
カメラを開く
1 |
src = cv2.VideoCapture(0) |
カメラウィンドウのリサイズ
1 2 3 4 |
num = 1.2 height = 640 * num width = 480 * num |
画面を見て丁度良い感じのサイズが1.2倍だったので、リサイズ用の変数をセットします。
最初、カメラの解像度がわからなかったため、whileの中に下記コードを入れて取得しました。
1 2 3 |
fHight = im.shape[0] fWidth = im.shape[1] print str(fWidth) + " " + str(fHeight) |
print すると、LXTerminal 上に フレーム毎に数値が出力されました。
背景ウィンドウの表示
1 2 |
cv2.imshow("bg",img) cv2.imshow("bg_mask",img2) |
背景ウィンドウはwhileの中に入れる必要が無いので、先に表示させます。
(最初、whileの中に入れてしまったのですが、それだと結構遅延が発生しました。)
画面の表示
1 |
while True: |
フレーム取得、表示を繰り返します。
1 |
retval, frame = src.read() |
キャプチャ画面を取得します。
1 2 |
if frame is None: break |
フレームが無い場合終了します。
1 |
resizedImg = cv2.resize(frame,(int(width), int(height))) |
先ほどセットしたサイズにリサイズします。
1 2 3 |
# show cv2.imshow("left",resizedImg) cv2.imshow("right",resizedImg) |
imshowで画面を表示します。ちなみにここでの順番は、表示順には関係ありませんでした。
1 2 3 4 |
# quit esc key key = cv2.waitKey(33) if key == 27: break |
エスケープキーを押したら終了するコードです。
1 2 |
cv2.destroyAllWindows() src.release() |
終了時は、画面を破棄し、カメラを開放します。
実際に鼻の穴や耳の穴をみると、思いの外の臨場感で気持ち悪かったです。。。。。
追記(16/9/16)
Adafruit様のブログで紹介していただけました!
追記(16/11/18)
みんなのラズパイコンテスト2016で優秀賞頂きました!!
.@homemadegarbage みんなのラズパイコンテスト賞状とどいたよ! pic.twitter.com/G0rWBhO4Nz
— HomeMadeGarbage (@H0meMadeGarbage) 2016年11月13日
追記(17/1/18)
ITproさんで紹介して頂きました!