ARで穴をあける[ウソ穴]システムの作り方


## 紹介すること

個人で開発している『ウソ穴』というシステムの作り方を紹介します。

## ウソ穴とは

ウソ穴は、AR（拡張現実）とストリーミングを組み合わせて穴があいたかのような感覚を提供するシステムです。
こちらは、板にウソ穴を搭載したときの動画です。板に穴が空いているように見えますが、実際には板に穴はあいていません。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">壁に穴が開いてるように改良(動画30秒)<br>長編(5分)はYouTube(<a href="https://t.co/wD04rT7Vcd">https://t.co/wD04rT7Vcd</a>)<a href="https://twitter.com/hashtag/AR?src=hash&amp;ref_src=twsrc%5Etfw">#AR</a> <a href="https://twitter.com/hashtag/%E6%8B%A1%E5%BC%B5%E7%8F%BE%E5%AE%9F?src=hash&amp;ref_src=twsrc%5Etfw">#拡張現実</a> <a href="https://twitter.com/hashtag/AugmentedReality?src=hash&amp;ref_src=twsrc%5Etfw">#AugmentedReality</a> <a href="https://twitter.com/hashtag/%E7%A7%91%E5%AD%A6%E3%81%AE%E5%8A%9B%E3%81%A7%E7%A9%B4%E3%82%92%E9%96%8B%E3%81%91%E3%81%9F?src=hash&amp;ref_src=twsrc%5Etfw">#科学の力で穴を開けた</a> <a href="https://t.co/TNqoJ6e9vM">https://t.co/TNqoJ6e9vM</a> <a href="https://t.co/tygFg49SbP">pic.twitter.com/tygFg49SbP</a></p>&mdash; j4amountain (@zsipparu) <a href="https://twitter.com/zsipparu/status/1170657366393548801?ref_src=twsrc%5Etfw">September 8, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

穴をあけたように見せかけているので、このように体に穴をあける（あけたように見せかける）こともできます。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr"><a href="https://twitter.com/hashtag/%E3%82%A6%E3%82%BD%E7%A9%B4?src=hash&amp;ref_src=twsrc%5Etfw">#ウソ穴</a> で体に穴をあけた。カーテンとモニターが、のぞき見えるのわかるかな。<a href="https://twitter.com/hashtag/MA_FESTA?src=hash&amp;ref_src=twsrc%5Etfw">#MA_FESTA</a> ではいろんな人に穴をあけたい。 <a href="https://t.co/7X5CBvNNRg">pic.twitter.com/7X5CBvNNRg</a></p>&mdash; j4amountain (@zsipparu) <a href="https://twitter.com/zsipparu/status/1201909353059504128?ref_src=twsrc%5Etfw">December 3, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


## 概要図

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274270/744db504-ee64-2b4f-8921-0c485f4deb66.png" width="550">



## 仕組み図解


<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274270/41e21cee-748c-a620-54fa-5adeae76ebe2.png" width="550">

- ラズパイをストリーミングサーバにして、ラズパイに装着したカメラの映像を配信します。
- 配信した映像をテクスチャとした3Dオブジェクトを作成します。
- 作成した3DオブジェクトをARで表示されるようにします。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274270/ff1d255f-0304-29d1-3dca-17897c60f3dd.png" width="550">

- 板にARマーカを付けます。
- ARを設定したカメラを通して板を見るとを、ARマーカーの位置をもとに3Dオブジェクトが表示されます。これがウソ穴です。

　※ 写真はこちらから拝借 https://www.pexels.com/ja-jp/photo/1128065/

## セットアップの概要

ラズパイとWinPCで行うセットアップの概要です。

- ラズパイ (Raspberry Pi 4 Model B / 4GB)
    - カメラを付ける
    - MJPG-streamerでストリーミングサーバーにする
    - →カメラ映像をストリーミングで配信する
- WinPC (Window10)
    - Unityをインストール
    - VuforiaでARマーカーを認識
    - ARで3Dオブジェクを表示する
    - →穴の形状をした3Dオブジェクトを作成する
    - →3Dオブジェクトにストリーミング映像をテクスチャとしてマッピングする


## 参考リンク

