목적
피사체의 위치와 피사체가 바라보는 방향을 실시간으로 계산하는 헤드트래킹을 구현하기로 함, 이를 위해 Wiimote 두개와 IRED 3개로 구성된 표식자(태그)를 사용하였습니다.

특징
일반적으로 알려진 헤드트래킹은 피사체의 2차원 or 3차원 공간 좌표값(x,y,z) 성분만을 계산할 수 있는 제한이 있습니다.
본 3광점 태그를 이용한 헤드트래킹은 피사체의 위치와 더불어 방향벡터(로컬좌표계)까지 완전하게 계산하낼 수 있으므로 좀더 다양하게 응용할 수 있습니다.

용도
헤드 트래킹의 용도는 광범위하나 여기서는 3D 스캐닝 작업시, 여러차례 나눠서 스캐닝하지 않고, 한번에 피사체의 전체를 스캐닝하는데 활용하는 것을 가정하였습니다.

결과
헤드트래킹은 구현완료 하였으나 3D 스캐너 관련 개발은 현재 보류된 상태입니다. (3D 스캐너 관련글 링크)
One Step Full Scanning 구현은 언제가 될지 모르지만 꼭 완성하고 싶네요, 여러분이 먼저 하시렵니까??;;


방법 소개

3광점 표식자
헤드트래킹을 구현하기위해선 피사체에 고정되어 있는 정점(Vertex) 좌표값이 적어도 3개가 필요합니다. 물론 단순히 피사체의 위치벡터만 구하는 경우라면 두개의 Wiimote로 한개의 정점 좌표값만 구해도 됩니다만 이경우 정점의 3차원 좌표(x,y,z)값만을 알 수 있고 피사체의 2축 회전성분을 알수가 없으며, 정점 좌표가 2개인 경우엔 위치파악 및 2축 중 1축 회전성분을 알수 있습니다. 결국 피사체에 고정되어 있는 3개의 정점을 알아야 완전한 위치와 방향 성분을 갖는 벡터값을 구해낼 수 있습니다.

Wiimote
위모트는 한번에 4개까지의 IRED(적외선LED)광점을 인식할 수 있는 기능이 있습니다. 특정파장(940nm)의 적외선에 반응하므로 너무 밝지 않은 실내라면 별도의 장비없이도 쓸만한 광점인식 장비로 사용할 수 있습니다.  피사체에 3개의 IRED를 고정시킨 후 실시간으로 두개의 Wiimote로 그 광점들의 좌표값을 측정하는것으로 피사체의 위치와 바라보는 방향을 구할 수 있게됩니다.

3개의 정점으로 정의되는 Local 좌표계 구하기
위모트를 사용해보신 분들이라면 광점 3개를 읽어들이는것은 어렵지 않게 할 수 있습니다만, 실제 이값을 가지고 피사체의 벡터(위치와 방향)성분을 구해내는건 약간의 벡터연산에 대한 이해가 필요합니다.  사실 고등학교때부터 배워온 벡터지만,,  막상 써먹으려니 막막하더군요,  어쨌든 간만에 벡터연산 공부좀 하고 해답을 구했습니다.  정리해놓고 보면 별거 아니네요.


이제 순서대로 정리를 해보도록 하겠습니다.


1단계. 3광점의 3차원 좌표값 구하기

Wiimote 2개로 광점 3개의 2차원 좌표값들을 구하고 이값으로  3차원 좌표값 P1, P2, P3을 구해놓습니다.  이와 관련된 내용은3D 포인트 트래킹 구현하기 글에 소개되어 있습니다. 이값은 웹캠이나 기타 다른 방식으로 구할 수 도 있습니다.

준비된 결과 값:  P1(x,y,z)  P2(x,y,z)  P3(x,y,z)

2단계. 문제정의

3개의 정점으로 구성된 Local 좌표계 구하기라고 목표를 정의했지만, 사실 애매한 표현입니다. 수학적으로 엄밀한 표현까지는 아니더라도 좀더 구체적인 정의가 필요합니다.  그림으로 표현하는게 가장 이해하기 쉬울듯 합니다. 정의 방법에 따라 무수히 다른 방식의 좌표계 정의를 내릴 수 있을텐데요, 여기선 단계적으로 손쉽게 이해하고 계산하기 쉬운 방식으로 정의하겠습니다.



