360°動画からフォトグラメトリで現場を3Dモデル化する話—RICOH THETAやInsta360+ffmpeg+Reality Capture—

こんにちは、最近フォトグラメトリという技術に興味があるので、その話題をひとつ。

フォトグラメトリとは?

フォトグラメトリは写真測量の一種で、いろんなアングルから撮った写真から視差を画像解析することで、3Dモデルに変換するというスゴい技術です。

LiDAR(レーザー式の距離センサー)などの特殊な機材不要で(処理にはハイスペックなPCが必要ですが)、カメラさえあれば3Dスキャンすることができます。

さらに、硬貨のような小さなものでも、街や山のような巨大なものでも、鮮明な写真を撮れさえすればスキャンできるので、守備範囲の広さも大きな利点です。

先日の熱海土石流の際も、ニュースで放映されたドローン映像から、すぐに現場を3Dモデル化された方々がいて話題になっていました。

xtech.nikkei.com

撮影には手間がかかる

LiDARセンサーを使った3Dスキャンの場合は、直接対象物までの距離を測るので、スキャンしたそばから測定点の三次元座標データを得ることができます。

しかしフォトグラメトリの場合は、視差から位置を計算するので、ひとつの測定点に対して複数アングルから撮った写真が必要になります。

角度を変えてたくさんの写真を撮る必要があるので、シンプルに手間のかかる作業です。

360°カメラで楽に撮影したい

撮影の手間を減らす方法として、いちばん手っ取り早いのは広角レンズを使って撮影することです。

画質や補正誤差とのトレードオフにはなりますが、一枚に写る範囲を広くすれば、撮影枚数を減らすことができるはずです。

そして、(画角的な意味で)究極の広角が、180°越えの魚眼レンズを2つ備えた、RICOH THETAやInsta360などの360°カメラです。

これら360°カメラで撮影した写真・動画を、フォトグラメトリの入力データとして使うことができれば、「精度はそこまで必要ないから、ざっくり現場をスキャンしたい」という用途にぴったりなはずです。

今回はRealityCaptureを使いたい

ソフトによっては、標準で360°画像の入力に対応しているものもあるようです。それらを使う場合、話はここで終わりです。

bluebirdofoz.hatenablog.com

今回使いたいソフトは、最もメジャーなフォトグラメトリソフトのひとつRealityCaptureなんですが、(調べた限りでは)RealityCaptureは、まだ360°写真の入力には対応していないようです。

ちなみにこのRealityCapture、エクスポート以外の機能は無料で使えるので、気軽にこういった技術を体験することができます。

kuu-satsu.com

エクスポートしたい場合、買い切りライセンスだと約40万円と簡単には手の届かない価格帯ですが、入力データ量に応じてエクスポート時に課金される「PPIライセンス」というライセンス形式もあるそうで、個人用途にはこっちですね。結果を見てから課金するか判断できるのがありがたいです。

www.capturingreality.com

そして、これまた僕が調べられた範囲では、360°動画から方向毎の映像を切り出すには、いまのところAdobe Premire Proを使う方法が有名(?)なようです。

ar-bito.com

しかし、Premire Proを普段使いしていないと、この用途だけのために月2,700円のサブスクに入るのは、ちょっとワリに合いません。

もっとコスト的にお手軽に、360°映像から方向毎の映像を切り出す方法はないものか?→あった!

というわけで、ここからが本題です。

FFmpegで360°画像・動画を変換できるようになってた

FFmpeg、2000年公開の超強力な動画・音声加工ツールで、いまや色々なソフトで変換処理に利用されているので、ほとんどの人が知らず知らずのうちにお世話になっていると思います。

ja.m.wikipedia.org

2020年の6月にリリースされたバージョン4.3で、v360というフィルターが追加され、360°動画を変換できるようになっていました、スゴい!

オプションも豊富で、大抵の加工はFFmpegだけでできそうです。詳しくはドキュメントを読んでみてください。

ffmpeg.org

THETAで撮った動画をReality Captureで使うには?

さて、THETAで撮った動画をフォトグラメトリ用に切り出すため、今回はこの3ステップで変換します。

  1. Dual Fisheye(双魚眼、変換前の状態)
  2. Equirectangular(正距円筒図法)
  3. Rectilinear(任意の画角で切り出し&歪み補正)

1→2はTHETA V以降モデルなら不要です、今回はSを使うので、とりあえずリコー純正のTHETA基本アプリで変換することにしました。本当はFFmpegだけで直接1→3もできるんだと思うんですが、僕はまだFFmpeg使いこなしレベルが赤ちゃんなので、暫定措置です。

theta360.com

何度も変換すると、そのたびに誤差が蓄積されてしまうので、可能ならステップを減らした方が良いです。

そして、2→3をFFmpegで変換するというのが今回のテーマです。オプションで、切り出す画角・ヨー・ピッチ・ロール回転を指定できるので、方向を切り替えつつ一定間隔でJpeg画像として切り出します。

1視点から14方向ぶん切り出してみる

フォトグラメトリでは、画像同士を比較して同じものが写っているところを探して、その視差を測ります。そのため、死角がなく、画像同士が少しづつ重なるように撮影するのが良いそうです。

かといって、入力画像が多すぎると処理コストが上がってしまいますが、その辺の加減ができる知見が全く無いので、まずは次の設定でやってみます。

  • 立方面方向6 + 頂点方向8 = 14方向
  • 画角は縦横とも90°の正方形

