[3D 編集] SPIn-NeRF: マルチビュー セグメンテーションと知覚修復 (CVPR 2023)


プロジェクトのホームページ:spinnerf3d.github.io (論文とコードを含む)

まとめ

编辑任务:从3D场景中移除不需要的对象

NeRF は、新しいビュー合成方法として人気がありますただし、NeRF シーンの編集と操作は依然として課題です。重要な編集タスクの 1 つは、3D シーンから不要なオブジェクトを削除し、置換領域が視覚的に妥当であり、そのコンテキストと一貫性があるようにそれらを修正することです。

私たちの方法:ポーズ画像と1 つの入力画像を含む一連の疎な注釈が与えられた場合、まずターゲット オブジェクトの 3D セグメンテーション マスクを取得します次に、学習した2D 画像修復モデルを利用してその情報を 3D 空間に変換し、視点の一貫性を確保する知覚最適化ベースのアプローチを導入します。

この記事では、3D シーン修復方法を評価するための実際のシーン データセットを紹介します。


提示:以下是本篇文章正文内容,下面案例可供参考

1. はじめに

ナーフの普及に伴い、十分に研究されている 2D 画像修復タスクと同様に、オブジェクトの削除や 3D シーンの画像修復など、ナーフ シーンの編集と操作の必要性が増大し続けるでしょう [23]。ただし、障害は 3D 修復プロセス自体だけでなく、入力セグメンテーション マスクの取得にもありますまず、NeRF シーンはニューラル マップの重みに暗黙的にエンコードされているため、複雑で解釈できない表現が生じ、操作が容易ではありません。さらに、3D シーンの修復では、特定のビューで知覚的に現実的な外観を生成する必要があり、ビュー全体での外観の一貫性や幾何学的妥当性などの基本的な 3D プロパティも保持する必要がありますこの方法では、複数の画像に注釈を付ける (そして複数のビューの一貫性を保つ) 必要があり、ユーザーにとって負担となります

魅力的な代替案は、単一のビューに対して最小限の注釈セットのみを期待することです。これは、単一ビューの疎な注釈からオブジェクトのビュー一貫性のある 3D セグメンテーション マスクを取得できる方法を動機付けます。

私たちの方法: まず、ターゲット オブジェクト上の少数のユーザー定義の画像ポイント (および外部からのいくつかのネガティブ サンプル) から、アルゴリズムはビデオベースのモデル [4] を使用してマスクを初期化し、セマンティック NeRF [ 36,76,77] を使用して、一貫した 3D セグメンテーションに持ち上げます次に、事前トレーニング済み 2D ペインタ [48] をマルチビュー画像セットに適用すると、カスタム NeRF フィッティング手順を使用して、2D 修復の不一致の原因となる知覚損失 [72] を使用して 3D ペイントされたシーンを再構築します。

ここに画像の説明を挿入

2.関連作品

1.画像修復

初期の技術はパッチ [9、16] に依存しており、より最近のニューラル アプローチは知覚の実装と再構成を最適化します (例: [22、27、48])。視覚的な忠実度を向上させるために、敵対的トレーニング ([40,73])、フレームワークの改善 ([27,30,62,63])、多変量出力 ([73, 75])、多変量出力 ([73, 75])、スケール処理 ([21, 58, 69])、および知覚メトリクス ([22, 48, 72] など)。

私たちの研究では、Transformer [11、12] からインスピレーションを得た周波数領域変換 [7] を適用する事前トレーニング済み LaMa [48]を利用しています。ただし、これらはどれも 3D 画像に問題を解決するものではないため、一貫した方法でシーンの複数の画像を修復することは未開発の課題のままです。私たちの方法は、マルチビュー ベースの NeRF モデルを介して 3D で直接動作します。

2.NeRFの動作

