Depth Anythingを動画で使う際に知っておきたいこと

一枚の静止画から深度を推定する[Depth Anything V2](https://github.com/DepthAnything/Depth-Anything-V2)を動画で使う場合についてです。
Depth Anythingを活用したアプリを試したり、[dav2video](https://qiita.com/amaman/items/811176c185856a913714)を制作する中で得た知見をまとめました。



# フレーム毎のばらつきについて
https://x.com/amarectv/status/1892732324699111456

## リファレンスコード
Depth Anythingのリファレンススクリプト run_video.pyでは深度推定の後 以下の処理を行っています。
```python:run_video.py
depth = depth_anything.infer_image(raw_frame, args.input_size)		# 深度推定
depth = (depth - depth.min()) / (depth.max() - depth.min()) * 255.0
```
これは深度推定結果がノンスケールの様々な値を示すため 最大値と最小値を使って0.0から1.0になるように正規化する処理です。最後に画像として扱う為 255倍しています。
静止画を扱う場合はこれで良いですが、動画の場合はフレーム毎のばらつきの要因となります。

## ばらつく原理
私が視聴する動画の多くのシーンは次のような構成となります
- 背景
- 被写体
- 最前面のもの

このうち背景と被写体が殆ど動いていない場合にかぎれば 深度推定はフレーム毎に同じような値を返すことが期待できる。
つまり動きの少ない背景や被写体の深度推定はフレーム毎に(それなりに)一貫性があり安定していると言えます。
ただし、背景が無いと無地の背景は不安定になる場合がある。

問題は最前面に来るもの その多くは手や腕となり、これらは動いていることが多いのでフレーム毎に異なる深度値となります。
そして、最前面なので最大値となることが殆どです。

リファレンスコードはこのフレーム毎に異なる最大値を使って背景や被写体を含む全体を正規化するので、安定していた背景や被写体の深度値まで 手や腕の影響（最大値の影響）を受けてフレーム毎に一貫性のない”不安定な深度値”となってしまいます。

:::note
私がサンプル動画として使っている赤い服の女性の動画はこれらに該当し
Depth Anythingのリファレンスにとって都合の悪いサンプル動画となります。
- 背景が無く不安定（最小値が不安定）
- 最前面のものが動いているので不安定（最大値が不安定）
:::

## 修正案
この問題は前回使った最大値、最小値と新しい最大値、最小値をミックスすることで改善できます。
```python:修正案
self.min = (self.min*119 + depth.min()) / 120
self.max = (self.max*119 + depth.max()) / 120
min_max_range:float = self.max - self.min
depth   = (depth - self.min) / min_max_range
```
これで最大値と最小値が急激に変化することが無くなり、前後のフレームとほぼ同じレンジで正規化されるためフレーム間に一貫性のある安定した深度値が得られます。

## デメリット
この修正案は正確な最大値、最小値ではないので 正規化後にマイナスの値や1.0以上になる場合がでてきます。
オーバーした部分はクリップされて、その分つぶれた形状となってしまいます。
逆に無地の背景がクリップされて安定するのは副産物と言えます。
また、シーンやカットが変わって大きくレンジが変わる場合も最適なレンジになるまで時間がかかる（数秒）。

:::note
真横から見ると 右の修正後は最大値を超えた部分がつぶれて平になっているのが分かる。
ただし、これを正面からみた場合 まず気づくことはない。
![min_max.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624096/4cf4c5a1-cf67-40e6-9463-92a4e5b60888.png)
:::

## dav2video Ver1.0.3
Ver1.0.3ではさらに 最大値、最小値それぞれが”上がり傾向”にあるのか”下がり傾向”にあるのか調べて、一定期間以上 同じ傾向が続いた場合だけ新しい最大値、最小値をミックスするようにしました。
これにより 同じ運動を繰り返すシーンでの安定性が向上します。
（同じ運動が繰り返されている間はミックスされず レンジが変わらない）

また、最大値のレンジを5％多めに取りました。
これにより 最大値を少しオーバーしても形状として潰れませんが、5％では焼け石に水です。
もともと最大値をオーバーしても視聴中に気づくことは稀なので 不要と思いますがこういうアイデアもあるよといったところです。





# 深度付き動画の評価
私は次の2つで評価します。
- 立体化できているか
- 安定性（フレーム毎の一貫性）

立体化については例えば人の顔の場合 鼻が出っ張っていて、目がくぼんでいるなど、出るところが出ていて、引っ込むところが引っ込んでいること。
顔の画像を張らなくても 形状で顔であることがわかるとか表情まで分かるくらい立体化されていれば使う価値があると思います。

一方、卵のような形状に顔の画像を張り付けたような結果になってしまうこともあれば、平面になってしまうこともある。
画像が張り付けられているとぱっと見 問題なさそうですが、ストッキングをかぶって引っ張られたような印象の時は上手くいっていない。
これらは”2D動画をSbS（サイドバイサイド）の3D化した動画”の範疇※で立体化とは区別したい。
実際に3D撮影された映像のように”自然な視差”を再現できるかが重要で それに近い立体化ができるかを評価します。

安定化については 立体化が上手くできていたとしてもフレーム毎にばらつきがあって 止まっているはずのものがガチャガチャ動いていては視聴しにくい。

なので、立体化と安定性の両方が揃って初めて”使えるもの”となります。
1時間の動画のうちどの程度 これら2つを満たす 使えるシーンがあるかといったところが最終的な評価となります。

:::note
※ 2D動画を3D化した動画
下記 [解説動画](#depth-anythingの仕組み-解説動画)の3分あたりでセグメンテーション（セグメント化）の説明があります。
セグメント単位の前後関係を処理するまでが2D動画の3D化に求められる要件。
これで背景から被写体が浮き出して見える効果が得られる。
一方、私が求める立体化の要件はセグメント内（鼻、目などのパーツまで）を処理する、Depth Anythingのそこに魅力を感じます。
:::


# Depth Anythingの仕組み 解説動画
最後に分かりやすい解説動画があったので紹介します。
https://www.youtube.com/watch?v=5WaT2DSWwLU

9分あたりにある 基板と電子部品の話は利用者として知っておきたい点です。
基板だけの時は上手く基板が立体化されるけど、基板の上に電子部品を置くと 基板が平面になってしまう。
「手前にあるものは上手く立体化されるけど その後ろにあるものは平面になりがち」そんな性質があるように感じます。
2人居た場合に、手前の人は立体的に見えるけど 後ろの人は平たくなりがちと言ったところでしょうか。
なので、視たいものが前面に来るシーンを選んで視聴するのが良いですね。