Beautifl - Flash Gallery

Preview

カメラの前で体を動かしたら、箱が動く・・・みたいな実験
narutohyper 2010年3月4日 MIT License
?
パターンマッチングによるカメラの移動方向推定  
映像を縮小し、全体をすこしずつ移動して直前のフレームと  
重ねあわせ、その差を比較することで移動方向を推定。  
SSDという手法があるらしいが、それに近いのかも。  
CPU負荷とのたたかいなので、scaleSmall、search_count、threshold  
の組み合わせでよいところを設定。  
search_countを多くすると、速い動きにも対応できるが、  
それだけCPUに負担  
[注意]固定カメラで顔を動かしたりしても反応しません。  
運動カメラの動きをとらえます。
      // forked from utabi's パターンマッチングによるカメラの移動方向推定
// パターンマッチングによるカメラの移動方向推定
//
// 映像を縮小し、全体をすこしずつ移動して直前のフレームと
// 重ねあわせ、その差を比較することで移動方向を推定。
// SSDという手法があるらしいが、それに近いのかも。
// CPU負荷とのたたかいなので、scaleSmall、search_count、threshold
// の組み合わせでよいところを設定。
// search_countを多くすると、速い動きにも対応できるが、
// それだけCPUに負担
//
// [注意]固定カメラで顔を動かしたりしても反応しません。
// 運動カメラの動きをとらえます。