微分可能なボリューム レンダリング [18、52] と位置エンコーディング [13、49、53] に基づいて、NeRF [35] は新しいビューの合成において顕著なパフォーマンスを示しています。NeRF シーンをコントロールすることは依然として課題です。Clip-NeRF [55]、Object-NeRF [64]、LaTeRF [36]、その他 [25、32、70] では、NeRFによって表されるオブジェクトを突然変異および完成させるメソッドが導入されていますが、そのパフォーマンスは単純なオブジェクトに限定されています。著しく乱雑でテクスチャのあるシーンよりも、または一般的な修復以外のタスク (再ペイントやワーピングなど) に焦点を当てます。

私たちのアプローチに最も密接に関連しているのはNeRF-In [31] で、これは 2D 画像のジオメトリと明るさの事前分布を使用する同時代の未発表の研究ですが、不一致には対処せず、代わりに単純なピクセル損失を使用して、使用されるビューの数を削減します。フィッティングのために使用されるため、最終的なビューの合成品質が低下しますピクセル損失を使用するのと同様に、同時代の Remove-NeRF モデル [60] は、不確実なメカニズムに基づいてビューを除外することで不整合を軽減します対照的に、私たちの方法は、知覚[72]トレーニングメカニズムを介してビュー一貫性のある方法で2D情報を組み込むことにより、困難な現実世界のシーンのNeRF表現をマッピングできます。これにより、過度に制限された修復 (ぼやけが発生することがよくあります) が回避されます。

3. 背景: NeRF の知識

ブログ【3D再構築】NeRF原理+コード解説をご覧ください。


3. 方法

一連の RGB 画像 I = {I i } n i=1、対応する 3D ポーズg = {G i } n i=1、およびカメラの固有行列 K が与えられると、モデルはスパース ユーザー注釈付きの「ソース」ビュー。これらの入力から、シーンの NeRF モデルを生成し、新しい視点からペイントされた画像を合成できます。

最初に単一ビューのアノテーション ソースから初期 3D マスクを取得し (セクション 4.1.1)、次にセマンティック NeRF を適合させてマスクの一貫性と品質を向上させます (セクション 4.1.2)。4.2 では、ビューと復元されたマスクを入力として受け取る、ビュー一貫性のある修復メソッドについて説明します。私たちの方法は、新しい NeRF のフィッティングを監視するために、2D インペインター [48] の出力を外観とジオメトリの事前設定として利用します。

3.1. マルチビューセグメンテーション

3.1.1 マスクの初期化

注釈付きのソース コード ビューをI 1として示します。オブジェクトとソース ビューのスパース情報を対話型セグメンテーション モデル Edgeflow15] にフィードして、初期ソース オブジェクト マスクM 1 ^ \hat{M~1~}を推定します。M1_ _  ^次に、トレーニング ビューをM 1 ^ \hat{M~1~}M1_ _  ^を一緒に計算するビデオ インスタンス セグメンテーション モデル V [4, 57] を与えここに画像の説明を挿入、各ビューのマスクを取得します。,{cMi} ni=1
トレーニング ビューが隣接するビデオ フレームではないため、通常、初期マスクはしたがって、セマンティック NeRF モデル [36、76、77] を使用して不一致を解決し、マスク (4.1.2) を改善し、修復 (4.2) 用の各入力ビューのマスクを取得します。

3.1.2 ナーフに基づくセグメンテーション

マルチビュー セグメンテーション モジュールの入力は次のとおりです: RGB 画像、対応するカメラの固有パラメータと外部パラメータ、および初期マスク、ネットワークのセマンティックNeRF [76、図 2 を参照] をトレーニングします;x と視点方向 d (密度 σ を除く) (x) と色 c(x,d) は、 "objectness" の論理値s(x)を返します。次に、ターゲット確率 p(x) = Sigmoid(s(x)) を取得しますNeRF アーキテクチャとして、高速収束 Instant-NGP [3、37、38] を使用します。
ここに画像の説明を挿入

レイrに関連付けられた目的のオブジェクト性の論理値。色ではなく r 上の点の密度をレンダリングすることで取得されます。

ここに画像の説明を挿入

