[3D 생성 및 재구성] ZeroRF: Zero Pretraining을 통한 빠른 희소 뷰 360° 재구성

시리즈 기사 목차

제목 : ZeroRF: 사전 훈련이 필요 없는 Fast Sparse View 360° 재구성 작업
: Sparse Reconstruction, 확장: 이미지를 3D로, 텍스트를 3D로
저자 : Ruoxi Shi* Xinyue Wei* Cheng Wang Hao Su, UC San Diego
코드 : https:// github.com/eliphatfs/zerorf



요약

  ZeroRF는 신경장에서 희박한 뷰를 360° 재구성하기 위한 새로운 장면별 최적화 방법입니다. 현재 NeRF(Neural Radiation Field)는 고충실도 이미지 합성을 시연했지만 희박한 입력 보기로 작업하기 어렵고 데이터 의존성, 계산 비용 및 다양한 시나리오 전반의 일반화에 한계가 있습니다. 이러한 과제를 극복하기 위해 ZeroRF의 핵심은 먼저 맞춤형 깊이 이미지를 인수분해된 NeRF 표현에 통합하는 것입니다 . 기존 방법과 달리 ZeroRF는 신경망 생성기를 사용하여 특징 메시를 매개변수화하여 사전 훈련이나 추가 정규화 없이 효율적인 희소 보기 360° 재구성을 가능하게 합니다. 또한 3D 콘텐츠 생성 및 편집 분야의 애플리케이션으로 확장될 수도 있습니다.

I. 소개

  NeRF 및 후속 개발과 같은 신경장 표현의 획기적인 발전을 통해 충실도가 높은 이미지 합성, 최적화 가속화 및 다양한 다운스트림 애플리케이션이 가능하지만 조밀한 입력 뷰에 의존합니다. 특히 3D 콘텐츠 생성 작업의 경우 더욱 그렇습니다. 따라서 희박한 관점에서 재구성을 해결하는 것은 중요한 과제입니다.

  최근에는 희소 뷰 재구성 방법[7, 23, 26, 33, 41, 60, 64, 66, 77]이 점점 더 주목을 받고 있습니다. 일반화 가능한 너프라고도 하는 [7, 28, 33, 77]의 한 가지 접근 방식은 관심 있는 장면을 직접 재구성하기 위해 상당한 시간과 데이터 요구 사항이 있는 광범위한 사전 훈련에 의존합니다. 따라서 이러한 모델의 성능은 훈련 데이터의 품질과 밀접한 관련이 있으며 대규모 신경망의 높은 계산 비용으로 인해 대규모 신경망의 해상도가 제한됩니다. 또한 이러한 모델은 다양한 시나리오에서 효과적으로 일반화하기 어렵습니다. 장면별 최적화 패러다임을 따르는 다른 방법에는 시각적 언어 모델[23] 및 깊이 추정기[64] 와 같은 추가 모듈이 포함되어 재구성을 지원합니다 . 이는 좁은 기준선 측면에서는 효과적이지만 360° 재구성에서는 성능이 좋지 않습니다. . 또한, 항상 이용 가능하거나 정확하지 않을 수 있는 추가 감독에 의존하기 때문에 실제 데이터에 대한 적용 가능성이 제한됩니다 . 사람들은 또한 연속성[41], 정보 이론[26], 대칭[51] 및 빈도[73] 정규화에 걸쳐 사전 설계를 직접 수행했습니다. 그러나 추가적인 정규화로 인해 NeRF가 장면을 충실하게 재구성하지 못할 수 있습니다 [73]. 더욱이, 손으로 만든 사전 설정은 다소 미묘한 설정 변경을 수용하지 못하는 경우가 많습니다.

  또한 일반적으로 몇 시간의 훈련이 필요한 360° 재구성을 위한 기존 장면별 최적화 방법(대형 GPU에서도)은 인수분해된 Instant NGP[39] 또는 TensoRF[8]와 같은 NeRF 표현보다 훨씬 빠르게 수렴됩니다. 더 느리고 어렵습니다. 실제로 적용하기 위해.

  우리는 각각 4개 및 100개 시야각을 사용하여 NeRF 합성 데이터 세트의 레고 장면에 TensoRF [8]를 맞추고 훈련이 수렴된 후 그림 2와 같이 평면 기능에서 채널을 시각화했습니다.

여기에 이미지 설명을 삽입하세요.

Sparse 4 뷰 설정에서는 조명 효과가 시끄럽고 왜곡된 특징을 생성하는 반면, 조밀한(100) 뷰에서는 특징 평면이 레고의 직교 투영 이미지와 똑같아 보인다는 것을 분명히 알 수 있습니다. 우리는 triplane [6, 17] 및 Dictionary Fields [9] 표현에 대해 유사한 실험을 수행했으며 이것이 TensoRF에만 국한된 것이 아니라 이러한 그리드 기반 인수분해 표현에 대한 일반적인 현상이라는 것을 발견했습니다 . 따라서 희소 뷰 감독, 빠르게 최적화된 희소 뷰에서 인수분해된 기능이 깨끗하게 유지되면 재구성이 달성될 수 있다는 가설을 세울 수 있습니다 .

  제안: [61] 이전에 자른 깊이 이미지를 인수분해된 NeRF 표현으로 통합합니다(그림 3 참조). TensoRF, K-planes 또는 Dictionary Fields [8, 9, 17]와 같은 기능 그리드를 직접 최적화하는 대신 ZeroRF는 무작위로 초기화된 심층 신경망(생성기)을 사용하여 기능 그리드를 매개변수화합니다.
