In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

ジャグリングとFlash

動画:http://twitvid.com/DF27A
画像:http://www.flickr.com/photos/tensafefrogs/3584090438/in/set-72157619058523389/

参考:フィルタリング処理
forked from: 動体検知 + 肌色認識 | wonderfl build flash online
http://wonderfl.net/code/a95ff369b20c756ee8a612aaa620c7b028229624
Get Adobe Flash player
by fumix 03 Sep 2010
/**
 * Copyright fumix ( http://wonderfl.net/user/fumix )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/A1Hg
 */

/***
動画:http://twitvid.com/DF27A
画像:http://www.flickr.com/photos/tensafefrogs/3584090438/in/set-72157619058523389/

参考:フィルタリング処理
forked from: 動体検知 + 肌色認識 | wonderfl build flash online
http://wonderfl.net/code/a95ff369b20c756ee8a612aaa620c7b028229624
***/
package {
    import flash.display.*;
    import flash.events.*;
    import flash.geom.Point;
    import flash.geom.Rectangle;
    import flash.media.*;
    import flash.net.*;

    //動画ストリーミングの再生
    [SWF(width=465, height=465, backgroundColor=0xFFFFFF)]
    public class juggling extends Sprite {

        //変数
        private var nc:NetConnection;
        private var ns:NetStream;
        private var motion : MotionDetector;
        private var _bmdCurrent : BitmapData;
        private var video : Video;
        private var hige : BitmapData;
        private const VIDEO_URL:String = 'http://www.planet-ape.net/wonderfl/video_new.flv';
        private const IMAGE_URL:String = 'http://www.planet-ape.net/wonderfl/hige.png';
        
        //コンストラクタ
        public function juggling() {
            //画像読み込み
            var imgLoader:Loader = new Loader();
            imgLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadComplete); 
            imgLoader.load(new URLRequest(IMAGE_URL));
            
            //コネクションの生成
            nc=new NetConnection();
            nc.addEventListener(NetStatusEvent.NET_STATUS,onNetStatus);
            nc.addEventListener(SecurityErrorEvent.SECURITY_ERROR,onSecurityError);
            nc.connect(null);
            

        }
        
        private function loadComplete(event : Event) : void {
            event.target.removeEventListener(Event.COMPLETE, loadComplete);
            //取得した画像を入れる
            hige = new BitmapData (event.target.loader.content.bitmapData.width, event.target.loader.content.bitmapData.height, true);
            //画像のマスクを有効に
            hige.copyPixels(event.target.loader.content.bitmapData,event.target.loader.content.bitmapData.rect , new Point(0,0),event.target.loader.content.bitmapData);
        }

        //通信状態イベントの処理
        private function onNetStatus(evt:NetStatusEvent):void {
            //成功
            if (evt.info.code=="NetConnection.Connect.Success") {
                //ストリームの生成
                ns=new NetStream(nc);
                ns.checkPolicyFile = true;
                ns.addEventListener(NetStatusEvent.NET_STATUS,onNetStatus);
                ns.addEventListener(AsyncErrorEvent.ASYNC_ERROR,onAsyncError);
                //メタデータの取得
                var obj:Object=new Object();
                obj.onMetaData=onMetaData;
                ns.client=obj;

                //ビデオの生成
                video=new Video(360,480);
                //addChild(video);
                video.attachNetStream(ns);
                   
                //ビデオの再生
                ns.play(VIDEO_URL);
                //動体検知クラス
                motion = new MotionDetector();
                motion.startDetect(video);
                        
                //取得した画像を切り取って_bmdCurrentに入れる
                _bmdCurrent = new BitmapData (465, 465, true, 0xFFFFFF);

                addChild(new Bitmap(_bmdCurrent));
    
                addEventListener(Event.ENTER_FRAME,onEnterFrameHandler);
                
                //速度とかメモリとかチェック(重要じゃないので無視無視)
                //addChild(new Stats());
            }
            //停止
            else if (evt.info.code=="NetStream.Play.Stop") {
                trace("Stop");
            }
            //エラー
            else if (evt.info.level=="error") {
                trace("Error");
            }
        }
        
        private function onEnterFrameHandler(event : Event=null) : void {
            //検知した動体の配列
            var mousePointArray:Vector.<ExRectangle> = motion.watchingRectArray;

            //動体描画用のスプライト用意
            var rectSprite:Sprite = new Sprite();

            //動体の数だけループ
            for(var idx:int=0; idx<mousePointArray.length; idx++){
                var tgt_rec:Rectangle = mousePointArray[idx].rect;
                //動体を某画像に
                var sp:Sprite = new Sprite();
                sp.addChild(new Bitmap(hige));
                //動体の大きさに合わせて画像を拡大縮小(縦横比は維持)
                sp.x = tgt_rec.x;
                sp.y = tgt_rec.y;
                var w:Number = tgt_rec.width;
                var h:Number = sp.height * w / sp.width;
                sp.width = w*1.2;
                sp.height = h*1.2;
                rectSprite.addChild(sp);
            }
            //動画を描画
            _bmdCurrent.draw (video);
            
            //デバッグ用:それぞれを有効にするとフィルタリングされた動画になります
            //_bmdCurrent.draw(motion._copy);
            //_bmdCurrent.draw(motion._now);
            
            //動体画像を描画
            _bmdCurrent.draw(rectSprite);
            
        }

        //メタデータ取得イベントの処理
        private function onMetaData(info:Object):void {
        }

        //セキュリティーエラーイベントの処理
        private function onSecurityError(evt:SecurityErrorEvent):void {
            trace("SecurityError");
        }

        //同期エラーイベントの処理        
        private function onAsyncError(evt:AsyncErrorEvent):void {
            trace("AsyncErrorEvent");
        }
    }
}

