Beautifl - Flash Gallery

Thumbnail : 自動新技術化アルゴリズムになる予定だった何か
自動新技術化アルゴリズムになる予定だった何か
beinteractive 2010-01-09 MIT License

再生するにはFlash Playerが必要です。デスクトップのブラウザでご覧ください。

package
{
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.Loader;
	import flash.display.LoaderInfo;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.filters.BlurFilter;
	import flash.filters.ColorMatrixFilter;
	import flash.filters.GlowFilter;
	import flash.geom.Matrix;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import flash.net.FileReference;
	import flash.utils.ByteArray;
	import frocessing.color.ColorHSV;
	
	/**
	 * 自動新技術化アルゴリズムになる予定だった何か.
	 *
	 * もう飽きたので誰か夢を叶えてください。
	 *
	 * - ステージクリックで画像ファイルを選択出来ます
	 * - 肌色以外のところをいい感じに隠してくれ (たらいいなぁ…) ます
	 * - 処理時間かかります
	 */
	public class CheckmateFinal extends Sprite
	{
		//----------------------------------------
		// Constatns
		//----------------------------------------
		
		private static const YIQFILTER:ColorMatrixFilter = new ColorMatrixFilter([
			0.0, 0.0, 0.0, 0.0, 0.0,
			0.0, 0.0, 0.0, 0.0, 0.0,
			0.596, -0.274, -0.322, 0.0, 0.0,
			0.0, 0.0, 0.0, 1.0, 0.0
		]);
		private static const BLURFILTER:BlurFilter = new BlurFilter(8, 8);
		private static const MASKBLURFILTER:BlurFilter = new BlurFilter(18, 18);
		
		private static const ZERO:Point = new Point(0, 0);
		
		private static const SKIN_THRESHOLD_MIN_I:uint = 20;
		private static const SKIN_THRESHOLD_MAX_I:uint = 65;
		
		private static const DIVIDE_MAX_LEVEL:uint = 7;
		
		//----------------------------------------
		// Constructor
		//----------------------------------------
		
		public function CheckmateFinal()
		{
			Wonderfl.capture_delay(20);
			setupBackground();
			setupEventHandler();
		}
		
		//----------------------------------------
		// Properties
		//----------------------------------------
		
		private var _file:FileReference;
		private var _displayBitmap:Bitmap;
		
		//----------------------------------------
		// Initializing
		//----------------------------------------
		
		private function setupBackground():void
		{
			var bg:Sprite = new Sprite();
			bg.graphics.beginFill(0xffffff);
			bg.graphics.drawRect(0, 0, stage.stageWidth, stage.stageHeight);
			bg.graphics.endFill();
			addChild(bg);
		}
		
		private function setupEventHandler():void
		{
			stage.addEventListener(MouseEvent.CLICK, stageClickHandler);
		}
		
		//----------------------------------------
		// Stage Event Handlers
		//----------------------------------------
		
		private function stageClickHandler(e:MouseEvent):void
		{
			selectImageFile();
		}
		
		//----------------------------------------
		// File Selection
		//----------------------------------------
		
		private function selectImageFile():void
		{
			_file = new FileReference();
			_file.addEventListener(Event.SELECT, fileSelectHandler);
			_file.browse();
		}
		
		//----------------------------------------
		// FileReference Event Handlers
		//----------------------------------------
		
		private function fileSelectHandler(e:Event):void
		{
			_file.addEventListener(Event.COMPLETE, fileLoadCompleteHandler);
			_file.load();
		}
		
		private function fileLoadCompleteHandler(e:Event):void
		{
			processImageData(_file.data);
			_file = null;
		}
		
		//----------------------------------------
		// Image Processing
		//----------------------------------------
		
		private function processImageData(bytes:ByteArray):void
		{
			var loader:Loader = new Loader();
			loader.contentLoaderInfo.addEventListener(Event.COMPLETE, imageDataLoadCompleteHandler);
			loader.loadBytes(bytes);
		}
		
		private function imageDataLoadCompleteHandler(e:Event):void
		{
			var loaderInfo:LoaderInfo = e.target as LoaderInfo;
			var matrix:Matrix = getScaleMatrixToFitStage(loaderInfo.width, loaderInfo.height);
			var image:BitmapData = new BitmapData(stage.stageWidth, stage.stageHeight, false, 0x000000);
			image.draw(loaderInfo.content, matrix);
			makeImaginationInterpolation(image);
			displayImage(image);
		}
		
		private function getScaleMatrixToFitStage(w:uint, h:uint):Matrix
		{
			var scale:Number = 1.0;
			
			if (w > stage.stageWidth || h > stage.stageHeight) {
				if (w > h) {
					scale = stage.stageWidth / w;
				}
				else {
					scale = stage.stageHeight / h;
				}
			}
			
			var x:Number = (stage.stageWidth - (w * scale)) / 2.0;
			var y:Number = (stage.stageHeight - (h * scale)) / 2.0;
			
			var matrix:Matrix = new Matrix();
			matrix.scale(scale, scale);
			matrix.translate(x, y);
			
			return matrix;
		}
		
		private function makeImaginationInterpolation(bitmapData:BitmapData):void
		{
			// Create working bitmapdata
			var aBitmapData:BitmapData = bitmapData.clone();
			
			// YIQ Convertion
			aBitmapData.applyFilter(aBitmapData, aBitmapData.rect, ZERO, YIQFILTER);
			aBitmapData.applyFilter(aBitmapData, aBitmapData.rect, ZERO, BLURFILTER);
			
			// Extract Skin Color
			aBitmapData.threshold(aBitmapData, aBitmapData.rect, ZERO, '>', SKIN_THRESHOLD_MAX_I, 0xff000000, 0x000000ff);
			aBitmapData.threshold(aBitmapData, aBitmapData.rect, ZERO, '<', SKIN_THRESHOLD_MIN_I, 0xff000000, 0x000000ff);
			aBitmapData.threshold(aBitmapData, aBitmapData.rect, ZERO, '!=', 0x000000, 0xffffffff, 0x00ffffff);
			
			// Reduce Noise
			aBitmapData.applyFilter(aBitmapData, aBitmapData.rect, ZERO, BLURFILTER);
			aBitmapData.threshold(aBitmapData, aBitmapData.rect, ZERO, '<', 0x555555, 0xff000000, 0x00ffffff);
			aBitmapData.threshold(aBitmapData, aBitmapData.rect, ZERO, '!=', 0x000000, 0xffffffff, 0x00ffffff);
			
			// Divide Image
			var dividedRects:Array = [];
			divideImage(aBitmapData, aBitmapData.clone(), aBitmapData.rect, 0, dividedRects);
			
			// Make mask
			var mask:Sprite = new Sprite();
			var color:uint = new ColorHSV(Math.random() * 360, 0.8, 0.8).value;
			var l:uint = dividedRects.length;
			mask.graphics.beginFill(color);
			for (var i:uint = 0; i < l; ++i) {
				var r:Rectangle = dividedRects[i];
				mask.graphics.drawRect(r.x, r.y, r.width, r.height);
			}
			mask.graphics.endFill();
			
			var maskBitmapData:BitmapData = new BitmapData(bitmapData.width,  bitmapData.height, true, 0x00000000);
			maskBitmapData.draw(mask);
			maskBitmapData.applyFilter(maskBitmapData, maskBitmapData.rect, ZERO, new GlowFilter(color, 1.0, 24, 24, 24));
			bitmapData.copyPixels(maskBitmapData, maskBitmapData.rect, ZERO, maskBitmapData, ZERO, true);
		}
		
		private function divideImage(bitmapData:BitmapData, tempBitmapData:BitmapData, rect:Rectangle, level:uint, dividedRects:Array):void
		{
			if (level >= DIVIDE_MAX_LEVEL) {
				return;
			}
			
			tempBitmapData.fillRect(tempBitmapData.rect, 0xff00ffff);
			tempBitmapData.copyPixels(bitmapData, rect, ZERO);
			
			var whiteBounds:Rectangle = tempBitmapData.getColorBoundsRect(0xffffff, 0xffffff);
			
			if (whiteBounds.isEmpty()) {
				if (rect.width <= stage.stageWidth / 8 && rect.height <= stage.stageHeight / 8) {
					if (rect.width >= stage.stageWidth / 128 && rect.height >= stage.stageHeight / 128) {
						dividedRects.push(rect);
					}
				}
				return;
			}
			
			var blackBounds:Rectangle = tempBitmapData.getColorBoundsRect(0xffffff, 0x000000);
			
			if (blackBounds.isEmpty()) {
				return;
			}
			
			
			var x:Number = blackBounds.x + rect.x;
			var y:Number = blackBounds.y + rect.y;
			var w:Number = blackBounds.width / 2.0;
			var h:Number = blackBounds.height / 2.0;
			
			if (w < 1.0 || h < 1.0) {
				return;
			}
			
			var lv:uint = level + 1;
			
			divideImage(bitmapData, tempBitmapData, new Rectangle(x, y, w, h), lv, dividedRects);
			divideImage(bitmapData, tempBitmapData, new Rectangle(x + w, y, w, h), lv, dividedRects);
			divideImage(bitmapData, tempBitmapData, new Rectangle(x, y + h, w, h), lv, dividedRects);
			divideImage(bitmapData, tempBitmapData, new Rectangle(x + w, y + h, w, h), lv, dividedRects);
		}
		
		//----------------------------------------
		// Display
		//----------------------------------------
		
		private function displayImage(image:BitmapData):void
		{
			if (_displayBitmap == null) {
				_displayBitmap = addChild(new Bitmap(image)) as Bitmap;
			}
			else {
				_displayBitmap.bitmapData = image;
			}
		}
	}
}