그림. 로컬 좌표계와 P1,P2,P3 관계 정의도

3단계. 계산하기

우리가 구해야할 값은 위 그림에서 u1,u2,u3로 표현된 벡터값 입니다.  각각 Local좌표계의 x,y,z축에 해당하는 벡터입니다. 헤드 트래킹의 경우, 월드좌표계안에서 헤드의 위치와 방향을 바로 이 좌표계 정보로 표현 한다는 개념입니다.
아래의 수식은 실제로 Away3D라는 플래시 3D엔진에서 Number3D 자료형의 벡터연산 메소드(함수)를 이용하여 계산하는 예입니다.  벡터 빼기 연산은 sub() 로, 벡터 외적 연산은 cross() 로 표기됩니다.

  P1->P2 를 벡터A 로 정의합니다.
  P1->P3 을 벡터B 로 정의합니다.

A.sub( P1 , P2);
B.sub( P1 , P3);

  이제 위 정보로 u1,u2,u3를 구합니다. 이때 방향성분이 중요하고 크기(길이)는 중요치 않습니다.
  (참고로 위 그림에서 u벡터들의 길이는 실제값이 아니고 임의의 크기로 표현했음에 주의바랍니다.)
  여기선, 두 벡터를 외적(Cross Prduct)하면 서로 직교하는 제 3의 벡터를 구할 수 있다는 것을 응용합니다.
  첫번째 로컬 좌표축 성분 벡터인 u1은 A와 B의 벡터 합으로 정의하고
  두번째 로컬 좌표축 성분 벡터인 u2는 A와 B의 외적으로 정합니다.
  세번째 로컬 좌표축 성분 벡터인 u3는 u1과 u2의 외적으로 구합니다.

u1.add( A, B);
u2.cross( A, B);
u3.cross( u2,u1);

위 연산으로 벌써 결과가 구해졌습니다. 벡터연산 함수를 사용하면 뺄셈 2번, 덧셈 1번, 곱셈2번으로 끝나는 매우 간단한 연산입니다. 이렇게 구한 벡터 3개는 모두 나머지 두 벡터와 직교하는 벡터가 되며, 로컬좌표계의 3개의 축 정보로 사용할 수 있습니다. 보통 좌표계의 축벡터는 크기가 중요하지 않으므로, 아래와 같이 크기가 1인 단위벡터로 변환하는 연산(Normalize)을 해줍니다.  위 그림에서는 u1,u2,u3 벡터를 단위길이 벡터로 변환한 것이 ^u1, ^u2, ^u3 로 표현되었습니다.

u1.normalize();
u2.normalize();
u3.normalize();


결과값과 활용


여기서 구한  u1,u2,u3를 이용하여 로컬좌표계를 정의할 수 있고, 월드좌표계와 좌표계변환 연산등을 할 수 있습니다. 좌표계 변환은 행렬연산과 관련된 또다른 주제가 됩니다. 그 부분은 기회가 되면 Away3D같은 3D 엔진 예제와 함께 설명 드릴 예정입니다.  계획했던 연관 프로젝트 진행이 중단되어 아직 보여드릴 만한 응용예제가 없네요, 추후 예제가 생기면 추가하겠습니다. 아래의 이미지는 실제 헤드트래킹 과정을 기록해둔 동영상의 한장면을 캡쳐한 것입니다. 머리의 움직임에따라 우측에 보이는 화면속 삼각형(+로컬좌표축)의 위치와 방향이 변합니다.


사진. 헤드트래킹 장면 예

우측 부분에 월드 좌표축과 삼각형 위에 붙은 작은 좌표축(헤드의 로컬좌표축)이 희미하게나마 보입니다.

끝으로
헤드트래킹을 구현한지가 벌써 반년이 넘어버렸습니다. 그동안 정리는 뒤로 미루고 새로운 일을 벌리곤 했지만 지나보니 정리를 안해놓으니 남는게 없네요;;  앞으로라도 좀더 정리된 자료를 만드는 습관을 들여서 개인 프로젝트 히스토리 관리도 하고 약간이나마 공공지식에의 공언도 해야겠다는 생각이 듭니다.  그러나,,,오랜만에 그림 하나 그리는데 왜이리 힘든지요 ^^;;


관련링크

1. 외적(Cross Product)연산 : wikipedia
2. 1광점 트래킹하기 (3차원 좌표값 실시간추적)