import flash.display.BitmapData;
import flash.filters.ColorMatrixFilter;
import flash.filters.ConvolutionFilter;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.media.Video;
import flash.utils.setInterval;

/**
 * 動体検知クラス
 * @author fumix
 * これがメイン:
 */
class MotionDetector
{
    private var _watchingRectArray:Vector.<ExRectangle>;
    private var _nowRectArray:Vector.<ExRectangle>;
    private var idCounter:int;
    private var video:Video;
    private var now:BitmapData;
    private var copy:BitmapData;
    private var rect:Rectangle;
    private var pt:Point;
    private var noiseReduction:ConvolutionFilter;
    private var grayScale:ColorMatrixFilter;
    private var skin:ColorMatrixFilter;
    /**
     *コンストラクタ 
     * 
     */
    public function MotionDetector()
    {
        _watchingRectArray = new Vector.<ExRectangle>;
        _nowRectArray = new Vector.<ExRectangle>;
        idCounter = 0;
        //ノイズリダクション
        noiseReduction = new ConvolutionFilter(3, 3);
        noiseReduction.bias = -(0x1000 + 0x100 * 6);
        noiseReduction.matrix = [
                    1,  1, 1,
                    1, 16, 1,
                    1,  1, 1
       ];
        grayScale = new ColorMatrixFilter([
            0.3, 0.59, 0.11, 0, 0,
            0.3, 0.59, 0.11, 0, 0,
            0.3, 0.59, 0.11, 0, 0,
            0, 0, 0, 1, 0
        ]);
        skin = new ColorMatrixFilter([
            0, 0, 0, 0, 0,
            -0.43, -0.85, 1.28, 0, 198.4,
            1.28, -1.07, -0.21, 0, 108.8,
            0, 0, 0, 1, 0
        ]);
    }
    
