Webカメラの映像をアスキーアート化するAS3習作

20081023-j362sik3rwn4jku4mhwuh4aru6.render ⇔ 20081023-3mxi5nwbdmqfu163hd27fi676.render  → そして宇宙へ…

http://n00dle.web.fc2.com/webcamasciify.html
任意のキーを入力すると表示モードが変わります。もちろんMacFirefoxでは効かない。マウスイベントで処理すればよかった。


荒いなあ。Adobeのサンプルコードぐらいの精度にしたいんだが…。実態は、各ピクセルの明るさを文字列の長さで等分して、ドット数順に並んだAscii文字をそれぞれに割り振ってるだけだからか。一度Ascii文字をビットマップデータに描写してドット数を判定する箇所がプログラムに組み込むとうまく動かなくてめんどくさくなったから、Adobeのサンプルからドット数順に並んだ文字列を拝借した。素っ裸でコード書いてたんだけど、体も壁も白くてスクショ撮っても何がなんだか分からなかったから、しょうがなく黒いTシャツを着た。HMDに映して遊んでみる。


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
	{
		private var video:Video;
		private var textField:TextField;
		private var textFormat:TextFormat;
		private var bd:BitmapData;
		private var picW:uint;
		private var picH:uint;
		private var matrix:Matrix;
		private var pixels:Array;
		private var asciiArray:Array;
		private 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 (false); //描写モードのスイッチ
			
			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);
		}
		
		private function entKey(event:KeyboardEvent):void
		{
			swit = !(swit);
		}
		
		//メインループ
		private 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("");
		}
		
		private 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};
		}
		
		private 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;
		}
	}
}