목표
2대의 Wiimote를 이용하여 3D 공간에 위치한 광점(IRED)의 x,y,z 좌표값을 구한다.

본 기능은 다음의 경우에 응용할 수 있습니다.
1. 3D 스캐너 만들기
2. 헤드 트래킹(단순 위치 파악형 및 3광점 헤드 트래킹)
3. 3D 마우스

※ 3광점 헤드 트래킹:
3개의 IRED를 이용하면 단순히 위치를 파악하는것 뿐아니라 어느방향을 바라보는지도 알수 있습니다. 관련링크 참조

준비물
Wiimote 2개,  IRED(적외선LED) (참고링크2 참조) 
기타 Wiimote 사용을 위해 필요한 게이트웨이 SW 및 BT 장치 등.

Wiimote 설치 및 연동
Wiimote와 Flash를 연동하는 방법은  참고링크1.을 참고하시기 바랍니다. 

Wiimote로 측정 가능한 정보
wiimote에는 3축 가속센서, 여러개의 버튼이 포함되어있고, 내장된 적외선 카메라로 적외선 광점의 좌표값을 4개까지 인식할 수 있습니다. 물론 wiimote에서 출력해주는 좌표값은 카메라 스크린을 기준으로한 x,y 좌표(2D)좌료 값이므로 이것만으로는 3D좌표값을 구할수 없습니다. 하지만 wiimote를 2개 이상 사용한다면, 이 값들의 조합으로 3D 좌표값을 구해 낼 수 있습니다.
참고로 일반적인 WebCam으로도 가능합니다. 다만 wiimote를 사용하므로써 광점을 인식처리해주는 이미지 처리과정이 필요없고, wiimote의 기타 센서 기능을 함께 사용할 수 있으므로 목적에따라 선택하여 사용하면 됩니다.
아래의 이미지는 WiiFlash Server에서 기본 제공되는 데모 실행화면입니다. 버튼의 눌림상태와 각종 센서들의 수치값이 실시간으로 보여지는 것을 볼 수있습니다.




예제 소스
아래에 첨부된 소스는 Wiimote 2개( WA와 WB)를 이용하여  3차원공간상에 위치한 광점(ir Point)의 좌표값을 계산하는 예제 소스입니다. 저는 이를 기초로하여 3D 스캐너와 헤드트래킹을 구현했습니다. 좀더 고급 수학을 이용한 복잡한 방법이 있을듯 합니다만  저는 잘 모르고요, 제가 아는 범위의 단순한 삼각함수를 이용했습니다. 중.고등학교 과정의 수학이면 이해 가능할 듯 합니다.

