[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;
}
}