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

Scrolling Bg Optical Flow (Lucas-Kanade method)

/**
 * Copyright Hasufel ( http://wonderfl.net/user/Hasufel )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/2nb2
 */

// forked from Saqoosha's Optical Flow (Lucas-Kanade method)
// Wanted to do a simple scrolling optical analysis proof of concept using Saqoosha's code
// We can build intelligent tracking...endless possibilities, for IA and more!
// thx Saqoosha!
// @Hasufel
package {
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.Graphics;
    import flash.display.Shape;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.geom.Rectangle;
    
    [SWF(width=465, height=465, backgroundColor=0,frameRate=30)]

    public class OpticalFlowLK extends Sprite {
        private static const WIDTH:uint = 465;
        private static const HEIGHT:uint = 240;
        private static const toDegree:Number = 180 / Math.PI;

        private static const _imageRect:Rectangle = new Rectangle(0, 0, WIDTH, HEIGHT);
        private static const _starsCols:Array = [0xffffffff,0xffffcdff,0xffbebebe,0xffc7c7c7,0xffaaaaaa];
        private static const _starsNum:uint = 5000;
        private var _star:Particle;
        private var currImage:BitmapData = new BitmapData(WIDTH, HEIGHT, true, 0xFF000000);
        private var prevImage:BitmapData = new BitmapData(WIDTH, HEIGHT, true, 0xFF000000);
        private var tmp:BitmapData = new BitmapData(WIDTH, HEIGHT, true, 0xFF000000);

        private var container:Sprite;
        private var arrow:Shape;
        private var dir:Shape;
        private var g:Graphics;

        private static const winSize:uint = 16;//8
        private static const winStep:uint = winSize * 2 + 1;

        public function OpticalFlowLK() {
            var blackBg:Bitmap = new Bitmap(new BitmapData(465,465,false,0x000000));
            addChild(blackBg);
            addChild(new Bitmap(currImage)) as Bitmap;
            container = Sprite(addChild(new Sprite()));
            arrow = Shape(addChild(new Shape()));
            arrow.x = arrow.y = 465 / 2;
            dir = Shape(container.addChild(new Shape()));
            g = dir.graphics;
            prepareStars();
            addEventListener(Event.ENTER_FRAME, render);
        }

        private function render(e:Event):void {         
                tmp = prevImage;
                prevImage = currImage;
                currImage = tmp;
                starsScroll();

                var curr:Vector.<uint> = currImage.getVector(_imageRect);
                var prev:Vector.<uint> = prevImage.getVector(_imageRect);
                
                var i:int, j:int, k:int, l:int;
                var address:uint;
                
                var gradX:int, gradY:int, gradT:int;
                var A2:Number, A1B2:Number, B1:Number, C1:Number, C2:Number;
                var u:Number, v:Number, uu:Number, vv:Number, n:int;
                
                var wmax:int = WIDTH - winSize - 1;
                var hmax:int = HEIGHT - winSize - 1;
                
                g.clear();
                
                uu = vv = n = 0;
                
                for (i = winSize + 1; i < hmax; i += winStep) { // y
                    for (j = winSize + 1; j < wmax; j += winStep) { // x
                        A2 = 0;
                        A1B2 = 0;
                        B1 = 0;
                        C1 = 0;
                        C2 = 0;
                        for (k = -winSize; k <= winSize; ++k) { // y
                            for (l = -winSize; l <= winSize; ++l) { // x
                                address = (i + k) * WIDTH + j + l;
                                
                                gradX = (curr[(address - 1)>>0] & 0xff) - (curr[(address + 1)>>0] & 0xff);
                                gradY = (curr[(address - WIDTH)>>0] & 0xff) - (curr[(address + WIDTH)>>0] & 0xff);
                                gradT = (prev[address>>0] & 0xff) - (curr[address>>0] & 0xff);
                                
                                A2 += gradX * gradX;
                                A1B2 += gradX * gradY;
                                B1 += gradY * gradY;
                                C2 += gradX * gradT;
                                C1 += gradY * gradT;
                            }
                        }
                        var delta:Number = (A1B2 * A1B2 - A2 * B1);
    
                        if (delta) {
                            /* system is not singular - solving by Kramer method */
                            var deltaX:Number;
                            var deltaY:Number;
                            var Idelta:Number = winSize / delta;//8/delta;
    
                            deltaX = -(C1 * A1B2 - C2 * B1);
                            deltaY = -(A1B2 * C2 - A2 * C1);
    
                            u = deltaX * Idelta;
                            v = deltaY * Idelta;
                            
                        } else {
                            /* singular system - find optical flow in gradient direction */
                            var Norm:Number = (A1B2 + A2) * (A1B2 + A2) + (B1 + A1B2) * (B1 + A1B2);
    
                            if (Norm) {
                                var IGradNorm:Number = winSize / Norm;//8/Norm;
                                var temp:Number = -(C1 + C2) * IGradNorm;
    
                                u = (A1B2 + A2) * temp;
                                v = (B1 + A1B2) * temp;
                                
                            } else {
                                u = v = 0;
                            }
                        }
                        
                        if (-winStep < u && u < winStep && -winStep < v && v < winStep) {
                            uu += u;
                            vv += v;
                            n++;
                            g.lineStyle(0, hsv(Math.atan2(v, u) * toDegree + 360,1,1));
                            g.moveTo(j, i);
                            g.lineTo(j + u * 3, i + v * 3);
                        }
                    }
                }
                
                uu /= n;
                vv /= n;
                
                var a:Number = Math.atan2(vv, uu) * toDegree + 360;
                arrow.graphics.clear();
                arrow.graphics.beginFill(hsv(a,1,1));
                arrow.graphics.drawRect(0, -0.5, 10, 1);
                arrow.graphics.moveTo(10, -2);
                arrow.graphics.lineTo(13.5, 0);
                arrow.graphics.lineTo(10, 2);
                arrow.graphics.endFill();
                arrow.scaleX = arrow.scaleY= Math.sqrt(uu * uu + vv * vv) * 10;
                arrow.rotation = a;
        }


        private function starsScroll():void {
            var p:Particle=_star;
            currImage.lock();
            currImage.fillRect(_imageRect,0x00000000);
            while(p){
                p.y+=p.vy;
                currImage.setPixel32(p.x>>0,p.y>>0,p.c);
                if (p.y>HEIGHT){p.y=0;p.x=randomNumber(0,WIDTH);}
                p=p.next;
            }
            currImage.unlock();
        }

        private function prepareStars():void {
            _star=new Particle();
            setProps(_star, {x:randomNumber(0,WIDTH),y:randomNumber(0,HEIGHT),vx:0,vy:randomNumber(1,10),c:_starsCols[randomNumber(0,4)]});
            var prev:Particle=_star;        
            for (var i:uint=0;i<_starsNum;++i){
                var next:Particle=new Particle();
                setProps(next, {x:randomNumber(0,WIDTH),y:randomNumber(0,HEIGHT),vx:0,vy:randomNumber(1,10),c:_starsCols[randomNumber(0,4)]});
                prev.next=next;
                prev=next;
            }

        }

        private function hsv(h:Number, s:Number, v:Number):uint {
            h %= 360;
            var i:int = h / 60;
            var f:Number = h / 60 - i;
            var b:int = v * 255;
            var p:int = b * (1 - s);
            var q:int = b * (1 - f * s);
            var t:int = b * (1 - (1 - f) * s);
            switch (i) {
                case 0: return b << 16 | t << 8 | p;
                case 1: return q << 16 | b << 8 | p;
                case 2: return p << 16 | b << 8 | t;
                case 3: return p << 16 | q << 8 | b;
                case 4: return t << 16 | p << 8 | b;
                case 5: return b << 16 | p << 8 | q;
            }
            return 0;
        }

        private function setProps(o:*,p:Object):void {
            for (var k:String in p) {o[k]=p[k];}
        }
        
        private function randomNumber(low:int, high:int):int{
            return Math.round(Math.random() * (high - low) + low);
        }

    }
}

class Particle {
    public var x:Number,y:Number,vx:Number,vy:Number,c:uint,t:uint,power:int,clock:int=0,col:uint,next:Particle;
}