雑食エンジニアの気まぐれレシピ

日ごろ身に着けた技術や見知った知識などの備忘録的なまとめ.主にRaspberry Piやマイコンを使った電子工作について綴っていく予定.機械学習についても書けるといいな.

LookingGlassPortraitにVTuberの配信(アーカイブ)を表示する

LookingGlassは卓上3Dディスプレイとして非常に優秀ですが,3Dデータでないと表示できないという欠点?があります.
最近VTuberの配信にハマっているので,何とかその配信(当然2D)をLookingGlassで表示できないかと試してみました.

で,そのあたりを動画にまとめて,ぽんぽこちゃんねる様の「ぽんぽこ24 vol6」という企画のCM枠に投稿させていただきました.
(実際にはぽんぽこ24を盛り上げるためになんか作りたいなと思ってやったネタなので,順序が逆なのですが(笑))
それがコチラ.
www.youtube.com やってることとしては,動画の各場面から奥行きを推定して疑似的な3DデータとしてLookingGlassに突っ込む,という感じです.
※注 リアルタイム描画ではないです.

以下はその手順および解説になります.

はじめに

奥行き推定については,
MiDaSとSPMを使って2D動画から3D動画を作成する
様の記事に書いてあることをほぼそのまま使わせていただきました.

手順についてもこと細かく書いてくださっているので,正直私が言えることないです.

ですので,奥行き推定以外の部分を中心に,動画をLookingGlassで表示するための方法全体の流れについて書いていきたいと思います.

動画から連番画像の生成

今回使う奥行き推定はあくまで画像に対して行うので,動画を画像に分解したのち,改めて動画に結合しなおすというプロセスが必要です.

連番静止画像の生成方法は沢山ありますが,ここでは"ffmpeg"というツールを使います.ffmpegコマンドラインから操作するツールで,入れておくと動画関係の大抵のことはこなせるので非常に便利です.

ffmpegのインストール方法は他所に任せるとして(といってもダウンロードしてきてpathを通すだけですが),連番画像の生成は以下のコマンドになります.

ffmpeg -i 入力動画のパス -vcodec png -r 30 image_%04d.png

各引数については以下.

  • i
    • 処理を行う対象の動画パスを指定
  • vcodec
    • 変換後のコーデック.pngとかjpegとか.当然動画のコーデックも指定できるが,今回は使用しない.
  • r
    • fps.1秒当たり何フレームに分割するかを指定する.元の動画のfpsか,それ以下の値を指定する.

そして最後に,出力するファイルのパスを指定します.これはprintfのようにファイル名をフォーマット指定することができて,"image_%04d.png"とするとimage_0001.pngから連番で画像を保存してくれます.

奥行き推定

MiDasという学習済ネットワークを使用します.が,"はじめに" で書いた通り,MiDaSとSPMを使って2D動画から3D動画を作成する様が非常に丁寧に手順を書いてくださっているので,詳しくはそちらをご覧ください.
先ほど生成した連番画像を"input"のフォルダに入れて実行すると,"output"のフォルダに生成結果が保存されます.

ちなみに生成物はdepth画像のほか,".pfm"という拡張子も保存されるのですが,これはrgbdの4次元をそのまま保存する独自の形式みたいです.このファイルは無駄に重いので,不要な場合は生成しないことをお勧めします.

"MiDas-master/utils.py"のwrite_depthメソッド内部の

write_pfm(path + ".pfm", depth.astype(np.float32))

コメントアウトすると生成しなくなります.

補足ですが,MiDasはGoogleが以前に「人物が前面に映った画像から奥行きを推定する」という目的で作ったネットワークに対して,大量の(人物以外の)データセットも食わせて学習したものとなります.

既存のデータセット約200万件に加え,3D映画からデータセットとして使えそうな部分を抽出して使っています.(この様々なデータセットを正規化して学習できるようにした部分が提案手法です.たぶん.)

そのため人物以外の奥行き推定にも頑強ですし,3D映画を学習に使っていることからCG系のオブジェクトに対しても多少期待できます.

RGBD画像生成

LookingGlass用の公式転送ツールのHoloPlayStudioでは,下に示すような左側にRGB画像/右側にDepth画像を結合した画像をimportすると,内部で合成して3D画像として読み込んでくれます.この形式の画像をRGB-D画像といいます.

したがって,まずはこの形式の画像を作成する必要があります.

といっても,連番画像を結合していけばいいだけです.pythonで書くとこんな感じですね.

import cv2
import glob
import os

SRC_RGB_DIR = "RGBの連番画像があるディレクトリのパス"
SRC_DEPTH_DIR = "Depthの連番画像があるディレクトリのパス"
OUTPUT_DIR = "RGB-Dの連番画像を保存するディレクトリのパス"