이에 대한 직관은 불확실한 감독 하에서 대부분의 경우 신경망이 검색 그리드보다 더 잘 일반화된다는 것입니다. 이론적으로 신경망은 인식하고 기억하기 쉬운 데이터보다 노이즈 및 인공물에 대한 저항력이 더 높습니다 [21, 22, 61]. 이 설계에는 추가적인 정규화나 사전 훈련이 필요하지 않으며 여러 표현에 균일하게 적용할 수 있습니다. 또한 매개변수화는 주어진 목표 기능 메시를 달성할 수 있는 일련의 심층 네트워크 매개변수가 있다는 점에서 "무손실"입니다.

  새로운 장면별 최적화 방법인 ZeroRF는 가장 적합한 결합 희소 뷰 360° 재구성을 찾기 위해 매개변수화 및 다양한 분해 표현을 위해 다양한 생성 네트워크에 대한 광범위한 실험을 수행합니다.

  1. 사전 훈련 모델이 필요하지 않으므로 훈련 데이터의 잠재적인 편향과 해상도나 카메라 분포와 같은 설정에 대한 제한이 없습니다.
  2. 훈련 및 추론은 분해된 NeRF 표현을 기반으로 구축되어 30초 이내에 실행되므로 빠릅니다.
  3. 이는 기본 인수분해 표현과 동일한 이론적 표현력을 갖습니다.
  4. 네트워크 합성[38] 및 개방형 조명[30] 벤치마크에 대한 희소 뷰 입력을 사용한 최첨단 품질의 새로운 뷰 합성

ZeroRF의 고품질 360° 재구성 기능을 통해 우리의 방법은 3D 컨텐츠 생성 및 편집을 포함한 다양한 분야에 적용될 수 있습니다.

2. 관련업무 및 사전지식

2.1 새로운 관점 종합

  신경 렌더링 기술은 새로운 뷰 합성에서 사실적인 렌더링 품질을 달성할 수 있는 길을 열어줍니다. NeRF(Neural Radiation Fields)는 방사선 필드를 저장하고 볼륨 렌더링 렌더링 품질로 상당한 렌더링을 가능하게 하는 MLP(다층 퍼셉트론)를 최초로 도입한 것입니다. 이후 Plenoxels와 DVGO는 복셀 기반 표현을 채택했고 TensoRF, Instant-NGP 및 DiF[9]는 훈련을 가속화하기 위한 분해 전략을 제안했으며 MipNeRF와 RefNeRF는 좌표 기반 MLP였으며 Point-NeRF는 포인트 클라우드 기반 표현에 의존했습니다. 일부 방법은 밀도 필드를 부호 있는 거리 함수(SDF) [Neuralangelo, Unisurf, Permutosdf, Neus] 로 대체 하거나 밀도 필드를 그리드 표현 [Mobilenerf, Neumanifold, Bakedsdf] 으로 변환하여 표면 재구성을 개선합니다. 이러한 방법을 사용하면 렌더링 품질에 심각한 영향을 주지 않고 고품질 메시를 추출할 수 있습니다. 또한 [3d gaussian splatting, 4d gaussian splatting, Deformable 3d gaussians] 작업에서는 가우시안 스플래터를 사용하여 실시간 방사선장 렌더링을 구현했습니다.

2.2 딥 네트워크 이전

  일반적으로 심층 신경망의 성공은 대규모 데이터 세트에서 학습할 수 있는 능력에 기인한다고 여겨지지만, 심층 신경망의 아키텍처는 실제로 학습 전에 많은 수의 기능을 포착합니다.
확률론적 합성곱 네트워크의 특징에 대한 선형 분류기를 훈련하면 무작위 추측보다 훨씬 더 높은 성능을 얻을 수 있습니다[20]. 무작위로 초기화된 네트워크는 또한 소수의 학습자를 특징으로 합니다[1, 18, 50]. 이러한 귀납적 편향에서 시작하여 이미지 표현 학습을 개선하기 위한 다양한 접근 방식을 사용하여 이러한 무작위 특징을 증류함으로써 사전 분석을 더욱 발전시킬 수 있습니다.

  이들 연구와 대조적으로, 이전 깊이 이미지 [61]는 추가 증류 없이 이 이전 깊이를 직접 활용합니다 . 결과는 GAN 생성기가 높은 잡음 임피던스를 갖는 매개변수화된 구조로 사용될 수 있으므로 잡음 제거, 초해상도 및 인페인팅과 같은 이미지 복원 작업에 적용될 수 있음을 보여줍니다. 이는 다양한 이미징 및 현미경 응용 분야에 추가로 적용될 것이며[36, 43, 54, 55, 62] 깊이 디코더의 이론적이고 실제적인 개선으로 확장될 것입니다[21, 22]. ZeroRF는 방사선장의 매개변수화에 깊은 사전 변수를 삽입함으로써 유사한 패러다임을 따릅니다 .

2.3 스파스 뷰 재구성

  NeRF는 정보가 부족하여 희박한 관찰에 한계를 나타냅니다. 문제를 해결하기 위해 일부 방법은 사전 지식을 전송하고 대상 장면에서 모델을 미세 조정하기 위해 다수의 데이터 세트에 대해 [Mvsnerf, SRF, Sharf, Grf, pixelnerf]를 사전 훈련합니다 . 반면, 또 다른 연구 접근 방식은 수동으로 설계된 정규화 방법을 통해 각 시나리오를 최적화하는 데 중점을 두고 있습니다 [Putting nerf on a Diet, Flipnerf, Mixnerf, Geconer, Sparf, Freenerf] . 예를 들어, 의미론적 일관성을 높이기 위해 DietNeRF는 CLIP 시각적 변환기를 사용하여 상위 수준 기능을 추출합니다. 이들 중 다수는 정보 이론을 기반으로 교차 뷰 불일치를 완화하기 위해 손실 함수를 설계합니다. SPARF는 3D 정보 부족을 보완하기 위해 대응 또는 깊이 추정을 위해 사전 훈련된 네트워크를 활용합니다.

2.4 네르프

  NeRF는 MLP를 통해 3차원 장면 방사선장을 나타냅니다. 3D 위치 x와 뷰 방향 d를 입력하면 볼륨 밀도 σ x 및 뷰 관련 색상 c x 가 출력됩니다 .
여기에 이미지 설명을 삽입하세요.

2.5 텐소RF

  TensoRF는 NeRF의 MLP를 대체하고 훈련 속도를 높이기 위해 특징 볼륨을 선택합니다. 이는 CANDECOMP/PARAFAC 분해 또는 VM 분해를 사용하여 특징 볼륨을 요인으로 분해합니다. ZeroRF는 VM 분해를 사용합니다. 3차원 텐서 T∈R I,J,K 가 주어 지면 텐서를 여러 벡터와 행렬로 분해합니다.
여기에 이미지 설명을 삽입하세요.

여기서 v r a 는 벡터 인자이고 M r b, c 는 행렬 인자입니다.

