2012年9月21日 星期五

[教學] AIRKinect 的魔法帽子

Kinect 與Flash 的連接, 我們透過AIRKinect (安裝參考)簡單地完成. 以下為大家介紹魔法帽子範例, 當一個人走進Kinect 範圍, 在螢幕為她蓋上一個魔法帽子.


  1. 在程序啟動時, 對AIRKinect 物件加入三個事件監聽器:
      CameraImageEvent.RGB_IMAGE_UPDATE - 影像每次更新產生的事件
      UserEvent.USERS_WITH_SKELETON_ADDED - 發現一個新骨架時事件
      UserEvent.USERS_WITH_SKELETON_ADDED - 骨架離開Kinect 事件
    if (Kinect.isSupported()) {
      // <----- get Kinect instance
      _device = Kinect.getDevice();
      
      // <----- add listener for Kinect
      _device.addEventListener(CameraImageEvent.RGB_IMAGE_UPDATE, rgbImageUpdateHandler, false, 0, true);
      _device.addEventListener(UserEvent.USERS_WITH_SKELETON_ADDED, skeletonsAddedHandler, false, 0, true);
      _device.addEventListener(UserEvent.USERS_WITH_SKELETON_REMOVED, skeletonsRemovedHandler, false, 0, true);
      
      ...
      
      // <----- start the Kinect
      _device.start(settings);
    }
    

  2. 當發現一個新骨架, 我們建立一個帽子MovieClip, 產生魔法效果:

    帽子(_Hat1) 魔法效果是由Flash IDE 的時間軸製作
    ...
    [Embed(source="assets/hat1.swf")]
    private var _Hat1:Class;
    ...
    private function skeletonsAddedHandler(event:UserEvent):void{
      for each(var addedUser:User in event.users){
        var sprite:Sprite = new Sprite();
        var head:SkeletonJoint = addedUser.getJointByName("head");
        var headRGB:Point = head.position.rgb;
        sprite.name = addedUser.trackingID.toString();
        sprite.x = headRGB.x;
        sprite.y = headRGB.y;
        
        // <----- add the MovieClip to stage
        sprite.addChild(new _Hat1() as MovieClip);
        _sprites.push(sprite);
        this.addChild(sprite);
      }
    }
    

  3. 若骨架離開Kinect, 把相對於這個骨架的帽子刪除:
    private function skeletonsRemovedHandler(event:UserEvent):void{
      for each(var removedUser:User in event.users){
        for(var i:int=0; i<_sprites.length; i++){
          var id:String = removedUser.trackingID.toString();
          if(_sprites[i].name==id){
            this.removeChild(_sprites[i]);
            _sprites.splice(i, 1);
          }
        }
      }
    }
    

  4. 影像每次更新時, 追蹤每個骨架頭部(head) 和頸部(neck) 關節位置, 計算兩者距離來決定帽子大小, 並利用TweenLite 把帽子移至頭部:

    Kinect 關節名稱
    private function rgbImageUpdateHandler(event:CameraImageEvent):void{
      // <----- get Kinect RGB Camera image to display
      _bitmapData.draw(event.imageData);
      
      for each(var user:User in _device.users) {
        if (user.hasSkeleton) {
          var head:SkeletonJoint = user.getJointByName("head");
          var neck:SkeletonJoint = user.getJointByName("neck");
          var headRGB:Point = head.position.rgb;
          var neckRGB:Point = neck.position.rgb;
          var diff:Number = Math.abs(neckRGB.y - headRGB.y);
          var diffBase:Number = 50;
          var scale:Number = diff/diffBase;
          var id:String = user.trackingID.toString();
          
          // <----- find the MovieClip
          for(var i:int=0; i<_sprites.length; i++){
            if(_sprites[i].name==id){
              // <----- change the MovieClip position
              TweenLite.to(_sprites[i], 0.1, {x:headRGB.x, y:headRGB.y, scaleX:scale, scaleY:scale});
              break;
            }
          }
        }
      }
    }
    
大家可以 [下載] 範例試試看.

後記:
微軟推出的Kinect 硬體在不同的版本, 是有不同的支援上限:
Kinect for Xbox 360 是由2009 年6 月份推出, 支援追蹤兩組骨架, 解像度640x480 @30Hz.
Kinect for Windows 是由2012 年2 月份推出, 支援追蹤四組骨架, 解像度1280x960 @30Hz.

9 則留言:

匿名 提到...

您好,想請教一下

我在測試的時後,發覺當User離開偵測範圍後,他原本的骨架資訊並不會立即被清除,仍存在於Kinect傳給我程式端的資訊中,這樣子我使用者相關的判斷語法很容易因此而不正確

請問是我少了什麼設定嗎?

我是用AIR Kinect 2.2 + Adobe AIR

謝謝

匿名 提到...

補充說明: User的資訊會在User離開偵測範圍後大約5~10秒,Kinect才會辨識出來使用者已離開,它才會發派UserEvent.USERS_WITH_SKELETON_REMOVED的事件

匿名 提到...

你好, 我一直卡在_Hat1的class上, 我看你的example是在flash中有一個movieclip叫做hat1, 但是並沒有建立movieclip class, 我請問如何connect _Hat1和hat1, 我是個airkinect的初哥, 希望你能解答我的疑難, 謝謝你

Turtler 提到...

你好 ^_^

首先, 把SWF 登記成類別:
[Embed(source="assets/hat1.swf")]
private var _Hat1:Class;

加入場景的方法:
sprite.addChild(new _Hat1() as MovieClip);

匿名 提到...

感謝版主的回答, 那如果我要handle幾個movieclips要怎麼辦, 如果說我除了帽子外, 還有褲和上衣, 那我需要每個movieclip都登記成swf類別嗎, 如果我在一個flash IDE把每個movieclips的actionscript setting把他們變成class, 這個方法可行嗎

Turtler 提到...

把MovieClip 集結在一個SWF 中, 這個方法是可以.

先把SWF 動態載入:
var loader:Loader = new Loader();
loader.load(new URLRequest("assets/hat1.swf"));
loader.contentLoaderInfo.addEventListener(Event.COMPLETE,onComplete);

當SWF 載入完成, 便可以使用:
public function onComplete(e:Event):void
{
var loaderInfo:LoaderInfo = e.target as LoaderInfo;
var MyHat:Class = loaderInfo.applicationDomain.getDefinition("Hat") as Class;
var hat:MovieClip = new MyHat();
addChild(hat);
}

匿名 提到...

感謝版主細心回覆, 還想問問版主你有方法減少skeleton的noise嗎,即是減少截取數據的次數

Turtler 提到...

如果是skeleton 經常改變, 令帽子不斷閃動.
試試延長TweenLite 的移動時間 (範例的duration 是0.1 秒完成), 減少閃動情況 ^_^

匿名 提到...

你好我想請問一下,範例是下載後開啟執行就可以了嗎?
我的Kinect已經裝好了,Kinect for Windows SDK v1.7。
可是我執行時只有出現灰色的畫面還有一開始左上角出現一下下亮晶晶,這樣是對的嗎?