{{ label!='' ? 'Label : ' : (q!='' ? '검색 : ' : '전체 게시글') }} {{ label }} {{ q }} {{ ('('+(pubs|date:'yyyy-MM')+')') }}

[프로젝트] VR 사용자를 위한 충돌 방지 어플리케이션

이제는 VR의 시대를 준비해야 할 때입니다..

  세간에 큰 충격을 주었던 <Half Life: Alyx>의 등장을 기점으로 VR의 세계는 완전히 달라졌습니다. 하지만 우리가 VR의 세계에 입문하기에 안타까운 점은 내 방이 너무나도 좁다는 사실입니다. VR에 몰입하다 보면 크고 작은 상해를 입곤 합니다. 저도 제 손가락을 책상 모서리에 찧은 적이 한두 번이 아니니까요!

VR을 사용하는 것은 생각보다 쉬운 일이 아닙니다..


  이런 문제를 우리 스스로는 어떻게 해결할 수 있을까요? 안타깝게도 저는 돈도, 시간도 많지 않습니다. 그에 반해 <Vive>, <Meta>와 같은 기업들은 이를 위해 많은 돈과 시간을 투자하고 있죠! 그들을 이길 수는 없겠지만, 우리도 한 번 이러한 문제
를 해결하기 위해 해결책을 제시해보자구요.

  많은 VR 기기는 사용자의 충돌을 방지하기 위해 좋은 기능들을 내장하고 있습니다. 하지만 이는 어디까지나 기기에 내장된 기능이죠. 사용자들을 끌어들이기 위한 마케팅 수단입니다. 우리는 <Oculus Quest 2>를 구매하지 않으면 <Meta>의 가디언 기능을 사용할 수 없습니다. 우리는 <HTC Vive>를 구매하지 않으면 <Vive>의 샤프론 카메라를 활용할 수 없습니다. 기기 독립적인 충돌 방지 방법은 없을까요?


우리가 직접 만들면 되죠!

  따라서 본 프로젝트에서는 VR 사용자의 충돌 방지를 위한 기기 독립적인 방법을 제안합니다. 간단히 요약하면, 외부 카메라를 이용해 주변을 촬영하고, 촬영된 영상을 기반으로 3D 맵을 만들어 VR 환경 내에 구축하는 방법입니다. 전체적인 파이프라인을 요약해서 보여준 그림은 다음과 같습니다:

파이프라인


영상 촬영 및 스티칭

  먼저, 영상을 촬영해야 합니다. 이는 당연히 사용자가 해야죠! 우리가 해줄 수 없습니다. 사용자는 VR을 사용할 공간에서 천천히 360도를 돌면서 촬영하면 됩니다.

  하지만 이를 어떻게 이용할 수 있을까요? 먼저 우리는 스티칭을 진행하였습니다. 사용자는 360도를 돌면서 촬영할 때, 손이 떨리거나 촬영의 각도가 바뀌고, 또 돌면서 고정된 곳에서 촬영하지 않고 스스로가 움직일 수도 있습니다. 따라서 스티칭을 이용해 이러한 오차들을 최소화할 수 있다고 믿었습니다. 이 결과물은 다음과 같습니다:

파노라마 이미지


  이로서 360도의 정보를 담은 파노라마 이미지를 만들었습니다. 이를 이용해서 어떻게 충돌 방지를 성취할 수 있을까요? 바로 깊이 추정입니다. 깊이 추정이란, 평면에 투영된 2차원의 이미지를 다시 3차원의 깊이 정보를 가진 이미지로 변환하는 것입니다.


