見出し画像

カメラ映像をAR(拡張現実)で見えるようにする方法

概要

ARマーカを認識し、3Dオブジェクトを表示するコンテンツを作ります。
3Dオブジェクトのテスクチャ(表面)をカメラ映像にします。
『ARマーカ→3Dオブジェクト→オブジェクトのテスクチャにカメラ映像』
というものを作る方法を紹介します。
※仕組みを思いつくまで8か月以上かかりました。ふぅ

デモの紹介

概要の内容じゃ、何のことかわからん!と思いますのでデモ作りました。
ラズベリーパイ(以後、ラズパイ)にカメラを付けて、その📷の前に時計を置いています。この📷からの映像をARコンテンツで表示します。

ARコンテンツでラズパイカメラの映像を表示したところです。

システム構成図

コンテンツを作るためのシステム構成図を紹介します。

ラズパイにカメラをつけます。
ラズパイと同じネットワークにパソコン(windows)を繋げます。
Windowsにカメラをつけます。
ARマーカは、印刷しておきます。

システム概要

どのような仕組みになっているのか紹介します。
A-Frameというフレームワークを使って、WEBブラウザで利用できるARコンテンツを用意します。
WebブラウザでARコンテンツを開きます。ARマーカを映すと、そこに3Dオブジェクトがレンダリングされます。
3Dオブジェクトのテスクチャには、imgタグを使いjpeg画像を設定します。
img タグは定期的にjpeg画像を更新します。
jpeg画像は、シェルスクリプトで定期的に更新します。
画像を定期的に更新することで、動画っぽく見せます。

画像更新は以下フロー図のように、"画像そのものの更新(lp0.sh)"がラズパイ内部で実行していて、"ARコンテンツの画像更新(ar.html)"がクライアント側(今回はWindows)で動いています。

作り方

6つのステップで作ります。ステップ(1)~(4)の手順はリンク先に書いてあります。(5),(6) は後で紹介します。

(1) ラズパイOSのセットアップ (※作り方
(2) ラズパイにカメラを設定 (※ (2)(3)(4)の作り方
(3) ラズパイをWebサーバにする
(4) ラズパイをストリーミングサーバにする
(5) ar.htmlの作成と必要なjsモジュールを準備する
(6) ラズパイのカメラ映像をjpeg出力するshell(lp0.sh)を作成する

ステップ(5)の作り方

ar.html の作成と、jsモジュールの準備をします。
最終状態はこんな感じになります。

使うjsモジュールは3つ、②と③は公開されたURLを指定できるので、①のみダウンロードします。
aframe-html-shader.min.js
  → https://github.com/mayognaise/aframe-html-shader にありました。
aframe.min.js
aframe-ar.js

ar.html を作成します。

<!-- ar.html -->
<html>
 <head>
   <meta charset="utf-8">
   <meta name="viewport" content="initial-scale=1.0,user-scalable=no,maximum-scale=1,width=device-width">
   <title>test</title>
   <script src="https://aframe.io/releases/0.6.1/aframe.min.js"></script>
   <script src="aframe-html-shader.min.js"></script>
   <script src="https://jeromeetienne.github.io/AR.js/aframe/build/aframe-ar.js"></script>
   <style type="text/css">
     #target {
       width: 128px;
       height: 128px;
       position: absolute;
       background: rgba(200,200,200,1.0);
     }
     #htmlTarget {
       position: absolute;
       z-index: 1;
       height: 100%;
       width: 100%;
       overflow: hidden;
       position: fixed;
       top: 0;
     }
   </style>
   <script>
       AFRAME.registerComponent('update-html', {
         init: function () {
           this.lettersEntity = document.getElementById('lettersEntity');
           this.lettersIndex = 0;
           this.lettersUpdateDur = 1000 / this.lettersEntity.getAttribute('material').fps;
           this.lettersTime = 0;
           this.pre = document.getElementById('pre');
         },
         tick: function (t, dt) {
           this.lettersTime += dt;
           if (this.lettersTime >= this.lettersUpdateDur) {
             var nowSrc = document.getElementById('pre').src;
             var nxtSrc = "";
             nxtSrc = "img0.jpg?" + dt;
             document.getElementById('pre').src = nxtSrc;
             this.lettersTime = 0;
           }
         }
       });
   </script>
 </head>
 <body>
   <a-scene update-html arjs="debugUIEnabled:false;">
     <a-marker preset="hiro">
       <a-entity
         id="lettersEntity"
           geometry="primitive: box;
           width: 1;
           height: 1"
           rotation="0 0 180"
           position="0 0 0"
           material="
             shader: html;
             target: #target;
             transparent: true;
             ratio: width;
             fps: 1.5"
       ></a-entity>
     </a-marker>
     <a-entity camera></a-entity>
   </a-scene>
   <!-- HTML to render as a material. -->
   <div id="htmlTarget" style="width:0px;height:0px;">
     <center>
       <div id="target">
         <img src="img0.jpg?20181118" alt="" width="" height="" border="0" id="pre" />
       </div>
     </center>
   </div>
 </body>
</html>

ステップ(6)の作り方

lp0.sh を作ります。(1万回 jpegファイルを作り続けます)
今回はMJPG-streamerを使いストリーミング環境を用意したので、スナップショットは http://{ラズパイのIP}:{ポート番号}/?action=snapshot で取得します。

#!/bin/bash

for((i=0; i<10000; i++))
do
 wget -O img0.jpg http://{ラズパイのIP}:{ポート番号}/?action=snapshot
 echo "No. [ $i ] img renew"
 sleep 0.2s
done

※補足※
スナップショットではなく、raspistill コマンドを使う場合は、こんな感じにします。試したところ、スナップショットより画像作成に時間がかかるのでスナップショットをオススメします。

raspistill -o /var/www/00/img0.jpg -w 128 -h 128

動かし方

①ストリーミング配信を開始します。
 
※以下は、ポートを 8090番にしています。

piusr@rasp:~ $ cd mjpg-streamer/mjpg-streamer-experimental/
piusr@rasp:~/mjpg-streamer/mjpg-streamer-experimental $ ./mjpg_streamer -o "./output_http.so -p 8090 -w ./www" -i "./input_raspicam.so -x 128 -y 128 -fps 30 -q 10"

②シェルスクリプト lp0.sh を開始します。
 
※実行権限は chmod で設定しておく

$ ./lp0.sh

③Windowのブラウザで、https://{ラズパイIP}:{ポート番号}/ar.html を開きます。
④Windows カメラ📷に ARマーカー(Hiro)を映します。
 
→ Hiroのマーカーの上に時間を表示する3Dオブジェクトが表示されれば成功です。

 緑色の枠:実際の状態、Hiroのマークがあります
 赤色の枠:ARコンテンツでは、Hiroマークの上に時刻が表示されます

さいごに

冒頭にも書きましたが、この仕組みを思いつくまで8か月以上かかりました。その間、何度もあきらめたり、絶望したり、ふて寝したりと苦戦しました。完成して一安心です。でもまだ初歩段階なので、今後改善していきたいと思います。

それと、このコンテンツの使い道についても、おいおい書いていきたいと思います。

こんな弱小ブログでもサポートしてくれる人がいることに感謝です。