🕳️

ARで2つの壁を貫通する仕組みを開発しています

2022/08/28に公開

ARで壁に穴をあける(デモ動画)

何を言ってるかわからないと思いますでのデモ動作をご覧ください。

https://youtu.be/G7gJNwqv2Cw

デモ動画では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マーカーは以下のスクリプトで画像出力できます。

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番を出力します。上記スクリプトを以下のように変更します。

ID_0_番
# 0: ID番号,150x150ピクセル
generator = aruco.drawMarker(dictionary, 0, 150)
ID_1_番
# 1: ID番号,150x150ピクセル
generator = aruco.drawMarker(dictionary, 1, 150)

このように板に印刷したARマーカーを貼り付けます。

これで環境の説明は以上です。

ラズパイの設置

環境が用意できたので設置します。
ラズパイ①とラズパイ②をARマーカー付きの板の裏側に貼り付けます。

ARマーカー付きの板を縦に並べ、手前の板の前にWindows接続のカメラを設置すれば完了です。

スクリプトの準備

Windows環境に『2つの壁を貫通するPythonスクリプトusoana_double_penetrate.py』を用意します。

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