似而非キャニーエッジ
似而非キャニーエッジ Fake canny edge
解説:http://aquioux.blog48.fc2.com/blog-entry-693.html
@author YOSHIDA, Akio (Aquioux)
/**
* Copyright Aquioux ( http://wonderfl.net/user/Aquioux )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/7GCV
*/
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;
}
}