COP(Center of Projection)에서 Image plane의 각 pixel을 향하는 vector 구하기
-
이 글은 미완성된 글이며, 곧 마무리할 예정입니다.
일반적으로 머신러닝을 사용하는 모델을 구현하는 논문에서 PyTorch로 코드를 보면 meshgrid라는 함수를 종종 볼 수 있다. 이 함수는 scalar 값 또는 1차원 tensor로 된 $n$개의 input을 받아서 좌표계의 grid를 생성한다. 이 함수에 관한 설명은 pytorch 공식 문서에 잘 나와있다. 자세한 내용은 PyTorch 공식 문서의 meshgrid 함수 페이지를 참고하면 된다.
그런데 개인적으로 이 함수가 언제 자주 쓰이는지, 그리고 언제 사용하는 게 유용한지 감이 잘 잡히지 않았다. 그러나 컴퓨터비전 또는 그래픽스 논문 구현 코드를 찾아보면 종종 사용하는 함수이다. 어떨 때 쓰일 수 있는지를 한 번 정리해두면 나중에 실제로 모델을 구현할 때 참고할 수 있을 것 같아서 몇 가지 예시로 간단하게 정리해보려고 한다.
1. 이미지의 각 픽셀마다 뻗어나가는 ray의 방향 벡터 계산
NeRF(Neural Radiance Fields)에서 카메라 좌표계의 중심에서 이미지의 각 픽셀로 뻗어나가는 ray의 방향 벡터를 구하는 과정에서 쓰일 수 있다. 아래는 그 내용을 구현한 함수이다.
# 하나의 image에 관해 각각의 모든 픽셀을 지나는 ray의 origin과 viewing direction(pose)
def get_rays(h: int, w: int, focal_length: float, pose: torch.Tensor):
# Pixel coordinate
i, j = torch.meshgrid(
torch.arange(w, dtype=torch.float32).to(pose),
torch.arange(h, dtype=torch.float32).to(pose),
indexing='ij')
i, j = i.transpose(-1, -2), j.transpose(-1, -2)
# Pixel coordinate에서의 각각의 픽셀이 이미지의 중점(w와 h의 각각의 2분의 1)으로부터 얼마나 떨어져 있는지
rays_d = torch.stack([(i - w * .5) / focal_length,
-(j - h * .5) / focal_length,
-torch.ones_like(i)
], dim=-1)
# Pixel별 ray의 viewing direction을
# world coordinate에서의 camera rotation 방향으로 align 시킨다.
# shape of rays_d: (width, height, 3) = (width, height, 1, 3) * (3, 3)
'''
계산 과정에서 broadcasting이 된다는 걸 고려하자.
(width, height, 1, 3) * (3, 3) => (width, height, 3, 3) * (width, height, 3, 3)
sum[(width, height, 3, 3), dim=-1] => (width, height, 3)
'''
rays_d = torch.sum(rays_d[..., np.newaxis, :] * pose[:3,:3], dim=-1)
# shape of rays_o: (width, height, 3)
# (3) => (width, height, 3)
rays_o = pose[:3,-1].expand(rays_d.shape)
return rays_o, rays_d
위의 코드에서 torch.meshgrid 함수를 써서 2차원 이미지에서 각 픽셀의 좌표를 grid로 계산하는 과정이다. 2차원 좌표계를 다룰 때는 대체로 인자의 순서를 어떻게 주는지가 중요해진다. $xy$ coordinate으로 다루는지 아니면 $yx$ coordinate으로 다루는지에 따라서 말이다. 위의 예시에서는 첫 번째 인자를 width, 두 번째 인자를 height으로 줌으로써 $x$값을 좌표의 앞쪽에 오게 한 것이다.
그런데 width는 오른쪽 방향으로 늘어나야 하고 height는 아래 방향으로 늘어나야 하므로 transpose를 취한다. 이후 stack을 사용하여 $x$축 좌표 값과 $y$축 좌표 값을 $z$축 좌표 값인 -1과 같은 차원의 원소로 묶는다. 여기서 주의할 점은 $y$축은 위의 방향으로 증가하므로 그전에 아래 방향으로 증가하는 $y$축 좌표 값에 -1을 곱해서 $y$축의 증가 방향과 일치하도록 한다. 위의 그림에서 설명한 과정을 따라가 보면 이해하기 쉽다.