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

forked from: ドロネー三角分割でWEBカメラ表示

s26 さんの「ドロネー三角分割でWEBカメラ表示」 http://wonderfl.net/c/wlLw のフォークになります。
実質的にコード全取っ替えになったんで、新規に起こしました。
/**
 * Copyright Aquioux ( http://wonderfl.net/user/Aquioux )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/ws5u
 */

package {
    //import aquioux.display.bitmapDataEffector.Edge2nd;
    //import aquioux.display.bitmapDataEffector.GrayScale;
    //import aquioux.display.bitmapDataEffector.Posterize;
    //import aquioux.display.bitmapDataEffector.Smooth;
    import com.bit101.components.HSlider;
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.Graphics;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.geom.Point;
    import flash.geom.Rectangle;
    [SWF(width = "465", height = "465", frameRate = "60", backgroundColor = "#FFFFFF")]
    
    /**
     * forked from: ドロネー三角分割でWEBカメラ表示
     * @see    http://aquioux.net/blog/?p=2962
     * @see    http://wonderfl.net/c/wlLw
     * @see    http://wonderfl.net/c/4cr7
     * @author Aquioux(Yoshida, Akio)
     */
    public class Main extends Sprite {
        // サイズ
        private const IMAGE_WIDTH:int  = 465;
        private const IMAGE_HEIGHT:int = 465;
        
        // ウェブカメラ・ユニット
        private var webcam_:WebCamera;

        // ウェブカメラの映像を格納する BitmapData
        private var webcamBmd_:BitmapData;
        
        // 画像処理各種フィルター
        private var smooth_:Smooth;            // 平滑化
        private var grayscale_:GrayScale;    // グレイスケール
        private var posterize_:Posterize;    // 減色
        private var edge_:Edge2nd;            // エッジ検出
        
        // 画像処理フィルター適用に必要なもの
        private const RECT:Rectangle   = new Rectangle(0, 0, IMAGE_WIDTH, IMAGE_HEIGHT);
        private const ZERO_POINT:Point = new Point(0, 0);
        
        // ウェブカメラの映像 BitmapData から getVector で取得したデータを格納する Vector 配列
        private var colorPixelList_:Vector.<uint>;    // フルカラー
        private var grayPixelList_:Vector.<uint>;    // グレイスケール
        
        // ドロネー
        private var delaunay_:Delaunay;
        
        // スライダー
        private var slider_:HSlider;
        
        // 描画精度調整値
        private var accuracy_:int;
        
        // ドロネー三角描画
        private var g_:Graphics;

       
        // コンストラクタ
        public function Main() {
            // ウェブカメラのセット
            try {
                webcam_ = new WebCamera(IMAGE_WIDTH, IMAGE_HEIGHT, this.stage.frameRate);
            } catch (err:Error) {
                throw new Error(err.message);
                return;
            }
            // ウェブカメラのイベントハンドラ登録
            webcam_.addEventListener(Event.ACTIVATE, setup);
        }
        
        // ウェブカメラのイベントハンドラ
        private function setup(e:Event):void {
            e.target.removeEventListener(Event.ACTIVATE, arguments.callee);
            
            // ウェブカメラの映像を格納する BitmapData の生成
            webcamBmd_ = new BitmapData(IMAGE_WIDTH, IMAGE_HEIGHT);
            
            // 画像処理フィルター生成
            smooth_ = new Smooth();            // 平滑化
            smooth_.strength = 4;
            grayscale_ = new GrayScale();    // グレイスケール
            posterize_ = new Posterize();    // 減色
            posterize_.degree = 6;
            edge_ = new Edge2nd();            // エッジ検出
            
            // ウェブカメラの映像 BitmapData から getVector で取得したデータを格納する Vector 配列の生成
            colorPixelList_ = new Vector.<uint>(IMAGE_WIDTH * IMAGE_HEIGHT, true);
            grayPixelList_  = new Vector.<uint>(IMAGE_WIDTH * IMAGE_HEIGHT, true);

            // ドロネークラスの生成
            delaunay_ = new Delaunay();
            
            // スライダーの生成
            var shiftValue:int = 20;
            var minValue:int = 10;
            var maxValue:int = 30;
            var initValue:int = (maxValue-minValue) / 2 + minValue;
            slider_ = new HSlider(this, shiftValue, IMAGE_HEIGHT- shiftValue, sliderHandler);
            slider_.width = IMAGE_WIDTH - (shiftValue * 2);
            slider_.setSliderParams(minValue, maxValue, initValue);

            // 描画精度調整値を初期化
            accuracy_ = initValue;
            
            // ドロネー三角描画場所の指定
            g_ = this.graphics;
            
            // ENTER_FRAME イベントハンドラ登録
            addEventListener(Event.ENTER_FRAME, update);
        }
        
        // ENTER_FRAME イベントハンドラ
        private function update(event:Event):void {
            // ウェブカメラの映像
            webcamBmd_ = webcam_.bitmapData;
            
            // ウェブカメラの映像のカラーピクセル情報を待避
            colorPixelList_ = webcamBmd_.getVector(RECT);
            
            // 画像処理
            smooth_.applyEffect(webcamBmd_);        // 平滑化
            grayscale_.applyEffect(webcamBmd_);        // グレイスケール
            posterize_.applyEffect(webcamBmd_);        // 減色
            edge_.applyEffect(webcamBmd_);            // 境界線抽出
            
            // 画像処理後のウェブカメラの映像のピクセル情報を取得
            grayPixelList_ = webcamBmd_.getVector(RECT);
          
            // ドロネー母点の処理
            // ドロネー母点格納 Vector 配列
            if (points != null) {
                points.length = 0;
                points = null;
            }
            var points:Vector.<Point> = new Vector.<Point>();
            // ステージの四隅をドロネー母点として登録
            points.push(
                new Point(0, 0),
                new Point(IMAGE_WIDTH, 0),
                new Point(0, IMAGE_HEIGHT),
                new Point(IMAGE_WIDTH, IMAGE_HEIGHT)
            );
            // ドロネー母点生成
            var len:int = grayPixelList_.length;
            for (var i:int = 0; i < len; i++) {
                if (grayPixelList_[i] > 0xFF000000)    // 全ピクセル中、黒以外がドロネー母点となり得るが……
                    if (Math.random() * accuracy_ < 1)    // すべてをドロネー母点とはせず、 accurary_ で間引く
                        points.push(new Point(i % IMAGE_WIDTH, i / IMAGE_HEIGHT >> 0));
            }
            
            // 描画
            g_.clear();
            delaunay_.render(g_, points, delaunay_.compute(points), colorPixelList_);
        }
        
        // スライダーのイベントハンドラ
        private function sliderHandler(event:Event):void {
            accuracy_ = HSlider(event.target).value >> 0;
        }
    }
}