    /**
     * 動体検知スタート 
     * @param img 検知したい動画
     * @return 
     * 
     */
    public function startDetect(img:Video):void
    {
        video = img;
        now = new BitmapData(video.width,video.height,false);
        copy = new BitmapData(video.width,video.height,false);
        rect = new Rectangle(0, 0, video.width,video.height);
        pt = new Point;
        //30msで検知
        setInterval(Check,30);
    }
    /**
     * 動体検知
     * 
     */
    private function Check():void
    {
        //一時配列のリセット
        var _tempRectArray:Array = new Array();

        //bitmapの処理        
        now.draw(video);
        copy = now.clone();
        //オレンジの玉を強調させるフィルタリング
        copy.applyFilter(now, rect, pt, skin);
        //グレイスケール
        now.applyFilter(now, rect, pt, grayScale);
        //閾値による色の置き換えでオレンジの玉部分のみを抽出する
        now.threshold(now, rect, pt, ">", 0xff111111, 0xffffffff);
        now.threshold(now, rect, pt, "!=", 0xffffffff, 0xff000000);
        now.threshold(copy, rect, pt, "!=", 0x0060ff, 0xff000000, 0x00c0c0);
        //ノイズリダクション
        now.applyFilter(now, rect, pt, noiseReduction);

        //ブロック化
        _nowRectArray = blocked(now);

        //今回検出分がそれぞれ、いくつのwatch分に重なってるか
        //複数のwatch中のrectとintersectしている → watch中のrectと最もたくさんintersectしているものに統合
        checkOverlapRectNumber(_watchingRectArray,_nowRectArray,_tempRectArray);
        
        //重なりデータを下に、新規watch候補を選定
        checkNewWatchArray(_watchingRectArray,_tempRectArray);
        
        //候補から、判定
        _watchingRectArray = setNewWatchArray(_tempRectArray);
    }
    
    /**
     * ブロック化 
     * @param dst 対象となるビットマップ
     * @return ブロック化した配列
     * 
     */
    private function blocked(dst:BitmapData):Vector.<ExRectangle>
    {
        var rect:Rectangle = dst.getColorBoundsRect(0xffffff,0xffffff,true);
        var area:Rectangle = new Rectangle(0,0,dst.width,1);
        var temp:BitmapData = new BitmapData(dst.width,1,false,0x000000);
        var zero:Point = new Point();
        var tempArray:Vector.<ExRectangle> = new Vector.<ExRectangle>;
        
        while( !rect.isEmpty()){
            area.y = rect.top;
            temp.copyPixels(dst,area,zero);
            rect = temp.getColorBoundsRect(0xffffff,0xffffff,true);
            dst.floodFill(rect.x,area.y,0xff00ff);
            var br:Rectangle = dst.getColorBoundsRect(0xFFFFFF, 0xFF00FF);
            br.inflate(4,4);
            dst.fillRect(br, 0x0000ff);
            rect = dst.getColorBoundsRect( 0xffffff, 0xffffff, true );
            tempArray.push(new ExRectangle(br));
        }
        
        return tempArray;
        
    }
    

    /**
     * 今回検出分がそれぞれ、いくつのwatch分に重なってるか
     * @param watchArray watchしている矩形の配列
     * @param nowArray 今回の矩形の配列
     * @param tempArray watch候補の矩形の配列
      * 
     */
    private function checkOverlapRectNumber(watchArray:Vector.<ExRectangle>,nowArray:Vector.<ExRectangle>,tempArray:Array):void
    {
        for(var i:int=0; i<nowArray.length; i++){
            var rec_ref:ExRectangle = nowArray[i];
            var isectsNum:int = rec_ref.isects.length;
            
            //1つもwatch中のrectとintersectしてない → 新規追加
            if(isectsNum == 0){
                if(rec_ref.rect.width*rec_ref.rect.height <= 1024*0.7){
                    //結構小さい → 廃棄
                    _nowRectArray.splice(Number(i), 1);
                    i--;
                }else{
                    //新規生成
                    rec_ref.event = "create";
                    rec_ref.life = 1;
                    rec_ref.id = ++idCounter;
                    tempArray.push(rec_ref);
                }
            
            //複数のwatch中のrectとintersectしている → watch中のrectと最もたくさんintersectしているものに統合
            }
            else if(isectsNum >= 2){
                var rect_integrate:ExRectangle = null;
                for(var j:int=0; j<isectsNum-1; j++){
                    var rec_now:ExRectangle = rec_ref.isects[j];
                    var rec_next:ExRectangle = rec_ref.isects[j+1];
                    rect_integrate = (rec_now.isects.length >= rec_next.isects.length) ? rec_now : rec_next;
                } 
                for(j=0; j<isectsNum; j++){
                    if(rect_integrate != rec_ref.isects[j]){
                        var rec_disappear:ExRectangle = rec_ref.isects[j];
                        for(var k:int=0; k<watchArray.length; k++){
                            if(watchArray[k] == rec_disappear){
                                watchArray.splice(k, 1);
                                break;
                            }
                        }
                    }
                }
            }
        }
        
    }
    