깊이 인식

  한쪽 눈을 감아보세요. 이 때 여러분들은 눈의 시신경에 투영된 평면 이미지만을 바라보게 됩니다. 즉, 거리를 전혀 알 수 없죠. 하지만 실제로 그런가요? 여러분들의 뇌는 이미 여러분들의 경험을 토대로 깊이를 추정하고 있습니다. 내가 한쪽 눈만 뜨고 있더라도, 여전히 내 핸드폰이 어느정도의 거리에 있는지 추정할 수 있죠.

  이는 곧 인공지능 역시 2차원 이미지를 가지고 깊이를 추정할 수 있다는 가능성을 시사합니다. 우리도 경험(데이터)를 토대로 깊이를 추정하고 있으니까요! 그러한 결과를 멋지게 내어주는 인공지능이 있습니다. 바로 MiDas입니다. 

  우리는 이 인공지능 모델을 이용해 주어진 이미지의 깊이 추정을 시도하기로 했습니다. 스티칭된 이미지인지라, 정확도가 그렇게 높지는 않았지만 그래도 꽤 쓸만 했죠. 다음은 그 결과입니다:

깊이 이미지


이 이미지를 토대로 무엇을 할 수 있을까요? 

  바로, 3차원에 깊이 정보를 매핑하는 것입니다. 깊이 이미지는 3차원의 정보를 지니고 있죠. 평면 이미지의 x값과 y값, 그리고 깊이값입니다. 곧 이는 우리의 세계는 3차원 (x, y, z)에 사상될 수 있음을 의미합니다. 그 공식은 다음과 같죠:



사상 공식

  이때 w와 h는 각각 이미지의 너비와 높이, θy와 θp는 각각 yaw 각도가 pitch 각도, VFOV는 수직 시야각입니다.


  이를 이용해서 VR 환경에 오브젝트를 생성할 수 있습니다. 우리는 표현에 집중하지 않으므로, 단순히 점 하나 당 작은 구체 오브젝트로 대응시켜 생성했습니다:

깊이 이미지와 실제 사상된 VR 환경


하지만 조금 과하네요. 징그럽기도 하고.

  표현된 오브젝트는 생각보다 많은 정보들을 포함하고 있습니다. 이는 성능에 영향을 미치죠. 깊이 이미지에서 상대적으로 중요하지 않은 정보와, 상대적으로 중요한 정보는 무엇일까요? 우리는 그 해답이 경계선에 있다고 생각하였습니다. 우리는 물체를 경계선으로 구분합니다. 하나의 근거를 들어보죠: 군인들은 위장을 위해 인체의 경계선을 없애는 작업들을 합니다. 나뭇가지나 풀 등을 몸에 붙이는 것이죠.

  따라서 경계선을 제외한 모든 정보들은 날려버리기로 했습니다. 그래도 우리는 여전히 물체를 식별할 수 있을 것이라는 믿음에서 온 결정이었죠. 우리는 다음과 같은 절차로 깊이 이미지의 후처리를 진행하였습니다:

  1. 원본 이미지의 경계선을 검출합니다.
  2. 경계선이 검출된 이미지와 깊이 이미지를 마스킹합니다.


경계선 검출

  경계선 검출은 Canny Edge Detection을 활용했습니다. 컨볼루션 연산을 이용한 획기적인 경계선 검출 방법입니다.

원본 이미지

경계선 검출 이미지

  이렇게 검출된 이미지를 기반으로, 경계선 부분만 깊이 이미지를 도려냈습니다. 이는 마치 경계선 검출된 이미지의 모양으로 도장을 찍은 것과 같죠:

마스킹된 깊이 이미지

  (선이 굵어진 이유는 별도의 후처리를 진행하였기 때문입니다)


이제는 조금 그럴 듯 해지는데요..

깊이 이미지가 사상된 VR 환경

  하지만 여전히 많은 문제가 있었습니다. 첫째로, 이렇게 경계선을 검출했음에도 성능에 문제가 있었다는 것이죠. 이는 3차원 오브젝트인 구체로 깊이 이미지의 점을 사상하였기 때문입니다. 따라서 오버헤드를 줄이기 위해, 2차원 평면의 점으로 변경하였죠:

3D 오브젝트에서 2D 오브젝트로의 변경


사실 깊이 추정은 한계가 많습니다.

  이런 식으로 사상된 깊이 이미지의 데이터는 정확하지 않습니다. 왜냐하면, 깊이 이미지는 상대적인 깊이만을 추정하기 때문이죠. 예를 들어 같은 위치에서 각도를 살짝 틀어서 두 개의 사진을 찍으면, 두 사진이 겹치는 부분의 깊이 데이터는 일치하지 않습니다. 다음 그림을 참고해보세요:

