ARで2つの壁を貫通する仕組みを開発しています
ARで壁に穴をあける(デモ動画)
何を言ってるかわからないと思いますでのデモ動作をご覧ください。
デモ動画では2枚のARマーカーを縦に重ねてそれぞれの板に穴があいたようなギミックを施しています。
環境
- Windows(カメラ付き)
- ラズパイ①(カメラ付き)
- ラズパイ②(カメラ付き)
- ARマーカー付きの板
Windows(カメラ付き)
2つの壁を貫通するPythonスクリプトはWindows11(カメラ付き)環境で実行します。
PS C:\> python --version
Python 3.9.5
PS C:\> pip list
Package Version
--------------------- --------
:
numpy 1.23.1
opencv-contrib-python 4.5.4.60
Pillow 9.2.0
必要モジュールのインストールコマンド
$ pip install Pillow
$ pip install numpy
$ pip install opencv-contrib-python
ラズパイ①(カメラ付き)
ストリーミング配信にラズパイ(カメラ付き)を使用します。
MJPEG-Streamerをインストールしています。
MJPEG-Streamerのインストールコマンドは以下です。ラズパイにカメラデバイスを有効にしたあとで実施します。
$ sudo apt update
$ sudo apt install build-essential libjpeg9-dev imagemagick libv4l-dev cmake -y
$ sudo apt install -y git
$ git clone https://github.com/jacksonliam/mjpg-streamer.git
$ cd /mjpg-streamer/mjpg-streamer-experimental
$ sed -i 's/add_subdirectory.*input_opencv/#add_subdirectory(plugins\/input_opencv/g' CMakeLists.txt
$ sudo make
$ sudo make install
$ cp /mjpg-streamer/mjpg-streamer-experimental/input_raspicam.so /mjpg-streamer/
※たまにinput_raspicam.so
が作成されないことがありました。そのような場合は、バックアップしておいたinput_raspicam.so
ファイルをコピーしたら動きました。
ラズパイ②(カメラ付き)
『環境②:ラズパイ(カメラ付き)』と同じ構成にします。
ARマーカー付きの板
2種類のARマーカーを印刷し板に貼り付けます。
ARマーカーは以下のスクリプトで画像出力できます。
import cv2
aruco = cv2.aruco
dictionary = aruco.getPredefinedDictionary(aruco.DICT_4X4_50)
def arGenerator():
fileName = "ar.png"
# 0: ID番号,150x150ピクセル
generator = aruco.drawMarker(dictionary, 0, 150)
cv2.imwrite(fileName, generator)
img = cv2.imread(fileName)
arGenerator()
ID0
番とID1
番を出力します。上記スクリプトを以下のように変更します。
# 0: ID番号,150x150ピクセル
generator = aruco.drawMarker(dictionary, 0, 150)
# 1: ID番号,150x150ピクセル
generator = aruco.drawMarker(dictionary, 1, 150)
このように板に印刷したARマーカーを貼り付けます。
これで環境の説明は以上です。
ラズパイの設置
環境が用意できたので設置します。
ラズパイ①とラズパイ②をARマーカー付きの板の裏側に貼り付けます。
ARマーカー付きの板を縦に並べ、手前の板の前にWindows接続のカメラを設置すれば完了です。
スクリプトの準備
Windows環境に『2つの壁を貫通するPythonスクリプトusoana_double_penetrate.py
』を用意します。
import asyncio
from PIL import Image, ImageDraw, ImageFont
import numpy as np
import io
import cv2
import cv2.aruco as aruco # (Windows10) python -m pip install opencv-contrib-python
import sys
targetVideo = 0 # Windowsのカメラデバイス
mappingVideoA = "http://{{ラズパイ①のIPアドレス}}:8080/?action=stream"
mappingVideoB = "http://{{ラズパイ②のIPアドレス}}:8080/?action=stream"
# cap - windows接続カメラの設定
cap = cv2.VideoCapture(targetVideo, cv2.CAP_MSMF)
cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc('M', 'J', 'P', 'G'))
cap.set(cv2.CAP_PROP_FPS, 60) # カメラFPSを60FPSに設定
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280) # カメラ画像の横幅を1280に設定
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720) # カメラ画像の縦幅を720に設定
# ------------------
aruco_dict = aruco.getPredefinedDictionary(aruco.DICT_4X4_50)
# 幅,高さ取得
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
size = (width, height)
frame_rate = int(cap.get(cv2.CAP_PROP_FPS))
fn = 0
map_vid_A = cv2.VideoCapture(mappingVideoA)
map_vid_B = cv2.VideoCapture(mappingVideoB)
while cap.isOpened():
map_vid_A.set(cv2.CAP_PROP_POS_FRAMES, fn)
map_vid_B.set(cv2.CAP_PROP_POS_FRAMES, fn)
ret, frame = cap.read()
# Check if frame is not empty
if frame is None :
break
for lp in range(2):
# ARマーカー検出
parameters = aruco.DetectorParameters_create()
corners, ids, _ = aruco.detectMarkers(frame, aruco_dict, parameters=parameters)
frame = aruco.drawDetectedMarkers(frame, corners, ids)
scale_percent = 100
width = int( frame.shape[1] * scale_percent / 100 )
height= int( frame.shape[0] * scale_percent / 100 )
dim = (width, height)
frame = cv2.resize(frame, dim, interpolation = cv2.INTER_AREA)
org_frame = frame
if np.all(ids != None):
for i in range( len(ids) ):
c = corners[ int(i) ]
print("lp={0}, detecNum= {1} ids[{2}][0] = {3}".format(lp, len(ids), i, ids[i][0]))
if ids[i][0] == 0:
ret_v, im_src = map_vid_A.read()
else:
ret_v, im_src = map_vid_B.read()
x1 = (c[0][0][0], c[0][0][1])
x2 = (c[0][1][0], c[0][1][1])
x3 = (c[0][2][0], c[0][2][1])
x4 = (c[0][3][0], c[0][3][1])
im_dst = frame
size = im_src.shape
pts_dst = np.array([x1, x2, x3, x4])
pts_src = np.array(
[
[0 , 0],
[size[1]-1 , 0],
[size[1]-1 , size[0]-1],
[0 , size[0]-1]
],
dtype=float
)
h, status = cv2.findHomography(pts_src, pts_dst)
temp = cv2.warpPerspective(im_src.copy(), h, (org_frame.shape[1], org_frame.shape[0]))
cv2.fillConvexPoly(org_frame, pts_dst.astype(int),0,16)
org_frame = cv2.add(org_frame, temp)
frame = org_frame
# ディスプレイに表示
cv2.imshow('frame', frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# Release everything:
cap.release()
cv2.destroyAllWindows()
上記スクリプトの以下に示す箇所を実施環境にあわせて修正します。
targetVideo = 0 # Windowsのカメラデバイス
mappingVideoA = "http://{{ラズパイ①のIPアドレス}}:8080/?action=stream"
mappingVideoB = "http://{{ラズパイ②のIPアドレス}}:8080/?action=stream"
実行
ARで壁に穴をあけるPythonスクリプトを実行する手順です。
1.ラズパイ①でストリーミング配信を開始する
以下コマンドを実行しストリーミング配信を開始します。
$ cd /mjpg-streamer/mjpg-streamer-experimental/
$ mjpg_streamer \
-o "output_http.so -w ./www" \
-i "./input_raspicam.so -x 400 -y 400 -fps 30 -q 100"
上記コマンド実行後、Windows環境のブラウザで、http://{{ラズパイ①のIPアドレス}}:8080/?action=stream
を開き、ラズパイ①からの映像が配信されていれば成功です。
2.ラズパイ②でストリーミング配信を開始する
ラズパイ①と同様のコマンドでストリーミング配信を開始します。
3.ARで壁に穴をあけるPythonスクリプトを実行する
以下のコマンドを実行しスクリプトを実行します。しばらくするとWindows接続のカメラからの映像が表示されます。
PS C:\Users\usr01\usoana> ls
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 2022/08/28 7:51 4337 usoana_double_penetrate.py
PS C:\Users\usr01\usoana> python usoana_double_penetrate.py
以上で、実行手順は完了です。Windows接続のカメラにARマーカーを映し穴が開けば成功です。
ーーーーーー
今後の開発について
デモ動画を見ていただくとわかる通り映像がカクカクしているし、遅延も発生しています。より自然に穴が開いたようにみせるため"カクカク"と"遅延"をなくすようにしていきたいと思います。また、3枚以上の壁に穴をあけられるようにしたいです。
Discussion