3. 본 조의 방법

  ZeroRF 파이프라인은 그림 3에 나와 있습니다. 고정된 표준 가우스 노이즈 샘플 의 심층 생성기 네트워크 를 입력으로 사용하여 평면과 벡터가 TensoRF-VM 방식으로 생성되어 분해된 텐서 피처 볼륨을 형성합니다. 그런 다음 피처 볼륨은 렌더링 광선에서 샘플링되고 MLP(다층 퍼셉트론)(MSE 손실이 있는 표준 볼륨 렌더링 프로세스)에 의해 디코딩됩니다.

  ZeroRF의 주요 아이디어는 훈련되지 않은 심층 생성 네트워크를 공간 특징 그리드의 매개변수화로 적용하는 것입니다 ( 자세한 내용은 코드의 Tensorial3D 네트워크 참조: 코드 특징 공간을 형성하기 위해 두 개의 3D 텐서를 생성 ). 네트워크는 희소한 관찰로부터 다양한 규모의 패턴을 학습할 수 있으며, 희소한 뷰 재구성에 대한 이전 작업과 달리 일반적으로 광범위한 수동 조정이 필요한 추가 업샘플링 트릭이나 명시적 정규화가 필요하지 않고 자연스럽게 보이지 않는 뷰로 일반화할 수 있습니다.
중요한 설계는 공간 구성(특징 볼륨의 표현), 표현 생성기의 구조, 특징 디코더의 구조입니다 .

여기에 이미지 설명을 삽입하세요.

3.1 특징량의 분해

  매개변수화를 위해 심층 생성 네트워크를 적용하는 원리는 일반적으로 모든 그리드 기반 표현에 적용 가능합니다. 가장 간단한 해결책은 Volume 기능을 직접 매개변수화하는 것입니다 . 그러나 높은 렌더링 품질이 필요한 경우 피처 볼륨이 특히 크고 메모리를 많이 소모하며 계산적으로 비효율적입니다. TensorRF는 텐서 분해를 사용하여 피처 볼륨의 낮은 순위 특성을 활용합니다. 벡터가 상수인 경우 [17]에서 사용된 3면 표현은 TensoRF-VM으로 표현되는 벡터의 특별한 경우로 간주될 수 있습니다. DiF는 특징 볼륨을 서로 다른 주파수를 인코딩하는 여러 개의 작은 볼륨으로 분해합니다 . Instant-NGP[39]는 특성의 정보가 본질적으로 희박하기 때문에 다중 해상도 해시맵을 채택합니다.

  이러한 분해에서 해싱은 인접한 단위 간의 공간 상관 관계를 깨뜨리므로 깊이 사전을 적용할 수 없습니다. 매개변수화에 심층 생성 네트워크 ( TensorRF, triplane 및 DiF )를 사용할 수 있습니다.
우리는 1D 벡터, 2D 행렬 및 3D 볼륨을 생성하기 위한 생성기 아키텍처를 구축하고 이를 기반으로 세 가지 분해를 모두 실험합니다 . 유사한 작동 원리로 인해 이전 기술보다 더 나은 성능을 얻습니다. TensoRF-VM은 최고의 성능을 가지며 인수분해를 위한 최종 선택입니다.

3.2 생성기 아키텍처

  심층 매개변수화의 품질은 프레임워크에 따라 크게 달라집니다. 지금까지 대부분의 생성기는 심층 디코더(DD), 안정 확산(SD), 변형 자동 인코더(VAE), Kadinsky의 디코더 및 ViT 디코더 장치 기반 SimMIM 생성을 포함한 Conv 및 Attention 아키텍처 입니다 . ZeroRF는 2D 컨볼루션, 풀링 및 업샘플링 레이어를 1D 및 3D로 변환하여 다양한 분해에 필요한 해당 1D 및 3D 생성기를 얻습니다 .

  이러한 생성기는 고품질 콘텐츠를 생성하기 위해 매우 큰 데이터 세트에 맞도록 설계되었으므로 처음에는 상당히 큽니다 . 이로 인해 개별 NeRF 시나리오의 경우 실행 시간이 불필요하게 길어지고 수렴 속도가 느려집니다. 다행스럽게도 우리는 모델의 너비와 깊이를 축소해도 수렴 이후 ZeroRF의 성능이 변하지 않는다는 것을 발견했습니다. 따라서 우리는 블록의 구성을 유지하지만 훈련 속도를 향상시키기 위해 이러한 아키텍처의 크기를 수정합니다 . 추론 중에는 방사선 필드 표현만 저장해야 하고 생성기는 저장하지 않아도 되므로 ZeroRF를 렌더링하는 동안 기본 인수분해 방법에 비해 오버헤드가 0입니다 .

  우리는 SD VAE와 그 디코더 부분, Kadinsky 디코더가 새로운 뷰 합성에서 동등하게 효과적이며 깊이 디코더가 뒤따르는 반면 SimMIM 아키텍처는 방사선 필드 이전의 깊이로서 효과적이지 않다는 것을 발견했습니다 . SD/Kadinsky 인코더는 대부분 원래의 컨볼루션 구조이며 Kadinsky는 처음 두 블록에 self-attention을 추가합니다. 우리는 (수정된) SD 디코더를 생성기 아키텍처의 최종 선택으로 선택합니다. 왜냐하면 이것이 계산 집약도가 가장 낮기 때문입니다 .

3.3 디코더 아키텍처

  우리의 디코더 아키텍처는 SSDNeRF를 따릅니다 . 선형 보간(쌍선형 또는 삼선형)을 사용하여 기능 그리드에서 디코딩하고 이를 첫 번째 선형 레이어로 투영하여 밀도와 모양 디코딩 간에 공유되는 기본 기능 코드를 생성합니다 . 우리는 공유 기능 코드가 형상과 모양을 긴밀하게 결합하여 플로터를 줄이는 데 도움이 될 수 있음을 발견했습니다 . SiLU 활성화를 적용 하고 밀도 예측을 위해 다른 선형 레이어를 호출합니다 . 색상 예측을 위해 SH(구형 고조파)로 뷰 방향을 인코딩하고 이를 선형 레이어 투영을 통해 기본 기능에 추가하여 뷰 종속성을 추가합니다. 그런 다음 SiLU 활성화를 적용하고 밀도 예측과 유사한 또 다른 선형 레이어를 사용하여 RGB 값을 예측하며 다음과 같이 표현됩니다.