簡単にするために、s(r(t i )) はsiで表されます次に、分類損失を使用して光線、物体性の確率を監視しますPb(r)=シグモイド
ここに画像の説明を挿入

Lcls をトレーニングする場合はロジット値の監視付き更新を制限するために分離されますこれにより、勾配の更新によって密度値 σ が変更されるため、既存のジオメトリを変更できなくなります。

ここに画像の説明を挿入
最適化後、ターゲット確率をしきい値処理し、確率が 0.5 を超えるピクセルをマスクすることにより、 3D 一貫性マスク {Mi} n i=1が取得されます。最後に、マスクをさらに改善するために 2 段階の最適化を採用します。初期 3D マスクを取得した後、マスクはトレーニング ビューからレンダリングされ、二次マルチビュー セグメンテーション モデルを初期推定として監視するために使用されます。

3.2. マルチビューの修復

図 3 は、次の損失でトレーニングされた、ビュー一貫性のある Inpainting NeRF の概要を示しています。
ここに画像の説明を挿入

このうち、L' rec はマスクされていないピクセルの再構成損失、L LPIPSL Depthは知覚と奥行き損失を定義し、重みは λ です。

ここに画像の説明を挿入

3.2.1 RGB 前

Inpaint NeRF は、RGB 入力、カメラの内部パラメータと外部パラメータ、および対応するオブジェクト マスクを使用して、マスクされたオブジェクトがシーンに削除された NeRF を適合させます。まず、各イメージとマスクのペア ( I i、 Mi )をイメージ インペインター INP に供給します。ここに画像の説明を挿入

各ビューは独立してペイントされ、NeRF を監視するために直接使用されるため、3D の不一致により結果が不鮮明になります (図 7 を参照)。この論文では、平均二乗誤差 (MSE) を使用してマスクされた領域を最適化する代わりに、知覚損失 L PIPS [72] を使用して画像のマスクされた部分を最適化し、同時に MSE を使用してマスクされていない部分を最適化します。次のように計算されます。
ここに画像の説明を挿入

このうち、B は 1 から n までのバッチ インデックス、I ^ \hat{I}^ iは、NeRF を使用してレンダリングされた i 番目のビューです。マルチビュー Ipaint NeRF とセグメンテーション モデル セマンティック NeRF は同じアーキテクチャを使用します (図 2 を参照)。

3.2.2 事前の深さ

知覚損失があっても、Inpaint ビュー間の違いにより、モデルが縮退ジオメトリ(たとえば、各ビューからの異なる情報を考慮して、カメラの近くに「霧」ジオメトリが形成される可能性があります)。したがって、NeRF モデルの追加ガイドとしてインペイントの深度マップを使用し、知覚損失を計算するときに重みを分離し、シーンの色にのみ適合するために知覚損失を使用しますこのために、不要なオブジェクトを含む画像に最適化された NeRF を使用し、トレーニング ビューに対応する深度マップをレンダリングします。深度マップは、式 1 を点の色の代わりにカメラまでの距離に置き換えることによって計算されます。
ここに画像の説明を挿入
レンダリングされた深度をインペインターに与えて、描画された深度マップを取得します。ここに画像の説明を挿入

LaMa [48] を使用した深度修復は、十分に高品質な結果が得られ、前処理ステップとして計算されます。これらの深度マップは、深度の L2 距離をレンダリングすることによって、描画された NeRF ジオメトリを監視するために使用されます。
ここに画像の説明を挿入

3.2.3 パッチベースの最適化

知覚損失 (式 7) を計算するには、最適化中に完全な入力ビューを提示する必要があります。各ピクセルのレンダリングには MLP を介した複数の順方向パスが必要であるため、これは高解像度画像にとって高価なプロセスであり、次のような問題が発生します。(i) バッチ サイズ |B| を小さくする必要があり、対応するメモリ内計算グラフが減少する。 (ii) バッチ サイズ |B| = 1 であっても最適化が遅い。

