forked from: 似而非キャニーエッジ

by mapache forked from 似而非キャニーエッジ (diff: 411)
似而非キャニーエッジ Fake canny edge
解説:http://aquioux.blog48.fc2.com/blog-entry-693.html
@author YOSHIDA, Akio (Aquioux)
♥0 | Line 227 | Modified 2010-10-05 23:43:38 | MIT License
play

ActionScript3 source code

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

// forked from Aquioux's 似而非キャニーエッジ
package {
    import com.bit101.components.Label;
    import com.bit101.components.Slider;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.geom.ColorTransform;
    /**
     * 似而非キャニーエッジ Fake canny edge
     * 解説:http://aquioux.blog48.fc2.com/blog-entry-693.html
     * @author YOSHIDA, Akio (Aquioux)
     */
    [SWF(width = "465", height = "465", frameRate = "30", backgroundColor = "#000000")]
    
    public class Main extends Sprite {
        
        private var smoothing:EffectorSmoothing;
        private var binarization:EffectorBinarization;

        private var sliderBinarization:Slider;
        private var labelBinarization:Label;
        private var prevThreshold:Number;
        
        private var sliderSmoothing:Slider;
        private var labelSmoothing:Label;
        private var prevSmooth:Number;
        
        public function Main() {
            Wonderfl.capture_delay(20);

            // エフェクタ連鎖
            var chain:ChainEffectors = new ChainEffectors();
            smoothing    = new EffectorSmoothing();
            binarization = new EffectorBinarization();
            chain.addEffector(smoothing);
            chain.addEffector(binarization);
            chain.addEffector(new EffectorEdge());
        
            // Model を生成
            try {
                var model:Model = new Model(stage);
            } catch (err:Error) {
                trace(err.message);
                return;
            }

            // View を生成
            var view:View = new View(model);
            addChild(view);
            
            // Controller 1
            var min:uint = 0;
            var max:uint = 255;
            sliderBinarization = new Slider(Slider.HORIZONTAL, this, 0, 0, sliderBinarizationHandler);
            sliderBinarization.width = 200;
            sliderBinarization.setSliderParams(min, max, uint(max / 2));
            labelBinarization = new Label(this, sliderBinarization.width + 3, sliderBinarization.y - 3);
            labelBinarization.transform.colorTransform = new ColorTransform(0, 0, 0, 1, 255);
            sliderBinarizationHandler(null);
            
            // Controller 2
            min = 0;
            max = 16;
            sliderSmoothing    = new Slider(Slider.HORIZONTAL, this, 0, sliderBinarization.height + 5, sliderSmoothingHandler);
            sliderSmoothing.width = 200;
            sliderSmoothing.setSliderParams(min, max, 2);
            labelSmoothing = new Label(this, sliderSmoothing.width + 3, sliderSmoothing.y - 3);
            labelSmoothing.transform.colorTransform = new ColorTransform(0, 0, 0, 1, 255);
            sliderSmoothingHandler(null);
            
            // 開始
            model.chain = chain;
            model.start();
        }
        
        private function sliderBinarizationHandler(event:Event):void{
            var value:uint = (event) ? event.target.value : sliderBinarization.value;
            labelBinarization.text = "Threaold : " + String(value);
            
            if (prevThreshold != value) {
                binarization.threshold = value;
                prevThreshold = value;
            }
        }
        private function sliderSmoothingHandler(event:Event):void{
            var value:uint = (event) ? event.target.value : sliderSmoothing.value;
            labelSmoothing.text = "Smooth strength : " + String(value);
            
            if (prevSmooth != value) {
                smoothing.strength = value;
                prevSmooth = value;
            }
        }
    }
}


    import flash.display.BitmapData;
    import flash.display.Stage;
    import flash.events.Event;
    import flash.events.EventDispatcher;
    import flash.geom.Matrix;
    import flash.media.Camera;
    import flash.media.Video;
    /**
     * Web Camera の映像にエフェクトをかける(MVC の Model)
     * エフェクトロジックは effector クラスとして外部で定義する
     * @author YOSHIDA, Akio (Aquioux)
     */
    class Model extends EventDispatcher {
        // --------------------------------------------------
        // View へ渡すデータ(プロパティ)
        // --------------------------------------------------
        /**
         * 加工済みのカメラ画像
         */
        public function get data():BitmapData { return _data; }
        private var _data:BitmapData;
        
        // --------------------------------------------------
        // 外部から受け取るデータ(プロパティ)
        // --------------------------------------------------
        /**
         * エフェクタ連鎖
         */
        public function set chain(value:ChainEffectors):void {
            _chain = value;
        }
        private var _chain:ChainEffectors;
        
        // --------------------------------------------------
        // 外部との通信をおこなうメソッド
        // --------------------------------------------------
        /**
         * 対 View 用メソッド
         * このメソッドの終了時にイベントを発行するので、View との通信手段となる
         * @private
         */
        private function update():void {
            _data.draw(video, matrix);
            _data = _chain.applyEffect(_data);
            dispatchEvent(new Event(Event.CHANGE));
        }
        
        // --------------------------------------------------
        // その他のメソッド
        // --------------------------------------------------
        /**
         * コンストラクタ
         * コンストラクタの引数はステージとする。各種データはアクセサーによって取り込むものとする
         * @param    stage    ステージ
         */
        private var stage:Stage;

        // カメラが表示するサイズ
        private var cameraWidth:uint;
        private var cameraHeight:uint;
        // カメラ
        private var camera:Camera;
        private var video:Video;
        private var matrix:Matrix;
        public function Model(stage:Stage, cw:Number = 0, ch:Number = 0) {
            this.stage = stage;
            this.cameraWidth  = (cw == 0) ? stage.stageWidth  : cw;
            this.cameraHeight = (ch == 0) ? stage.stageHeight : ch;
            
            _data = new BitmapData(cameraWidth, cameraHeight, false);
            matrix = new Matrix( -1, 0, 0, 1, cameraWidth, 0);
            
            // カメラ
            camera = Camera.getCamera();
            if (camera) {
                // camera のセットアップ
                camera.setMode(cameraWidth, cameraHeight, stage.frameRate);
                // video のセットアップ
                video = new Video(cameraWidth, cameraHeight);
                video.attachCamera(camera);
            } else {
                throw new Error("カメラがありません。");
            }
        }
        
        /**
         * 処理開始
         * Event.ENTER_FRAME を使う場合、このメソッドを設定する。
         * Controller から通知されるイベントだけで処理する場合、このメソッドは不要。
         */
        public function start():void {
            stage.addEventListener(Event.ENTER_FRAME, enterFrameHandler);
        }
        /**
         * イベントハンドラ
         * @private
         */
        private function enterFrameHandler(event:Event):void {
            update();
        }
    }


    import flash.display.Bitmap;
    import flash.events.Event;
    /**
     * Web Camera のスクリーン(MVC の View)
     * @author YOSHIDA, Akio (Aquioux)
     */
    class View extends Bitmap {
        /**
         * コンストラクタ
         * @param    model    Model
         */
        private var model:Model;
        public function View(model:Model) {
            this.model = model;
            this.model.addEventListener(Event.CHANGE, changeHandler);
        }
        
        /**
         * Model との通信手段
         * @param    event    発生したイベント
         */
        private function changeHandler(event:Event):void {
            // Model からデータを受け取り、視覚化
            this.bitmapData = model.data;
        }
    }


    import flash.display.BitmapData;
    /**
     * エフェクタ連鎖
     * @author YOSHIDA, Akio (Aquioux)
     */
    class ChainEffectors {
        private var effectors:Array = [];
    
        /**
         * コンストラクタ
         * @param    model    Model
         */
        public function ChainEffectors() {
        }
        
        /**
         * エフェクタの追加
         * @param    effector    エフェクタ
         */
        public function addEffector(effector:AbstractEffector):void {
            effectors.push(effector);
        }
        
        /**
         * エフェクタの適用
         * @param    value    エフェクタをかける BitmapData
         */
        public function applyEffect(value:BitmapData):BitmapData {
            var n:uint = effectors.length;
            for(var i:int=0; i < n; i++){
                var effector:AbstractEffector = effectors[i];
                value = effector.applyEffect(value);
            }
            return value;
        }
    }


    import flash.display.BitmapData;
    import flash.geom.Point;
    /**
     * BitmapData エフェクト用抽象クラス
     * @author YOSHIDA, Akio
     */
    class AbstractEffector {
        /*
         * BitmapData.applyFilter で destPoint として使用する Point オブジェクト
         */
        protected const ZERO_POINT:Point = new Point(0, 0);
        
        /*
         * コンストラクタ
         */
        public function AbstractEffector() {}
        
        /*
         * 効果の適用
         * @param    value    効果をかける BitmapData
         */
        public function applyEffect(value:BitmapData):BitmapData {
            return effect(value);
        }
        
        /*
         * 効果内容、具体的なコードはサブクラスで定義する
         * @param    value    効果をかける BitmapData
         */
        protected function effect(value:BitmapData):BitmapData {
            return value;
        }
    }


    import flash.display.BitmapData;
    import flash.geom.Rectangle;
    /**
     * 二値化
     * @author YOSHIDA, Akio (Aquioux)
     */
    class EffectorBinarization extends AbstractEffector {
        public function set threshold(value:int):void {
            if (value < 0)   value = 0;
            if (value > 255) value = 255;
            _threshold = value;
        }
        private var _threshold:int = 127;    // 閾値:0 ~ 255
        
        private var cloneBitmapData:BitmapData;    // 一時処理用 BitmapData
        private var rect:Rectangle;
        
        /*
         * 二値化実行
         * @param    value    効果をかける BitmapData
         */
        override protected function effect(value:BitmapData):BitmapData {
            if (cloneBitmapData) cloneBitmapData.dispose();
            cloneBitmapData = new BitmapData(value.width, value.height);
            cloneBitmapData = value.clone();
            rect = cloneBitmapData.rect;
            value.fillRect(rect, 0xFF000000);
            value.threshold(cloneBitmapData, rect, ZERO_POINT, ">", _threshold, 0xFFFFFFFF, 0x000000FF, false);
            return value;
        }
    }


    import flash.display.BitmapData;
    import flash.display.BlendMode;
    import flash.filters.ConvolutionFilter;
    import flash.geom.Rectangle;
    /**
     * エッジ検出(ラプラシアン・オペレータ)
     * 参考: 「OpenGL+GLSLによる画像処理プログラミング」 工学社 酒井幸市 P107 「5.1.2 エッジ検出フィルタ」
     * @author YOSHIDA, Akio (Aquioux)
     */
    class EffectorEdge extends AbstractEffector {
        // ConvolutionFilter のマトリクス(ラプラシアン)
        private var laplacian:Array = [
            0,  1, 0,
            1, -4, 1,
            0,  1, 0
        ];

        /*
         * エッジ検出実行
         * @param    value    効果をかける BitmapData
         */
        override protected function effect(value:BitmapData):BitmapData {
            value.applyFilter(value, value.rect, ZERO_POINT, new ConvolutionFilter(3, 3, laplacian));
            return value;
        }
    }


    import flash.display.BitmapData;
    import flash.filters.ColorMatrixFilter;
    /**
     * 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)
     */
    class EffectorGrayScale extends AbstractEffector {
        // ColorMatrixFilter 用マトリクス
        private const GRAYSCALE_MATRIX:Array = [
            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.3375, 0.66375, 0.12375, 0, 0,        // 0.33*1.125, 0.59*1.125, 0.11*1.125
            0.3375, 0.66375, 0.12375, 0, 0,
            0.3375, 0.66375, 0.12375, 0, 0,
            */
            0,   0,    0,    1, 0
        ];
        // ColorMatrixFilter
        private const GRAYSCALE_FILTER:ColorMatrixFilter = new ColorMatrixFilter(GRAYSCALE_MATRIX);
        
        /*
         * グレイスケール実行
         * @param    value    効果をかける BitmapData
         */
        override protected function effect(value:BitmapData):BitmapData {
            value.applyFilter(value, value.rect, ZERO_POINT, GRAYSCALE_FILTER);
            return value;
        }
    }


    import flash.display.BitmapData;
    import flash.filters.BitmapFilterQuality;
    import flash.filters.BlurFilter;
    /**
     * BlurFilter による平滑化
     * @author YOSHIDA, Akio (Aquioux)
     */
    class EffectorSmoothing extends AbstractEffector {
        /*
         * ぼかしの量
         * @param    value    数値
         */
        public function set strength(value:Number):void {
            blurFilter.blurX = blurFilter.blurY = value;
        }
        /*
         * ぼかしの質
         * @param    value    数値
         */
        public function set quality(value:int):void {
            blurFilter.quality = value;
        }
        // ブラーフィルタ
        private var blurFilter:BlurFilter;


        public function EffectorSmoothing() {
            blurFilter = new BlurFilter(2, 2, BitmapFilterQuality.MEDIUM);
        }
        
        /*
         * 平滑化実行
         * @param    value    効果をかける BitmapData
         */
        override protected function effect(value:BitmapData):BitmapData {
            value.applyFilter(value, value.rect, ZERO_POINT, blurFilter);
            return value;
        }
    }