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; } } } } 自動新技術化アルゴリズムになる予定だった何か