簡単な解決策は、ダウンスケールされたイメージをレンダリングし、ダウンスケールされた修復イメージと比較することですが、ダウンスケール係数が大きい場合、これにより情報が失われる可能性があります。画像作業 (SinGAN [46] や DPNN [14] など) と 3D 作業 (ARF [71] など) に基づいて、パッチベースで計算を実行します。完全なビューをレンダリングする代わりに、より小さいビューをレンダリングし、それらを比較します。知覚損失に応じて、修復イメージ内の対応するパッチを使用します。

マスクされていない領域に適合させるために、L' rec (式 6) はマスクされていないピクセルからのみ光線をサンプリングします知覚損失と再構成損失を分離することで、シーンの残りの部分に対する不必要な変更を回避しながら、マスク内の不一致を防ぎます。

3.2.4 マスクの改良

ここでは、画像修復をガイドするためにマルチビュー データをさらに活用することを検討します。特に、2D インペインターによって現在生成されているトレーニング イメージの一部が他のビューで表示される場合があります。この場合、これらの詳細は他のビューから取得できるため、幻覚を見る必要はありません。この不必要な修復を防ぐために、マスクをリファインする方法を提案します。

各ソース イメージ、深度、マスク タプル (I s、D s、M s ) について、少なくとも 1 つの他のビューで表示されるIdのピクセルを置き換えてソース マスクを縮小します。この調整ステップの後、部分のみが縮小されます。すべてのトレーニング ビューで望ましくないオブジェクトによって遮蔽されているIdはマスクされたままになります。したがって、インペインタが埋める必要がある領域が小さくなり、インペイントが向上します。


4. 実験

ここに画像の説明を挿入

定性的な比較は、ニューラル ボリューム オブジェクト選択 (NVOS) [44])、ビデオ セグメンテーション [4]、および人間による注釈付きマスク (GT) を使用したマルチビュー セグメンテーション モデルで行われます。2 段階アプローチとは、マルチビュー セグメンテーション モデルを 2 回実行することを指し、最初の実行の出力が 2 回目の実行の初期化として機能します(4.1.2 を参照)。私たちの方法は、NVOS よりもノイズが少なく、オブジェクトの一部のセグメント (例: 最下段の花の最下段) も見逃しますが、ビデオ セグメンテーションのみよりも多くの詳細 (例: 花のぼやけたエッジ) をキャプチャします。私たちの 2 段階のアプローチは、1 段階の出力に欠けているいくつかの側面を埋めるのに役立ちます。

ここに画像の説明を挿入

合成されたビューは相互に一貫性を保っていることに注意してください。ただし、ビューに依存する効果 (たとえば、ピアノの裸の部分の照明など) は依然として残っています。

ここに画像の説明を挿入

NeRF (マスクされていない画像)、NeRF-In、NeRF-In (単一マスクされたトレーニング画像)、および私たちの方法による。NeRF-In は著しくぼやけていますが、NeRF-In (シングル) はマスクの境界線に近い部分のディテールに苦労する傾向があります。

5. インストールとコードの説明

1. プロジェクトのインストール

まずプロジェクトのクローンを作成してから、依存関係をインストールします

git clone
pip install -r requirements.txt
pip install -r lama/requirements.txt

次に、データを準備します。以下は公式データリンクです。

https://drive.google.com/drive/folders/1N7D4-6IutYD40v9lfXGSVbWrd47UdJEC?usp=share_link

スタチュー圧縮パッケージをダウンロードし、次のコマンドで解凍します。

unzip statue.zip -d data

そのデータ形式は次のとおりです (images2 の方が解像度が高く、ラベルには削除するターゲットのマスクが含まれています)。

statue
├── images
│   ├── IMG_2707.jpg
│   ├── IMG_2708.jpg
│   ├── ...
│   └── IMG_2736.jpg
└── images_2
    ├── IMG_2707.png
    ├── IMG_2708.png
    ├── ...
    ├── IMG_2736.png
    └── label
        ├── IMG_2707.png
        ├── IMG_2708.png
        ├── ...
        └── IMG_2736.png