package{
	import org.wiiflash.Wiimote;
	import org.wiiflash.events.ButtonEvent;
	import org.wiiflash.events.WiimoteEvent;
	import flash.events.*;
	import flash.display.*;
	import flash.text.TextField;
	
	public class PositionDetector extends Sprite{

		public const D2R:Number = Math.PI / 180;
		public const R2D:Number = 180 / Math.PI;
		// Calibration 수치
		public const IRX2DEG:Number = 41;
		public const IRY2DEG:Number = 32;
		private var L:Number = 500;				//500mm
		private var WA_LRCenterDeg:Number = 60;
		private var WA_UDCenterDeg:Number = 0;
		private var WB_LRCenterDeg:Number = 120;
		private var WB_UDCenterDeg:Number = 0;

		// p(x,y,z)산출 기본자료
		private var WAirx:Number;
		private var WAiry:Number;
		private var WBirx:Number;
		private var WBiry:Number;

		private var p1x:Number;
		private var p1y:Number;
		private var p1z:Number;

		// create a new Wiimote
		private var WA:Wiimote = new Wiimote();
		private var WB:Wiimote = new Wiimote();
				
		public function PositionDetector(){
			//ir value update EventListener 설정
			WA.addEventListener( Event.CONNECT, onWiimoteConnect );
			WA.addEventListener( IOErrorEvent.IO_ERROR, onWiimoteConnectError );
			WA.addEventListener( Event.CLOSE, onCloseConnection );
			WA.addEventListener( WiimoteEvent.UPDATE, WiimoteAUpdateHandler );
			WB.addEventListener( Event.CONNECT, onWiimoteConnect );
			WB.addEventListener( IOErrorEvent.IO_ERROR, onWiimoteConnectError );
			WB.addEventListener( Event.CLOSE, onCloseConnection );
			WB.addEventListener( WiimoteEvent.UPDATE, WiimoteBUpdateHandler );								
			WA.connect ();
			WB.connect ();
			Canvas2D.point.visible = false;
		}

		function onCloseConnection ( pEvent:Event ):void{
			trace("onClose: " + pEvent.target );
		}
		
		function onWiimoteConnectError ( pEvent:IOErrorEvent ):void{
			trace("onError: " + pEvent.target );
		}
		
		function onWiimoteConnect ( pEvent:Event ):void{
			trace( "Wiimote successfully connected: " + pEvent.target );
		}		

		public function WiimoteAUpdateHandler(pEvent:WiimoteEvent){
			if(pEvent.target.ir.p1){	
				WAirx = WA.ir.x1;
				WAiry = WA.ir.y1;
			}			
		}
		public function WiimoteBUpdateHandler(pEvent:WiimoteEvent){
			if(pEvent.target.ir.p1){	
				WBirx = WB.ir.x1;
				WBiry = WB.ir.y1;
				Canvas2D.point.visible = true;				
				CalcProcess();
			}else{
				Canvas2D.point.visible = false;
			}
		}
		public function CalcProcess(){
			//irx => WADeg => WAm(기울기)  , WBm도함께
			var WADeg:Number = WA_LRCenterDeg - ( WAirx - 0.5 ) * IRX2DEG;
			var WAm:Number = Math.tan( WADeg * D2R);
			trace( WADeg, WAm);
			var WBDeg:Number = WB_LRCenterDeg - ( WBirx - 0.5 ) * IRX2DEG;
			var WBm:Number = Math.tan( WBDeg * D2R);

			// 기울기를 알고있는 두 선분의 교점P(x,z) 구하기  
			// WA는 (0,0)  WB는 (L,0)을 지나는경우이며,  WA/WB 수평평면이 x/z 평면이다.
			p1x = ( -WBm * L ) / ( WAm - WBm ) ;
			p1z = WAm * p1x;

			// py구하기
			var Lxz:Number = Math.sqrt( p1x * p1x + p1z * p1z ) ;
			var WA_UDDeg:Number = WA_UDCenterDeg - ( WAiry - 0.5 ) * IRY2DEG;
			p1y = - Math.tan( WA_UDDeg *D2R ) * Lxz;

			// p(x,y,z)표시
			PointXYZ.text = "px = " + String(p1x);
			PointXYZ.text += "\npy = " + String(p1y);
			PointXYZ.text += "\npz = " + String(p1z);			
			
			var Lcan = Canvas2D.WB.x - Canvas2D.WA.x;
			Canvas2D.point.x = Canvas2D.WA.x + p1x  * Lcan / L;
			Canvas2D.point.y = Canvas2D.WA.y - p1z * Lcan / L;
		}
	}
}


예제 소스 설명

1. 장비 설치에 따른 수치 정의
측정하기 전에 WA와 WB의 world좌표상의 거리 L과  x축을 기준으로 한 회전각을 정의합니다.
좀더 노력하면 자동으로 Calibration하여 이값들을 구해낼 수 도 있으나, 본 예제는 기초적인 수학으로 3차원 자표값을 구해내는것을 목표로 하므로, 단순하게 직접 측정하여 기입하는 방식을 사용합니다.
 정의되야할 정보는 아래와 같습니다.

private var L:Number = 500;                           // WA와 WB 사이의 거리, 이경우 500mm
private var WA_LRCenterDeg:Number = 60;      // WA의 좌우 회전각 (Deg)
private var WA_UDCenterDeg:Number = 0;        // WA의 상하 회전각
private var WB_LRCenterDeg:Number = 120;     // WB의 좌우 회전각
private var WB_UDCenterDeg:Number = 0;        // WB의 좌우 회전각
public const IRX2DEG:Number = 41;                // Wiimote의 수평 화각이다.  (측정 값이며 오차있음)
public const IRY2DEG:Number = 32;                // Wiimote의 수직 화각이다.  (측정 값이며 오차있음)


그림1. Wiimote 설정치 참조도 (xz평면도)

