forked from: 画像をフラクタル化 forked from: 単位面積あたりの偏差がナントカカントカ
forked from 画像をフラクタル化 forked from: 単位面積あたりの偏差がナントカカントカ (diff: 1)
ActionScript3 source code
/**
* Copyright _perfect ( http://wonderfl.net/user/_perfect )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/sdkB
*/
<?xml version = "1.0" encoding = "utf-8"?>
<!-- forked from undo's 画像をフラクタル化 forked from: 単位面積あたりの偏差がナントカカントカ -->
<!-- forked from undo's 単位面積あたりの偏差が小さい場合は赤く塗りつぶす forked from: pngで保存 -->
<!-- forked from undo's pngで保存 forked from: モザイク forked from: モザイクがうまくいかない -->
<!-- forked from undo's モザイク forked from: モザイクがうまくいかない -->
<!-- forked from undo's モザイクがうまくいかない -->
<mx:Application xmlns:mx = "http://www.adobe.com/2006/mxml" layout = "absolute" applicationComplete = "init();">
<mx:Script>
<![CDATA[
/*
* やったーフラクタル化できたよー
* 思いっきり同期処理なのでデカい画像だとしばらく止まります
* 300〜500pixくらいで勘弁してください
*/
import com.adobe.images.PNGEncoder;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.net.FileFilter;
private var fr:FileReference = new FileReference();
private var ba:ByteArray = new ByteArray();
private var mosaic:ByteArray = new ByteArray();
private var rl:Loader = new Loader();
private var bmd:BitmapData;
private var bm:Bitmap = new Bitmap();
private var pbmd:BitmapData;
private var hensaThreshold:Number;
//フラクタルを囲む四角形の色
private var rectColor:uint = 0x888888;
private function init():void
{
this.image.addChild(bm);
}
private function onBrowseBtnClick():void
{
//画像ファイルを参照
this.fr.addEventListener(Event.SELECT, onFileSelect);
this.fr.browse([new FileFilter("Image", "*.jpg;*.gif;*.png")]);
}
private function onFileSelect(evt:Event):void
{
this.fr.removeEventListener(Event.SELECT, onFileSelect);
this.fr.addEventListener(Event.COMPLETE, onFileComplete);
this.fr.load();
}
private function onFileComplete(evt:Event):void
{
//画像をByteArrayに格納
this.fr.removeEventListener(Event.COMPLETE, onFileComplete);
this.ba = this.fr.data;
this.rl.contentLoaderInfo.addEventListener(Event.COMPLETE, onPreviewLoadComp);
this.rl.loadBytes(this.ba);
}
private function onPreviewLoadComp(evt:Event):void
{
this.rl.contentLoaderInfo.removeEventListener(Event.COMPLETE, onPreviewLoadComp);
this.bmd = new BitmapData(this.rl.contentLoaderInfo.width, this.rl.contentLoaderInfo.height, false);
this.bmd.draw(rl);
this.bm.bitmapData = this.bmd;
this.bm.scaleX = this.bm.scaleY = 1;
this.bm.scaleX = this.bm.scaleY = Math.min(this.width / this.bm.width, this.height / this.bm.height);
//trace(this.bmd.width, this.bm.height);
}
private function onMosaicBtnClick():void
{
if (this.ba.length > 0)
{
this.mosaic = null;
this.mosaic = new ByteArray();
this.pbmd = this.bmd.clone();
this.bm.bitmapData = this.pbmd;
startMosaic();
this.mosaic = PNGEncoder.encode(this.pbmd);
}
else
{
//画像を参照してね
}
}
//
//画像をMosaicする
//
private function startMosaic(p:Point = null, w:Number = -1, h:Number = -1):void
{
if(p == null)p = new Point(0,0);
if(w == -1)w = this.bmd.width;
if(h == -1)h = this.bmd.height;
var t:int = Math.floor(Number(this.time.text));
this.hensaThreshold = Math.floor(Number(this.thr.text));
var endFlg:Boolean = false;
if(t > w || t > h || w == 1 || h == 1)
{
//wもしくはhが最小値以下になったら普通に平均出して終了
endFlg = true;
}
var newColor:uint = 0;
var newAlpha:uint = 0;
var newRed:uint = 0;
var newGreen:uint = 0;
var newBlue:uint = 0;
var newColorArray:Array = new Array();
newColorArray = [];
//指定された範囲の色を取る
for (var i:int = 0; i < w; i++)
{
for (var j:int = 0; j < h; j++)
{
newColor = this.bmd.getPixel32(p.x + i, p.y + j);
newAlpha += newColor >> 24 & 0xff;
newRed += newColor >> 16 & 0xff;
newGreen += newColor >> 8 & 0xff;
newBlue += newColor & 0xff;
newColorArray.push(newColor);
}
}
//各チャンネルの平均を出しておく
newAlpha = Math.floor(newAlpha / (w * h));
newRed = Math.floor(newRed / (w * h));
newGreen = Math.floor(newGreen / (w * h));
newBlue = Math.floor(newBlue / (w * h));
newColor = (newAlpha << 24 | newRed << 16 | newGreen << 8 | newBlue);
//色の標準偏差をとる
var alphaHensa:Number=0;
var redHensa:Number=0;
var greenHensa:Number=0;
var blueHensa:Number=0;
for(var colorIndex:int = 0; colorIndex < newColorArray.length; colorIndex++)
{
//アルファ
alphaHensa += (newColorArray[colorIndex]>>24 & 0xff - newAlpha)*(newColorArray[colorIndex]>>24 & 0xff - newAlpha);
//赤
redHensa += (newColorArray[colorIndex]>>16 & 0xff - newRed)*(newColorArray[colorIndex]>>16 & 0xff - newRed);
//緑
greenHensa += (newColorArray[colorIndex]>>8 & 0xff - newGreen)*(newColorArray[colorIndex]>>8 & 0xff - newGreen);
//青
blueHensa += (newColorArray[colorIndex] & 0xff - newBlue)*(newColorArray[colorIndex] & 0xff - newBlue);
}
alphaHensa = Math.sqrt(alphaHensa/(w * h));
redHensa = Math.sqrt(redHensa/(w * h));
greenHensa = Math.sqrt(greenHensa/(w * h));
blueHensa = Math.sqrt(blueHensa/(w * h));
//偏差がしきい値以下の場合、つまり色のバラつきが少ない場合
//そのRectangleは平均値で塗りつぶして終了
if(alphaHensa < hensaThreshold && redHensa < hensaThreshold && greenHensa < hensaThreshold && blueHensa < hensaThreshold)
{
this.pbmd.fillRect(new Rectangle(p.x,p.y,w,h), newColor);
drawRectangle(p.x,p.y,w,h);
}
else if(endFlg)
{
//wまたはhが小さすぎた場合も終了
this.pbmd.fillRect(new Rectangle(p.x,p.y,w,h), newColor);
drawRectangle(p.x,p.y,w,h);
}
else
{
//偏差がしきい値以上の場合つまり色のバラつきが大きい場合は
//さらに2分割して再起処理にかける。
var newW:int;
var newW2:int;
var newH:int;
var newH2:int;
var newPoint:Point;
if(w>h)
{
newW = Math.floor(w/2);
newW2 = Math.round(w/2);
newH = h;
newH2 = h;
newPoint = new Point(p.x+newW,p.y);
}
else
{
newW = w;
newW2 = w;
newH = Math.floor(h/2);
newH2 = Math.round(h/2);
newPoint = new Point(p.x, p.y+newH);
}
startMosaic(p,newW,newH);
startMosaic(newPoint,newW2,newH2);
}
}
private function drawRectangle(x:Number,y:Number,w:Number,h:Number):void
{
//確定した四角形を枠取りする
for(var i:int = 0; i < w; i++)
{
this.pbmd.setPixel(x+i,y,this.rectColor);
this.pbmd.setPixel(x+i,y+h,this.rectColor);
}
for(var j:int = 0; j < h; j++)
{
this.pbmd.setPixel(x,y+j,this.rectColor);
this.pbmd.setPixel(x+w,y+j,this.rectColor);
}
}
private function onMosaicLoadComp(evt:Event):void
{
this.rl.removeEventListener(Event.COMPLETE, onMosaicLoadComp);
this.rl.scaleX = this.rl.scaleY = 1;
this.rl.scaleX = this.rl.scaleY = Math.min(this.width / this.rl.width, this.height / this.rl.height);
}
private function onSaveBtnClick():void
{
//画像を保存
if (this.mosaic.length > 0)
{
this.fr.save(this.mosaic, "mosaic.png");
}
else
{
//mosaicしてね。
}
}
]]>
</mx:Script>
<mx:Image id = "image" />
<mx:HBox paddingTop = "10" paddingLeft = "10">
<mx:Button id = "browseBtn" label = "参照" width="50" click = "onBrowseBtnClick();" />
<mx:TextInput id = "time" text = "5" width = "30" />
<mx:Label text = "←粗さ" />
<mx:TextInput id = "thr" text = "50" width = "30" />
<mx:Label text = "←しきい値0〜127" />
<mx:Button id = "createBtn" label = "モザイク!" click = "onMosaicBtnClick();" />
<mx:Button id = "saveBtn" label = "保存" click = "onSaveBtnClick();" />
</mx:HBox>
</mx:Application>