エッジ保存平均化(ちょっと重いです)
Flickrから猫の画像をとってきて、それを絵画調にしてます(画像はランダム)。
画像は350×350に整形してるからつぶれるのもあるかも。
エッジ保存平均化というもので、kuwaharaという手法を利用しています。
最初のほうは画像を取りに行ってるので真っ暗です。
スペックによってはかなり重いかもしれません。
クリックで元の画像と入れ替わります。
/**
* Copyright sakef ( http://wonderfl.net/user/sakef )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/jxZu
*/
/*
Flickrから猫の画像をとってきて、それを絵画調にしてます(画像はランダム)。
画像は350×350に整形してるからつぶれるのもあるかも。
エッジ保存平均化というもので、kuwaharaという手法を利用しています。
最初のほうは画像を取りに行ってるので真っ暗です。
スペックによってはかなり重いかもしれません。
クリックで元の画像と入れ替わります。
*/
package
{
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Loader;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Matrix;
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.system.LoaderContext;
import flash.system.Security;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
[SWF(width="465", height="465", backgroundColor="0xFFFFFF", frameRate="40")]
public class Main extends Sprite
{
private static const KEYWORD:String = "cat";
private var xml_loader:URLLoader;
private var img_loader:Loader;
private var tf:TextField;
private var source:BitmapData;
private var canvas:BitmapData;
private var bmp:Bitmap;
private var w:Number;
private var h:Number;
private var aves:Array;
private var block:Number;
private var size:Number;
private var click:int;
public function Main()
{
// crossdomain.xmlを読み込む
Security.loadPolicyFile("http://api.flickr.com/crossdomain.xml");
Security.loadPolicyFile("http://farm1.static.flickr.com/crossdomain.xml");
Security.loadPolicyFile("http://farm2.static.flickr.com/crossdomain.xml");
Security.loadPolicyFile("http://farm3.static.flickr.com/crossdomain.xml");
Security.loadPolicyFile("http://farm4.static.flickr.com/crossdomain.xml");
// 計算中に表示するテキスト
tf = addChild(new TextField) as TextField;
tf.autoSize = TextFieldAutoSize.LEFT;
tf.text = "画像ロード & 計算中・・・。";
tf.x = tf.y = 40;
// 画像のロード準備
xml_loader=new URLLoader();
xml_loader.addEventListener(Event.COMPLETE, onCompleteSearch);
xml_loader.load(new URLRequest("http://api.flickr.com/services/rest/?method=flickr.photos.search&api_key=25c5f4bc0087edf4d6efdc567daf0a64&tags=" + KEYWORD + "&per_page=10"));
}
// XMLロード後に実行される関数
private function onCompleteSearch(e:Event):void
{
xml_loader.removeEventListener(Event.COMPLETE, onCompleteSearch);
var xml:XML=XML(xml_loader.data);
if (xml.@stat == "ok")
{
var photo:XML=xml.photos.photo[10*Math.random() >> 0] as XML;
var url:String="http://farm" + photo.@farm + ".static.flickr.com/" + photo.@server + "/" + photo.@id + "_" + photo.@secret + ".jpg";
img_loader=new Loader();
img_loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onCompletePhoto);
img_loader.load(new URLRequest(url), new LoaderContext(true));
}
}
// 画像のロード後に実行されっる関数
private function onCompletePhoto(e:Event):void
{
// 画像の作成
img_loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, onCompletePhoto);
var raw_img:BitmapData = (img_loader.content as Bitmap).bitmapData;
var x_scale:Number=350 / raw_img.width;
var y_scale:Number=350 / raw_img.height;
source = new BitmapData(350, 350, false, 0x000000);
canvas = new BitmapData(350, 350, false, 0x000000);
source.draw(raw_img, new Matrix(x_scale, 0, 0, y_scale));
// 計算の準備
block = 5;
w = canvas.width;
h = canvas.height;
size = block * block;
aves = [];
// 計算開始
kuwahara();
// 画像の表示
removeChild(tf);
bmp= addChild(new Bitmap(canvas)) as Bitmap;
bmp.x=bmp.y=57.5;
// クリック用
click = 0;
stage.addEventListener(MouseEvent.CLICK, onClick);
}
// 画像クリック用の関数
private function onClick(e:MouseEvent):void
{
if(click%2 == 0) bmp.bitmapData = source;
else bmp.bitmapData = canvas;
click ++;
}
// 画像処理を行う関数
private function kuwahara():void
{
source.lock();
canvas.lock();
for(var i:int=0 ; i<w ; i++)
{
for(var j:int=0 ; j<h ; j++)
{
var min:uint = 0xff;
var select:int = -1;
var tmp:uint;
var x0:int = (i - block + 1);
var y0:int = (j - block + 1);
var x1:int = (i + block - 1);
var y1:int = (j + block - 1);
// 左上のブロックを計算
if (x0 >= 0 && y0 >= 0)
{
tmp=calcColor(x0, y0, 0);
if (min > tmp)
{
min=tmp;
select=0;
}
}
// 右上のブロックを計算
if (x1 < w && y0 >= 0)
{
tmp=calcColor(i, y0, 1);
if (min > tmp)
{
min=tmp;
select=1;
}
}
// 右下のブロックを計算
if (x1 < w&& y1 < h)
{
tmp=calcColor(i, j, 2);
if (min > tmp)
{
min=tmp;
select=2;
}
}
// 左下のブロックを計算
if (x0 >=0 && y1 < h)
{
tmp=calcColor(x0, j, 3);
if (min > tmp)
{
min=tmp;
select=3;
}
}
var color:uint = (select == -1) ? (source.getPixel(i, j)):(aves[select]);
canvas.setPixel(i, j, color);
}
}
source.unlock();
canvas.unlock();
}
// 各領域の平均と、最大最小の差を計算する
private function calcColor(xx:int, yy:int, n:int):uint
{
var r:uint=0;
var g:uint=0;
var b:uint=0;
var sr:uint=0;
var sg:uint=0;
var sb:uint=0;
var maxw:int = xx + block;
var maxh:int = yy + block;
var rmax:uint=0x00;
var gmax:uint=0x00;
var bmax:uint=0x00;
var rmin:uint=0xff;
var gmin:uint=0xff;
var bmin:uint=0xff;
var color:uint;
for (var i:int=xx; i < maxw; i++)
{
for (var j:int=yy; j < maxh; j++)
{
color = source.getPixel(i, j);
r = (color >> 16) & 0xFF;
g = (color >> 8) & 0xFF;
b = color & 0xFF;
if (rmax < r) rmax=r;
if (gmax < g) gmax=g;
if (bmax < b) bmax=b;
if (rmin > r) rmin=r;
if (gmin > g) gmin=g;
if (bmin > b) bmin=b;
sr += r;
sg += g;
sb += b;
}
}
aves[n] = ((sr / size) << 16) | ((sg / size) << 8) | (sb / size);
return (rmax - rmin + gmax - gmin + bmax - bmin) / 3;
}
}
}