2. 작동프로세스
Wiimote의 update 이벤트 발생시마다,  WA,WB모두에서 irPoint 가 발견될 경우에만 WA,WB의 irx,iry를 구한 뒤, p(x,y,z) 값  px,py,pz을 구합니다.


3. 실제 좌표값을 구하는 수학은 간단한 삼각함수를 이용합니다.  소스를 보시고 이해해 보시거나 직접 삼각함수로 연산을 해보시면 됩니다. 단, 한가지 알아둘 사항은 Wiimote에서 출력해주는 좌표값은 0~1 사이의 값이기 때문에 이값을 각도에 해당하는 값으로 변환해줘야 삼각함수 연산이 가능합니다.  이때문에 Wiimote의 화각정보를 미리 측정해 둬야하는데요, 가령 Wiimote의 좌우방향 화각이 30도라고 합시다.   이경우 광점이 가장 왼쪽경계에서 발견된다면  wiimote의 irx값은 0으로 출력됩니다.  그리고 만약 우측 경계부분에서 광점이 발견되면 irx의 값은 1이 됩니다.  또한 Wiimote 화각의 중심점에서 발견될 경우 이값은 정확이 0.5로 출력됩니다.

즉, 아래와 같이 변환될 수 있습니다.
 irx  0  0.25  0.5 0.75
1
중심기준 각도(deg)
 -15  -7.5 0
 7.5  15

실제 코드상에서는 아래와 같이 계산합니다.
var WADeg:Number = WA_LRCenterDeg - ( WAirx - 0.5 ) * IRX2DEG;


추가로, D2R 과 R2D 는 Deg각도 값과 Radian값을 변환해주는 상수 값입니다. 
Degree값을 Radian값으로 바꾸려면 D2R을 곱해주면 됩니다.  마찬가지로 Rad를 Deg로 변환하려면 R2D값을 곱해주면 됩니다.




동영상1. 3차원 좌표값 실시간 구하기


사진1. 설치예




SymBall은 2004년 겨울 플래시를 처음 접한 후 만든 첫 작품? 입니다.
이번에 로보밥 블로그 오픈을 계기로 다시 Online에 올려봤습니다. 한번 심심풀이 심볼 해보시죠!!

본 어플의 목적:
  동그란 Ball들을 3D 공간상에 자유롭게 배치하여 3D 상징(Symbol)을 만드는 것입니다.

왜 Symball인가?
  Ball로 만들어진 상징물(Symbol)이므로  새로운 합성어로 Symball 이란 단어를 만들어 봤습니다.


3D Symball  강아지 만들어본 예

[3D Symball  강아지 만들어본 예]


사용법:

사용법은 매우 간단하여 10분이면 독파가능한 수준입니다. 
키보드 키를 눌러서 바로바로 작동하게 하는 방법을 사용하여, 불필요한 기능별 메뉴 선택작업이 없으며,
가령 A 키는  Add 를 연상시키며, D 키는 Delete키를 연상시킬 수 있도록 키를 배정하여 기억하기 쉽게 구성하였습니다.
하지만, 화면상의 키를 마우스로 눌러도 아무 작동도 안하고, 키보드로 눌러줘야 한다는점을 잊지 말아주세요!
화면상의 아이콘 키들은  본 어플 작동을 위한 모든 기능키들을 화면에 보여줘서 곧바로 모든 키를 익힐 수 있도록 하기 위함입니다.  궁굼한 기능키에 마우스를 올려보면 기능키 mini도움말이 바로 뜹니다.
화면상의 아이콘 키들의 기능만 알면, 본 어플 작동법을 마스터 하는것입니다.

일단, 아래의 글을 읽고 작동해보신다면 더 쉽게 이해가 되실 듯 합니다.


기능키 소개

------- Ball 다루기  ----------------

사이트 접속 후, 처음 뜨는 도움말 창을 보신 후, Close 버튼을 눌러서 도움말창을 닫습니다.

이제부터는 키보드 키와 마우스를 이용하여 3D 조형을 해봅니다.

1단계. Ball추가하기   -  Add key

 A(dd) 키를 눌러보세요.   ->  Ball이 하나 생성됩니다.
 주의.  키보드상의 A키를 눌러야 A 기능이 작동됩니다. A키를 마우스로 클릭하면 A키 도움말만 뜹니다.