서로 다른 두 각도의 깊이 추정


  가운데에 있는 사각형은 적색 시야의 범위와 청색 시야의 범위 양쪽에서 볼 수 있는 사각형이지만, 그 사각형의 깊이는 서로 다르게 추정됩니다. 상대적인 깊이니까요!

  이는 우리가 스티칭을 이용해서 단 한 번의 깊이 추정만으로 프로세스를 진행하는 이유 중 하나이기도 했습니다. 스티칭을 이용하지 않고, 상대적인 깊이를 보정하는 작업도 시도해보았지만, 결론적으로는 폐기하였습니다. 두 사진에서 겹치는 부분을 식별하고, 해당 부분들의 깊이 값을 이용해 전체적인 깊이 값을 보정하는 것이죠.

깊이 보정

  이러한 처리를 실패한 이유는, 생각보다 특징점 식별이 제대로 되지 않는 환경이 많았기 때문이었습니다. 따라서, 스티칭을 해 한 번에 깊이 인식을 하는 것으로 방향이 굳혀지는 계기가 되었죠.

  이는 깊이 추정의 본질적인 문제를 드러내는 것이기도 합니다. "실제 거리를 추정할 수 없습니다."


따라서, 어쩔 수 없이 사용자의 손을 타야겠네요.

  우리는 VR 게임 내에서 이러한 문제들을 사용자의 추가적인 설정으로 해결할 수 있다고 생각했습니다. 우리가 제시한 세 가지의 설정 값은 다음과 같습니다:

  • 가장 가까운 점 까지의 거리
  • 점과 점 사이의 거리 비율
  • 회전 각도
    VR 게임 내에서의 설정 창

  깊이 이미지는 상대적인 깊이를 표현합니다. 따라서 사용자가 VR에서 나타나는 가장 가까운 물체와의 거리를 본인 환경의 실제 물체와 맞추게 되면, 해당 물체는 온전히 위치할 수 있게 됩니다. 

  두 번째는 점들 사이의 거리 비율입니다. 가장 가까운 물체는 그 위치가 맞았다 하더라도, 더 멀리 있는 물체는 엇나가있을 수 있습니다. 새총의 고무줄을 늘였다 줄였다 한다고 생각해보세요. 새총의 막대에 묶인 고무줄 부분(가장 가까운 물체)의 위치는 고정되어 있지만, 나머지 고무줄 부분의 위치는 막대로부터 멀어졌다, 가까워졌다 할 수 있습니다. 우리는 그것을 임의로 조정할 수 있도록 했죠.

  마지막으로 세 번째는 회전 각도입니다. VR 게임은 사용자가 실제 환경에서 어디를 바라보고 서있는지 알 수 없습니다. 사용자는 방 안의 피아노를 바라보고 게임을 시작했을 수도, 창문을 바라보고 시작했거나, 컴퓨터 의자를 바라보고 시작했을 수도 있습니다. 우리는 충돌 방지를 위해서 게임 내에서 재현하는 환경과 실제 환경의 회전 값을 일치시켜야 할 필요가 있습니다. 이는 사용자의 손에 달린 문제죠.


멋진 결과를 봐보세요!


  이는 정량적인 측정을 통해 이 결과를 설명하기는 어렵습니다만, 정성적으로 판단해보았을 때 꽤나 괜찮다는 생각이 드네요. 물론 이 결과에는 개선해야 할 사항들이 너무나도 많습니다. 하지만 어쩌겠어요. 우리는 돈도, 시간도 없었는 걸요. 그래도 꽤 그럴 듯 하지 않나요? 


  본 프로젝트는 대학교 캡스톤 프로젝트로 2022년 상반기에 4명의 인원이 약 5개월 동안 진행하였으며 Unity의 경우 필자가 관여하지 않았습니다.


끝.

댓글

이 블로그의 인기 게시물

[코딩의탑] 4층: 툰 쉐이딩

[코딩의탑] 3층: 바다 렌더링

[코딩의탑] 5층: 포탈(Portal), 더 나아가기