여기에 이미지 설명을 삽입하세요.

F x 는 특성장, σ(·) 는 시그모이드 함수, Θ•는 선형 레이어입니다. TensoRF 및 DiF에 사용되는 디코더와 달리 이 디코더는 위치 인코딩을 사용하지 않습니다 . 그렇지 않으면 잠재적으로 위치 정보가 누출되어(이전 깊이를 넘어) ZeroRF 성능이 파괴되거나 저하됩니다 .

4. 실험

4.1 실험 구성

  실험에서는 AdamW 최적화 프로그램인 β 1 =0.9, β 2 =0.98, 가중치 감쇠 0.2를 사용했습니다. 학습률은 0.002에서 시작하여 코사인 일정에 따라 0.001로 감소합니다. 10,000번의 반복을 위해 ZeroRF를 훈련합니다. 볼륨 렌더링 프로세스 중에 우리는 각 광선에 대해 1024개의 포인트를 균일하게 샘플링하고 점유 잘라내기 및 폐색 선별을 사용하여 이 프로세스 속도를 높입니다 .

4.2 데이터 세트 및 지표

  평가 지표는 PSNR, SSIM 및 L PIPS 이며 모든 입력 뷰는 카메라 변환 벡터에서 KMeans를 실행하고 클러스터 중심에 가장 가까운 뷰를 선택하여 얻습니다.

  1. NeRF 합성 데이터 세트 :
      NeRF 합성 데이터 세트에는 다양한 재료와 형상의 8개 개체가 포함되어 있습니다. 실험은 4개 또는 6개의 뷰를 입력으로 사용하여 수행되었으며 모델은 200개의 테스트 뷰에서 평가되었습니다.

  2. OpenIllumination 데이터세트 :

  단일 조명 하에서 복잡한 기하학적 구조를 가진 8개의 물체로 구성된 조명 수준의 실제 데이터 세트, 38개의 훈련 보기에서 추출된 4개 또는 6개의 보기 및 10개의 테스트 보기에서 평가되었습니다.

  1. DTU 데이터 세트 :

  DTU는 주로 360도 재구성보다는 전방을 향한 물체에 중점을 두지만, 완전성을 위해 그림 6에 DTU에 대한 결과를 포함합니다. 3개의 뷰를 입력으로 사용하고 나머지 뷰에서 모델을 테스트합니다.

4.2 결과 표시

  최첨단 퓨샷 NeRF 방법과의 비교:

1.RegNeRF: 연속성과 사전 훈련된 RealNVP 정규화 기반,
2.DietNeRF: 사전 훈련된 CLIP 모델 사용
3.InfoNeRF: 엔트로피를 정규화 도구로 사용
4.FreeNeRF: 주파수 정규화 기반
5.FlipNeRF: 사전 공간 대칭 사용

여기에 이미지 설명을 삽입하세요.

여기에 이미지 설명을 삽입하세요.

  대부분의 기본 모델은 합성 결과에서 플로터 및 눈에 띄는 색상 변화(그림의 빨간색 상자에 강조 표시됨)를 포함하여 다양한 정도의 명백한 결함을 나타냅니다. 사전 훈련된 사전 모델의 경우 RegNeRF 사전 모델은 넓은 기준 이미지에 대해 훈련되지 않았으며 360° 설정에서 객체를 재구성하지 못했습니다. 흥미롭게도 CLIP을 사전 모델로 사용하는 DietNeRF는 합성 이미지보다 실제 이미지에서 더 나은 성능을 발휘했으며 효과가 더 좋았습니다. CLIP의 사전 훈련 데이터 분포와 일치합니다. FreeNeRF 및 FlipNeRF는 너프 합성에서는 비교적 잘 수행되지만 OpenIllumination에서는 실패합니다.

4.3 분석

  희소 보기 수의 영향 . 설계 실험은 그림 . ZeroRF는 희소 뷰에서 기본 TensoRF 표현에 비해 상당한 이점을 가지고 있습니다. 뷰가 더 조밀해지면 ZeroRF는 더 작은 이점에도 불구하고 경쟁력을 유지합니다.

  특징 볼륨 분해 방법 . ZeroRF 생성기를 삼중, TensoRF 및 DiF에 적용하고 NeRF 합성 데이터세트(6개 뷰 입력)의 성능을 비교합니다. 결과를 표 3 에 나타내었다 . 발전기 포함은 지속적으로 기본 표현을 개선하고 둘 다 최첨단 성능을 달성합니다. 이는 심층 매개변수화를 사용하는 원리가 일반적으로 그리드 기반 표현에 적용 가능함을 보여줍니다.

여기에 이미지 설명을 삽입하세요.

  생성기 프레임워크 . 표 4는 다양한 프레임워크의 남서쪽 기능을 비교하고 그림 8은 다양한 Generator 평면 기능의 채널을 시각화합니다. 사전에 직접 최적화된 평면이 없으면 지형지물에 고주파수 노이즈가 있고 가시적인 뷰 경계가 있습니다. 이에 비해 SD 디코더와 Kadinsky 모델은 깨끗하고 좋은 기능을 제공했습니다. SimMIM의 세심한 ViT 디코더는 패치 분할을 사용하며 블록화된 아티팩트를 볼 수 있습니다. MLP는 메시에서 매우 부드러운 전환을 가정하므로 장면 내용을 충실하게 표현하지 않습니다. 일반적으로 컨벌루션 아키텍처는 장면과 가장 일치하는 기능을 생성합니다.

여기에 이미지 설명을 삽입하세요.
  소음의 중요성 . 입력 노이즈는 선험적으로 핵심입니다. 이를 0으로 초기화된 훈련 가능한 기능으로 바꾸면 프레임워크가 완전히 중단됩니다(표 4의 마지막 행). 잡음이 고정되지 않으면 성능 향상이 관찰되지 않습니다. 잡음 크기에 비해 학습률이 작기 때문에 잡음의 구조는 훈련 전체에서 변하지 않고 유지됩니다. 그러나 추가 오버헤드가 발생하고 수렴 속도가 느려집니다. 따라서 훈련 중에는 소음을 고정된 상태로 유지합니다.

5. 특정 용도