2단계. Ball 이동하기

 생성된 Ball을 마우스로 Drag하면 이동합니다.

 이때 마우스 왼쪽 버튼대신 키보드 Space버튼을 이용할 수 도 있습니다.

 ☆ 오른손 검지손가락 휴식모드:
  본 기능은 디자인, 프로그래밍, 웹서핑, 게이밍 작업시 너무 많은 클릭으로 고생하고 있는 검지손가락을
  잠시나마 쉬게 해줄 수 있도록 특별히 개발한 기능입니다. 제가 이때쯤 손가락이 너무 아팠습니다. ㅡㅡ; )

3단계. 특정 지점에 Ball 생성하기

 Ball을 Drag중에 A 버튼을 누르면 마우스 현 위치에 Ball이 생성됩니다.
 이 기능으로 원하는 위치에 Ball들을 배치할 수 있습니다.

4단계. Ball 지우기   - Delete key

 지우려는 Ball 위에 마우스를 가져다 된 후,  D(elete) 키를 눌러줍니다.

5단계. Ball 선택하기  - Select key

 Ball을 하나 선택하려면, 원하는 Ball에 마우스를 가져간후 S(elect) Key를 눌러줍니다.
 여러번 반복하여 S 선택가능합니다.

6단계. Ball 영역 선택하기  

 여러개의 Ball을 한번에 선택하려면,  마우스로 해당영역을 Drag 해줍니다.

7단계. 다중 선택된 Ball들의 이동

 다중 선택된 Ball중의 하나를 선택후 드레그하면 전체 이동됩니다.

8단계. 다중 선택된 Ball들의 삭제

 D(elete) Key를 눌러주면 선택된 Ball들이 일괄적으로 삭제됩니다.

9단계. Ball의 복사 Copy

C(opy) key;  특정 Ball(들)이 선택된 상태에서 C키를 눌러주면 선택된 Ball들이 현재 위치에 복제됩니다.
                   선택된 Ball들을 Drag or 3D 회전 하면서 Copy를 수행하면, 손쉽게 특정 모양들을 복제해 낼 수 있습니다.
                   가령 직선을 회전하면서 copy를 반복하면 원이 됩니다.  2축회전을 하면서 복사를 하면 구가 됩니다.


---------  3D 이동 기능  X,Y,Z 평명이동 --------------

X-Y평면 드레그 모드

 X key를 눌러주면  선택된 Ball(들)을 Drag시 x,y 평면상 이동이 됩니다.  (기본상태)
 일반적인, 마우스 좌/우, 상/하 이동이 됩니다.

Z-X평명 드레그 모드

 Z key를 눌러주면 선택된 Ball(들)을 Drag시 z,x 평면상 이동이 됩니다.
 즉, 위아래 이동시 모니터 뒷쪽, 앞쪽으로 이동 (멀고,가까워지는 이동)
 일반모드로 되돌아 가려면 X key를 눌러줘야합니다.


----------  3D View 보기 및 선택 Ball 회전하기   ------------------
3D View 모드
선택된 Ball이 없는경우, 전체 Ball이 해당되고,
선택된 Ball이 있다면 선택된 Ball만 회전되게 됩니다.

V(iew) 키를 눌러주면 Z축과 Y축 회전용 핸들이 보입니다.
용도1. 작품 감상: 이 두 핸들을 조절하여 Z/Y축 회전 속도를 부여하면,  3D공간상의 회전하는 조형물을 볼 수 있습니다.
용도2. 조형 작업: 특정 Ball들만 선택하여 회전하여 3D 배치 작업에 이용합니다.
          힌트.  회전중에도 S, F, C, D키 사용이 가능합니다. 이를 통해 복잡한 3D 조형물도 손쉽게 만들 수 있습니다.

--- 저장하기 ---
PgUp key를 누르면 뜨는 폼에 제목을 적어줍니다. 
중요, 저장하려는 Ball 들은 선택된 상태여야합니다.  미선택된 Ball들은 저장되지 않습니다.

--- 불러오기 ---
PgDown key
검색어를 입력하여 찾기 기능을 지원하며, 목록에서 선택후 불러오기 하면, 저장되었던 Symball을 볼 수 있습니다.
참고로, 상단의 Sample download는 원 data가 손실되어 작동하지 않는상태입니다.

--- Ball 크기와 색상 변경 ----

