In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

顔領域抽出でモーフィングっぽいの

顔領域抽出でモーフィングっぽいの
* 
* ImagePipes Concept
* http://imagepipes.kayac.com/
* を使って顔領域を抽出し、
* 顔領域がつながることを優先して、フェードイン・アウト。
* 写真によっては結構見れるものになっていると思う。
* 
* smileとかfaceとか工夫しても、
* 検索して取り出した写真に正面の顔が写ってないことが多い。
* 「girl tokyo」とか表示されているのに、うまく取得できない時用の
* おじさんが写っていることも多いけど、ご容赦。
* 
* =====
* 1)YahooPipes経由でFlickrから、「girl tokyo」の検索結果を取得
* 2)偏ると面白くないので、ランダムに画像のURLを取り出す。
* 3)ImagePipesから、画像毎の顔領域を取得
* 4)うまく取れない場合(写真に正面の顔が写ってないことも少なくない)、
* あらかじめ用意した、うまく取れることがわかっている画像で再取得。
* 5)顔領域がつながることを優先して、変形させながらフェードイン・アウト。
* 
*
/**
 * Copyright umhr ( http://wonderfl.net/user/umhr )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/36YK
 */

/*
 * 顔領域抽出でモーフィングっぽいの
 * 
 * ImagePipes Concept
 * http://imagepipes.kayac.com/
 * を使って顔領域を抽出し、
 * 顔領域がつながることを優先して、フェードイン・アウト。
 * 写真によっては結構見れるものになっていると思う。
 * 
 * smileとかfaceとか工夫しても、
 * 検索して取り出した写真に正面の顔が写ってないことが多い。
 * 「girl tokyo」とか表示されているのに、うまく取得できない時用の
 * おじさんが写っていることも多いけど、ご容赦。
 * 
 * =====
 * 1)YahooPipes経由でFlickrから、「girl tokyo」の検索結果を取得
 * 2)偏ると面白くないので、ランダムに画像のURLを取り出す。
 * 3)ImagePipesから、画像毎の顔領域を取得
 * 4)うまく取れない場合(写真に正面の顔が写ってないことも少なくない)、
 * あらかじめ用意した、うまく取れることがわかっている画像で再取得。
 * 5)顔領域がつながることを優先して、変形させながらフェードイン・アウト。
 * 
 * */

