FLARToolKit : How to – Multiple Instances of Multiple Markers.
요번에는 지난번에 말했던 다중(이하 ‘멀티’로 표기) 마커들에 대한 부분들을 다뤄볼까 한다.
요번 알고리즘으로 멀티 마커에 의한 멀티 객체 표현을 다룬 squidder.com 의 소스를 사용할 것이다. 아래 관련 문서와 소스를 다운 받자.
원문 포스트 :
FLAR how-to: Multiple instances of multiple markers.
다운로드 :
Download Source Code
우선 어떻게 작동이 되는지와 좌표계가 어떻게 출력이 되는지 아래 영상으로 우선 확인해 보자.
하단의 trace 결과를 보면 x, y, z축의 값을 알 수 있다.
일반 웹캠으로는 퀄리티와 속도가 안나온다. 하나 사야하나... 흠
패키지 구조는 다음과 같다.
Package
Class public class MultiFLARExample
Inheritance MultiFLARExample → PVFLARBaseApplication → FLARBaseApplication
요번에도 다이어그램 문서와 다이어그램을 확인해 봐야 한다.
(분석하는데 반나절이란 시간을 소요했다.)
아래와 같은 API 문서와 다이어그램을 살펴본 후에 중요한 작동 포인트를 살펴보겠다.
API Documentation :
FLARSquidderKit/docs/index.htm
Basic Diagram :
분석하기 위해서 다이어그램을 만들었는데, 이거 주인에게 말하고 해야 하는게 아닌가 싶다.
하지만 많은 사람들이 한눈에 구조를 파악할 수 있게 만들어준 것을 고마워 할지도 모르겠다는 생각도 해본다. ㅡㅡ;
소스와 api 문서와 다이어그램을 자세히 보았다면 어떤 구조인지 알 것이다.
실행 순서를 파악하는 것도 중요하다. 아래는 작동되는 순서를 출력한 결과이다.
## MultiFLARExample : constructor
## PVFLARBaseApplication : constructor
## FLARBaseApplication : constructor
## FLARBaseApplication : _loadMarkersAndCamera()
## MultiFLARExample : _init( [Event type="complete" bubbles=false cancelable=false eventPhase=2] )
## PVFLARBaseApplication : _init( [Event type="complete" bubbles=false cancelable=false eventPhase=2] )
## FLARBaseApplication : _init( [Event type="complete" bubbles=false cancelable=false eventPhase=2] )
## FLARBaseApplication : _setUpMarkers()
## FLARBaseApplication : _attachWebCam()
INFO: Papervision3D 2.1 rev920 (August 11th, 2009)
## PVFLARBaseApplication : startRendering()
## PVFLARBaseApplication : _onRenderTick(event)
## MultiFLARExample : _detectMarkers()
## PVFLARBaseApplication : _onRenderTick(event)
## MultiFLARExample : _detectMarkers()
기본 셋팅이 완료되면 PVFLARBaseApplication .startRendering() 메서드가 작동이 되면서 반복문(Event.EnterFrame)이 시작된다.
반복에서 사용되는 메서드 호출은 PVFLARBaseApplication ._onRenderTick(), MultiFLARExample ._detectMarkers() 두 곳이다. 여기에서 멀티 객체와 멀티 마커 인식을 체크하는 것이다.
코드 중에 PVFLARBaseApplication .transformMatrix( target : DisplayObject3D , r:FLARTransMatResult) 메서드가 있다. 이 부분에서 좌표값을 확인 할 수 있다.
화면 표시 방식을 mirror(역 방향 처리) 시키면 x 축의 +, -가 반대로 출력이 되는 현상이 있어서 우선은 헛갈리겠지만 반대로 제스처(행동, 행위)를 해줘야 했다. 반대로 계산하도록 만들려다가 좌표계가 복잡한 관계로 그냥 두었다. ㅡㅡ;
잘 짜여진 구조이긴 한데, 이걸 간단히 설명을 하자니 힘들고,
자세히 설명하자니 깊이 파고 들어가야 하는데… 우선 시작해 보자.
우선 아래의 구조를 좀 더 자세히 살펴봐야 한다.
MultiFLARExample → PVFLARBaseApplication → FLARBaseApplication
위 상속 구조에서 핵심적으로 작동하는 것은 FLARBaseApplication 이라고 할 수 있다. 이곳에서 카메라 인식 및 멀티 마커 등록과 작동이 행해진다.
내부 구조에서 다음과 같은 객체 속성 멤버를 확인해야 한다.
/** flar::FLARBaseApplication */
protected var _flarDetector : FLARSquidderMarkerDetector;
protected var _baseLoader : BaseLoader;
protected var _markers : Array ;
‘protected ‘로 되어있다는 것은 상속 구조에서 접근 허용을 뜻한다. 그리고 메서드일 경우,
상위에서 호출이 되면 최초 클래스의 동일 ‘override ‘된 메서드도 같이 호출이 된다.
아래 코드에서 선언된 이벤트 같은 경우를 말한다. 이곳에서는 아래 코드 부분의 이벤트가 중요한 역할을 한다.
/** flar::FLARBaseApplication */
protected function _init( event : Event ) : void {
// ...
_flarDetector = new FLARSquidderMarkerDetector( _flarParam , _flarCodes , _sizes , _sizes.length ) ;
_flarDetector.addEventListener ( FLARDetectorEvent.MARKER_ADDED , _handleMarkerAdded ) ;
_flarDetector.addEventListener ( FLARDetectorEvent.MARKER_REMOVED , _handleMarkerRemove ) ;
}
protected function _handleMarkerAdded( event : FLARDetectorEvent ) : void {
}
protected function _handleMarkerRemove( event : FLARDetectorEvent ) : void {
}
// 최상위 클래스에서 이벤트가 발생하면 MultiFLARExample 클래스의 메서드가 호출이 일어난다.
/** MultiFLARExample */
override protected function _handleMarkerAdded( event : FLARDetectorEvent ) : void {
// 큐브 생성 이벤트 핸들러
_addCube( event.codeId , event.codeIndex ) ;
}
override protected function _handleMarkerRemove( event : FLARDetectorEvent ) : void {
// 큐브 삭제 이벤트 핸들러
_removeCube( event.codeId , event.codeIndex ) ;
}
핵심 포인트 : 복잡한 마커들을 사용할 경우 알고 넘어가기...
위 코드에서 사용된 flar:detector:FLARSquidderMarkerDetector 클래스는 FLARToolKit API를 변경한 것으로써
마커가 단순한 패턴인 경우에 퍼포먼스를 향상 시킨는 코드이다.
만약, 다른 복잡한 마커를 사용하게 된다면 아래 코드를 원래 계산으로 변경해야 한다.
var borderWidth:Number = (100 - i_code[0].markerPercentWidth) / 20;
//マーカの枠高を算出
var borderHeight:Number = (100 - i_code[0].markerPercentHeight) / 20;
↓
var borderWidth:Number = i_code[0].markerPercentWidth / 10;
//マーカの枠高を算出
var borderHeight:Number = i_code[0].markerPercentHeight / 10;
이제 하나씩 살펴보고 작동 원리를 이해해 보자.
최초에 FLARBaseApplication Constructor에서 _loadMarkersAndCamera( ) 메서드가 호출이 된다.
이곳에서 MultiFLARExample Constructor에서 등록된 _markers Array 객체에 있는 패턴들이 FLARMarkerObj 클래스에 등록을 시키고 _baseLoader 객체에 등록이 되고, load가 이루어 진 후 완료 시 _init 메서드가 호출이 되며, 최초 MultiFLARExample 클래스에서 super를 사용하여 ’override ‘된 메서들이 호출이 되며 셋팅이 완료 된다.
아래 코드들을 확인해 보면 한 눈에 알 수 있을 것이다.
/** flar::FLARBaseApplication */
public function FLARBaseApplication( ) {
super ( ) ;
_loadMarkersAndCamera( ) ;
}
protected function _loadMarkersAndCamera( ) : void {
_baseLoader = new BaseLoader( ) ;
_baseLoader.addAsset ( "assets/flar/camera_para.dat" , { id:"FLARCamera" , type :"Binary" } ) ;
for ( var i : int = 0 ; i & lt; _markers.length ; i++ ) {
var markerObj : FLARMarkerObj = FLARMarkerObj( _markers[ i ] ) ;
_baseLoader.addAsset ( markerObj.markerSrc , { id:"FLARPattern" + i } ) ;
}
_baseLoader.addEventListener ( Event.COMPLETE , _init ) ;
_baseLoader.load ( ) ;
}
// 여기서 알아야 할 것은 _init 메서드가 일어날 경우 추상 클래스의 메서드가 작동하는 것이 아니다.
// 최초 클래스인 MultiFLARExampled의 _init(event)가 실행이 되면서
// super._init(event);를 사용하여 추상(구상) 클래스 초기 셋팅을 실행한다.
// 아래 코드는 실행 우선 순위로 _init 메서드의 표시하였고, 작동에 대한 설명을 달았다.
/** MultiFLARExample */
protected function _init( event : Event ) : void {
super ._init( event ) ;
// PointLight3D 객체를 생성하여 큐브 생성시 사용된다.
}
/** flar::PVFLARBaseApplication*/
override protected function _init( event : Event ) : void {
super ._init( event ) ;
// FLAR과 PV3D가 셋팅이 된다.
}
/** flar::FLARBaseApplication*/
protected function _init( event : Event ) : void {
// camera_para.dat 및 *.pat 파일들을 셋팅한다.
}
protected function _setUpMarkers( ) : void {
// 마커를 셋업시킨다.
}
protected function _attachWebCam( ) : void {
// 카메라와 비디오를 셋팅한다.
}
/** flar::PVFLARBaseApplication - _init(event) 메서드에서 실행이 된다. */
public function startRendering( ) : void {
// 반복 이벤트 등록.
addEventListener( Event.ENTER_FRAME , _onRenderTick ) ;
}
protected function _onRenderTick( event : Event = null ) : void {
// 반복하며 비디오를 그리고 마커를 찾고, 화면을 렌더링한다.
_flarBitmap.bitmapData .draw ( _video ) ;
_detectMarkers( ) ;
renderer.renderScene ( scene , _camera , viewport ) ;
}
/** MultiFLARExample */
override protected function _detectMarkers( ) : void {
// 마커를 찾고, 좌표를 업데이트 한다.
}
이제 어떻게 작동이 되는지 파악이 될 듯 싶다.
이해를 돕기 위해 코드 대신 간략한 코멘트를 작성을 하였다. 코드는 해당 클래스를 클릭하여 보길 바란다.
만약, 파악이 안된다면 API 문서와 다이어그램을 다시 한번 살펴보길 바란다.
이렇게 하여 반복문이 실행이 되면서 마커들을 찾고 마커에 의해 반응 처리가 이루어지는 것이다.
작성 후기 :
마커가 너무 단순하면 카메라와 비디오 처리에서 비슷한 표면을 찾아 마커로 잘못 인식하는 경우가 발생한다.
좀 더 명확한 심볼을 갖고 작업을 해야 오작동을 막을 수 있다.
웹캠으로 녹화시 현재의 심볼 때문에 촬영하기 힘들었다.