F(unction) key를 누르면  마우스 스크롤 모드가 토글 되며 변경됩니다.
 모드1. Ball Size 조절 모드
 모드2. Ball 색상 조절 모드

모드에 따라 선택된 Ball(들)에 마우스 올린 후 스크롤을 해보면  크기 or 색상이 변화됩니다.


--- 기능키 숨기기 모드 ---

H(idden)키를 눌러주면, 안내키들을 숨기기/보이기 를 반복. 이미 기능키들을 충분히 익힌 후에 사용하면 좋은 기능

--- 도움말 ----

mini 도움말: 화면에 있는 기능안내키 A, S, D, F 등에 마우스를 올려놓으면 각 키들의 도움말이 화면 하단에 뜹니다.

자세한 도움말:  Q(uestion) Key를 눌러주면 도움말 창이 뜹니다.
                     본 도움말 모드에서 화면상에 떠 도는 키를 클릭해주면,  각 기능키별 상세한 도움말이 보입니다.


-- 안내. 2014년 8월 현재 --
  무료로 서버 호스팅을 지원하던 paran.com 서비스가 종료되어  제가 올렸던 서비스도 종료되게 되었습니다.  차후 재등록 되면 링크를 올리도록 하겠습니다!!





따뜻한 봄 날,  3D관련 전시회에 갔다가 신기한 녀석을 봐 버렸습니다.
빔프로젝터에 카메라가 달려있는 녀석이었는데,  이게 3D 스캐너라는 겁니다.
보자마자 원리가 보이더군요... 하지만 이게 정말 된단 말이야???


[사진1. 코엑스 3D영상기기 전시회에서 본 빔프로젝터 이용한 3D 스캐너 ]

예전부터 3D스캐너를 만들고 싶다는 생각은 했지만,  고가의 레이져 장비가 있어야 된다는 생각에 포기했었는데요,
뒤통수 맞은 기분이었습니다.  아니, 생각의 덫이 하나 떨어져 나가서 홀가분해졌다고 해야할까요!!

어쨌든, 중요한건,,,,  오홋! 그럼 나도 만들 수 있겠다.

그래서 일단 만들어봤습니다.
준비물은 매우 간단한 편입니다.

하드웨어: 일반 USB웹캠 1대(2대면 더 좋음),  빔프로젝터 1대,  고정용 스탠드.
개발환경: 제 경우 플래시 액션스크립트 3.0으로 직접 구현하는 것을 목표로 하였습니다.
              ( 보통은 C/C++, MATLAB,  기타 Tool을 이용하더군요,  관련링크 참조)



[사진2. 자작 3D 스캐너 - 웹캠 1대와 빔프로젝터 1대로 구성]

참고사항. 함께 설치된 Wiimote 2개는 IR Tracking용으로 일반적인 3D 스캐닝 용도로는 없어도 됩니다.



[동영상1. 스캐닝 후 공간분할이 잘 되었는지 확인하는 영상입니다. ]





[동영상2. 스캔 결과 Data(3D Points Cloud)를 별도의 MeshLab 뷰어로 확인해 봤습니다. ]

수만개의 단색 점을 3D 공간에 표시하기만 해도 대략적 형상이 나오는것을 확인 할 수 있습니다.



[동영상3. 몇가지 스캔 결과물을 모아놓은 영상입니다. ]

1. 종전의 영상은단색 점인것과 달리 본 영상의 점은 색상있는 점입니다.
2. 수만개의 점중에 일부 점만 무작위 추출하여 매핑을 한 영상입니다.
3. 위 결과를 라운드 처리하여 본 결과입니다.
4. 벽면의 붙인 그림을 스캔할 결과물입니다. 평면의 물체가 굴곡면으로 스캔된 것을 볼 수 있습니다.
    렌즈굴곡 보정을 안해줘서 그런것 같습니다.


사실 아직 완성된건 아니고, 렌즈굴곡보정, 노이즈처리, Point Cloud로 Polygon 형성, 3D Viewer 등등의 작업이 남아있습니다. 차후에 좀더 개량을 해야하겠습니다.  가능하면 관련 이론 소개를 별도의 문서로 올려보도록 하겠습니다. 새로운 참고자료도 본 글을 통해 업데이트 하도록 하겠습니다.


추가사항(2010.Nov.15)

스캔원리에대해 요약 소개:

