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

[WebCam]エッジの方向

http://aquioux.blog48.fc2.com/blog-entry-680.html の内容を WebCam に適用したものです。
/**
 * Copyright Aquioux ( http://wonderfl.net/user/Aquioux )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/rAYK
 */

// http://aquioux.blog48.fc2.com/blog-entry-680.html の内容を WebCam に適用したものです。
package {
	import flash.display.Sprite;
	import flash.events.Event;
	/**
	 * 各種フィルタを使ったウェブカム画像の加工
	 * @author YOSHIDA, Akio (Aquioux)
	 */
	[SWF(width = "465", height = "465", frameRate = "30", backgroundColor = "#FFFFFF")]
	
	public class Main extends Sprite {
		
		public function Main() {
			Wonderfl.capture_delay(20);

			// Model を生成
			try {
				var model:Model = new Model(stage);
			} catch (err:Error) {
				trace(err.message);
				return;
			}

			// View を生成
			var view:View = new View(model);
			addChild(view);
			view.commands = model.commands;
			
			// 開始
			model.start();
		}
	}
}


	import flash.display.BitmapData;
	import flash.display.GraphicsPathCommand;
	import flash.display.Stage;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.filters.ConvolutionFilter;
	import flash.geom.Matrix;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import flash.media.Camera;
	import flash.media.Video;
	/**
	 * Web Camera の映像にエフェクトをかける(MVC の Model)
	 * エフェクトロジックは effector クラスとして外部で定義する
	 * @author YOSHIDA, Akio (Aquioux)
	 */
	class Model extends EventDispatcher {
		// --------------------------------------------------
		// 定数
		// --------------------------------------------------
		// 描画間隔
		private const INTERVAL:uint = 6;
		// エッジ検出の強さ
		private const DEPTH:Number = 5.0;

		
		// --------------------------------------------------
		// View へ渡すデータ(プロパティ)
		// --------------------------------------------------
		public function get commands():Vector.<int> { return _commands; }
		private var _commands:Vector.<int>;

		public function get data():Vector.<Number> { return _data; }
		private var _data:Vector.<Number>;

		
		// --------------------------------------------------
		// 外部との通信をおこなうメソッド
		// --------------------------------------------------
		/**
		 * 対 View 用メソッド
		 * このメソッドの終了時にイベントを発行するので、View との通信手段となる
		 * @private
		 */
		// ConvolutionFilter 用マトリクス
		private const HORISON_MATRIX:Array = [
			-1,     0, 1,
			-DEPTH, 0, DEPTH,
			-1,     0, 1
		];
		private const VERTICAL_MATRIX:Array = [
			-1, -DEPTH, -1,
			 0,  0,      0,
			 1,  DEPTH,  1
		];
		private const HORISON_FILTER:ConvolutionFilter  = new ConvolutionFilter(3, 3, HORISON_MATRIX);
		private const VERTICAL_FILTER:ConvolutionFilter = new ConvolutionFilter(3, 3, VERTICAL_MATRIX);
		private const ZERO_POINT:Point = new Point(0, 0);
		private function update():void {
			bmd.draw(video, matrix);	// ウェブカム映像取り込み
			grayscale.applyEffect(bmd);	// グレイスケール適用
			cloneBmd = bmd.clone();		// 複製
			// ConvolutionFilter 適用
			bmd.applyFilter(bmd, rect, ZERO_POINT, HORISON_FILTER);
			cloneBmd.applyFilter(cloneBmd, rect, ZERO_POINT, VERTICAL_FILTER);
			// 平滑化適用
			smoothing.applyEffect(bmd);
			smoothing.applyEffect(cloneBmd);
			
			// View 用データ data の決定
			var cnt:uint = 0;
			for (var i:int = 0; i < numY; i++) {
				for (var j:int = 0; j < numX; j++) {
					var startX:uint = j * INTERVAL;
					var startY:uint = i * INTERVAL;
					var colorX:uint = bmd.getPixel(startX, startY);
					var colorY:uint = cloneBmd.getPixel(startX, startY);
					var xx:uint = colorX & 0xFF;
					var yy:uint = colorY & 0xFF;
					var radian:Number = Math.atan2(yy, xx);
					var strength:Number = Math.sqrt(xx * xx + yy * yy);
					var endX:Number = Math.cos(radian) * strength / INTERVAL + startX;
					var endY:Number = Math.sin(radian) * strength / INTERVAL + startY;
					_data[cnt * 4]     = startX;
					_data[cnt * 4 + 1] = startY;
					_data[cnt * 4 + 2] = endX;
					_data[cnt * 4 + 3] = endY;
					cnt++;
				}
			}
			
			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 bmd:BitmapData;
		private var cloneBmd:BitmapData;
		private var matrix:Matrix;
		private var rect:Rectangle;
		private var numX:uint;
		private var numY:uint;
		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;
			
			bmd      = new BitmapData(cameraWidth, cameraHeight, false);
			cloneBmd = bmd.clone();
			matrix   = new Matrix( -1, 0, 0, 1, cameraWidth, 0);
			rect     = bmd.rect;
			
			numX = cameraWidth  / INTERVAL;
			numY = cameraHeight / INTERVAL;
			// View 用データの生成
			_commands = new Vector.<int>(numX * numY * 2, true);
			_data     = new Vector.<Number>(numX * numY * 4, true);
			// View 用データ commands の決定
			var n:uint = _commands.length / 2;
			for (var i:int = 0; i < n; i++) {
				_commands[i * 2]     = GraphicsPathCommand.MOVE_TO;
				_commands[i * 2 + 1] = GraphicsPathCommand.LINE_TO;
			}
			
			// カメラ
			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("カメラがありません。");
			}
		}
		
		/**
		 * 処理開始
		 */
		private var grayscale:EffectorGrayScale;
		private var smoothing:EffectorSmoothing;
		public function start():void {
			grayscale = new EffectorGrayScale();
			smoothing = new EffectorSmoothing();
			smoothing.strength = 8;
			stage.addEventListener(Event.ENTER_FRAME, enterFrameHandler);
		}
		/**
		 * イベントハンドラ
		 * @private
		 */
		private function enterFrameHandler(event:Event):void {
			update();
		}
	}


	import flash.display.Graphics;
	import flash.display.GraphicsEndFill;
	import flash.display.GraphicsPath;
	import flash.display.GraphicsPathCommand;
	import flash.display.GraphicsSolidFill;
	import flash.display.GraphicsStroke;
	import flash.display.IGraphicsData;
	import flash.display.Sprite;
	import flash.events.Event;
	/**
	 * Web Camera のスクリーン(MVC の View)
	 * @author YOSHIDA, Akio (Aquioux)
	 */
	class View extends Sprite {
		/**
		 * コンストラクタ
		 * @param	model	Model
		 */
		private var model:Model;
		public function View(model:Model) {
			init();
			this.model = model;
			this.model.addEventListener(Event.CHANGE, changeHandler);
		}
		
		private var graphicsData:Vector.<IGraphicsData>;
		private var path:GraphicsPath;
		private function init():void{
			var stroke:GraphicsStroke = new GraphicsStroke();
			stroke.thickness = 0;
			stroke.fill = new GraphicsSolidFill(0x000000);

			var commands:Vector.<int> = new Vector.<int>();
			var data:Vector.<Number> = new Vector.<Number>();
			path = new GraphicsPath(commands, data);

			var endfill:GraphicsEndFill = new GraphicsEndFill();

			graphicsData = new Vector.<IGraphicsData>();
			graphicsData.push(stroke);
			graphicsData.push(path);
			graphicsData.push(endfill);
		}
		
		public function set commands(commands:Vector.<int>):void {
			path.commands = commands;
		}
		
		/**
		 * Model との通信手段
		 * @param	event	発生したイベント
		 */
		private function changeHandler(event:Event):void {
			// Model からデータを受け取り、視覚化
			path.data = model.data;
			
			var g:Graphics = this.graphics;
			g.clear();
			g.drawGraphicsData(graphicsData);
		}
	}


	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.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.6, 0.1, 0, 0,
			0.3, 0.6, 0.1, 0, 0,
			0.3, 0.6, 0.1, 0, 0,
			0,   0,   0,   1, 0
		];
		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;
		}
	}