楽するためにPythonでコマンドを組み立てる

あとはコマンドを組み立てて実行するだけですが、14パターンの引数をベタで打っていくのはさすがに面倒です。

オプションを組み替えたりとか試行錯誤もしたいので、Pythonでサクッとコマンドを組み立てて実行することにしました。

ffmpeg-pythonなるラッパーライブラリもあるようですが、別の環境で使う時にバージョン互換とか気をつけるのも面倒なので、素のPythonでいきます。

Pythonスクリプト

視点の回転をリストにしておいて、順に代入してsubprocessでコマンドを実行するスクリプトです。

解像度を480x480pxにしたのは、THETA S合わせで1920px ÷ 360° × fov90° = 480pxってことです。

import subprocess
import os

path = r'<作業パス>' + '\\'

# 出力フォルダ、なければ作る
output_path = fr'{path}img' + '\\'
os.makedirs(output_path, exist_ok=True)

input_file_path = f'{path}input.MP4'

# 視点リスト(yaw,pitch,roll)
transforms = (
    (   0, 90,0),
    (   0,-90,0),

    ( -90,  0,0),
    (   0,  0,0),
    (  90,  0,0),
    ( 180,  0,0),

    (-135, 45,0),
    ( -45, 45,0),
    (  45, 45,0),
    ( 135, 45,0),

    (-135,-45,0),
    ( -45,-45,0),
    (  45,-45,0),
    ( 135,-45,0),
    )

# 画像切り出しのフレームレート、撮影時の移動スピードをみて調節
fps = 1

# 各視点で書き出す
for index, transform in enumerate(transforms):

    # 出力ファイル名をつくる
  # %04dで4桁連番
    output_file_path=fr'{output_path}output_{index}_%04d.jpg'

    # v360ライブラリのオプション
    v360_options = ':'.join([
        'input=e', # Equirectangular projection.
        'output=rectilinear', # Regular video.
        'h_fov=90',
        'v_fov=90',
        'w=480',
        'h=480',
        # 'interp=gauss',
        f'yaw={transform[0]}',
        f'pitch={transform[1]}',
        f'roll={transform[2]}'
    ])

    # コマンドをつくる
    command = f'ffmpeg -i "{input_file_path}" -vf v360={v360_options} -q 1 -r {fps} "{output_file_path}"'
  # ffmpeg -i "<作業パス>\input.MP4" -vf v360=input=equirect:output=rectilinear:h_fov=90:v_fov=90:w=1920:h=1920:interp=mitchell:yaw=135:pitch=-45:roll=0 -r 1 "<作業パス>\img\output_13_%04d.jpg"

  # ffmpegを実行
    subprocess.call(command, shell=True)

実践

さて、道具の方は整ったので、どの程度使いものになるのか、実際に試してみます。

休日に、皇居の清水門(科学技術館のところ)を通りかかったので、THETA Sを自撮り棒に取り付け、高くかざして3mくらいの位置に掲げて、サーっと歩いて動画を撮影してきました。

f:id:at_you_key:20210731180230j:plain

五輪期間中ということもあり、不審に思われないか少し緊張しましたが、無事に撮影を終え、100秒程度の動画を撮ることができました。

f:id:at_you_key:20210731162622j:plain

THETA Sで撮ったままのデータは双魚眼のmp4なので、まずはTHETA基本アプリで正距円筒図法に変換します。

f:id:at_you_key:20210731162641j:plain

そうしたら、先程のPythonスクリプトを実行して画像を切り出します、まずは試しに1フレーム/秒としてみました。

1フレームにつき14枚切り出すので、100秒なら1400枚になります。

f:id:at_you_key:20210731162653j:plain

うん、なかなか良さそうです

あとはRealityCaptureのフォルダ入力で、画像を書き出したフォルダを指定して、「ALIGNMENT」タブの「Align Images」を実行するだけです。

FFmpegで指定した出力形式「rectilinear」は、レンズ歪み補正済み画像として使えるはずなので、設定はデフォルトのままで大丈夫だと思います。

うまくいった!

そして処理結果がこちら、THETA Sの動画モードだとさすがに解像度低いんじゃないかと心配していたんですが、これくらいならじゅうぶん実用できそうです、これはいい!

twitter.com

ちゃんと視点ごとに14枚の画像がアラインされています。

f:id:at_you_key:20210803084113j:plain

たとえば、何かの搬入経路の干渉確認とか、何かの設置場所の検討とか、何かの見通しの遮蔽物の確認とかに使えそうです。

今後の課題

1. 切り出し枚数と精度のバランス

今回は1視点から14方向切り出して使いましたが、14もいらないんじゃないかという気もします。枚数が増えると処理コストも増すので、必要な精度が得られる最小限の枚数に抑えたいところです。

切り出しパターンを変えることは簡単にできるので、何パターンか試して比較してみようと思います。

2. CLIで工程を自動化

Reality Captureにはコマンドラインインターフェースがあるようなので、これを使えばワークフローをぜんぶ自動化することもできそうです、今後の課題。

indyzone.co.jp

3. RealityCaptureを使いこなす

RealityCaptureは使ってみるとわかる通り、とてもたくさんの機能・設定項目があります。 使いこなせばアウトプットのクオリティをガンガン上げていけるはずなので要勉強。

まずはこのページ👇がとても参考になりそうなので、メモ note.com

それでは、今日はここまで。