forked from: 画像をフラクタル化 forked from: 単位面積あたりの偏差がナントカカントカ

by _perfect forked from 画像をフラクタル化 forked from: 単位面積あたりの偏差がナントカカントカ (diff: 1)
♥0 | Line 200 | Modified 2011-10-07 18:44:27 | MIT License
play

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>