package {
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.Sprite;
    import flash.display.BlendMode;
    import flash.events.Event;
    import flash.filters.BlurFilter;
    import flash.filters.ColorMatrixFilter;
    import flash.filters.ConvolutionFilter;
    import flash.geom.Matrix;
    import flash.geom.Point;
    import flash.geom.Rectangle;
    import flash.geom.ColorTransform;
    import flash.media.Camera;
    import flash.media.Video;
    import net.hires.debug.*;
    import flash.text.*;


    import alternativ5.engine3d.materials.FillMaterial;
    import alternativ5.engine3d.primitives.Box;
    import alternativ5.engine3d.events.MouseEvent3D
    import alternativ5.types.Point3D;

    import flash.display.Sprite;
    import flash.display.BlendMode;


    
    [SWF(frameRate="30")]
    Wonderfl.capture_delay( 20 );
    
    public class SimpleDemo extends Sprite {
        private var camera:Camera;
        private var video:Video;
        private var videoWidth:int = 465;
        private var videoHeight:int = 465;
        private var raw:BitmapData;
        private var rect:Rectangle;
        private var pt:Point = new Point(0,0);
        
        private var xp:int,yp:int;
        // Properties for Patch Search
        private var now:BitmapData;
        private var nowMedium:BitmapData;
        private var nowSmall:BitmapData;
        private var last:BitmapData;
        private var lastMedium:BitmapData;
        private var lastSmall:BitmapData;
        private var scaleSmall:Number = .12;
        private var scaleMedium:Number = .25;
        private var mSmall:Matrix = new Matrix();
        private var mMedium:Matrix = new Matrix();
        // 検索の順番
        // 7 8 1
        // 6 0 2
        // 5 4 3
        private var compareX:Array = [ 0, 1, 1, 1, 0, -1, -1, -1, 0];
        private var compareY:Array = [ 0, -1, 0, 1, 1, 1, 0, -1, -1];
        private var dNow:int = 0;
        private var dResult:Point = new Point(0,0);
        private var score:Number;
        private var scoreNow:Number;
        private var scoreLast:Number;
        private var count:int = 0;
        private var t:Number;
        private var ii:int;
        private var aVec:Vector.<uint>;
        private var bVec:Vector.<uint>;
        private var scoreIndiv:Number;
        private var scoreNowIndiv:uint;
        private var current:int;
        private var dif:int;

        //Sprite
        private var arrow:Sprite;
        
        public function SimpleDemo() {
            camera=Camera.getCamera();
            if (camera==null) {
            } else {
                start();
            }
        }

        private function start():void {
            camera.setMode(640, 480,30);
            video = new Video(videoWidth, videoHeight);
            video.attachCamera(camera);
            
            raw = new BitmapData(videoWidth,videoHeight);
            rect = raw.rect;
            
            //this.addChild(video);
            //this.addChild(new Bitmap(raw));
            
            arrow = new Sprite();
            
            arrow.graphics.beginFill(0xff0000);
            arrow.graphics.moveTo(0,-2);
            arrow.graphics.lineTo(40,-2);
            arrow.graphics.lineTo(40,-10);
            arrow.graphics.lineTo(63,0);
            arrow.graphics.lineTo(40,10);
            arrow.graphics.lineTo(40,2);
            arrow.graphics.lineTo(0,2);
            arrow.graphics.lineTo(0,-2);
            arrow.graphics.endFill();
            
            arrow.x = videoWidth/2;
            arrow.y = videoHeight/2- arrow.height/2;
            

            //
            mSmall.scale(scaleSmall,scaleSmall);
            mMedium.scale(scaleMedium,scaleMedium);
            
            now = new BitmapData(videoWidth,videoHeight,false);
            nowMedium = new BitmapData(videoWidth*scaleMedium, videoHeight*scaleMedium, false);
            nowSmall = new BitmapData(videoWidth*scaleSmall, videoHeight*scaleSmall, false);
            last = new BitmapData(videoWidth,videoHeight,false);
            lastMedium = new BitmapData(videoWidth*scaleMedium, videoHeight*scaleMedium, false);
            lastSmall = new BitmapData(videoWidth*scaleSmall, videoHeight*scaleSmall, false);
            

            // テンプレートを作成します
            var template:BasicTemplate = new BasicTemplate();
            addChild(template);

            //this.addChild(arrow);
            this.addChild(new Stats);
            
            // プリミティブを作成します
            var box:Box;
            box = new Box(600, 600, 600);
            box.cloneMaterialToAllSurfaces(new FillMaterial(0x0000FF,1,BlendMode.NORMAL,1,0x0000000));

            // 3Dシーンのルートに追加します
            template.scene.root.addChild(box);
            template.cameraContoller.lookAt(new Point3D());
            
            var dbg:TextField=new TextField()
            dbg.selectable=true;
            dbg.mouseEnabled=true;
            dbg.height=100
            var format:TextFormat=new TextFormat();
            format.color=0x666666
            format.size=12;
            format.font='_ゴシック';
            dbg.defaultTextFormat=format
            dbg.x=300
            addChild(dbg)

            // Event.ENTER_FRAME 時に実行されるレンダリングのイベントです。
            // レンダリング前に実行したい処理を記述します。
            template.onPreRender = function():void {

                // 立方体を回転させます (角度はラジアン)
                raw.lock();

                raw.draw(video);
                
                var detected:Object = detectDirection(raw);

                arrow.rotation = (Math.atan2(detected.point.x,detected.point.y)* -180 / Math.PI) +90;
                
                                
                if(arrow.rotation>0&&arrow.rotation<=90){
                    
                } else if(arrow.rotation==180){
                    dbg.appendText(String(arrow.rotation)+"\n");                        
                    box.rotationY += 2 * Math.PI / 180;                        
                } else if(arrow.rotation==0){
                    dbg.appendText(String(arrow.rotation)+"\n");                        
                    box.rotationY -= 2 * Math.PI / 180;                            
                } else {
                    dbg.appendText(String(arrow.rotation)+"\n");                        
                }                                        
                //arrow.scaleX = detected.distance/25;
                //arrow.scaleY = detected.distance/25;

            }

        }




        private function detectDirection(bd:BitmapData):Object {
            
            now.copyPixels(bd,rect,pt);
            nowMedium.draw(bd,mMedium);
            nowSmall.draw(bd,mSmall);

            var p:Point = getDirection(new Point(0,0),nowSmall,lastSmall, 8, 0.1);
            //var p2:Point = getDirection(new Point(p.x*scaleMedium/scaleSmall,p.y*scaleMedium/scaleSmall),nowMedium,lastMedium, 2, 0.05);
            var d:Number = Point.distance(pt, p)*(1/scaleSmall);

            //trace(p,Point.distance(pt, p)*(1/scaleSmall),Math.atan2(p.x,p.y));

            last.copyPixels(bd,last.rect,pt);
            lastMedium.copyPixels(nowMedium,lastMedium.rect,pt);
            lastSmall.copyPixels(nowSmall,lastSmall.rect,pt);
            
            return {point:p,distance:d};
        }
        private function getDirection(centerP:Point,a:BitmapData,b:BitmapData,search_count:int, threshold:Number):Point{
            dNow = 0;
            dResult = centerP;
            score = 50000000;
            scoreNow = 0;
            scoreLast = 0;
            count = 0;
            t = (a.width-1) * (a.height-1) *threshold;

            while(score > t){
                dNow = 0;
                
                for (ii = 0 ; ii < 9; ii++){
                    
                    scoreNow = getScore(a, b, compareX[ii] + dResult.x, compareY[ii] + dResult.y);
                    
                    if (scoreNow < score){
                        //前回のものよりスコアが低いなら、そっちを優先
                        dNow = ii;
                        score = scoreNow;
                    }
                };
                
                dResult = new Point(dResult.x + compareX[dNow],dResult.y+compareY[dNow]);
                
                count ++;
                if(count >= search_count){
                    // search_countまでに条件が満たなかったら中断
                    //trace("break");
                    break;
                }
            }
            
            return dResult;
        }
        private function getScore(a:BitmapData,b:BitmapData,x:int,y:int):Number{
            //x,yずらした画像の差分をベクトルで計算し、合計スコアを返す
            
            aVec = a.getVector(a.rect);
            bVec = b.getVector(b.rect);
            scoreIndiv = 0;
            scoreNowIndiv = 0;
            current = 0;
            dif = x + y*a.width;
            
            for (yp = 1; yp < a.height-1 ; yp++){
                for (xp = 1; xp < a.width-1 ; xp++){
                    current = yp*a.width + xp;
                    if( 0 <= current+dif && current+dif < aVec.length){
                        scoreNowIndiv = Math.abs(aVec[current] - bVec[current+dif]);
                        if ((0x00000000 < scoreNowIndiv ) && (scoreNowIndiv < 0x00ffffff)){
                            scoreIndiv += (scoreNowIndiv / 0xffffff);
                        }
                    }
                }
            }
            return  scoreIndiv;
        }

    }
}