独自のデータをトレーニングしたい場合は、colmap をインストールし、次のコードを使用してポーズ情報を検索できます。

python imgs2poses.py <your_datadir>

2. 元の NeRF を実行して深度情報を取得します。

rm -r LaMa_test_images/*  
rm -r output/label/*
python DS_NeRF/run_nerf.py --config DS_NeRF/configs/config.txt --render_factor 1 --prepare --i_weight 1000000000 --i_video 1000000000 --i_feat 4000 --N_iters 4001 --expname statue --datadir ./data/statue --factor 2 --N_gt 0

その後、レンダリングされた視差 (アンチデプス) がフォルダー内に配置されlama/LaMa_test_images、関連する前景マスク タグがlama/LaMa_test_images/label.

3. LaMa を実行して、幾何学的ガイドと見かけのガイドを生成します

LaMa は主にマスクを結合し、深度マップと RGB マップ内のターゲットを削除し、背景で塗りつぶして、非常にリアルな修復イメージを取得します。

cd lama

export TORCH_HOME=$(pwd) && export PYTHONPATH=$(pwd)
python bin/predict.py refine=True model.path=$(pwd)/big-lama indir=$(pwd)/LaMa_test_images outdir=$(pwd)/output

ペイントされた視差 (変更された深度マップ) をlama/output/label. にコピーして配置しますdata/statue/images_2/depth。コードは次のとおりです。

dataset=statue
factor=2
rm -r ../data/$dataset/images_$factor/depth
mkdir ../data/$dataset/images_$factor/depth
cp ./output/label/*.png ../data/$dataset/images_$factor/depth

次に、修復された RGB イメージを生成します。

dataset=statue
factor=2

rm -r LaMa_test_images/*
rm -r output/label/*
cp ../data/$dataset/images_$factor/*.png LaMa_test_images
mkdir LaMa_test_images/label
cp ../data/$dataset/images_$factor/label/*.png LaMa_test_images/label
python bin/predict.py refine=True model.path=$(pwd)/big-lama indir=$(pwd)/LaMa_test_images outdir=$(pwd)/output
rm -r ../data/$dataset/images_$factor/lama_images
mkdir ../data/$dataset/images_$factor/lama_images
cp ../data/$dataset/images_$factor/*.png ../data/$dataset/images_$factor/lama_images
cp ./output/label/*.png ../data/$dataset/images_$factor/lama_images

インペイントされた RGB イメージは にありlama/output/label、 にコピーされますdata/statue/images_2/lama_images

4. マルチビュー インペインター NeRF を実行します。

次のコマンドを使用すると、最後の InPainted NeRF 最適化が展開されます。InPainted NeRF の結果ビデオは毎回保存されますi_video。合計トレーニング回数N_iters

python DS_NeRF/run_nerf.py --config DS_NeRF/configs/config.txt --i_feat 200 --lpips --i_weight 1000000000000 --i_video 1000 --N_iters 10001 --expname statue --datadir ./data/statue --N_gt 0 --factor $factor

5. キーコード

  1. データを読み込み、前処理して、GroundTruth を取得します

use_batching=True は、すべての画像データを重ね合わせ、そこから光線をサンプリングすることを意味します。

# 0.首先得到 colmap预处理得到的深度信息----------------------------------------------------------
if args.dataset_type == 'llff':
    if args.colmap_depth:
        depth_gts = load_colmap_depth( args.datadir, factor=args.factor, bd_factor=.75, prepare=args.prepare)  # n_img个list,每一个包含: depth(3879) coord(3879,2) weight(3879)归一化
    images, poses, bds, render_poses, i_test, masks, inpainted_depths, mask_indices = load_llff_data(args.datadir,  args.factor,  recenter=True,  bd_factor=.75,  spherify=args.spherify, prepare=args.prepare, args=args)     
    # mask代表前景掩码


H, W, focal = hwf 

# 1.创建 nerf model---------------------------------------------------------------------------
render_kwargs_train, render_kwargs_test, start, grad_vars, optimizer = create_nerf_tcnn(args)



# 2.得到 rays_rgb(射线o,d 加上img,mask)---------------------------------------------------------
labels = np.repeat(masks[:, None], 3, axis=1)  # [N, 3, H, W, 1]
rays = np.stack([get_rays_np(H, W, focal, p)
                   for p in poses[:, :3, :4]], 0)    # 维度[N, ro+rd, H, W, 3],比如 (29, 2, 378, 504, 3)
rays_rgb = np.concatenate([rays, images[:, None]], 1)    # (29, 2+1, 378, 504, 3)      ray融入了img,
rays_rgb = np.concatenate([rays_rgb, labels], -1)        # (29, 2+1, 378, 504, 3+1)    ray融入了img,mask

rays_rgb = np.reshape(rays_rgb, [-1, 3, 4])          # (29, 378, 504, 2+1, 3+1) --> (5524848, 3, 4)




# 3.得到 rays_inp (射线o,d 加上img,inpainted_depths)-----------------------------------------------
rays = np.stack([get_rays_np(H, W, focal, p)
                 for p in poses[:, :3, :4]], 0)  # [N, ro+rd, H, W, 3]  (29, 2, 378, 504, 3)
                 
labels = np.expand_dims(inpainted_depths, axis=-1)    # [N, H, W, 1]
labels = np.repeat(labels[:, None], 3, axis=1)        # [N, 3, H, W, 1]
      
rays_inp = np.concatenate([rays, images[:, None]], 1) # (29, 2+1, 378, 504, 3) ray融入了img
rays_inp = np.concatenate([rays_inp, labels], -1)     # (29, 2+1, 378, 504, 3+1) ray融入了img, inpainted_depths

rays_inp = np.reshape(rays_inp, [-1, 3, 4])           # (29, 378, 504, 2+1, 3+1) --> (5524848, 3, 4)




# 4.得到 rays_depth(colmap得到的4个深度值)-----------------------------------------------------------
if args.colmap_depth(True):
    for i in i_train(循环29张图):
        rays_depth = np.stack(get_rays_by_coord_np(H, W, focal, poses[i, :3, :4], depth_gts[i]['coord']),
                                      axis=0)  # 2 x N x 3
        # depth_gts[i]['coord']维度(27112),是图像上前景点的绝对坐标。这行代码,根据坐标得到对应的2711个射线
        # 具体函数定义如下:
        def get_rays_by_coord_np(H, W, focal, c2w, coords):
            i, j = (coords[:, 0] - W * 0.5) / focal, -(coords[:, 1] - H * 0.5) / focal  # (2711) (2711)
            dirs = np.stack([i, j, -np.ones_like(i)], -1)                               # (2711, 3) 
           rays_d = np.sum(dirs[..., np.newaxis, :] * c2w[:3, :3], -1)
           rays_o = np.broadcast_to(c2w[:3, -1], np.shape(rays_d))                      # c2w 最后一列是原点O
        return rays_o, rays_d                                                           # (2711, 3) (2711, 3) 


        depth_value = np.repeat(depth_gts[i]['depth'][:, None, None], 3, axis=2)   # (2711) 广播至(2711,1 ,3) 绝对深度
        weights = np.repeat( depth_gts[i]['weight'][:, None, None], 3, axis=2)     # (2711) 广播至(2711,1 ,3) 归一化权重
        rays_depth = np.concatenate([rays_depth, depth_value, weights], axis=1)    # (2711,4,3) 4分别代表 colmap估计的射线0+d,深度,权重
        rays_depth_list.append(rays_depth)
        
    rays_depth = np.concatenate(rays_depth_list, axis=0)      # (112377, 4 3) 29张图的前景射线,深度叠加在一起



# 5.得到 rays_rgb_clf 和 rays_inp--------------------------------------------------------------------
rays_rgb_clf = rays_rgb[rays_rgb[:, :, 3] == 0].reshape(-1, 3, 4)    # (5524848,3,4) -> (4827239,3,4) 只保留了mask==0的部分
rays_inp = rays_inp[rays_rgb[:, :, 3] != 0].reshape(-1, 3, 4)            # (5524848,3,4) -> (697609,3,4) 只保留了inpaint_mask!=0的部分

# 6.修改 rays_rgb
if not args.prepare:
   rays_rgb = rays_rgb[rays_rgb[:, :, 3] == 1].reshape(-1, 3, 4)        # (5524848,3,4) -> (23906,3,4)只保留了mask==0的部分



# 7.迭代产生 GroundTruth
raysRGB_iter = iter(DataLoader(RayDataset(rays_rgb), batch_size=N_rand, shuffle=True, num_workers=0,
                              generator=torch.Generator(device=device)))
raysINP_iter = iter(DataLoader(RayDataset(rays_inp), batch_size=N_rand, shuffle=True, num_workers=0,
                               generator=torch.Generator(device=device)))
raysRGBCLF_iter = iter(DataLoader(RayDataset(rays_rgb_clf), batch_size=N_rand, shuffle=True, num_workers=0,
                                  generator=torch.Generator(device=device)))

batch_inp = next(raysINP_iter).to(device)     # (1024,3,4)
batch = next(raysRGB_iter).to(device)         # (1024,3,4)
batch_clf = next(raysRGBCLF_iter).to(device)  # (1024,3,4)

# 7.1 target_s
batch_rays, target_s = batch[:2], batch[2]
target_s, label_s = target_s[:, :3], target_s[:, 3]           #(10243)(1024)射线的颜色和 mask(都是1)

# 7.2 target_inp
batch_inp, target_inp = batch_inp[:2], batch_inp[2]
target_inp, depth_inp = target_inp[:, :3], target_inp[:, 3]   #(10243)(1024)inpaint mask中的射线和深度

# 7.3 batch_clf
batch_rays_clf, target_clf = batch_clf[:2], batch_clf[2]      #(10243)(1024)只保留mask=0的前景 射线的颜色和 mask(都是0)

# 7.4 得到来自colmap的深度 gt
raysDepth_iter = iter(DataLoader(RayDataset(rays_depth), batch_size=N_rand, shuffle=True,  num_workers=0,   generator=torch.Generator(device=device))) if rays_depth is not None else None
batch_depth = next(raysDepth_iter).to(device)
batch_rays_depth = batch_depth[:2]        # 2 x B x 3
target_depth = batch_depth[2, :, 0]       # B=1024
ray_weights = batch_depth[3, :, 0]        # B=1024
  1. 削除対象のマスクを読み込み、 cv2.dilate を使用して展開します(主に正確なマスクを考慮し、ターゲットの影をカバーできない可能性があるなど)
    for i, f in enumerate(mskfiles):    # mskfiles就是前景的mask,在label文件夹内
        try:
            msk = imread(f)
            msk = msk / msk.max()
            msk = cv2.resize( msk, (imgs.shape[1], imgs.shape[0]), interpolation=cv2.INTER_NEAREST)
                print(msk.shape)

            msk = cv2.dilate(msk, np.ones((5, 5), np.uint8), iterations=5)
            masks.append(msk)
            mask_indices.append(i)

2. DS_NeRF/run_nerf_helpers_tcnn.py の NeRF_TCNN 関数は、xyz (位置) と d (光線方向) を色と密度の値にマップします。

合計 4 つのレンダリング。各入力は異なるレイです。

rgb, disp, acc, depth, extras = render(H, W, focal, chunk=args.chunk, 
                                       rays=batch_rays_clf,   verbose=i < 10, retraw=True, **render_kwargs_train)  

rgb_complete, _, _, _, extras_complete = render(H, W, focal, chunk=args.chunk, 
                                               rays=batch_rays,  verbose=i < 10, retraw=True, detach_weights=True, **render_kwargs_train)

_, disp_inp, _, _, extras_inp = render(H, W, focal, chunk=args.chunk, 
                                      rays=batch_inp, verbose=i < 10, retraw=True,  **render_kwargs_train)                  # disp_inp (1024)预测每个点weight得到的深度图

_, _, _, depth_col, extras_col = render(H, W, focal, chunk=args.chunk, 
                                       rays=batch_rays_depth, verbose=i < 10, retraw=True, depths=target_depth,  **render_kwargs_train)             # depth_col (1024)      

レンダリングコードは毎回同じで、具体的な転送プロセスは次のとおりです

    def forward(self, input):
        x = input[:, :3]        # (655363)
        d = input[:, 3:]        # (655363)

        # sigma
        x = (x + self.bound) / (2 * self.bound)  # to [0, 1]  self.bound=100
        x = self.encoder(x)          # 16倍分辨率的hash表,得到(6553632)
        h = self.sigma_net(x)        # 得到(6553616)

        sigma = h[..., 0]
        geo_feat = h[..., 1:]

        # color
        d = (d + 1) / 2  # tcnn SH encoding requires inputs to be in [0, 1]
        d = self.encoder_dir(d)

        h = torch.cat([d, geo_feat], dim=-1)
        h = self.color_net(h)

        # sigmoid activation for rgb
        color = h                   # (65536,3)

        outputs = torch.cat([color, sigma[..., None]], -1)
        return outputs
  1. 損失を計算する
optimizer.zero_grad()
img_loss = img2mse(rgb, target_clf)    # 输入(1024,3) 输出:0.05 函数img2mse = lambda x, y: torch.mean((x - y) ** 2)
psnr = mse2psnr(img_loss)              # 函数 mse2psnr = lambda x: -10. * torch.log(x) / torch.log(torch.Tensor([10.]))


if not args.masked_NeRF and not args.object_removal:     # True
       img_loss += img2mse(rgb_complete, target_s)       # rgb_complete 是用batch_rays渲染出的颜色
       if 'rgb0' in extras_complete and not args.no_coarse:
                img_loss0 = img2mse(extras_complete['rgb0'], target_s)
                img_loss += img_loss0

# 3.计算深度损失
if args.depth_loss:
        if args.weighted_loss:
           depth_loss = img2mse(depth_col, target_depth)  # target_depth(1024),是colmap的深度

img_loss0 = img2mse(extras['rgb0'], target_clf)    # (10243)
loss = loss + img_loss0

300 反復ごとに、LPIP セマンティック損失を計算します。


np.random.shuffle(idx)      # 一个batch中,随机抽取几张图片
random_poses = poses[idx]

# 按比例裁剪出patch。实验中缩放因子分别是28
patch_len = (hwf[0] / / lpips_render_factor / / patch_len_factor,
             hwf[1] / / lpips_render_factor / / patch_len_factor)


rgbs, disps, (Xs, Ys) = render_path(random_poses, hwf, args.chunk, render_kwargs_test,
                                    render_factor=lpips_render_factor,rgb_require_grad=True,need_alpha=False,detach_weights=True,patch_len=patch_len,masks=masks[idx])                    
# 得到 (4,23,31,3) (4,23,31) 以及随机选的batch个中心点坐标


prediction = ((rgbs[_] - 0.5) * 2).permute(2, 0, 1)[None, ...]            # (23,31,3)

target = ((images[idx[_]] - 0.5) * 2).permute(2, 0, 1)[None, ...]
target = transform(target)[:, :, Xs[_]:Xs[_] + patch_len[0], Ys[_]:Ys[_] + patch_len[1]]         #  (23,31,3)

# 计算损失(LPIPS就是预训练的vgg):
lpips_loss += LPIPS(prediction, target).mean()

おすすめ

転載: blog.csdn.net/qq_45752541/article/details/132037526