5.1 텍스트를 3D로 와 이미지를 3D로.

  ZeroRF의 강력한 희소 볼륨 재구성 기능을 고려할 때 간단한 아이디어는 기존 모델을 사용하여 일관된 다중 뷰 생성을 수행하고 ZeroRF를 적용하여 희소 뷰를 3D로 끌어올리는 것입니다. 이미지를 3D 작업으로 변환하고 Zero123++를 사용하여 단일 이미지를 6개 뷰로 업그레이드하고 생성된 이미지에 ZeroRF를 맞춥니다. 텍스트를 3D로 변환하는 경우 먼저 SDXL을 호출하여 텍스트에서 이미지를 생성하고 앞서 설명한 이미지 3D 프로세스가 적용됩니다. 그림 9에 표시된 것처럼 ZeroRF는 생성된 다중 뷰 이미지에서 신뢰할 수 있는 고품질 재구성을 생성할 수 있습니다. A100 GPU에서는 ZeroRF를 장착하는 데 30초밖에 걸리지 않습니다.

여기에 이미지 설명을 삽입하세요.

5.2 메쉬 텍스처 및 텍스처 편집

  메쉬 텍스처링 및 텍스처 편집. ZeroRF는 제공된 고정 형상을 사용하여 모양을 재구성할 수도 있습니다. 메시에서 4개의 이미지를 무작위로 렌더링하고 이를 큰 이미지로 타일링한 다음 Instruct-Pix2Pix [4]를 적용하여 텍스트 프롬프트에 따라 이미지를 편집합니다. 그런 다음 네 개의 이미지에 ZeroRF를 맞추고 색상 값을 매시 표면에 다시 굽습니다. 이 경우 그림 10과 같이 ZeroRF 피팅에 20초밖에 걸리지 않습니다.

여기에 이미지 설명을 삽입하세요.

2. 데이터 읽기

코드는 다음과 같습니다(예제).

data = pd.read_csv(
    'https://labfile.oss.aliyuncs.com/courses/1283/adult.data.csv')
print(data.head())

여기에서 사용되는 URL 네트워크에서 요청한 데이터입니다.


6. 코드 분석

1. 설치환경

먼저 python3.7 및 cuda11.x 환경을 설치하십시오.

# bashrc文件中,将默认cuda指向11.5
export PATH=/usr/local/cuda-11.5/bin:$PATH
export LD_LIBRARY_PATH=/usr/local/cuda-11.5/lib64:$LD_LIBRARY_PATH

# 创建环境
conda create -y -n ssdnerf python=3.7
conda activate ssdnerf

# 安装 PyTorch
conda install pytorch==1.12.1 torchvision==0.13.1 torchaudio==0.12.1 cudatoolkit=11.3 -c pytorch

# 安装 MMCV and MMGeneration
pip install -U openmim
mim install mmcv-full==1.6
git clone https://github.com/open-mmlab/mmgeneration && cd mmgeneration && git checkout v0.7.2
pip install -v -e .
cd ..

# 安装 SpConv
pip install spconv-cu114

# Clone this repo and install other dependencies
git clone <this repo> && cd <repo folder> && git checkout ssdnerf-sd
pip install -r requirements.txt

# 安装 InstantNGP依赖
git clone https://github.com/ashawkey/torch-ngp.git

cd lib/ops/raymarching/
pip install -e .
cd ../shencoder/
pip install -e .
cd ../../..

2. 이미지를 3D로