//package {
    import flash.display.BitmapData;
    import flash.display.DisplayObject;
    import flash.display.IBitmapDrawable;
    import flash.events.ActivityEvent;
    import flash.events.Event;
    import flash.events.EventDispatcher;
    import flash.geom.Matrix;
    import flash.geom.Point;
    import flash.geom.Rectangle;
    import flash.media.Camera;
    import flash.media.Video;
    /**
     * WebCamera Unit
     * @author YOSHIDA, Akio (Aquioux)
     */
    /*public*/ class WebCamera extends EventDispatcher {
        /**
         * ウェブカメラの映像
         */
        public function get bitmapData():BitmapData {
            _bitmapData.draw(video_, matrix_);
            return _bitmapData;
        }
        private var _bitmapData:BitmapData;
        
        private var video_:Video;            // ビデオ
        private var matrix_:Matrix;            // 鏡像になるようにさせるための Matrix
        
        /**
         * コンストラクタ
         * @param    cameraWidth        カメラ幅
         * @param    cameraHeight    カメラ高
         */
        public function WebCamera(cameraWidth:int, cameraHeight:int, frameRate:int = 30) {
            // 外部へ渡す BitmapData の生成
            _bitmapData = new BitmapData(cameraWidth, cameraHeight, true, 0x0);
            
            // 鏡像になるよう、Matrix で左右反転
            matrix_ = new Matrix(-1, 0, 0, 1, cameraWidth, 0);
            
            // カメラ準備
            var camera:Camera = Camera.getCamera();
            if (camera) {
                // camera のセットアップ
                camera.setMode(cameraWidth, cameraHeight, frameRate);
                // video のセットアップ
                video_ = new Video(cameraWidth, cameraHeight);
                video_.attachCamera(camera);
                // カメラがアクティブになるまで待つ
                camera.addEventListener(ActivityEvent.ACTIVITY, activeHandler);
            } else {
                throw new Error("カメラがありません。");
            }
        }
        // ACTIVITY イベントハンドラ
        private function activeHandler(e:ActivityEvent):void {
            e.target.removeEventListener(ActivityEvent.ACTIVITY, arguments.callee);
            dispatchEvent(new Event(Event.ACTIVATE));
        }
    }