- [第8回: MJPG-streamerのインストール](https://blue-black.ink/?page_id=2245)
- [AR（拡張現実）で床に埋まる3Dオブジェクトを作ろう](https://note.com/agw/n/n78182a6866e8?magazine_key=m1dcf448b96a2)
- [Unityで筒型のオブジェクトを作ろう](https://note.com/agw/n/n300a9b40ae02?magazine_key=m1dcf448b96a2)
- [Unity+VuforiaでAR(拡張現実)しよう](https://note.com/agw/n/n526010605270?magazine_key=m1dcf448b96a2)


## ※注意※

- セキュリティ対策は不十分です。
- ウソ穴のシステムは安定稼働に課題があり、ストリーミングサーバが意図せず停止することがあります。（連続稼働は5分くらいが限度）
- 備忘録なので説明が不十分なところがあります。それら情報も`参考リンク`で紹介した記事など参照することで確認できると思います。


## ラズパイをセットアップしよう

簡単ですがラズパイのセットアップ手順を紹介します。OSは、Rasbian（2019-09-26）をインストールしました。

ssh接続します。

```
ssh pi@raspberrypi.local
# パスワード raspberry
```

ホスト名を変更します。

```
sudo nano /etc/hostname
sudo nano /etc/hosts
```

- 更新します。

```
# パッケージ更新
$ sudo apt-get update
$ sudo apt-get upgrade -y
$ sudo apt-get dist-upgrade

# ファームウェアを更新
$ sudo rpi-update
```

- ※先ほどの更新をシェルでまとめて実行するとこうなります。

```
# setup.sh という空ファイル作成
$ sudo touch setup.sh
```

```shell:setup.sh
#!/bin/sh

sudo apt-get update
sudo apt-get upgrade -y
sudo apt-get dist-upgrade -y
sudo rpi-update -y
```

```
# 実行権限を付与
$ chmod +x setup.sh
# シェル実行
$ ./setup.sh
```

- Wi-Fi接続設定をします。

```conf:/etc/wpa_supplicant/wpa_supplicant.conf
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1

network={
        ssid="xxxxx-1"
        psk="xxxx"
        priority=0
        scan_ssid=1
        id_str="home1"
}

network={
        ssid="xxxxx-2"
        psk="xxxx"
        priority=0
        scan_ssid=1
        id_str="home2"
}

```

- sudo raspi-configで色々設定していきます。

- パスワード変更
    - `1 Change User Password Change password for the current user`
- ロケールを設定
    - `4 Localisation options - Change Locale - ja_JP.UTF-8`
- タイムゾーンを設定
    - `4 Localisation Options - I2 Change Timezone - Asia - Tokyo` 
- Wifiロケールを設定します
    - `4 Localisation Options - I4 Change Wi-fi Country - JP Japan`

- pipバージョンアップ

```
$ sudo pip install --upgrade pip
```

必須ではないですが、Windowsとのデータ移行が楽になるので、sambaをインストールします。

```
$ sudo apt-get install samba
$ cd /etc/samba
```

- /etc/samba/smb.confの最後尾に以下を追記します。

```
[pi-raspberry]
path = /home/pi
read only = No
guest ok = Yes
force user = pi

[tmp-raspberry]
path = /var/tmp
read only = No
guest ok = Yes
force user = pi
```

- sambaを再起動するとWindowsのエクスプローラ経由でアクセスできるようになります。

```
$ sudo service smbd restart
$ sudo service nmbd restart
```

- カメラモジュールを認識させます。

```
$ sudo raspi-config
```

`5 Interfacing Options` - `P1 Camera` - `はい` - `了解`

- OSを再起動後に再接続(ssh接続)します。
- カメラが認識されているか確認します。

```
$ vcgencmd get_camera
```

`supported=1 detected=1`と表示されれば認識成功です。


## ラズパイにMJPG-streamerをインストールしてストリーミングサーバにしよう

- mjpeg-streamerをインストールして、起動します。

```
$ sudo apt-get install build-essential libjpeg8-dev imagemagick libv4l-dev cmake -y
$ git clone https://github.com/jacksonliam/mjpg-streamer.git
$ cd mjpg-streamer/mjpg-streamer-experimental
```

- CMakeLists.txtを編集します。
- ⾏頭にʼ#ʼを追加してコメントアウトします。（これすると、make実行時のエラー発生を対策できるみたいです）

```
#add_subdirectory(plugins/input_opencv)
```


```
$ make
$ sudo make install
```

- ※よくわからんのだが、↓をするといいらしい

```
~/mjpg-streamer/mjpg-streamer-experimental $ cp input_raspicam.so ../
```

- MJPEG-streamerのインストールが完了したので、ストリーミング配信を試します。
- コマンドを実行し配信を開始したら、ブラウザで `http://{{ラズパイのIP}}:8080/`にアクセスして、ラズパイカメラの映像が表示されれば成功です。

```
# ストリーミング起動
$ /usr/local/bin/mjpg_streamer -i "input_raspicam.so -x 640 -y 480 -fps 15 -q 80" -o "output_http.so -p 8080 -w /usr/local/share/mjpg-streamer/www" 
# -> http://{{ラズパイのIP}}:8080/
# -> http://{{ラズパイのIP}}:8080/javascript_simple.html
# -> http://{{ラズパイのIP}}:8080?action=snapshot
# -> http://{{ラズパイのIP}}:8080?action=stream
```

- ウソ穴では、このような設定にしていました。

```
# ウソ穴
$ /usr/local/bin/mjpg_streamer -i "input_raspicam.so -x 64 -y 64 -fps 10 -q 80" -o "output_http.so -p 8090 -w /usr/local/share/mjpg-streamer/www" 
# -> http://{{ラズパイのIP}}:8090?action=stream

```

- 配信を開始するコマンドをShellにしておくと便利です。

```shell:/usr/local/bin/start_mjpeg.sh
#!/bin/sh
/usr/local/bin/mjpg_streamer -i "input_raspicam.so -x 256 -y 256 -fps 15 -q 80" -o "output_http.so -p 8090 -w /usr/local/share/mjpg-streamer/www"
```

- 実行権限を付与します。

```
chmod +x start_mjpeg.sh
```


## ラズパイでストリーミング再生をスケジュール実行しよう

- Unityからストリーミングサーバへの要求が厳しいのか、ウソ穴を実行すると5分くらいでMJPEG-streamerが落ちるという課題があります。そこで、1分単位でMJPEG-streamerが止まっていたら実行するようにスケジュールを設定します。

```shell:mjpeg.sh
#!/bin/sh
/usr/local/bin/mjpg_streamer -i "input_raspicam.so -x 64 -y 64 -fps 10 -q 80" -o "output_http.so -p 8090 -w /usr/local/share/mjpg-streamer/www" 
```

- スケジュール実行のルールを書いたファイルを作ります（ファイル名は任意）

```conf:cron.conf
0-59 * * * * /bin/bash /home/pi/mjpeg.sh
@reboot /bin/bash /home/pi/mjpeg.sh
```

- cronにジョブとして登録します。

```
$ crontab cron.conf
```

※注意※
ルールを書いたファイル`cron.conf`を編集した場合は、再度ジョブ登録が必要です。


## ラズパイOSイメージのバックアップ

- ラズパイOSのセットアップが完了したら、バックアップすることをオススメします。
- Disk Imageで、WindowsにラズパイOSのバックアップイメージを保存できます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274270/08db6cbb-6c48-c775-961a-77ecb444944f.png" width="400">


## Unityプロジェクトを作ろう

- ここからはWindowsでの作業になります。Unityがインストールされていることが前提です。
- ストリーミングのスナップショットをテクスチャにマッピングするスクリプト`GetNetworkImage6.cs`を作成します。

```c#:GetNetworkImage6.cs
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class GetNetworkImage6 : MonoBehaviour
{
    private float timeleft;

    IEnumerator RenewImage(){
        string url = "http://{{ラズパイのIP}}:8090/?action=snapshot";
        WWW www = new WWW(url);
        Debug.Log (www);
        yield return www;
        RawImage rawImage = GetComponent<RawImage>();
        rawImage.texture = www.textureNonReadable;
    }

    void Start()
    {
        StartCoroutine( RenewImage() );
    }

    void Update()
    {
        StartCoroutine( RenewImage() );
    }
}
```

- `GetNetworkImage6.cs`をAssetに登録します。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274270/8b619520-e48c-3f28-f91b-85e9968a6f22.png" width="400">


- 詳細は割愛しますが、各オブジェクトの設定はこのようになります。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274270/cf2d7616-dced-66b5-3f50-80515be1214e.png" width="400">

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274270/495d80a1-47f2-f381-6e57-e8cfba057510.png" width="400">

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274270/cd50164c-42ad-66fe-9f1e-05778d95b5b9.png" width="400">

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274270/8ee2ebee-3186-c0cc-307b-e1908293b49a.png" width="400">

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274270/c25f3e22-b469-d1db-1920-dd7151766861.png" width="400">

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274270/c8c70b97-b1e3-c49e-be1e-45677dd341f9.png" width="400">

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274270/5b460549-0508-606f-0c9b-778e49c7ee14.png" width="400">

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274270/fd0303fc-abb9-7776-e3d0-dbae05b45b85.png" width="400">

---

以上でおわりです。