[DIY]흙수저 VR 자작 프로젝트: 적외선 카메라 트래킹 실전편
2019. 6. 1. 09:28
프로젝트 글목록
2020/05/01 - [자작/VR] - [DIY]흙수저 VR 자작 프로젝트: 사물의 이동과 방향표현
2019/06/01 - [자작/VR] - [DIY]흙수저 VR 자작 프로젝트: 적외선 카메라 트래킹 실전편
2019/06/01 - [자작/VR] - [DIY]흙수저 VR 자작 프로젝트: 적외선 카메라 트래킹 이론편
2019/05/31 - [자작/VR] - [DIY]흙수저 VR 자작 프로젝트: 기획
개요
이번에는 지난 글에서 설명한 내용을 직접 자바 언어로 구현해서 실제 물체(점)의 위치를 찾아내보도록 하겠습니다.
전체 코드는 아래 링크에서 확인할 수 있습니다.
w4ei756fg/object_tracking_IRcamera
참고로 이 프로젝트에서 개발되는 모든 코드는 이 깃허브 계정을 통해 오픈됩니다.
사진 촬영
테스트를 위해 종이 위에다 점 몇 개를 찍어 보았습니다.
이 A4종이 위를 좌표 공간이라 생각하고 오른쪽 아래 모서리를 원점으로 해서 좌표를 적어놨습니다. 단위는 cm입니다.
원래는 카메라 두 대를 고정시켜 놓고 영상을 찍으며 실시간으로 트래킹을 하는 것이 목표지만 테스트이기 때문에 카메라 하나를 들고 각각 다른 위치에서 사진을 두 번 찍었습니다.
카메라의 좌표는 각각 (0, 0, 14), (29.7, 0, 14)입니다.
여기서 잠깐! 컴퓨터는 저 사진 속 점을 인식하지 못합니다. 보통은 opencv를 써서 인식하는데 이번에는 테스트니까 직접 사진에다 빨간색(#FF0000) 점을 찍고 자바 내장 함수를 이용해 그 색의 점만 찾도록 하였습니다.
코딩
프로그램에서 카메라 객체를 추가하고 사진으로부터 점의 좌표를 불러오는 코드입니다.
public class CameraTracker {
private ArrayList<Camera> cam = new ArrayList<Camera>();
private ArrayList<Vector3> points = new ArrayList<Vector3>();
public CameraTracker() {
addCamera(0, 0, 14, -0.85582, -0.680354, 0);
addCamera(29.7, 0, 14, 0.950546, -0.683191, 1);
//addCamera(61.2,1.19,4.15, 0, 0, 0);
//addCamera(96.3 - 29.26,27.25,47.0, 0, -Math.PI/2, 1);
updateCamera();
findPoint();
showPoints();
}
/*...*/
}
카메라 트래킹은 CameraTracker 클래스 안에서 처리됩니다. addCamera 메소드를 통해 카메라의 위치와 방향값을 입력받고 Camera 클래스를 생성하면, findPoint 메소드 호출시켜 직선을 추적하고 최종적으로 추적된 점들은 points라는 리스트에 저장됩니다.
public void findPoint() {
double th = 0.6;
points = new ArrayList<Vector3>();
ArrayList<Vector3>[] tl = new ArrayList[cam.size()];
for(int i = 0; i < tl.length; i++)
tl[i] = cam.get(i).getTraceLine();
double nearDist = th;
Vector3 near = new Vector3();
for(int i = 0; i < tl.length - 1; i++)
for(int j = 0; j < tl[i].size(); j++)
for(int k = i + 1; k < tl.length; k++) {
nearDist = th;
for(int l = 0; l < tl[k].size(); l++)
if (i != k) { // not self
Vector3 p1 = cam.get(i).getPos(), v1 = tl[i].get(j), p2 = cam.get(k).getPos(), v2 = tl[k].get(l);
double dist = distLineToLine(p1, v1, p2, v2);
show("");
if(dist < th) {
show("I:점 감지됨");
Vector3 v3 = v1.cross(v2).cross(v1); // 최단거리 벡터와 v1이 이루는 평면의 법선벡터
double u = v3.dot(p1.minus(p2)) / v2.dot(v3);
Vector3 p3 = p2.plus(v2.mul(u)); // l2측 최근점
Vector3 p4 = p3.plus(v1.cross(v2).mul(-dist / v1.cross(v2).abs())); // l1측 최근점
//show("직선 (p1,v1)측 근접점: " + p3.toString());
//show("직선 (p2,v2)측 근접점: " + p4.toString());
Vector3 p = p3.plus(p4).mul(0.5);
if (dist < nearDist) { // 같은 카메라 안에서 하나만 인식
if (nearDist != th) {
near = null;
nearDist = -1;
} else {
near = p;
nearDist = dist;
}
}
show("중간점: " + p.toString());
}
show("dist: " + dist + "(" + i + "번 카메라의 " + j + "번째 광선과 " + k + "번 카메라의 " + l + "번째 광선)" );
}
if (nearDist != th && near != null) {
points.add(near);
}
}
}
카메라와 점을 지나는 직선을 가지고 점들의 좌표를 찾는 코드입니다. 지난 포스팅에서 장황하게 설명했던 내용들이 이 사진 한 장으로 정리됩니다.
테스트 결과
이제 직접 코드를 컴파일해서 실행시켜 보겠습니다.
사진으로부터 추출된 점의 좌표와 직선들
Points는 픽셀 좌표계 위의 점이고 TraceLine은 카메라 좌표계 위의 직선(벡터)입니다. 무슨 말인지 모르겠다면 이전 글을 보고 오세요.
추적한 점들의 좌표 결과값
최종적으로 구한 점들의 좌표입니다. 이것이 사진 2장만 가지고 공간 상의 위치를 추적한 결과입니다. 그런데 결과값을 보면 모든 점들이 한 평면(종이) 위에 있는데도 불구하고 z축 좌표가 제각각인 걸 볼 수 있습니다. 특히 3,4,7번 좌표가 많이 튀는데요. 결과값을 그림으로 나타내 보았습니다.
테스트 결과 분석
처음에 찍은 점과 비교해 세 개의 점이 틀린 위치에 있는 걸 볼 수 있습니다. 왜 이상한 위치에 점이 찍혔을까 생각해봤습니다.
먼저 입력된 카메라의 정보가 정확하지 않아서 오차가 발생했을 수 있습니다. 스마트폰을 손으로 들고 촬영한 것이기 때문에 분명히 위치나 방향에 오차가 있을 겁니다. 이는 추적된 점들의 z좌표가 조금씩 다른 것으로 확인이 가능합니다. 그렇지만 이렇게 일부 점만 좌표가 크게 튀는 것은 설명이 안 됩니다.
그렇다면 뭐가 문제였을까요? 제가 구현한 적외선 트래킹은 여러 개의 직선의 교점을 구함으로써 물체를 찾습니다. 아래 그림을 봅시다.
여기 그림과 같이 한 평면 위에 카메라와 점이 있다고 칩시다. 앞선 내용에서 공간 위의 점의 좌표는 카메라와 점을 지나는 직선들의 교점을 구함으로써 찾는다고 했습니다. 그림의 녹색 선이 그 직선이죠. 다음 그림을 봅시다.
실제 점의 갯수는 2개지만 위 그림처럼 직선이 만나서 생기는 교점의 갯수는 4개가 됩니다. 따라서 실제로 존재하지 않는 파란 점을 물체로 인식하게 되고 제가 구현한 알고리즘에서는 이를 진짜 물체와 구분하지 못하기 때문에 이런 결과가 나타난 것입니다. 아까 그림에다 선을 그어보면 틀린 점들이 어떤 다른 점과 만나서 생긴 점인지 알 수 있습니다.
연두색 점이 실제 물체가 있는 곳이고 파란색 작은 점이 잘못 트래킹된 점(실제 트래킹 결과)입니다. 빨간 점을 지난 직선이 다른 직선과 만나서 파란 점을 생기는 걸 확인할 수 있습니다.
문제 해결(?) 방법
물체(hmd,컨트롤러)의 형태는 변하지 않기 때문에 정상적으로 추적한 점들을 이용해 잘못 추적한 점을 추정하여 바로잡을 수 있습니다. 하지만 애초에 적외선 트래킹의 목적이 물체의 위치와 방향을 구하는 건데 이건 점 세 개만 확실히 알아도 구현 가능하기 때문에 굳이 모든 점을 찾을 필요는 없습니다. 따라서 잘못 추적한 점들은 모두 무시해서 물체 트래킹을 정확하게 할 수 있도록 하였습니다.
직구한 부품들이 바다 건너 국내로 들어왔습니다. 오늘 중(최초작성일 5/11)이나 담주 월요일쯤 도착할 것 같네요. 실제 부품을 가지고 노는 건 좀 더 오래 걸리지 않을까 싶습니다.
다음 포스팅에서는 물체의 방향을 표현하는 데 쓰이는 '사원수'와 물체의 이동과 방향변환에 대해서 간단하게 짚고 넘어가겠습니다.