package{
	import flash.display.SimpleButton;
	import flash.display.Sprite;
	import flash.display.Shape;
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.Graphics;
	import flash.display.Loader;
	import flash.events.Event;
	import flash.events.IOErrorEvent;
	import flash.events.MouseEvent;
	import flash.filters.DropShadowFilter;
	import flash.net.URLLoader;
	import flash.net.URLRequest;
	import flash.text.TextField;
	import flash.system.LoaderContext;
	import flash.geom.Matrix;
	import flash.geom.Rectangle;
	import com.adobe.serialization.json.*;
	[SWF(width = "465", height = "465", backgroundColor = 0, frameRate = "30")]

	public class Main extends Sprite{
		private var layerFaces:Sprite = new Sprite;
		private var faceRectangle_array:Array;
		private var imgRectangle_array:Array;
		private var img_array:Array;
		private var loadedImgCount:int;
		private var isLine:Boolean;
		private var pipesurl:String = "http://pipes.yahooapis.com/pipes/pipe.run?_id=4Hfb79Zo3BGT9IDkxAnzeQ&_render=json&urls=";
		private var media:Namespace;
		private var credit:Object;
		private var credit_array:Array = [["http://www.matzmtok.com/wonderfl/checkmate/image/bush.png", " GEORGE W. BUSH", "http://www.whitehouse.gov/about/presidents/georgewbush/"], ["http://www.matzmtok.com/wonderfl/checkmate/image/obama.png", "Barack H. Obama", "http://www.whitehouse.gov/administration/president_obama/"], ["http://farm4.static.flickr.com/3154/2883492436_c962012edc.jpg", "Emily in the ball pool", "http://www.flickr.com/photos/gingerfuhrer/2883492436/"], ["http://farm1.static.flickr.com/181/431971314_4e6ede5188.jpg", "Polka Dotted Shirt Boy", "http://www.flickr.com/photos/foundphotoslj/431971314/"], ["http://mztm.heteml.jp/umhr/wonderfl/tumblr_koxl0xJLqx1qzm8kto1_500.jpg", "麻生内閣総理大臣記者会見", "http://www.kantei.go.jp/jp/asophoto/2009/04/10kaiken.html"]];
		private var creditFixed_array:Array;
		private var inputTf:TextField;
		private var count:int;
		private var photoCount0:int;
		private var photoCount1:int;
		private var creditTf:TextField;
		private var backSprite:Sprite;
		private var frontSprite:Sprite;
		
        public function Main():void 
        {
			addChild(layerFaces);
			addEventListener(MouseEvent.MOUSE_DOWN, function(e:MouseEvent):void { isLine = !isLine } );
			
			inputTf = new TextField();
			inputTf.x = 32;
			inputTf.y = 445;
			inputTf.border = true;
			inputTf.borderColor = 0xFFFFFF;
			inputTf.width = 366;
			inputTf.height = 19;
			inputTf.textColor = 0xFFFFFF;
			inputTf.text = "girl tokyo";
			inputTf.type = "input";
			inputTf.filters = [new DropShadowFilter()];
			addChild(inputTf)
			
			var btnSp:Sprite = new Sprite();
			btnSp.graphics.beginFill(0xFFFFFF, 0.8);
			btnSp.graphics.drawRoundRect(400, 445, 32, 19, 8, 8);
			btnSp.buttonMode = true;
			addChild(btnSp);
			btnSp.addEventListener(MouseEvent.CLICK, reStart );
			
			start();
		}
		
		private function reStart(e:MouseEvent):void {
			removeEventListener(Event.ENTER_FRAME,ENTER_FRAME);
			while (layerFaces.numChildren) {
				layerFaces.removeChildAt(0);
			}
			start();
		}
		
        public function start():void 
        {
			//yahoopipes経由でFlckrの写真リストを取得
            var myLoader:URLLoader = new URLLoader();
			myLoader.addEventListener (Event.COMPLETE, onCompStep1);
			myLoader.addEventListener(IOErrorEvent.IO_ERROR, IO_ERROR);
			myLoader.load(new URLRequest("http://pipes.yahooapis.com/pipes/pipe.run?_id=Tkmh9Xn63RGd9RQo37Vd_w&_render=rss&num=40&q="+encodeURI(inputTf.text)));
			
		}
		public function IO_ERROR(e:IOErrorEvent):void {
			e.target.removeEventListener (Event.COMPLETE, onCompStep1);
			inputTf.text = "Yahoo Pipesとの接続がうまく行ってないみたい。また来てね。";
		}
        
		public function onCompStep1(e:Event = null, is2nd:Boolean = false):void {
			trace("===")
			var URLs:Array = [];
			if (e) {
				//写真リストの取得の場合は、XMLの内容を取得する。
				var loader:URLLoader = e.target as URLLoader;
				loader.removeEventListener(Event.COMPLETE, onCompStep1);
				var xml:XML = new XML(loader.data);
				media = new Namespace("http://search.yahoo.com/mrss/");
				default xml namespace = media;
				credit = { };
				for ( var i:int = 0; i < xml.channel.item.length(); i++ ) {
					if (Math.random() < 0.9) { continue };
					var url:String = String(xml.channel.item[i].media::group.media::content.@url).replace("http://", "http://farm" + xml.channel.item[i].media::group.@farm + ".").replace("_m", "");
					URLs.push(url);
					credit[url] = [String(xml.channel.item[i].title), String(xml.channel.item[i].link)];
				}
			}
			//とりあえず顔領域抽出できる確立の高い画像を追加。
			for ( var j:int = 0; j < 5; j++ ) {
				URLs.push(credit_array[j][0]);
				credit[credit_array[j][0]] = [credit_array[j][1], credit_array[j][2]];
			}
			URLs.length = 4;
			
			//yahooPipes経由でImagePipesの顔領域抽出
			var myURLLoader:URLLoader = new URLLoader;
			myURLLoader.addEventListener(Event.COMPLETE,onCompStep2);
			myURLLoader.load( new URLRequest(pipesurl + encodeURIComponent(URLs.join(','))) );
		}
		private function onCompStep2(e:Event):void {
			//顔領域抽出が有効なものが2つ以上あれば、
			//その写真を読み込んで、次へ
			//2つ未満ならやりなおし。
			e.target.removeEventListener(Event.COMPLETE, onCompStep2);
			var decoder:JSONDecoder = new JSONDecoder(e.target.data);
			var json:Object = decoder.getValue();
			var items:Array = json.value.items;
			var loadedCount:int = 0;
			loadedImgCount = 0;
			img_array = [];
			imgRectangle_array = [];
			creditFixed_array = [];
			faceRectangle_array = [];
			for ( var i:int = 0; i < items.length; i++ ) {
				if (items[i].rects && items[i].rects.length) {
					trace("!!",items[i].url)
					faceRectangle_array.push(new Rectangle(Number(items[i].rects[0].x), Number(items[i].rects[0].y), Number(items[i].rects[0].width), Number(items[i].rects[0].height)));
					var myLoader:Loader = new Loader;
					myLoader.name = String(loadedCount);
					myLoader.load( new URLRequest(items[i].url),new LoaderContext(true));
					myLoader.contentLoaderInfo.addEventListener (Event.COMPLETE,onCompStep3);
					loadedCount ++;
				}else {
					trace("??",items[i].url)
				}
			}
			if (loadedCount < 2) {
				onCompStep1(null, true);
			}
		}
		
		private function onCompStep3(e:Event):void {
			loadedImgCount ++;
			var num:int = e.target.loader.name;
			e.target.loader.removeEventListener(Event.COMPLETE, onCompStep3);
			var rect:Rectangle = new Rectangle();
			rect.x = e.target.content.x+num*300;
			rect.y = e.target.content.x;
			rect.width = e.target.content.width;
			rect.height = e.target.content.height;
			img_array[num] = e.target.content;
			imgRectangle_array[num] = rect;
			creditFixed_array[num] = "<a href='"+credit[String(e.target.url)][1]+"'>"+credit[String(e.target.url)][0]+"</a>";
			if (loadedImgCount == faceRectangle_array.length) {
				drawImg();
			};
		}
		private function drawImg():void {
			backSprite = new Sprite();
			layerFaces.addChild(backSprite);
			frontSprite = new Sprite();
			layerFaces.addChild(frontSprite);
			
			creditTf = new TextField();
			creditTf.width = 465;
			creditTf.height = 21;
			creditTf.textColor = 0xFFFFFF;
			creditTf.htmlText = creditFixed_array[1];
			creditTf.filters = [new DropShadowFilter()];
			layerFaces.addChild(creditTf);
			count = 0
			photoCount0 = 0;
			photoCount1 = 1;
			addEventListener(Event.ENTER_FRAME, ENTER_FRAME);
			
		}
		private function ENTER_FRAME(e:Event = null):void {
			count ++;
			while (frontSprite.numChildren) {
				frontSprite.removeChildAt(0);
			}
			while (backSprite.numChildren) {
				backSprite.removeChildAt(0);
			}
			backSprite.addChild(morph(1-count/100,photoCount1,photoCount0));
			frontSprite.addChild(morph(count/100,photoCount0,photoCount1));
			frontSprite.alpha = (1-count/100);
			if(count == 100){
				photoCount0 = (photoCount0 + 1) % loadedImgCount;
				photoCount1 = (photoCount1 + 1) % loadedImgCount;
				creditTf.textColor = 0xFFFFFF;
				creditTf.htmlText = creditFixed_array[photoCount1];
				count = 0;
			}
		}
		
		private function morph(num:Number,p0:int,p1:int):Sprite{
			var wh_array:Array = [1, 1, 1, 1, 1, 1];
			wh_array[0] = (1-num)+num*(faceRectangle_array[p1].x / faceRectangle_array[p0].x);
			wh_array[1] = (1-num)+num*(faceRectangle_array[p1].width / faceRectangle_array[p0].width);
			wh_array[2] = (1-num)+num*((imgRectangle_array[p1].width - faceRectangle_array[p1].x - faceRectangle_array[p1].width) / (imgRectangle_array[p0].width - faceRectangle_array[p0].x - faceRectangle_array[p0].width));
			
			wh_array[3] = (1-num)+num*(faceRectangle_array[p1].y / faceRectangle_array[p0].y);
			wh_array[4] = (1-num)+num*(faceRectangle_array[p1].height / faceRectangle_array[p0].height);
			wh_array[5] = (1-num)+num*((imgRectangle_array[p1].height - faceRectangle_array[p1].y - faceRectangle_array[p1].height) / (imgRectangle_array[p0].height - faceRectangle_array[p0].y - faceRectangle_array[p0].height));
			
			return polygonmesh(imgRectangle_array[p0], faceRectangle_array[p0], img_array[p0], wh_array);
		}
		private function polygonmesh(imgRect:Rectangle,faceRect:Rectangle,bmp:Bitmap,wh_array:Array):Sprite {
			var mySprite:Sprite = new Sprite();
			var myTexture:BitmapData = new BitmapData(imgRect.width, imgRect.height);
			myTexture.draw(bmp,new Matrix(imgRect.width/bmp.width,0,0,imgRect.height/bmp.height),null,null,null);
			
			var vertices:Vector.<Number> = new Vector.<Number>();
			var indices:Vector.<int> = new Vector.<int>();
			var uvtData:Vector.<Number> = new Vector.<Number>();
			
			var tempRect:Rectangle = new Rectangle(0, 0, faceRect.x, faceRect.y);
			
			var _x:Number = 0;
			mySprite.addChild(drawTexture(tempRect, myTexture,[_x,0,wh_array[0],wh_array[3]]));
			_x += wh_array[0] * tempRect.width;
			tempRect = new Rectangle(faceRect.x, 0, faceRect.width, faceRect.y);
			mySprite.addChild(drawTexture(tempRect, myTexture,[_x,0,wh_array[1],wh_array[3]]));
			_x += wh_array[1] * tempRect.width;
			tempRect = new Rectangle(faceRect.x+faceRect.width,0,myTexture.width-(faceRect.x+faceRect.width),faceRect.y);
			mySprite.addChild(drawTexture(tempRect, myTexture,[_x,0,wh_array[2],wh_array[3]]));
			
			var _y:Number = wh_array[3] * tempRect.height;
			tempRect = new Rectangle(0,faceRect.y,faceRect.x,faceRect.height);
			_x = 0;
			mySprite.addChild(drawTexture(tempRect, myTexture,[_x,_y,wh_array[0],wh_array[4]]));
			_x += wh_array[0] * tempRect.width;
			tempRect = new Rectangle(faceRect.x,faceRect.y,faceRect.width,faceRect.height);
			mySprite.addChild(drawTexture(tempRect, myTexture,[_x,_y,wh_array[1],wh_array[4]]));
			_x += wh_array[1] * tempRect.width;
			tempRect = new Rectangle(faceRect.x+faceRect.width,faceRect.y,myTexture.width-(faceRect.x+faceRect.width),faceRect.height);
			mySprite.addChild(drawTexture(tempRect, myTexture,[_x,_y,wh_array[2],wh_array[4]]));
			
			_y += wh_array[4] * tempRect.height;
			_x = 0;
			tempRect = new Rectangle(0,faceRect.y+faceRect.height,faceRect.x,myTexture.height-(faceRect.y+faceRect.height));
			mySprite.addChild(drawTexture(tempRect, myTexture,[_x,_y,wh_array[0],wh_array[5]]));
			_x += wh_array[0] * tempRect.width;
			tempRect = new Rectangle(faceRect.x,faceRect.y+faceRect.height,faceRect.width,myTexture.height-(faceRect.y+faceRect.height));
			mySprite.addChild(drawTexture(tempRect, myTexture,[_x,_y,wh_array[1],wh_array[5]]));
			_x += wh_array[1] * tempRect.width;
			tempRect = new Rectangle(faceRect.x+faceRect.width,faceRect.y+faceRect.height,myTexture.width-(faceRect.x+faceRect.width),myTexture.height-(faceRect.y+faceRect.height));
			mySprite.addChild(drawTexture(tempRect, myTexture, [_x, _y, wh_array[2], wh_array[5]]));
			
			_x += wh_array[2] * tempRect.width;
			_y += wh_array[5] * tempRect.height;
			
			mySprite.x = (465 - _x) / 2;
			mySprite.y = (465 - _y) / 2;
			
			function drawTexture(tempRect:Rectangle, myTexture:BitmapData, wh_ar:Array):Shape {
				var textureRect:Rectangle = new Rectangle(tempRect.x / myTexture.width, tempRect.y / myTexture.height, tempRect.width / myTexture.width, tempRect.height / myTexture.height);
				
				var mySh:Shape = new Shape();
				var myGraphics:Graphics = mySh.graphics;
				if(isLine){
					myGraphics.lineStyle(1, 0xFF);
				}
				// 三角形の頂点座標を加える(第1引数)
				vertices = new Vector.<Number>();
				indices = new Vector.<int>();
				uvtData = new Vector.<Number>();
				vertices.push(wh_ar[0], wh_ar[1]);   // 頂点0
				vertices.push(wh_ar[0]+wh_ar[2]*tempRect.width, wh_ar[1]);   // 頂点1
				vertices.push(wh_ar[0], wh_ar[1]+wh_ar[3]*tempRect.height);   // 頂点2
				vertices.push(wh_ar[0] + wh_ar[2] * tempRect.width, wh_ar[1] + wh_ar[3] * tempRect.height);   // 頂点3
				// 三角形の頂点番号の組合わせを加える(第2引数
				indices.push(0, 1, 2);   // 左上半分: 頂点0-1-2
				indices.push(1, 2, 3);   // 右下半分: 頂点1-2-3
				// テクスチャマッピングのuv座標を加える(第3引数)
				uvtData.push(textureRect.x, textureRect.y);   // 頂点0
				uvtData.push(textureRect.x+textureRect.width, textureRect.y);   // 頂点1
				uvtData.push(textureRect.x, textureRect.y+textureRect.height);   // 頂点2
				uvtData.push(textureRect.x+textureRect.width, textureRect.y+textureRect.height);   // 頂点3
				myGraphics.beginBitmapFill(myTexture);
				myGraphics.drawTriangles(vertices, indices, uvtData);
				myGraphics.endFill();
				return mySh;
			}
			return mySprite;
		}
	}
}