//}

//package aquioux.display.bitmapDataEffector {
    import flash.display.BitmapData;
    import flash.filters.BitmapFilterQuality;
    import flash.filters.BlurFilter;
    import flash.geom.Point;
    /**
     * BlurFilter による平滑化
     * @author YOSHIDA, Akio (Aquioux)
     */
    /*public*/ class Smooth implements IEffector {
        /*
         * ぼかしの強さ
         * @param    value    数値
         */
        public function set strength(value:Number):void {
            filter_.blurX = filter_.blurY = value;
        }
        /*
         * ぼかしの質
         * @param    value    数値
         */
        public function set quality(value:int):void {
            filter_.quality = value;
        }

        // ブラーフィルタ
        private var filter_:BlurFilter;

        private const ZERO_POINT:Point = EffectorUtils.ZERO_POINT;

        /*
         * コンストラクタ
         */
        public function Smooth() {
            filter_ = new BlurFilter(2, 2, BitmapFilterQuality.MEDIUM);
        }
        
        /*
         * 効果適用
         * @param    value    効果対象 BitmapData
         */
        public function applyEffect(value:BitmapData):BitmapData {
            value.applyFilter(value, value.rect, ZERO_POINT, filter_);
            return value;
        }
    }
//}

//package aquioux.display.bitmapDataEffector {
    import flash.display.BitmapData;
    import flash.filters.ColorMatrixFilter;
    import flash.geom.Point;
    /**
     * ColorMatrixFilter による BitmapData のグレイスケール化(NTSC 系加重平均による)
     * 参考:Foundation ActionScript 3.0 Image Effects(P106)
     *         http://www.amazon.co.jp/gp/product/1430218711?ie=UTF8&tag=laxcomplex-22
     * @author YOSHIDA, Akio (Aquioux)
     */
    /*public*/ class GrayScale implements IEffector {
        private const R:Number = EffectorUtils.LUM_R;
        private const G:Number = EffectorUtils.LUM_G;
        private const B:Number = EffectorUtils.LUM_B;

        private const MATRIX:Array = [
            R, G, B, 0, 0,
            R, G, B, 0, 0,
            R, G, B, 0, 0,
            0, 0, 0, 1, 0
        ];
        private const FILTER:ColorMatrixFilter = new ColorMatrixFilter(MATRIX);
        
        private const ZERO_POINT:Point = EffectorUtils.ZERO_POINT;
      
        /*
         * 効果適用
         * @param    value    効果対象 BitmapData
         */
        public function applyEffect(value:BitmapData):BitmapData {
            value.applyFilter(value, value.rect, ZERO_POINT, FILTER);
            return value;
        }
    }
//}

//package aquioux.display.bitmapDataEffector {
    import flash.display.BitmapData;
    import flash.geom.Point;
    /**
     * paletteMap による BitmapData の減色
     * 「実践画像処理入門」 培風館 内村圭一・上瀧剛 P16 「2.5 濃度値の量子化による減色処理」
     * @author YOSHIDA, Akio (Aquioux)
     */
    /*public*/ class Posterize implements IEffector {
        /*
         * 減色の段階
         * @param    value    段階
         */
        public function set degree(value:uint):void {
            // value の有効範囲は 2 ~ 256
            if (value <   2) value =   2;
            if (value > 256) value = 256;
            
            if (_gradation) {
                _gradation.fixed = false;
                _gradation.length = 0;
            } else {
                _gradation = new Vector.<uint>();
            }

            var prevVal:uint = 0xFF;
            for (var i:int = 0; i < 256; i++) {
                var val:uint = uint(i / (256 / value)) * 255 / (value - 1);
                rArray_[i] = val << 16;
                gArray_[i] = val <<  8;
                bArray_[i] = val;
                
                if (prevVal != val) {
                    _gradation.push(val);
                    prevVal = val;
                }
            }
            _gradation.fixed = true;
        }
        
        // 減色化によって計算された gradation の値を格納する Vector
        // degree を set することではじめて有効になる
        // length は degree になる
        public function get gradation():Vector.<uint> { return _gradation; }
        private var _gradation:Vector.<uint>;

        // paletteMap の引数となる各 Channel 用の Array
        private var rArray_:Array = [];
        private var gArray_:Array = [];
        private var bArray_:Array = [];
        
        private const ZERO_POINT:Point = EffectorUtils.ZERO_POINT;
        
        /*
         * コンストラクタ
         */
        public function Posterize() {
            degree = 8;    // degree のデフォルト
        }
        
        /*
         * 効果適用
         * @param    value    効果対象 BitmapData
         */
        public function applyEffect(value:BitmapData):BitmapData {
            value.paletteMap(value, value.rect, ZERO_POINT, rArray_, gArray_, bArray_);
            return value;
        }
    }
