[DIY]흙수저 VR 자작 프로젝트: 적외선 카메라 트래킹 이론편
2019. 6. 1. 01:22
프로젝트 글목록
2020/05/01 - [자작/VR] - [DIY]흙수저 VR 자작 프로젝트: 사물의 이동과 방향표현
2019/06/01 - [자작/VR] - [DIY]흙수저 VR 자작 프로젝트: 적외선 카메라 트래킹 실전편
2019/06/01 - [자작/VR] - [DIY]흙수저 VR 자작 프로젝트: 적외선 카메라 트래킹 이론편
2019/05/31 - [자작/VR] - [DIY]흙수저 VR 자작 프로젝트: 기획
개요
부품이 오기 전까지 이론 공부 좀 하고 트래킹 알고리즘을 짜는 중입니다. 제가 만들 VR은 두 가지 방법으로 트래킹을 구현합니다. 첫번째는 자이로스코프 센서를 이용하는 것입니다.
자이로 센서를 이용하면 빠른 반응속도로 실시간 트래킹이 가능합니다. 하지만 자이로 센서는 중력 가속도와 각속도만을 측정하기 때문에 위치를 알아내기 위해서 가속도를 두 번 적분해야 하고 이 과정에서 오차가 발생합니다. 그리고 처음 시작 지점으로부터 상대 위치만을 알 수 있다는 단점이 있습니다. 이 오차를 보정하기 위해 적외선 카메라를 사용합니다. 이 포스팅에서는 적외선 카메라를 이용한 트래킹 방법에 대해 적어보려 합니다.
시작하기에 앞서 설명이 전체적으로 깔끔하지가 않습니다ㅠㅠ 저도 이해를 잘 못한 부분도 있고 글에서 빠진 개념도 있어요. 혹시나 보면서 따라 만드실 분들은 차라리 글 맨 밑에 참고자료 링크 하나씩 보면서 하시는 게 좋을 거예요.
적외선 카메라를 이용한 트래킹이란?
먼저 HMD와 컨트롤러에 적외선 LED를 달아놓습니다. 그리고 이것을 카메라로 찍으면 사진에 LED 불빛이 찍힐 것입니다. 카메라의 위치와 사진에서의 불빛의 위치를 가지고 계산하면 LED의 실제 위치를 직선의 형태로 표현할 수 있습니다. 이 과정을 여러 개의 카메라로 반복하면 특정한 점으로 모이는 직선들을 찾을 수 있고 이 점이 실제 LED가 위치한 곳의 좌표가 됩니다.
좌표계
자세한 설명은 아래 링크로 대체하겠습니다.
[영상 Geometry #1] 좌표계 - 다크 프로그래머
참고로 이분 블로그에서 많은 도움을 얻었습니다.
요약하자면
픽셀 좌표계: 카메라가 찍은 사진의 평면좌표계(X,Y축=가로,세로축)
카메라 좌표계: 카메라를 원점으로 하는 공간좌표계(Z축: 카메라가 바라보는 방향, XY축=카메라를 앞에서 바라볼 때 가로세로축)
월드 좌표계: 카메라가 위치한 공간을 기준으로 한 공간좌표계
이를 그림으로 나타내면 대략 이렇습니다.
카메라로 사진을 찍으면 일단 점의 위치는 픽셀 좌표계로 표현됩니다. 이를 카메라 좌표계에 나타내고 다시 월드 좌표계로 나타냅니다. 물론 이렇게 해서 점의 위치를 정확하게 알아낼 수 없습니다. 점과 카메라의 거리는 알 수 없기 때문입니다. 하지만 점과 카메라를 지나는 직선은 알 수 있습니다.
카메라 내부 파라미터
그런데 같은 LED를 같은 위치와 각도에서 찍는다 해도 카메라의 종류에 따라 픽셀 좌표계에 찍히는 점의 위치도 다를 겁니다. 카메라 렌즈의 시야각에 따라서도 달라질 것이고 렌즈와 이미지 센서와의 거리(초점거리)도 영향을 미칩니다. 이렇게 픽셀 좌표계에 영향을 주는 변수들을 카메라 내부 파라미터(intrinsic parameter)라고 합니다.
파라미터를 고려하여 픽셀 좌표계를 바로 월드 좌표계로 변환하려면 계산이 매우 복잡해지게 됩니다. 그래서 보통은 카메라 캘리브레이션이라는 과정을 거친 정규 좌표계를 사용합니다. 하지만 저는 간단하게 카메라의 시야각만을 이용해 픽셀 좌표계 점을 그대로 카메라 좌표계 상에서의 방향벡터로 변환하겠습니다.
시야각을 이용하여 카메라 좌표계로 변환하기
먼저 카메라의 시야각을 구하는 방법입니다. 카메라를 바닥을 바라보게 하고 특정 높이 위에 고정합니다. 그 다음 물체를 카메라의 중앙에 보이도록 올려 놓고 카메라에서 보이지 않을 때까지 왼쪽으로 밉니다. 왼쪽으로 민 위치가 아래 그림 속 점의 위치에 해당하게 됩니다. 처음 위치와 현재 위치의 거리를 w, 카메라의 높이를 h라 하면 그림 속의 식을 통해 가로 시야각을 구할 수 있습니다.
같은 방법으로 왼쪽 대신 위쪽으로 밀어서 w를 새로 구하거나 w에 (사진의 세로 해상도/가로 해상도)를 곱해서 세로 시야각을 구할 수 있습니다.
이렇게 해서 구한 갤럭시 노트8 후면 카메라의 시야각은 (0.567005, 0.445538)입니다.
시야각을 구했으니 픽셀 좌표계라는 평면을 카메라 좌표계라는 공간에 놓을 수 있게 되었습니다. 픽셀 좌표계의 점을 p(x, y), 사진 중앙점의 좌표를 (cx, cy), 사진의 해상도를 (iw, ih)라 하겠습니다. 픽셀 좌표계는 카메라 좌표계의 (-cx, -cy, z)를 원점으로 배치할 수 있습니다. 중앙점의 좌표를 빼는 이유는 픽셀 좌표계의 원점은 사진의 왼쪽 위 모서리인데 카메라 좌표계의 원점은 사진의 정중앙이기 때문입니다. z는 시야각을 통해 구할 수 있습니다. z를 구하는 식은
$$z=iw/2/tan(가로 시야각)$$
입니다.
픽셀 좌표계가 카메라 좌표계 위에 놓여지게 되면서 픽셀 좌표계 위에 있던 점(LED)도 카메라 좌표계에 놓이게 되었습니다. 따라서 이제 카메라 좌표계의 원점에서 점의 위치로 향하는 벡터를 구할 수 있습니다. 앞으로 이 벡터를 V라고 부르겠습니다.
이렇게 해서 구한 벡터 V는
$$V(x-cx, y-cy, z)$$
가 됩니다.
카메라 좌표계->월드 좌표계
방금 구한 V는 카메라 좌표계를 기준으로 합니다. 트래킹에 사용될 카메라들은 모두 다른 각도와 좌표에 위치해 있기 때문에 서로 다른 좌표계를 가지게 됩니다. 따라서 V를 월드 좌표계를 기준으로 변환해야 할 필요가 있습니다.
카메라를 포함한 3차원 물체의 각도는 각 축(x, y, z)을 기준으로 회전시켜 표현할 수 있습니다. x축 회전을 pitch, y축 회전을 yaw, z축 회전을 roll이라 합니다.
마찬가지로 좌표계도 3차원 물체로 본다면 좌표계를 회전시켜 다른 좌표계로 변환할 수 있습니다. 따라서 카메라 좌표계를 월드 좌표계에 있는 카메라 각도만큼 회전하면 월드 좌표계로 변환됩니다.
원점에 기준으로 점을 각 축으로 θ 라디안만큼 회전하는 행렬 Rx(θ),Ry(θ),Rz(θ)은 다음과 같습니다.
카메라의 각도가 (rx, ry, rz)일 때 V를 월드 좌표계로 변환한 벡터 Vw는 다음과 같습니다.
$$V_w=R_z(rz)R_y(ry)R_x(rx-\frac{\pi}{2})$$
rx에 90도를 빼준 이유는 카메라가 카메라 좌표계에서는 z축을 바라보고 있고 월드 좌표계에서는 y축을 바라보고 있기 때문입니다. 이렇게 해서 카메라와 점을 지나는 직선의 방향 벡터를 구할 수 있습니다. 다른 카메라로 같은 과정을 반복하면 마찬가지로 직선의 방향 벡터를 찾을 수 있고 이 직선들의 교점이 바로 월드 좌표계로 표현된 LED의 좌표가 됩니다.
교점이 생기지 않을 때 점 특정하기
그런데 실제로 여러 대의 카메라에서 한 점으로 향하는 직선을 구해보면 교점이 생기지 않습니다. 카메라의 좌표가 정확하지 않을 수도 있고 카메라의 해상도가 무한이 아니라서 오차가 생길 수밖에 없기 때문입니다. 따라서 꼬인 위치의 직선 사이에서 점의 위치를 찾아야 하는데 그 방법은 두 직선 사이의 최단 지점을 찾아 그 점들의 중점을 구하는 겁니다.
최단 거리를 이루는 두 직선 위의 점 찾기
직선L1과 L2 사이의 최단 지점(R1, R2)은 두 직선과 만나고 수직을 이루는 직선 L3 위의 점입니다. 따라서 외적을 통해 L3의 방향벡터를 알아낼 수 있습니다.1) 그리고 R1은 L2와 L3가 이루는 평면과 L1의 교점5)을, R2는 L1와 L3가 이루는 평면과 L2의 교점을 찾아 구할 수 있습니다.
아래는 실제 R1을 구하는 과정입니다.
직선 L1,L2가 각각 점 P1,P2를 지나고 벡터 V1,V2를 방향벡터로 가질 때, 직선 L3의 방향벡터 V3는
$$V_3=V_1\times V_2 \thinspace\thinspace...1)$$
L2와 L3가 이루는 평면 M2의 법선벡터 VM2은
$$V_{M_2} = V_3\times V_2$$
따라서 평면 M2의 위의 점 R1의 방정식은
$$V_{M_2}\cdot (R_1-P_2)=0 \thinspace\thinspace...2)$$
이고, L1 위의 점 R1의 방정식은
$$R_1 = P_1 + t\cdot V_1 \thinspace\thinspace...3)$$
이다. 2)와 3)의 식을 연립하여 t를 구하면
$$V_{M_2}\cdot (P_1 + t\cdot V_2 - P_2) = 0$$
$$t = \space\space \frac{V_{M_2}\cdot (P_2 - P_1)}{V_{M_2}\cdot V_2} \space\space...4)$$
가 된다. 4)에서 구한 t를 3)에 대입하면
$$∴ R_1 = P_1 + ((V_{M_2}\cdot (P_2 - P_1)) / (V_{M_2}\cdot V_2))\cdot V_1 ...5)$$
P1과 P2는 카메라의 좌표를 대입하면 됩니다.
같은 과정으로 R2를 구하고 (R1+R2)/2를 하면 드디어 점의 좌표를 찾을 수 있습니다.
오차가 큰 점과 다른 점 구분하기
카메라를 통해 추적해야 하는 점이 1개만 있는 것도 아니고 어떤 직선이 다른 카메라의 직선과 같은 점을 향하는지 카메라는 알 수 없습니다. 이럴 때는 경계값을 정해두고 두 직선의 최단거리가 경계값보다 크면 다른 점으로 인식하게 해서 해결할 수 있습니다. 두 직선 사이의 최단거리를 구할 땐 복잡하게 위 과정을 다 거치고 점 사이의 거리를 구하지 않고도 아래 공식으로 바로 구할 수 있습니다.
$$최단거리 = \space\space \space\space \space\space \frac{|(P_2-P_1)\cdot (V_1\times V_2)|}{|V_1\times V_2|}$$
다음 포스팅은 자바로 위 과정대로 소스짜고 직접 사진 찍어서 점 위치 추적하는 거랑 추적한 점들 가지고 오브젝트 트래킹 구현하는 거 올리겠습니다.
참고자료
좌표계 - https://darkpgmr.tistory.com/77
회전변환 - https://darkpgmr.tistory.com/81
좌표계 변환 - https://darkpgmr.tistory.com/84
회전행렬 - https://en.wikipedia.org/wiki/Rotation_matrix
평면과 선의 교차점 구하기 - http://www.gisdeveloper.co.kr/?p=792
꼬인 위치에 있는 두 직선 사이의 거리 - https://suhak.tistory.com/470
행렬곱 - https://terms.naver.com/entry.nhn?docId=945498&cid=47324&categoryId=47324
벡터의 외적 - https://namu.wiki/w/외적