眠れないからWebcamAsciify改良(悪)【エッジ検出】
← この顔ムカツク
http://n00dle.web.fc2.com/webcamasciifyedg.html
前回の"WebcamAsciify.as"をスコープ中心に修正して、サブクラスを書いた。Webカメラの画像をエッジ検出してからアスキー文字に置き換えている。だいぶ被写体が認識し易くなった。反面、出現するアスキー文字が少なくなってアスキーアートの面白みがなくなっている。
エッジ検出した段階の画像は以下(を白黒反転させたもの)のようになっているわけだ。
AS3 Video Edge and Motion Detect
http://www.laserpirate.com/as3edgeandmotion/
参考
- AS3で画像処理入門(1) - flashrod
http://d.hatena.ne.jp/flashrod/20061015#1160910969
グレースケール化とエッジ検出はまんまここから。とりあえずコード読みながら仕組みを考えてみる。"ラプラシアン 2 を用いた2 次微分"??んががが…
ソースは以下
WebcamAsciifyEdg.as -> WebcamAsciify.as
WebcamAsciifyEdg.as
package { import flash.display.*; import flash.events.*; import flash.filters.*; import flash.geom.*; import flash.media.*; import flash.text.*; [SWF(backgroundColor = "0x000000", frameRate = "30")] public class WebcamAsciifyEdg extends WebcamAsciify { public function WebcamAsciifyEdg() { } override public function entKey(event:KeyboardEvent):void { swit = swit; //スイッチ無効化 } override public function tick(event:Event):void { textField.text = ""; var output:String = ""; bd = new BitmapData(picW, picH); bd.draw(video, matrix); bd = edge(bd).clone(); pixels = new Array(); for (var y:uint = 0; y < picH; y++) { var row:Array=new Array(); for (var x:uint = 0; x < picW; x++) { var sampledColor:Number=bd.getPixel(x, y); row.push(sampledColor); } pixels.push(row); } for (var y:uint = 0; y < picH; y++) { for (var x:uint = 0; x < picW; x++) { var color:Number=pixels[y][x]; var rgb:Object=makeRgb(color); var avg:Number=(rgb.r + rgb.g + rgb.b)/3; var s:String = getCharFromColor(avg); output += s + s + s + s; } output+="\n\n"; } textField.appendText(output); } private function edge(bitmapData:BitmapData):BitmapData { var d:BitmapData = new BitmapData(bitmapData.width, bitmapData.height); var rec:Rectangle = new Rectangle(0, 0, bitmapData.width, bitmapData.height); var point:Point = new Point(0, 0); //グレースケール化 d.applyFilter(bitmapData, rec, point, new ColorMatrixFilter([1/3, 1/3, 1/3, 0, 0, 1/3, 1/3, 1/3, 0, 0, 1/3, 1/3, 1/3, 0, 0, 0, 0, 0, 255, 0])); //エッジ検出 var l:Array = [-1, -1, -1, -1, +8, -1, -1, -1, -1]; d.applyFilter(bitmapData, rec, point,new ConvolutionFilter(3, 3, l)); return d; } } }
WebcamAsciify.as
package { import flash.display.*; import flash.events.*; import flash.geom.*; import flash.media.*; import flash.text.*; [SWF(backgroundColor = "0x000000", frameRate = "30")] public class WebcamAsciify extends Sprite { public var video:Video; public var textField:TextField; public var textFormat:TextFormat; public var bd:BitmapData; public var picW:uint; public var picH:uint; public var matrix:Matrix; public var pixels:Array; public var asciiArray:Array; public var swit:Boolean; public static const videoWidth:int = 320, videoHeight:int = 240, pixelSize:Number =5; public function WebcamAsciify() { stage.scaleMode = StageScaleMode.NO_SCALE; stage.align = StageAlign.TOP_LEFT; swit = new Boolean (true); textFormat = new TextFormat(); textFormat.size = 6; textFormat.color = 0x00ff00; textFormat.font = "Courier New" sampleChars(); //Ascii文字テーブル生成 //Webカメラのビデオサイズを縮小して処理するための変数設定 picW = videoWidth / pixelSize; picH = videoHeight / pixelSize; matrix = new Matrix(); matrix.scale(1/pixelSize, 1/pixelSize); var camera : Camera = Camera.getCamera(); video = new Video(videoWidth, videoHeight); video.attachCamera(camera); textField = new TextField(); textField.autoSize = "left"; textField.selectable = false; textField.defaultTextFormat = textFormat; textField.antiAliasType=AntiAliasType.ADVANCED; addChild(textField); stage.addEventListener(KeyboardEvent.KEY_DOWN, entKey); addEventListener(Event.ENTER_FRAME, tick); } public function entKey(event:KeyboardEvent):void { swit = !(swit); } //メインループ public function tick(event:Event):void { textField.text = ""; var output:String = ""; bd = new BitmapData(picW, picH); bd.draw(video, matrix); pixels = new Array(); for (var y:uint = 0; y < picH; y++) { var row:Array=new Array(); for (var x:uint = 0; x < picW; x++) { var sampledColor:Number=bd.getPixel(x, y); row.push(sampledColor); } pixels.push(row); } for (var y:uint = 0; y < picH; y++) { for (var x:uint = 0; x < picW; x++) { var color:Number=pixels[y][x]; var rgb:Object=makeRgb(color); var avg:Number=(rgb.r + rgb.g + rgb.b)/3; var s:String = getCharFromColor(avg); output += s + s + s + s; } output+="\n\n"; } textField.appendText(output); } private function sampleChars():void { asciiArray = new Array(); var list:String = " .,;-~_+<>i!lI?/|)(1}{][rcvunxzjftLCJUYXZO0Qoahkbdpqwm*WMB8&%$#@"; asciiArray = list.split(""); } public function makeRgb(hex:Number):Object { var r:Number; var g:Number; var b:Number; r = (0xFF0000 & hex) >> 16; g = (0x00FF00 & hex) >> 8; b = (0x0000FF & hex); return {r:r,g:g,b:b}; } public function getCharFromColor(arg:uint):String { var i:uint=0; var _char:String; //"A"か" "で表現 if (swit == false) { if ( arg < Math.floor(256/3) ) { _char = "A"; } else { _char = " "; } } //複数のAscii文字で表現 if (swit == true) { for (var col:Number = 0; col < 256; col += Math.floor(256/asciiArray.length) ) { if (arg<=col) { _char = asciiArray[i]; } else { i++; } } } return _char; } } }