//}

//package aquioux.display.bitmapDataEffector {
    import flash.display.BitmapData;
    import flash.filters.ConvolutionFilter;
    import flash.geom.Point;
    /**
     * ConvolutionFilter によるエッジ検出 2次微分
     * 「OpenGL+GLSLによる画像処理プログラミング」 工学社 酒井幸市 P104 「5.1 差分フィルタ」
     * 「C言語で学ぶ実践画像処理」 オーム社 井上誠喜・他 P47 「4.7 ラプラシアンとゼロ交差により輪郭線を求める」
     * http://msdn.microsoft.com/ja-jp/academic/cc998604.aspx
     * @author YOSHIDA, Akio (Aquioux)
     */
    /*public*/ class Edge2nd implements IEffector {
        private const MATRIX:Array = [
            0,  1, 0,
            1, -4, 1,
            0,  1, 0
        ];
        private const FILTER:ConvolutionFilter = new ConvolutionFilter(3, 3, MATRIX);

        private const ZERO_POINT:Point = EffectorUtils.ZERO_POINT;

        /*
         * 効果適用
         * @param    value    効果対象 BitmapData
         */
        public function applyEffect(value:BitmapData):BitmapData {
            value.applyFilter(value, value.rect, ZERO_POINT, FILTER);
            return value;
        }
    }
//}

//package aquioux.display.bitmapDataEffector {
    import flash.display.BitmapData;
    /**
     * BitmapDataEffector 用 interface
     * @author YOSHIDA, Akio (Aquioux)
     */
    /*public*/ interface IEffector {
        function applyEffect(value:BitmapData):BitmapData;
    }
//}

//package aquioux.display.bitmapDataEffector {
    import flash.geom.Point;
    /**
     * bitmapDataEffector パッケージ内のクラスで共通に使う定数など
     * @author YOSHIDA, Akio (Aquioux)
     */
    /*public*/ class EffectorUtils {
        // BitmapData が備える各種メソッドの destPoint 用
        static public const ZERO_POINT:Point = new Point(0, 0);
        
        // グレイスケール用の各チャンネルの重みづけ
        // 単純な平均
        //static public const LUM_R:Number = 1/3;
        //static public const LUM_G:Number = 1/3;
        //static public const LUM_B:Number = 1/3;

        // NTSC系加重平均法(YIQ,YCbCr も同じ)
        static public const LUM_R:Number = 0.298912;
        static public const LUM_G:Number = 0.586611;
        static public const LUM_B:Number = 0.114478;
        
        // HDTV
        //static public const LUM_R:Number = 0.2126;
        //static public const LUM_G:Number = 0.7152;
        //static public const LUM_B:Number = 0.0722;
    }
//}


/**
 * Copyright nicoptere ( http://wonderfl.net/user/nicoptere )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/4cr7
 */
/**
 * Copyright s26 ( http://wonderfl.net/user/s26 )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/wlLw
 */