    /**
     * 重なりデータを下に、新規watch候補を選定
     * @param watchArray watchしている矩形の配列
     * @param nowArray 今回の矩形の配列
     * @param tempArray watch候補の矩形の配列
     * 
     */
    private function checkNewWatchArray(watchArray:Vector.<ExRectangle>,tempArray:Array):void
    {
        //重なりデータを下に、新規watch候補を選定
        for(var k:int=0; k<watchArray.length; k++){
            var rec_watch:ExRectangle = watchArray[k];
            var isectsNum:int = rec_watch.isects.length;
            if(isectsNum == 0){
                //0個の場合 → LIFE分継続
                //trace("watch_未intersect: id:" + rec_watch.id + " life:"+ rec_watch.life + " event: " + rec_watch.event);
                rec_watch.life -- ;
                rec_watch.event = "stay";
                tempArray.push(rec_watch);
            }else if(isectsNum == 1){
                //一つの場合 → 維持
                //trace("watch_1つintersect: " + rec_watch.id  + " life:" + rec_watch.life);
                rec_watch.isects[0].id = rec_watch.id;
                rec_watch.isects[0].event = "move";
                rec_watch.isects[0].life = 1;
                rec_watch.isects[0].isects = new Array();
                tempArray.push(rec_watch.isects[0]);
                
            }else{
                //複数個の場合 → union
                //それぞれの距離を測ったほうがいいかも
                //あるいは、一つのrectの面積の上限を決めるとか
                //(2つのrectが一旦unionされると以降つながり続ける→すれ違った人が、ずっと同じ人間だと認識されてしまうだろう)
                //計算面倒だなあ
                //trace("watch_複数にintersect: id:" + rec_watch.id + " 個数:" + rec_watch.isects.length + " life:" + rec_watch.life);
                while(rec_watch.isects.length > 1){
                    rec_watch.isects[0].rect = rec_watch.isects[0].rect.union(rec_watch.isects[1].rect);
                    rec_watch.isects.splice(1, 1);
                }
                rec_watch.isects[0].id = rec_watch.id;
                rec_watch.isects[0].event = "move";
                rec_watch.isects[0].life = 1;
                rec_watch.isects[0].isects = new Array();
                tempArray.push(rec_watch.isects[0]);
            }
        }
        
    }
    
    /**
     * 新規watchデータ生成
     * @param tempArray watch候補の矩形の配列
     * @return 生成されたwatchデータ
     * 
     */
    private function setNewWatchArray(tempArray:Array):Vector.<ExRectangle>
    {
        //候補から、判定
        var watchArray:Vector.<ExRectangle> = new Vector.<ExRectangle>;
        var tempLength:int = tempArray.length;
        for(var i:int=0; i<tempLength; i++){
            var rec_item:ExRectangle = tempArray[i];
            var infratex:int = (rec_item.rect.width - 4 <= 4) ? 0 : 4;
            var infratey:int = (rec_item.rect.height - 4 <= 4) ? 0 : 4;
            rec_item.rect.inflate(-infratex, -infratey);
            
            //誰ともintersectしてない寿命切れのrectを削除
            if(rec_item.event == "stay"){
                if(rec_item.life <= 0){
                }else{
                    watchArray.push(rec_item);
                }
            //移動、統合物
            }else if(rec_item.event == "move"){
                watchArray.push(rec_item);
            
            //新規
            }else if(rec_item.event == "create"){
                watchArray.push(rec_item);
            }
        }
        return watchArray;
    }

    public function get nowRectArray():Vector.<ExRectangle>
    {
        return _nowRectArray;
    }

    public function get watchingRectArray():Vector.<ExRectangle>
    {
        return _watchingRectArray;
    }
    
    public function get _now() : BitmapData {
        return now;
    }
    
    public function get _copy() : BitmapData {
        return copy;
    }
}

class ExRectangle
{
    public var rect:Rectangle;
    public var isects:Array;
    public var event:String;
    public var life:int;
    public var id:int;
    public function ExRectangle(r:Rectangle)
    {
        isects = new Array();
        rect = r;
        event = '';
        life = 0;
        id = 0;
    }    
}