for i in range(sum(pathname.endswith(".png") for pathname in os.listdir(SRC_RGB_DIR))):
    srcRgbFileName = SRC_RGB_DIR+"/"+"image_{0:>04d}.png".format(i+1)
    srcDepthFileName = SRC_DEPTH_DIR+"/"+"image_{0:>04d}.png".format(i+1)

    leftImg = cv2.imread(srcRgbFileName)
    rightImg = cv2.imread(srcDepthFileName)

    concatinatedImg= cv2.hconcat([leftImg,rightImg])
    cv2.imwrite(OUTPUT_DIR+"/"+"img_{0:>04d}.png".format(i+1) , concatinatedImg)
    print ("wrote " + str(i))

連番RGBD画像から動画の生成

初めに行った作業と逆で,連番画像から動画を生成します.これもffmpegでできると思いますが,私はpythonでやってました.

import cv2
import numpy as np
import os

INPUT_DIR = "先ほど出力した連番画像を出力したディレクトリ"

fourcc = cv2.VideoWriter_fourcc('m', 'p', '4', 'v')
sampleSourceFileName = INPUT_DIR+"/"+"img_0001.png"
sampleImg = cv2.imread(sampleSourceFileName)

height, width, channels = sampleImg.shape[:3]
print(str(height)+","+str(width))
video = cv2.VideoWriter("output.mp4", fourcc, 30, (width, height), True)

for i in range(len(os.listdir(INPUT_DIR))):
    srcFrameFileName = INPUT_DIR+"/"+"img_{0:>04d}.png".format(i+1)
    srcImg = cv2.imread(srcFrameFileName)

    video.write(srcImg)
    print("wrote " + str(i))

print("finished converting")
video.release()

最終的にはこのコードはさっきのやつと統合して,画像の結合と同時に動画化してました.

動画に音声をつける

動画を連番画像にした時点で音声情報が消えてしまっているので,元動画の音声部分を抜き出してくっつけます.ffmpegのコマンドはコチラ.

ffmpeg -i RGBD動画(無音) -i 元動画(音声付) -vcodec copy -acodec copy output.mp4

本当は音声を抜き出してから結合するのが正しいと思いますが,実際これでもうまくいきます.

なんでうまくいくのか気になったので少し調べてみました.読み飛ばし可.
ffmpeg Documentation の4.2章に,複数入力チャネルがあった場合の優先順位が書かれています.
曰く,チャネルごとに解像度/性能が高い方が優先されるっぽいです.
つまり今回の場合,音声チャネルは片方にしかないので後者が採用され,動画チャネルは画像結合によって画素数(=解像度が)高くなっている前者が採用されるということみたいです.

LookingGlassで鑑賞する

HoloPlayStudioでRGBD形式を選択してインポートすればOKです.UIはバージョンによって多少変わっていますが,大体それっぽいものがあるはず.

おわりに

というわけで,こんな感じで任意の動画をLookingGlass上で楽しむことができます.
といっても,現状の奥行き推定精度だと"なんとなく立体に見えるような・・・"程度でしょうか.技術の発展に期待しましょう.

またせっかくならリアルタイム描画にも挑戦してみたいところですね.今回のネットワークはそこそこ早めに処理してくれるように見えるので,つよつよGPUがあればもしかしたらできるのかもしれません.

ちなみに,ゲーム界隈ではReflact+ReGlassというツールを使うと,LookingGlass上でリアルタイムで3Dゲームができます(これもゲーム次第で相当強力なGPUが必要ですが) .
Refract - Looking Glass Documentation
ReGlass | 3D screenshots and video capture tool for Looking Glass Portrait.

この辺りは私も今回のことがきっかけで知ったクチなので全然詳しくないですが,手順が丁寧に書かれていて凄く簡単に導入できました.
ただ日本語の記事は全然見当たらないので,今度書いてみるかもしれません.

また,RGBD画像という形式も非常に面白いですよね.ただ左右にくっつけただけなんですが,このおかげで既存の画像/動画フォーマットでやり取りできるので非常に便利だと思います.

(YouTubeを探せば某アイドルゲーム等のRGBD形式のライブシーンもあったりして,LookingGlassで簡単に見られたりします.かなり圧巻です.)

今回のことを通して,LookingGlassの奥深さを知れたような気がします.きっかけをくれたぽこピーに感謝.
毎年ぽんぽこ24に向けて何か作ってるので,これを密かに「ぽんぽこ24駆動開発(PDD)」と呼んでます.来年も何かやりたいので,お二人にはぜひ次もお願いしたいところですね.例のごとく今年で最後と言っていましたが(笑).

それでは.