링크 자료중 byo3d pdf 문서를 보시면  chapter5  structured lighting 이라는 개념이 있습니다.
사실 이게 빔프로젝터로 3D스캐너를 만드는데 있어서 핵심내용입니다.
카메라와  빔프로젝트가 거리 L만큼 떨어져있고,  빔에서 선이 하나 발사되어 벽에 반사되어 카메라로 캡쳐되는 상황을 가정해보시면 카메라와 프로젝터의 상대각을 구할 수 있고, 간단한 삼각함수만으로 그 점의 3차원 x,y,z 좌표값(카메라+빔프로젝터를 기준으로한 상대적인)을 구하실 수 있을겁니다.  

사실 이것만으로도 3d스캐닝이 가능합니다. 빔프로젝터에서 점을 이동하면서 카메라에서 그 위치를 캡쳐하게되면, (매우 느리겠지만) 원하는 point cloud를 구할 수 있습니다. 근데 문제는 너무 느려서 비효율적입니다. 점하나당 한번의 캡쳐를 해야한다는 결론이나오므로 실용성이 없습니다.

그래서 Structed Lighiting 이라는 방식이 도입되었습니다.
이 방법을 사용하면 n번의 갭쳐만으로 2^n * row  개의 기준화소점군(라인)을 구해낼 수 있게됩니다.
즉, 8장을 캡쳐하는것 만으로도 256개의 기준선을 추출할 수 있고, 각 기준선마다 m개의 화소를 추출할 수 있게되어,  한차례( 해상도에 따라 다름: 가령 8장 안팍)의 연사촬영만으로 수만개의 화소점을 구해낼 수 있게됩니다.

원리는 빛을 동일한곳에 다른 패턴으로 방사하고 촬영한 후 이 값들을 단순히 누적하기만 하면, 거의 화소단위로 영역이 구분이 된다는 것입니다. byo3d pdf문서를 잘 살펴보시면 원리를 알게될 겁니다. 

링크6. 정보가 추가 되었습니다. 

정보를 제공해주신 분에의하면 해당 자료를 다운받아 따라해보니 스캔 결과물도 괜찮게 나온다고 하셨습니다. 1대의 웹캠과 1대의 빔프로젝터, 90도 직각으로 설계된 캘리브레이션 도구를 이용하여 3d 스캐너를 구현한 방식입니다.


추가사항(2010.DEC.10)


사진. 단계노출 및 그로부터 계산된 line으로 그룹화된 점 정보들.

최하단의 결과 이미지에서 동일한 색상 및 명함으로 이뤄진 화소점들(Line)을 구해낸것을 볼 수 있습니다. 이 결과값으로 화소점들의 3D 좌표값을 구할수 있습니다.


사진. Point Net 씨우기

계산할 점의 개수가 너무 많으므로 일부만을 선택하여 사용하기 위해서 특정 점들만 선택하는 과정입니다. 왼쪽이 Convert 이전의 상태이며, 이때 일부 점들은 수동으로 위치시킬 수 있으며(얼굴부분) 나머지는 자동으로 가장 근처에 있는 점이 선택되어 최종적인 3D 좌표값으로 변환되게 됩니다.


사진. 3D스캐닝 세트 전경



관련자료
1. 제가 아는한 자작 3D스캐너와 관련하여 가장 자세한 기술소개문이 있는 사이트입니다.
   소스와 유틸리티, 원리소개등의 모든자료가 있다네요.    http://mesh.brown.edu/byo3d/index.html
  
2. 위 사이트에서 배포하는 기술소개문 pdf 파일입니다.  http://mesh.brown.edu/byo3d/notes/byo3D.pdf
3. 3D Photography on your desk  http://www.vision.caltech.edu/bouguetj/ICCV98/
    한수 더 떠서 막대기와 조명기구만으로 만드는 3D스캐너입니다. 
4.  Model-Based Face Capture from Orthogonal Images 사진 두장으로 3D얼굴 재현하기 입니다.
     http://mesh.caltech.edu/ee148/projects/EngLanmanMayes/
5. 독일 공항에 설치된 화려한 색광을 사용하는 얼굴스캐너  http://mtcave.blogspot.com/2009/09/face-scanner-test.html

6. 켄터키 대학에서 구현한 3d 스캐너: http://www.engr.uky.edu/~lgh/soft/soft.htm 


+ Recent posts