[Weekly AINFT] Stradivarius 1편 - NeRF


스트라디바리우스가 왜 세계적인 명성을 날리고 있는지 아시나요? 안토니오 스트라디바리만의 독특한 목재 처리와 디자인으로 인해, 복제가 거의 불가능한 특유의 소리를 낸다고 합니다. BBC 다큐에서 스트라디바리우스의 소리에 대해 다뤘는데 분석 결과, 여타 바이올린과 구조가 조금 달랐다고 합니다.

울림통의 f-형 구멍은 대부분의 바이올린에서 대칭이지만, 스트라디바리우스 바이올린은 살짝 대칭이 어긋났다는 분석이었지요. 시각적 완벽함을 버리고 오직 청각적인 완벽함을 추구한 장인 정신과 그 연구 결과라고 평가되고 있습니다. 또한 악기 위에 칠해진 완벽한 비율의 바니쉬도 스트라디바리우스를 특별하게 하는 이유 중 하나라고 합니다.

이번 편에서는 스트라디바리우스의 이러한 독특한 비주얼을 좀 더 생동감 있는 AINFT로 구현하기 위한 방법으로 3D Modeling을 할 수 있는 모델 중 하나인 NeRF(Neural Radiance Fields)를 활용한 이야기해 보겠습니다. 그뿐만 아니라 직접 모델을 학습/추론해 볼 수 있도록 Ainize Workspace를 제공하였으니 많은 관심 부탁드립니다. 현실 세계에 있는 Stradivarius 바이올린을 왜 AINFT로 만들어 메타버스로 옮겨야 하는지에 대해 알고 싶으신 분들은 전편을 참고해 주세요.


제가 이번에 소개드릴 모델은 NeRF(Neural Radiance Fields)입니다. NeRF의 궁극적인 목표는 view synthesis입니다. NeRF는 하나의 물체를 여러 각도에서 촬영한 이미지들을 입력으로 받습니다. 이 이미지들을 이용해서 주어지지 않은 새로운 각도에서 물체의 모습을 만들어내는 것이죠.

NeRF(Neural Radiance Fields)

이미지를 다루는 모델에서는 CNN(Convolutional Neural Networks) 구조를 많이 사용했습니다. 하지만 NeRF에서는 CNN이 아닌 Skip connection이 포함된 Deep fully-connected neural network를 사용합니다.

모델의 입력으로 각 점의 3D 좌표값(x, y, z)과 광선의 방향에 대한 정보, 즉 어디서 바라보느냐에 대한 정보(Φ, Θ)를 이용합니다. (Φ, Θ)를 이용하는 이유는 각 점의 색상은 바라보는 방향에 따라 달라지기 때문(View-dependent)입니다. 따라서 5D 좌표 값(x, y, z, Φ, Θ)이 모델의 입력으로 주어지면 모델의 출력으로 각 점의 (R, G, B)값과 Volume Density(σ)값을 얻습니다.

그 후 볼륨 렌더링(Volume rendering)을 통해 (R, G, B, σ) 값들을 바탕으로 2D 이미지로 만듭니다. 최종적으로 얻은 렌더링 이미지와 해당 view에서의 ground-truth 이미지와의 픽셀 별 SE(Squared Error)를 Loss로 사용합니다. 하지만 픽셀 별로 차이를 계산하는 것은 비용이 크기 때문에 전체 이미지에 대해서 비교하지 않고 이미지를 grid로 나눈 후 일부만 샘플링하여 계산합니다.

여기서 Loss를 계산할 때 모델의 출력을 바로 이용하는 것이 아니라 출력을 렌더링 한 결과를 이용하는데 어떻게 학습이 진행될 수 있는 걸까요? 바로 Volume rendering function이 미분가능(Naturally differentiable) 하기 때문에 모델의 출력으로 얻은 이미지와 ground-truth 이미지에 대한 차이를 구할 수 있는 것입니다.


하지만 위의 과정 만으로는 좋은 성능을 달성하기 어려웠는데요, 추가적인 기법을 사용해 고해상도의 결과를 얻을 수 있었습니다. 바로 Positional Encoding을 적용한 것입니다.

Positional Encoding은 input에 위치적 정보를 부여하는 것입니다. NeRF에서는 high-frequency 부분을 더 잘 표현하기 위해 Positional Encoding을 이용했다고 하였습니다. 이미지에서 high-frequency 부분은 주로 디테일한 부분이기 때문에 Positional Encoding을 이용하면 이미지의 디테일한 부분까지 잘 나타낼 수 있습니다. 아래 사진에서 가장 오른쪽에 있는 사진이 Positional Encoding을 적용하지 않았을 때의 결과입니다. 레고 바닥, 바퀴 등등 디테일한 부분이 표현되지 못한 것을 볼 수 있습니다.

NeRF 사용해보기

이제 NeRF를 직접 사용해 보겠습니다. NeRF 논문에서 제시한 모델은 찾을 수 없어 pytorch-lightning을 사용하여 NeRF를 비공식적으로 구현한 NeRF_PL을 사용하겠습니다. 실습 환경은 Ainize Workspace를 통해 구성하였으며 link를 통해 생성할 수 있습니다.

우선 데이터를 가지고 COLMAP을 돌리겠습니다. COLMAP은 NeRF 기반의 모델이 나오기 전까지 3D 모델링을 위해 사용했던 3D reconstruction 툴입니다. 저희는 COLMAP을 통해 입력받은 이미지를 바탕으로 물체를 찍은 카메라 위치와 포즈를 재구성하겠습니다. 저는 재구성을 위해 automatic_reconstructor을 사용했지만 COLMAP GUI로 한 단계씩 진행하는 것이 성능에 좋을 것입니다.

COLMAP을 돌린 결과물은 다음 사진과 같이 나옵니다.

COLMAP 결과물이 나왔다면 이를 가지고 LLFF 데이터 셋의 형태로 바꾸겠습니다.

LLFF 데이터 셋 형태로 변환된 파일을 얻었습니다.

이제 학습을 진행하도록 하겠습니다. 저는 시간 관계상 1 Epoch만 진행하였습니다.

학습이 완료되었으면 Test를 돌려보겠습니다.

Test가 끝나면 물체의 360 렌더링 gif가 나오게 됩니다.

이렇게 하여 NeRF를 살펴보고 직접 사용까지 해보았습니다. 지금 사용한 NeRF는 학습 시간이 너무 오래 걸린다는 단점이 있었는데요(한 에폭에 약 5시간 정도 걸림). 다음 편에서는 기존 NeRF의 속도 문제를 개선한 Instant-NGP를 직접 사용한 내용으로 찾아오겠습니다.

Reference

4 Likes