먼저, 단일 이미지에서 6개의 뷰 RGBD를 생성하려면 Zero123++(https://github.com/SUDO-AI-3D/zero123plus)를 사용해야 합니다. 샘플 사진은 다음과 같습니다.
여기에 이미지 설명을 삽입하세요.
다음 코드를 실행합니다. (미리 wandb 계정에 로그인해야 합니다.)

python zerorf.py --load-image=examples/ice.png

Zerorf.py 코드 분석: 360도 비디오를 생성하려면 사진 하나만 입력하세요.

# 1.读入6张图像(默认已经用zero123++模型,生成了6个视角)-------------------------------------------------------
image = torch.tensor(numpy.array(Image.open(args.load_image)).astype(numpy.float32) / 255.0).cuda()                                      # (960,640,4)
images = einops.rearrange(image, '(ph h) (pw w) c -> (ph pw) h w c', ph=3, pw=2)[None]  # (1,6,320,320,4)

# 2.读入meta,生成每张图的intri(内参)和pose(外参)-----------------------------------------------
cond_intrinsics = data['cond_intrinsics']  # [fx, fy, cx, cy]  [350,350,160,160]

BLENDER_TO_OPENCV_MATRIX = numpy.array([
    [1,  0,  0,  0],
    [0, -1,  0,  0],
    [0,  0, -1,  0],
    [0,  0,  0,  1]
], dtype=numpy.float32)

poses = numpy.array([(numpy.array(frame['transform_matrix']) @ BLENDER_TO_OPENCV_MATRIX) * 2
        for frame in meta['sample_0']['view_frames']])      # 外参pose默认是固定的。有兴趣可以研究zero123模型

이미지 외부 매개변수 포즈는 mata.json에 미리 저장되어 있으며, 구체적인 값은 아래 그림과 같습니다.

여기에 이미지 설명을 삽입하세요.

# 3.读入缓存(第一次循环没有缓存,默认生成4个纯0向量)--------------------------------------------------------
code_list_, code_optimizers, density_grid, density_bitfield = self.load_cache(data)

# 4.根据内外参,生成射线---------------------------------------------------------------------------------
cond_rays_o, cond_rays_d = get_cam_rays(cond_poses, cond_intrinsics, h, w)        # 还是6张图的

# 5.decode 过程
loss, log_vars, out_rgbs, target_rgbs = self.loss_decoder( self.decoder, code, density_bitfield, cond_rays_o, cond_rays_d, ond_imgs)

loss.backward()


# 4.射线中采样batch(4096):self.ray_sample:-----------------------------------------------------
rays_o = cond_rays_o.reshape(num_scenes, num_scene_pixels, 3)    # 320*320*6=614400 -> (1,614400,3)
rays_d = cond_rays_d.reshape(num_scenes, num_scene_pixels, 3)    # 320*320*6=614400 -> (1,614400,3)
if num_scene_pixels > n_samples:                                 # 总射线数 > 4096
        sample_inds = [torch.randperm( target_rgbs.size(1), device=device
                     )[:n_samples if self.patch_loss is None and self.patch_reg_loss is None else n_samples // (self.patch_size ** 2)]
                      for _ in range(num_scenes)]                # 614400 根射线中,随机选4096根
        sample_inds = torch.stack(sample_inds, dim=0)
    scene_arange = torch.arange(num_scenes, device=device)[:, None]
    rays_o = rays_o[scene_arange, sample_inds]          # (1,4096,3)
    rays_d = rays_d[scene_arange, sample_inds]          # (1,4096,3)
    target_rgbs = target_rgbs[scene_arange, sample_inds]         # (1,4096,4)

# 4.5 初始化 code
code = code.to(next(self.parameters()).dtype)          # (1,3,8,20,20)初始化的全0向量

# 4.6 更新 code
for _ in range(update_extra_state):
    self.update_extra_state(code, *extra_args, **extra_kwargs)

#----------------------------以下是VolumeRenderer中的decode过程:------------------------------------

# 5.prepro(code)过程,用于生成三为特征。重点代码为:
self.code_proc_buffer = self.code_proc_pr_inv(self.preprocessor(self.code_proc_pr(code))
# self.preprocessor:TensorialGenerator生成两个3D张量-----------------------------------------------------------
	# 循环两次,通过随即噪声,生成两个Tensorial 3D张量 --> (2,524288=64^3)
	class TensorialGenerator(nn.Module):
	
	        self.subs = nn.ModuleList([(Tensorial3D)(in_ch=8, out_ch=16, noise_res=4)])  # 这里可选Tensorial1D Tensorial2D
	
	    def forward(self, _):
	        r = []
	        for sub in self.subs:
	            sub_out = sub()                            # 从噪音生成特征:(1,16,32,32,32)
	            r.append(torch.flatten(sub_out, 1))        # (1,16,32,32,32-> (524288)
	        return torch.cat(r, 1)                         # 循环两次,生成两个Tensorial 3D张量 --> (1,1048576)
## 其中,Tensorial3D网络随机生成噪声,其网络架构如下图:

여기에 이미지 설명을 삽입하세요.

6. 조밀하게 샘플링된 좌표 생성------------------------------- - ------------------------------------------------- - ----------------------------------

grid_size =  64
# 间隔采样
X = torch.arange(grid_size, dtype=torch.int32, device=device).split(S)   # (64):[0,1,2,...]
Y = torch.arange(grid_size, dtype=torch.int32, device=device).split(S)   # (64):[0,1,2,...]
Z = torch.arange(grid_size, dtype=torch.int32, device=device).split(S)   # (64):[0,1,2,...]

for xs in X:
    for ys in Y:
        for zs in Z:
            # 生成3D坐标
            xx, yy, zz = custom_meshgrid(xs, ys, zs)          # (64,64,64)
            coords = torch.cat([xx.reshape(-1, 1), yy.reshape(-1, 1), zz.reshape(-1, 1)],dim=-1)    #(262144,3)[0~64)之间 
            # 026243的索引,并打乱顺序
            indices = morton3D(coords).long()                 # [N=262144]
            # indices[0,4,32,36,256,260,288,292,2048,2052,2080,2084...262111, 262139, 262143]
            
            xyzs = (coords.float() - (grid_size - 1) / 2) * (2 * self.bound / grid_size)   # 归一化到(-1,1)
            
            # 添加噪声
            half_voxel_width = self.bound / grid_size    # 1/64
            xyzs += torch.rand_like(xyzs) * (2 * half_voxel_width) - half_voxel_width
# 7.point_decode-----------------------------------------------------------------------

输入:code(dens.color两个3D特征块: 2*16,32,32,32)),针对xyzs(262144=64^3, 3)做特征插值,得到(262144,16*2 输出
point_code = self.get_point_code(code, xyzs)
# self.get_point_code 具体展开如下:
class FreqFactorizedDecoder(TensorialDecoder):
    def get_point_code(self, code, xyzs):
        for i, (cfg, band) in enumerate(zip(preprocessor.tensor_config, self.freq_bands)):
            start = sum(map(numpy.prod, preprocessor.sub_shapes[:i]))         # 0
            end = sum(map(numpy.prod, preprocessor.sub_shapes[:i + 1]))       # 524288 = 16*32^3
            got: torch.Tensor = code[..., start: end].reshape(code.shape[0], *preprocessor.sub_shapes[i])     # (1,16,32,32,32)
            assert len(cfg) + 2 == got.ndim == 5, [len(cfg), got.ndim]
            coords = xyzs[..., ['xyzt'.index(axis) for axis in cfg]]          # (1,262144,3)
            if band is not None:
                coords = ((coords % band) / (band / 2) - 1)
            coords = coords.reshape(code.shape[0], 1, 1, xyzs.shape[-2], 3)   # (1,1,1,262144,3)
            codes.append(
                F.grid_sample(got, coords, mode='bilinear', padding_mode='border', align_corners=False)
                .reshape(code.shape[0], got.shape[1], xyzs.shape[-2]).transpose(1, 2)
            )                                                               # (262144,16)两次循环 got3D 特征不同
        return torch.cat(codes, dim=-1)

8. 그런 다음 렌더링이 나옵니다. ------------------------------ ---- --------------------------------- ---- ----

시그마, rgbs = self.point_code_render(point_code, dirs=None)

이전 단계에서 얻은 point_code 기능은 색상과 밀도를 별도로 렌더링하지 않습니다.

# 8.1 公共特征point_code,渲染sigma---------------------------------------------------------------
base_x = self.base_net(point_code)                  # linear:(32,64)
base_x_act = self.base_activation(base_x)           # Silu
sigmas = self.density_net(base_x_act).squeeze(-1)   # linear:(64,1)  ->(262144,1)

# 8.2 渲染RGB-----------------------------------------------------------------------------
if dirs is None:
    rgbs = None


#
density_grid = sigma 
mean_density = torch.mean(density_grid.clamp(min=0))  # - 0.977
density_thresh = min(mean_density, 0.05)

# 9. near_far_from_aabb
self.aabb:[-1,-1,-1,1,1,1], 具体函数见最后拓展
nears, fars = batch_near_far_from_aabb(rays_o, rays_d, self.aabb.to(rays_o), self.min_near)    # (1,4096)(1,4096)



# 10.march_rays_train 采样渲染(这块封装成c语言了,代码可见拓展)
# 该代码接受光线的起点、方向、网格数据等作为输入,并根据特定的算法进行光线追踪,最终计算出光线与场景的交点及相关信息。这段代码是用于实现神经辐射场(NeRF)模型中的光线追踪部分。
    xyzs = torch.zeros(M, 3, dtype=rays_o.dtype, device=rays_o.device)
    dirs = torch.zeros(M, 3, dtype=rays_o.dtype, device=rays_o.device)
    ts = torch.zeros(M, 2, dtype=rays_o.dtype, device=rays_o.device)

    get_backend().march_rays_train(rays_o, rays_d, density_bitfield, bound, contract, dt_gamma, max_steps, N, C, H,
                                   nears, fars, xyzs, dirs, ts, rays, step_counter, noises)
return xyzs, dirs, ts, rays

후속 조치를 취하세요. . .

확장하다

提示:这里对文章进行总结:

1.morton3D 인코딩

  Z 코딩이라고도 알려진 모튼 코딩은 다차원 공간의 좌표를 1차원 공간으로 매핑하는 데 사용되는 방법입니다 . 인코딩 후에도 1차원 공간에서 인접한 좌표가 인접하게 유지되도록 좌표의 비트를 인터리브합니다. 이 인코딩 방법은 다차원 공간에서 데이터를 빠르게 검색하고 처리하기 위해 공간 인덱싱, 그래픽, 컴퓨터 그래픽에 일반적으로 사용됩니다. Morton 인코딩에 2D 좌표를 사용할 때 2D 좌표(3, 5)가 있는 점이 있고 해당 이진 표현이 각각 (011, 101)이라고 가정합니다. Morton 인코딩은 이 두 이진수를 인터리브하여 010111(십진수로 23)을 얻습니다. 이는 2차원 좌표를 1차원 공간의 값으로 매핑합니다. Morton 코딩의 주요 목적은 인코딩 후에도 1차원 공간에서 인접한 좌표를 여전히 인접하게 유지하는 것입니다.

void morton3D(const at::Tensor coords, const uint32_t N, at::Tensor indices);

2.near_far_from_aabb: 가장 가까운 점과 가장 먼 점을 계산합니다.

void near_far_from_aabb(const at::Tensor rays_o, const at::Tensor rays_d, const at::Tensor aabb, const uint32_t N, const float min_near, at::Tensor nears, at::Tensor fars) {
    
    

구체적인 단계는 다음과 같습니다:

1. 스레드 인덱스를 기반으로 현재 처리되는 광선 인덱스 n을 계산합니다.
2. 광선 인덱스에 따라 해당 광선 시작점과 방향을 찾습니다.
3. x, y, z의 세 축에서 광선과 AABB의 교차 매개변수를 계산합니다.
4. 교차 매개변수를 기반으로 광선과 AABB의 가까운 점과 먼 점을 계산합니다.
5. 계산된 근거리 및 원거리 지점은 근거리 및 원거리에 저장됩니다.

이 커널 함수의 원리는 CUDA의 병렬 컴퓨팅 기능을 사용하여 각 광선에 대해 독립적인 계산을 수행하여 계산 프로세스 속도를 높이는 것입니다. 병렬 컴퓨팅을 통해 여러 광선을 동시에 처리할 수 있어 컴퓨팅 효율성이 향상됩니다. 구체적인 호출 기능은 다음과 같습니다.

__global__ void kernel_near_far_from_aabb(
    const scalar_t * __restrict__ rays_o,
    const scalar_t * __restrict__ rays_d,
    const scalar_t * __restrict__ aabb,
    const uint32_t N,
    const float min_near,
    scalar_t * nears, scalar_t * fars
) {
    
    
    // parallel per ray
    const uint32_t n = threadIdx.x + blockIdx.x * blockDim.x;
    if (n >= N) return;

    // locate
    rays_o += n * 3;
    rays_d += n * 3;

    const float ox = rays_o[0], oy = rays_o[1], oz = rays_o[2];
    const float dx = rays_d[0], dy = rays_d[1], dz = rays_d[2];
    const float rdx = 1 / dx, rdy = 1 / dy, rdz = 1 / dz;

    // get near far (assume cube scene)
    float near = (aabb[0] - ox) * rdx;
    float far = (aabb[3] - ox) * rdx;
    if (near > far) swapf(near, far);

    float near_y = (aabb[1] - oy) * rdy;
    float far_y = (aabb[4] - oy) * rdy;
    if (near_y > far_y) swapf(near_y, far_y);

    if (near > far_y || near_y > far) {
    
    
        nears[n] = fars[n] = std::numeric_limits<scalar_t>::max();
        return;
    }

    if (near_y > near) near = near_y;
    if (far_y < far) far = far_y;

    float near_z = (aabb[2] - oz) * rdz;
    float far_z = (aabb[5] - oz) * rdz;
    if (near_z > far_z) swapf(near_z, far_z);

    if (near > far_z || near_z > far) {
    
    
        nears[n] = fars[n] = std::numeric_limits<scalar_t>::max();
        return;
    }

    if (near_z > near) near = near_z;
    if (far_z < far) far = far_z;

    if (near < min_near) near = min_near;

    nears[n] = near;
    fars[n] = far;
}

3.march_rays_train

이 코드는 빛의 시작점, 방향, 그리드 데이터 등을 입력으로 받아 특정 알고리즘에 따라 광선 추적을 수행하고 최종적으로 빛과 장면의 교차점 및 관련 정보를 계산합니다. 이 코드는 NeRF(Neural Radiation Field) 모델의 광선 추적 부분을 구현하는 데 사용됩니다.

__global__ void kernel_march_rays_train(
    const scalar_t * __restrict__ rays_o,
    const scalar_t * __restrict__ rays_d,
    const uint8_t * __restrict__ grid,
    const float bound, const bool contract,
    const float dt_gamma, const uint32_t max_steps,
    const uint32_t N, const uint32_t C, const uint32_t H,
    const scalar_t* __restrict__ nears,
    const scalar_t* __restrict__ fars,
    scalar_t * xyzs, scalar_t * dirs, scalar_t * ts,
    int * rays,
    int * counter,
    const scalar_t* __restrict__ noises
) {
    
    
    // parallel per ray
    const uint32_t n = threadIdx.x + blockIdx.x * blockDim.x;
    if (n >= N) return;

    // is first pass running.
    const bool first_pass = (xyzs == nullptr);

    // locate
    rays_o += n * 3;
    rays_d += n * 3;
    rays += n * 2;

    uint32_t num_steps = max_steps;

    if (!first_pass) {
    
    
        uint32_t point_index = rays[0];
        num_steps = rays[1];
        xyzs += point_index * 3;
        dirs += point_index * 3;
        ts += point_index * 2;
    }

    // ray marching
    const float ox = rays_o[0], oy = rays_o[1], oz = rays_o[2];
    const float dx = rays_d[0], dy = rays_d[1], dz = rays_d[2];
    const float rdx = 1 / dx, rdy = 1 / dy, rdz = 1 / dz;
    const float rH = 1 / (float)H;
    const float H3 = H * H * H;

    const float near = nears[n];
    const float far = fars[n];
    const float noise = noises[n];

    const float dt_min = 2 * SQRT3() / max_steps;
    const float dt_max = 2 * SQRT3() * bound / H;
    // const float dt_max = 1e10f;

    float t0 = near;
    t0 += clamp(t0 * dt_gamma, dt_min, dt_max) * noise;
    float t = t0;
    uint32_t step = 0;

    //if (t < far) printf("valid ray %d t=%f near=%f far=%f \n", n, t, near, far);

    while (t < far && step < num_steps) {
    
    
        // current point
        const float x = clamp(ox + t * dx, -bound, bound);
        const float y = clamp(oy + t * dy, -bound, bound);
        const float z = clamp(oz + t * dz, -bound, bound);

        float dt = clamp(t * dt_gamma, dt_min, dt_max);

        // get mip level
        const int level = max(mip_from_pos(x, y, z, C), mip_from_dt(dt, H, C)); // range in [0, C - 1]

        const float mip_bound = fminf(scalbnf(1.0f, level), bound);
        const float mip_rbound = 1 / mip_bound;

        // contraction
        float cx = x, cy = y, cz = z;
        const float mag = fmaxf(fabsf(x), fmaxf(fabsf(y), fabsf(z)));
        if (contract && mag > 1) {
    
    
            // L-INF norm
            const float Linf_scale = (2 - 1 / mag) / mag;
            cx *= Linf_scale;
            cy *= Linf_scale;
            cz *= Linf_scale;
        }

        // convert to nearest grid position
        const int nx = clamp(0.5 * (cx * mip_rbound + 1) * H, 0.0f, (float)(H - 1));
        const int ny = clamp(0.5 * (cy * mip_rbound + 1) * H, 0.0f, (float)(H - 1));
        const int nz = clamp(0.5 * (cz * mip_rbound + 1) * H, 0.0f, (float)(H - 1));

        const uint32_t index = level * H3 + __morton3D(nx, ny, nz);
        const bool occ = grid[index / 8] & (1 << (index % 8));

        // if occpuied, advance a small step, and write to output
        //if (n == 0) printf("t=%f density=%f vs thresh=%f step=%d\n", t, density, density_thresh, step);

        if (occ) {
    
    
            step++;
            t += dt;
            if (!first_pass) {
    
    
                xyzs[0] = cx; // write contracted coordinates!
                xyzs[1] = cy;
                xyzs[2] = cz;
                dirs[0] = dx;
                dirs[1] = dy;
                dirs[2] = dz;
                ts[0] = t;
                ts[1] = dt;
                xyzs += 3;
                dirs += 3;
                ts += 2;
            }
        // contraction case: cannot apply voxel skipping.
        } else if (contract && mag > 1) {
    
    
            t += dt;
        // else, skip a large step (basically skip a voxel grid)
        } else {
    
    
            // calc distance to next voxel
            const float tx = (((nx + 0.5f + 0.5f * signf(dx)) * rH * 2 - 1) * mip_bound - cx) * rdx;
            const float ty = (((ny + 0.5f + 0.5f * signf(dy)) * rH * 2 - 1) * mip_bound - cy) * rdy;
            const float tz = (((nz + 0.5f + 0.5f * signf(dz)) * rH * 2 - 1) * mip_bound - cz) * rdz;

            const float tt = t + fmaxf(0.0f, fminf(tx, fminf(ty, tz)));
            // step until next voxel
            do {
    
    
                dt = clamp(t * dt_gamma, dt_min, dt_max);
                t += dt;
            } while (t < tt);
        }
    }

    //printf("[n=%d] step=%d, near=%f, far=%f, dt=%f, num_steps=%f\n", n, step, near, far, dt_min, (far - near) / dt_min);

    // write rays
    if (first_pass) {
    
    
        uint32_t point_index = atomicAdd(counter, step);
        rays[0] = point_index;
        rays[1] = step;
    }
}

4. TV 정규화

TV 정규화, 전체 이름은 Total Variation Regularization입니다. TV 정규화는 이미지의 기울기 크기를 최소화하여 이미지 평활화를 달성합니다.

특히 2차원 이미지의 경우 TV 정규화는 이미지의 기울기 크기를 최소화하여 평활화를 달성할 수 있습니다 . 이렇게 하면 이미지의 노이즈와 디테일이 억제됩니다. 이미지를 더 부드럽게 만듭니다. TV 정규화는 일반적으로 데이터 피팅과 매끄러움 간의 관계 균형을 맞추기 위해 최적화 문제의 정규화 용어에 적용됩니다.

3차원 재구성에 주로 사용되는 NeRF(Neural Radiance Fields)와 같은 방법의 경우 TV 정규화는 재구성 결과의 품질을 향상시키는 데 도움이 됩니다. 이는 3D 재구성에서 데이터 희소성 및 노이즈와 같은 요인으로 인해 재구성 결과에 불필요한 세부 사항과 노이즈가 포함되는 경우가 많기 때문입니다 .

또한 TV 정규화는 딥러닝 모델의 제약 조건을 강화하고 모델의 일반화 능력과 잡음 방지 능력을 향상시키는 데도 도움이 될 수 있습니다. 따라서 NeRF와 같은 3D 재구성 작업의 경우 TV 정규화를 적용하면 재구성 결과의 품질을 향상하고 모델의 견고성을 높이는 데 도움이 됩니다.

추천

출처blog.csdn.net/qq_45752541/article/details/135070014