import alternativ5.engine3d.controllers.CameraController;
import alternativ5.engine3d.core.Camera3D;
import alternativ5.engine3d.core.Object3D;
import alternativ5.engine3d.core.Scene3D;
import alternativ5.engine3d.display.View;
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageQuality;
import flash.display.StageScaleMode;
import flash.events.Event;


/**
 * BasicTemplate for Alternativa3D
 * Alternativa3Dを扱いやすくするためのテンプレートです
 * @author Yasu
 */
class BasicTemplate extends Sprite{
    /**
     * シーンインスタンスです。
     */
    public var scene:Scene3D;
    /**
     * ビューインスタンスです。
     */
    public var view:View;
    /**
     * カメラインスタンスです。
     */
    public var camera:Camera3D;
    /**
     * カメラコントローラーです。
     */
    public var cameraContoller:CameraController;
    
    private var _viewWidth:int;
    private var _viewHeight:int;
    private var _scaleToStage:Boolean;

    /**
     * 新しい BasicTemplate インスタンスを作成します。
     * @param    viewWidth
     * @param    viewHeight
     * @param    scaleToStage
     */
    public function BasicTemplate(viewWidth:int=640, viewHeight:int=480, scaleToStage:Boolean = true) {
        _viewWidth = viewWidth;
        _viewHeight = viewHeight;
        _scaleToStage = scaleToStage;
        
        // Creating scene
        scene = new Scene3D();
        scene.splitAnalysis = false; // not analysis for performance
        scene.root = new Object3D();
        
        // Adding camera
        camera = new Camera3D();
        camera.z = -1000;
        scene.root.addChild(camera);
        
        // camera contoller
        cameraContoller = new CameraController(this);
        cameraContoller.camera = camera;
        
        // set view
        view = new View();
        view.camera = camera;
        addChild(view);
        
        // stage
        if (stage) init();
        else addEventListener(Event.ADDED_TO_STAGE, init);
    }
    
    /**
     * 初期化されたときに実行されるイベントです。
     * 初期化時に実行したい処理をオーバーライドして記述します。
     */
    protected function atInit():void {}
    
    /**
     * 初期化されたときに実行されるイベントです。
     * 初期化時に実行したい処理を記述します。
     */
    private var _onInit:Function = function():void { };
    public function get onInit():Function { return _onInit; }
    public function set onInit(value:Function):void {
        _onInit = value;
    }
    
    /**
     * Event.ENTER_FRAME 時に実行されるレンダリングのイベントです。
     * レンダリング前に実行したい処理をオーバーライドして記述します。
     */
    protected function atPreRender():void {}
    
    /**
     * Event.ENTER_FRAME 時に実行されるレンダリングのイベントです。
     * レンダリング前に実行したい処理を記述します。
     */
    private var _onPreRender:Function = function():void{};
    public function get onPreRender():Function { return _onPreRender; }
    public function set onPreRender(value:Function):void {
        _onPreRender = value;
    }
    
    /**
     * Event.ENTER_FRAME 時に実行されるレンダリングのイベントです。
     * レンダリング後に実行したい処理をオーバーライドして記述します。
     */
    protected function atPostRender():void {
    }
    
    /**
     * Event.ENTER_FRAME 時に実行されるレンダリングのイベントです。
     * レンダリング後に実行したい処理を記述します。
     */
    protected var _onPostRender:Function = function():void{};
    public function get onPostRender():Function { return _onPostRender; }
    public function set onPostRender(value:Function):void {
        _onPostRender = value;
    }
    
    /**
     * レンダリングを開始します。
     */
    public function startRendering():void {
        addEventListener(Event.ENTER_FRAME, onRenderTick);
    }
    /**
     * レンダリングを停止します。
     */
    public function stopRendering():void {
        removeEventListener(Event.ENTER_FRAME, onRenderTick);
    }
    
    /**
     * シングルレンダリング(レンダリングを一回だけ)を実行します。
     */
    public function singleRender():void {
        onRenderTick();
    }
    
    private function init(e:Event = null):void {
        stage.scaleMode = StageScaleMode.NO_SCALE;
        stage.align = StageAlign.TOP_LEFT;
        stage.quality = StageQuality.HIGH;

        // resize
        stage.addEventListener(Event.RESIZE, onResize);
        onResize(null);
        
        // render
        startRendering();
        
        atInit();
        _onInit();
        
    }
    
    private function onRenderTick(e:Event = null):void {
        atPostRender();
        _onPostRender();
        scene.calculate();
        atPreRender();
        _onPreRender();
    }
    
    private function onResize(event:Event = null):void {
        if (_scaleToStage) {
            view.width = stage.stageWidth;
            view.height = stage.stageHeight;
        }else {
            view.width = _viewWidth;
            view.height = _viewHeight;
        }
    }
}