2012年9月20日 星期四

Some extensions for AIRKinect(4)KinectGestureRecognizer

最後來看一下如何辨識Gesture。Gesture本身的定義有點廣,幾乎非語言的肢體表達都可以算是。參考Human Interface Guidelines,它把Gesture分成三大類:
1. Static Gestures:也就是Potures,一個靜態的姿勢,例如user擺出T字型。
2. Continuous Gestures:只要user在Kinect前就一直追蹤。
3. Dynamic Gesture:基本上是一個動作,可能包含了方向、經過時間等等因素,例如Swipe手勢。
可以想像,有無數種Gesture的可能,也就是說我們沒辦法開發出一個或有限個辨識器來滿足每個應用程式的需求。因此在我們的extensions裡面,先做出一個GestureRecognizer辨識器的雛形,以及一個Manger來管理所有的GestureRecognizer,然後試著開發出一個Static與一個Dynamic Gesture Recognizer,往後只要依循同一個開發模式,慢慢加入新的辨識器,這個extensions就會越來越強大,而每個應用程式在使用不同的辨識器的時候做法都一樣,很輕易可以達到程式碼重複使用的目的。Continuous Gestures因為只是一直的追蹤,暫時先不納入我們的考量。

先在entensions這個package底下加入gesture資料夾,相關的功能我們放在這邊。首先Gesture的辨識從開始後必須不停的記錄與計算user的動作,因此我們建立一個GestureRecognizerManager類別來管理所有的辨識器,裡面有個Timer會不斷呼叫辨識器去計算user的Gesture。GestureRecognizerManager也是個隱藏在後面的Singleton,開發者不需要直接跟它打交道,未來也許可以考慮加入一些管理所有辨識器的方法。

然後建立一個KinectGestureRecognizer,它是辨識器的雛形,當辨識器new出來的時候,它會自動去呼叫GestureRecognizerManager加入它管理的清單中。需要移除的時候可以呼叫KinectGestureRecognizer的dispose()方法,它會移出那個清單,並且做釋放資源的工作。pause()跟resume()方法可以暫時停止Gesture的辨識,然後重新開始。我們希望Gesture觸發開始,結束,以及過程中可以告訴我們,並且可以告訴我們一些資訊,至於是哪些資訊依照每種Gesture會有不同。於是我們設計了可以在建構式的時候傳入onStart,onStop,以及onUpdate三個call back function,當辨識出Gesture的時候會呼叫這三個function,並且將辨識器的指標傳給這三個call back function,需要資訊的話就可以從辨識器指標去要。這邊不設計成事件的原因是,因為onUpdate在辨識成功後會不斷被呼叫,考量到效能call back function效率較高,也可以免去要設計許多的自訂事件來傳資料。call back function最難用的地方在接收參數很難在開發環境中提示開發者,我們把它簡化成接收一個 gr:KinectGestureRecognizer (或子類別),這個問題就不大了。我們在這裡不特別區分Static與Dynamic Gesture的介面,只另外設計一個addPosition(jointNames:Array)方法,因為Static Gesture只需要當時最新的關節座標去判斷,而Dynamic Gesture需要將一段時間內的點記錄下來,才能去判斷動作方向、路徑等等東西。這個方法需要傳入一個Array,裡面是SkeletonJoint裡定義的關節名稱常數,把我們需要記錄的關節名稱傳進去,這樣辨識器就會將一定長度(_positionDataSize)時間內的關節點world座標與時間記錄下來,格式是PositionData。最後,設計一個calculate()方法會不斷被呼叫,要在子類別覆寫這個方法,裡面就是做各種的運算,去判斷Gesture是否發生,並且適時的呼叫onStart,onStop,以及onUpdate。Dynamic Gesture則在calculate()一開始要呼叫。

假設我們開發了一個MyGestureRecognizer繼承KinectGestureRecognizer,在主程式中的用法就會像是這樣,先新增一個MyGestureRecognizer:
  _myGestureRecognizer = new MyGestureRecognizer(_user, onStart, onStop, onUpdate);

這樣辨識器就開始跑了,而call back function長這樣:
private function onStart(gr:MyGestureRecognizer):void 
{
  //做Gesture發生時要做的事
}

private function onStop(gr:MyGestureRecognizer):void 
{
  //做Gesture停止時要做的事
}

private function onUpdate(gr:MyGestureRecognizer):void 
{
  //做Gesture更新時要做的事,也許會需要MyGestureRecognizer提供
  //的資訊,例如gr.speed
}

不用時就呼叫:
  _myGestureRecognizer.dispose();

這樣用起來就超級簡單了!剩下的問題就是要針對應用程式的需求去開發我們需要的辨識器。Static辨識器還算簡單,只要判斷關節的相對位置就可以了,而Dynamic辨識器就複雜得多,甚至還需要各種不同的演算法。我在extensions裡面提供了HelloGestureRecognizer(Static)以及SwipeGestureRecognizer(Dynamic),提供給大家參考。SwipeGestureRecognizer是參考Kinect Toolbox改寫而成。

最後,提供原始檔給大家參考:
Extensions原始檔,請配合AIRKinect2.2版使用
FlashDevelop用的幾個Template檔
一個使用extensions的範例程式

p.s.我其實還想放ASDoc給大家,可是AIRKinect2.2沒有提供swc檔,而且又牽涉到.ane檔的問題,目前還沒有輸出成功。如果哪位大大有辦法成功弄出來,請留言告訴我,我會再補上來,謝謝!

上一篇:Some extensions for AIRKinect(3)KinectCursor與KinectButton

沒有留言:

張貼留言