import flash.display.Graphics;
import flash.geom.Point;
class Delaunay    
{
    static public var EPSILON:Number = Number.MIN_VALUE;
    static public var SUPER_TRIANGLE_RADIUS:Number = 1000000000;
    private var indices:Vector.<int>;
    private var circles:Vector.<Number>;
    public function compute( points:Vector.<Point> ):Vector.<int>
    {
        var nv:int = points.length;
        if (nv < 3) return null;
        var d:Number = SUPER_TRIANGLE_RADIUS;
        points.push(     new Point( 0, -d ), new Point( d, d ), new Point( -d, d )    );
        indices = Vector.<int>( [ points.length-3, points.length-2, points.length-1 ] );
        circles = Vector.<Number>( [ 0, 0, d ] );
        var edgeIds:Vector.<int> = new Vector.<int>();
        var i:int, j:int, k:int, id0:int, id1:int, id2:int;
        for ( i = 0; i < nv; i++)
        {
            for ( j = 0; j < indices.length; j+=3 )
            {
                if (     circles[ j + 2 ] > EPSILON         &&         circleContains( j, points[ i ] )    )
                {
                    id0 = indices[ j ];
                    id1 = indices[ j + 1 ];
                    id2 = indices[ j + 2 ];
                    edgeIds.push( id0, id1, id1, id2, id2, id0 );
                    indices.splice( j, 3 );
                    circles.splice( j, 3 );
                    j -= 3;
                }
            }
            for ( j = 0; j < edgeIds.length; j+=2 )
            {
                for ( k = j + 2; k < edgeIds.length; k+=2 )
                {
                    if(    (    edgeIds[ j ] == edgeIds[ k ] && edgeIds[ j + 1 ] == edgeIds[ k + 1 ]    )
                    ||    (    edgeIds[ j + 1 ] == edgeIds[ k ] && edgeIds[ j ] == edgeIds[ k + 1 ]    )    )
                    {
                        edgeIds.splice( k, 2 );
                        edgeIds.splice( j, 2 );
                        j -= 2;
                        k -= 2;
                        if ( j < 0 ) break;
                        if ( k < 0 ) break;
                    }
                }
            }
            for ( j = 0; j < edgeIds.length; j+=2 )
            {
                indices.push( edgeIds[ j ], edgeIds[ j + 1 ], i );
                computeCircle( points, edgeIds[ j ], edgeIds[ j + 1 ], i );
            }
            edgeIds.length = 0;
            
        }
        id0 = points.length - 3;
        id1 = points.length - 2;
        id2 = points.length - 1;
        for ( i = 0; i < indices.length; i+= 3 )
        {
            if ( indices[ i ] == id0 || indices[ i ] == id1 || indices[ i ] == id2 
            ||     indices[ i + 1 ] == id0 || indices[ i + 1 ] == id1 || indices[ i + 1 ] == id2 
            ||     indices[ i + 2 ] == id0 || indices[ i + 2 ] == id1 || indices[ i + 2 ] == id2 )
            {
                indices.splice( i, 3 );
                i-=3;
                continue;
            }
        }
        points.pop();
        points.pop();
        points.pop();
        return indices;
    }
    
    private function circleContains( circleId:int, p:Point ):Boolean 
    {
        var dx:Number = circles[ circleId ] - p.x;
        var dy:Number = circles[ circleId + 1 ] - p.y;
        return circles[ circleId + 2 ] > dx * dx + dy * dy;
    }
    
    private function computeCircle( points:Vector.<Point>, id0:int, id1:int, id2:int ):void
    {
        var p0:Point = points[ id0 ];
        var p1:Point = points[ id1 ];
        var p2:Point = points[ id2 ];
        var A:Number = p1.x - p0.x;
        var B:Number = p1.y - p0.y;
        var C:Number = p2.x - p0.x;
        var D:Number = p2.y - p0.y;
        var E:Number = A * (p0.x + p1.x) + B * (p0.y + p1.y);
        var F:Number = C * (p0.x + p2.x) + D * (p0.y + p2.y);
        var G:Number = 2.0 * (A * (p2.y - p1.y) - B * (p2.x - p1.x));
        var x:Number = (D * E - B * F) / G;
        circles.push( x );
        var y:Number = (A * F - C * E) / G;
        circles.push( y );
        x -= p0.x;
        y -= p0.y;
        circles.push( x * x + y * y );
    }
    
    public function render(graphics:Graphics, points:Vector.<Point>, indices:Vector.<int>,colorList:Vector.<uint>):void
    {
        var id0:uint, id1:uint, id2:uint;
        for ( var i:int = 0; i < indices.length; i+=3 ) 
        {
            id0 = indices[ i ];
            id1 = indices[ i + 1 ];
            id2 = indices[ i + 2 ];
            var cntX:int = int((points[id0].x + points[id1].x + points[id2].x) / 3);
            var cntY:int = int((points[id0].y + points[id1].y + points[id2].y) / 3);
            graphics.beginFill(colorList[cntY * 465 + cntX]);
            graphics.moveTo( points[ id0 ].x, points[ id0 ].y );
            graphics.lineTo( points[ id1 ].x, points[ id1 ].y );
            graphics.lineTo( points[ id2 ].x, points[ id2 ].y );
            graphics.lineTo( points[ id0 ].x, points[ id0 ].y );
            graphics.endFill();
        }
    }
}