Summer Memories Generator
この夏、どこにも行けなかったあなたへ。
* 顔写真と行きたい場所を入力するだけで、思い出の写真をご用意します。
* I can make you a photo album of your summer vacation. If you didn't go anywhere.
*
*
* STEP1 について
* 複数の顔を検出した場合はランダムでコラージュします。
*
* STEP2 について
* 顔写真が多くなるよう "friends" タグを追加して検索しています。
* 1文字目を "," にすることで入力したタグのみでの検索も可能です。
* 例:,azabu,festival
*
*
* 顔認識API detectFace();
* http://detectface.com/
/**
* Copyright buccchi ( http://wonderfl.net/user/buccchi )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/dc0l
*/
// forked from Event's Simple MediaRSS Viewer
/*
* この夏、どこにも行けなかったあなたへ。
* 顔写真と行きたい場所を入力するだけで、思い出の写真をご用意します。
* I can make you a photo album of your summer vacation. If you didn't go anywhere.
*
*
* STEP1 について
* 複数の顔を検出した場合はランダムでコラージュします。
*
* STEP2 について
* 顔写真が多くなるよう "friends" タグを追加して検索しています。
* 1文字目を "," にすることで入力したタグのみでの検索も可能です。
* 例:,azabu,festival
*
*
* 顔認識API detectFace();
* http://detectface.com/
*/
package {
import flash.display.*;
import flash.events.*;
import flash.net.URLRequest;
import flash.net.LocalConnection;
import flash.geom.ColorTransform;
import org.libspark.betweenas3.BetweenAS3;
import org.libspark.betweenas3.tweens.ITween;
import org.libspark.betweenas3.easing.*;
import org.libspark.betweenas3.events.TweenEvent;
import net.hires.debug.Stats;
import com.bit101.components.*;
[SWF(width="465", height="465", backgroundColor="#000000", frameRate="30")]
public class SummerMemoriesGenerator extends Sprite {
private var _faceSearcher:FaceSearcher;
private var _faceList:Vector.<FaceObject>;
private var _count:uint = 0;
private var _imageLoader:Loader;
private var _container:Sprite;
private var _photoSpr:Sprite;
private var _photoBm:Bitmap;
private var _isCompLoad:Boolean;
private var _isCompDetect:Boolean;
private var _isCompShow:Boolean = true;
private var _isFirstTime:Boolean = true; //初回再生判定
private var _collageList:Vector.<CollageObject>;
private var _it:ITween;
private var _panel:SettingPanel;
private var _stateLabel:Label;
public function SummerMemoriesGenerator() {
stage.align = StageAlign.TOP_LEFT;
stage.scaleMode = StageScaleMode.NO_SCALE;
addEventListener(Event.ADDED_TO_STAGE, init);
Wonderfl.capture_delay( 10 );
}
private function init(e:Event):void {
removeEventListener(Event.ADDED_TO_STAGE, init);
_faceSearcher = new FaceSearcher();
_imageLoader = new Loader();
_container = new Sprite();
addChild( _container );
_photoSpr = new Sprite();
_photoBm = new Bitmap();
_container.addChild( _photoSpr );
_photoSpr.addChild( _photoBm );
_stateLabel = new Label(this);
var colorTransform:ColorTransform = _stateLabel.transform.colorTransform;
colorTransform.color = 0xFFFFFF;
_stateLabel.transform.colorTransform = colorTransform;
_stateLabel.x = _stateLabel.y = 10;
_panel = new SettingPanel();
_container.addChild( _panel );
_panel.addEventListener(PanelEvent.COMPLETE, panelComplete);
stage.addEventListener(Event.RESIZE, resizeHandler);
resizeHandler(null);
}
private function resizeHandler(e:Event):void {
_panel.x = Math.floor((stage.stageWidth/2 -465/2));
_panel.y = Math.floor((stage.stageHeight/2 -465/2));
}
private function panelComplete(e:PanelEvent):void {
e.target.removeEventListener(PanelEvent.COMPLETE, panelComplete);
_container.removeChild(e.target as SettingPanel);
_faceList = e.faceList;
//
_collageList = new Vector.<CollageObject>();
for(var i:uint=0; i<e.urlList.length; i++){
_collageList.push( new CollageObject(e.urlList[i]) );
}
_stateLabel.text = "Please wait...";
loadImage(_count);
}
private function checkReady():void {
if( _isFirstTime ){
//1周目
if( _isCompLoad && _count==0 ){
_count++;
_isCompDetect = false;
_isCompLoad = false;
detectFace(_count-1);
loadImage(_count);
}else if(( _isCompLoad && _isCompDetect && _count==1) || ( _isCompLoad && _isCompDetect && _isCompShow )){
_count++;
_isCompDetect = false;
_isCompLoad = false;
_isCompShow = false;
detectFace(_count-1);
loadImage(_count);
showPhoto(_count-2);
if(_stateLabel.visible) _stateLabel.visible = false;
}
}else{
//2周目以降
if( _collageList.length == 0){
_stateLabel.text = "Sorry, memorial photos were not found.";
}else if( _isCompLoad && _isCompDetect && _isCompShow ){
showPhoto(_count-1);
if(_count < _collageList.length){
_count++;
}else{
_count = 1;
}
}
}
}
/* 写真読み込み */
private function loadImage(i:uint=0):void {
if(i < _collageList.length){
var str:String = _collageList[i].url.replace("_s.jpg", "_z.jpg");
_imageLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadImageComplete);
_imageLoader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, loadImageError);
_imageLoader.load(new URLRequest( str ));
}else{
//一周完了
_isCompLoad = true;
_isFirstTime = false;
_stateLabel.visible = false; //画像が1枚だけだった場合用
checkReady();
}
}
private function loadImageComplete(e:Event):void {
e.target.removeEventListener(Event.COMPLETE, loadImageComplete);
e.target.removeEventListener(IOErrorEvent.IO_ERROR, loadImageError);
if(_imageLoader.contentLoaderInfo.contentType == "image/gif"){
//gifの場合は画像が見つからなかったと判断
_collageList.splice(_count, 1); //リストから削除
loadImage();
}else{
_collageList[_count].content = _imageLoader.content;
_isCompLoad = true;
checkReady();
}
}
private function loadImageError(e:IOErrorEvent):void {
_imageLoader.unload();
}
/* 顔認識 */
private function detectFace(i:uint):void {
var obj:DisplayObject = _collageList[i].content;
var bmd:BitmapData = new BitmapData(obj.width, obj.height, false, 0xFFFFFF);
bmd.draw(obj);
_faceSearcher.detectFace( bmd, i/*, "xml/test_face.xml"*/ );
_faceSearcher.addEventListener(DetectEvent.SUCCESS, detectTargetFaceComplete);
}
private function detectTargetFaceComplete(e:DetectEvent):void{
_faceSearcher.removeEventListener(DetectEvent.SUCCESS, detectTargetFaceComplete);
_collageList[e.collageObjIndex].bmd = e.bmd;
_collageList[e.collageObjIndex].xmlObj = e.xmlObj;
_isCompDetect = true;
checkReady();
}
/* 写真フェード */
private function showPhoto(i:uint):void {
var resObj:Object = CollageMaker.createCollage( _collageList[i].bmd, _collageList[i].xmlObj, _faceList );
_photoBm.bitmapData = resObj.collageBmd;
_photoBm.smoothing = true;
//拡大の中心を _photoSpr の原点へ移動
_photoBm.x = -resObj.pivotObj.x;
_photoBm.y = -resObj.pivotObj.y;
//
var stageW:Number = stage.stageWidth;
var stageH:Number = stage.stageHeight;
var scale:Number = Math.max( stageW/_photoBm.width, stageH/_photoBm.height );
_photoSpr.scaleX = _photoSpr.scaleY = scale;
_photoSpr.x = stageW/2 - _photoSpr.width/2 - _photoBm.x*scale;
_photoSpr.y = stageH/2 - _photoSpr.height/2 - _photoBm.y*scale;
//
var zoom:Number = 1.3;
var goalX:Number = _photoSpr.x;
var goalY:Number = _photoSpr.y;
var pivotX:Number = resObj.pivotObj.x;
var pivotY:Number = resObj.pivotObj.y;
var faceW:Number = resObj.pivotObj.w;
var faceH:Number = resObj.pivotObj.h;
var limL:Number = (_photoSpr.width-stageW)/2 + faceW/2;
var limR:Number = (_photoSpr.width-stageW)/2 + stageW - faceW/2;
var limT:Number = (_photoSpr.height-stageH)/2 + faceH/2;
var limB:Number = (_photoSpr.height-stageH)/2 + stageH - faceH/2;
if( pivotX*scale < limL ){
//左にはみ出している場合
goalX -= (pivotX*scale - limL);
}else if( pivotX*scale > limR ){
//右にはみ出している場合
goalX -= (pivotX*scale - limR);
}
if( pivotY*scale < limT ){
//上にはみ出している場合
goalY -= (pivotY*scale - limT);
}else if( pivotY*scale > limB ){
//下にはみ出している場合
goalY -= (pivotY*scale - limB);
}
if( _it != null ) _it.stop();
_it = BetweenAS3.delay(
BetweenAS3.parallel(
BetweenAS3.serial(
BetweenAS3.tween( _photoSpr, {alpha:1}, {alpha:0}, 3, Linear.linear),
BetweenAS3.delay(BetweenAS3.tween( _photoSpr, {alpha:0}, {alpha:1}, 3, Linear.linear), 6)
),
BetweenAS3.tween( _photoSpr, {x:goalX, y:goalY, scaleX:scale*zoom, scaleY:scale*zoom}, null, 12, Linear.linear)
),
.8);
_it.addEventListener(TweenEvent.COMPLETE, tweenComplete);
_it.play();
}
private function tweenComplete(e:TweenEvent):void {
_it.removeEventListener(TweenEvent.COMPLETE, tweenComplete);
_isCompShow = true;
checkReady();
}
}
}
import flash.display.*;
import flash.events.*;
import flash.net.*;
import com.bit101.components.*;
import flash.geom.Matrix;
import flash.geom.ColorTransform;
import flash.system.LoaderContext;
class SettingPanel extends Sprite {
private var _fileReference:FileReference;
private var _faceSearcher:FaceSearcher;
private var _step1State:Label; //STEP1の状態を表示
private var _step2State:Label; //STEP2の状態を表示
private var _inputText:InputText;
private var _rssReader:MediaRSSReader;
private var _urlList:Array;
private var _faceBm:Bitmap;
private var _thumbBm:Bitmap;
private var _isReadyStep1:Boolean;
private var _isReadyStep2:Boolean;
private var _playBtn:PushButton;
private var _dummyPlayBtn:Sprite;
private var _checkMark1:Sprite;
private var _checkMark2:Sprite;
private var _faceList:Vector.<FaceObject>;
public function SettingPanel() {
_faceSearcher = new FaceSearcher();
_rssReader = new MediaRSSReader();
var url:String = "http://assets.wonderfl.net/images/related_images/6/64/6401/640135614df9dab6c1be318fedbd181c38ed2d74";
var loader:Loader = new Loader();
loader.load(new URLRequest(url), new LoaderContext(true));
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadComplete);
}
private function loadComplete(e:Event):void {
e.target.removeEventListener(Event.COMPLETE, loadComplete);
var bm:Bitmap = Bitmap(e.target.content);
addChild( bm );
var s:Shape = new Shape();
s.graphics.beginFill(0xF3F3F3, 1);
s.graphics.drawRect(8, 95, 449, 13);
s.graphics.drawRect(8, 286, 449, 13);
addChild( s );
_faceBm = new Bitmap( new BitmapData(50, 50, true, 0x0) );
_faceBm.x = 10;
_faceBm.y = 143;
addChild( _faceBm );
_thumbBm = new Bitmap( new BitmapData(435, 50, true, 0x0) );
_thumbBm.x = 10;
_thumbBm.y = 334;
addChild( _thumbBm );
_fileReference = new FileReference();
//step1
var expText1:Label = new Label(this, 10, 92, "Up-load the photograph of your face.");
changeColor( expText1, 0x000000 );
var loadImageBtn:PushButton = new PushButton(this, 10, 113, "Load Image", clickLoadImageBtn);
var webCamBtn:PushButton = new PushButton(this, 115, 113, "Web cam", clickWebCam);
_checkMark1 = drawCheckMark(14, 201);
_checkMark1.alpha = .2;
addChild(_checkMark1);
_step1State = new Label(this, 38, 200, "-");
_step1State.alpha = .2;
//step2
var expText2:Label = new Label(this, 10, 283, "Input the word that is where you wanted to go in this summer. ");
changeColor( expText2, 0x000000 );
_inputText = new InputText(this, 10, 304, "kamakura");
var searchBtn:PushButton = new PushButton(this, 218, 304, "Search", clickSearchBtn);
_inputText.setSize(205, 20);
_checkMark2 = drawCheckMark(14, 392);
_checkMark2.alpha = .2;
addChild(_checkMark2);
_step2State = new Label(this, 38, 391, "-");
_step2State.alpha = .2;
//slideshow
_playBtn = new PushButton(this, 352, 432, "Play slideshow!", clickPlayBtn);
_dummyPlayBtn = new Sprite();
_dummyPlayBtn.graphics.beginFill (0xF3F3F3, .7);
_dummyPlayBtn.graphics.drawRect(0, 0, _playBtn.width, _playBtn.height);
_dummyPlayBtn.x = _playBtn.x;
_dummyPlayBtn.y = _playBtn.y;
addChild(_dummyPlayBtn);
}
private function set isReadyStep1(v:Boolean):void {
_isReadyStep1 = v;
checkReady();
}
private function set isReadyStep2(v:Boolean):void {
_isReadyStep2 = v;
checkReady();
}
/******************************************************/
/* step1 */
private function clickLoadImageBtn(e:MouseEvent):void {
_fileReference.addEventListener(Event.SELECT, selectHandler);
var fileFilter:FileFilter = new FileFilter("Images", "*.jpg;*.jpeg;*.gif;*.png");
_fileReference.browse([fileFilter]);
isReadyStep1 = false;
}
private function selectHandler(e:Event):void {
_fileReference.removeEventListener(Event.SELECT, selectHandler);
if(_fileReference.size <= 1048576*2){
_fileReference.addEventListener(Event.COMPLETE, completeHandler);
_fileReference.load();
}else{
//容量オーバー
_step1State.text = "The capacity of the image is too large...";
_step1State.alpha = 1;
changeColor(_step1State, 0xFF0000);
}
}
private function completeHandler(e:Event):void {
_fileReference.removeEventListener(Event.COMPLETE, completeHandler);
var ld:Loader = new Loader();
ld.contentLoaderInfo.addEventListener(Event.INIT, loadCompleteHandler);
ld.loadBytes(_fileReference.data);
}
//ローカルイメージ読み込み完了
private function loadCompleteHandler(e:Event):void {
e.target.removeEventListener(Event.INIT, loadCompleteHandler);
//
var bm:Bitmap = e.target.content as Bitmap;
bm.smoothing = true;
var max:Number = Math.min(bm.width, bm.height);
var scale:Number = 50 / max;
var mtx:Matrix = new Matrix();
mtx.translate( (max-bm.width)/2, (max-bm.height)/2 );
mtx.scale(scale, scale);
_faceBm.bitmapData.draw(bm, mtx, null, null, null, true);
startDetectFace( bm.bitmapData );
}
//webカメラ
private function clickWebCam(e:Event):void {
var webCamPanel:WebCamPanel = new WebCamPanel();
webCamPanel.addEventListener( WebCamEvent.COMPLETE, shotComplete);
webCamPanel.addEventListener( WebCamEvent.CANCEL, shotCancel);
addChild( webCamPanel );
}
private function shotComplete(e:WebCamEvent):void {
e.target.removeEventListener( WebCamEvent.COMPLETE, shotComplete);
e.target.removeEventListener( WebCamEvent.CANCEL, shotCancel);
var scale:Number = 50/e.bmd.width;
var mtx:Matrix = new Matrix();
mtx.scale(scale, scale);
_faceBm.bitmapData.draw(e.bmd, mtx, null, null, null, true);
startDetectFace( e.bmd );
}
private function shotCancel(e:WebCamEvent):void {
e.target.removeEventListener( WebCamEvent.COMPLETE, shotComplete);
e.target.removeEventListener( WebCamEvent.CANCEL, shotCancel);
}
//顔認識
private function startDetectFace( bmd:BitmapData ):void {
_faceSearcher.detectFace( bmd, 0/*, "xml/miyazaki_face.xml"*/ );
_faceSearcher.addEventListener(DetectEvent.SUCCESS, detectBaseFaceComplete);
_faceSearcher.addEventListener(DetectEvent.ERROR, detectBaseFaceError);
_step1State.text = "Recognizing photos on process...";
_checkMark1.alpha = .2;
_step1State.alpha = 1;
changeColor(_step1State, 0x666666);
}
private function detectBaseFaceError(e:DetectEvent):void{
_faceSearcher.removeEventListener(DetectEvent.SUCCESS, detectBaseFaceComplete);
_faceSearcher.removeEventListener(DetectEvent.ERROR, detectBaseFaceError);
//エラー
_step1State.text = "Couldn't recognize face!";
changeColor(_step1State, 0xFF0000);
}
private function detectBaseFaceComplete(e:DetectEvent):void{
_faceSearcher.removeEventListener(DetectEvent.SUCCESS, detectBaseFaceComplete);
_faceSearcher.removeEventListener(DetectEvent.ERROR, detectBaseFaceError);
_faceList = CollageMaker.clipFace( e.bmd, e.xmlObj );
//認識成功
if( _faceList.length == 0){
_step1State.text = "Couldn't recognize face!";
changeColor(_step1State, 0xFF0000);
}else{
isReadyStep1 = true;
_step1State.text = (_faceList.length==1)? "Recognized 1 face." : "Recognized "+_faceList.length+" faces.";
_checkMark1.alpha = 1;
}
}
/******************************************************/
/* step2 */
private function clickSearchBtn(e:Event):void {
_rssReader.addEventListener(RSSReadEvent.SUCCESS, feedLoadComplete);
_rssReader.addEventListener(RSSReadEvent.ERROR, feedLoadError);
var tags:String = _inputText.text;
var tagList:Array = tags.split(",");
if(tagList[0]!="") tags = tags + ",friends";
_rssReader.loadFeed( tags );
_step2State.text = "Searching for photos...";
_checkMark2.alpha = 1;
_step2State.alpha = 1;
_checkMark2.alpha = .2;
changeColor(_step2State, 0x666666);
isReadyStep2 = false;
_thumbBm.bitmapData.dispose();
_thumbBm.bitmapData = new BitmapData(435, 50, true, 0x0);
}
private function feedLoadComplete(e:RSSReadEvent):void {
_rssReader.removeEventListener(RSSReadEvent.SUCCESS, feedLoadComplete);
_rssReader.removeEventListener(RSSReadEvent.ERROR, feedLoadError);
_urlList = e.urlList;
_step2State.text = "Find "+_urlList.length+" photos."
var len:Number = Math.min(_urlList.length, 8);
for(var i:uint=0; i<len; ++i) {
var ldr:Loader;
ldr = new Loader;
ldr.x = i; //通し番号として使用
ldr.contentLoaderInfo.addEventListener(Event.COMPLETE, thumbLoadComplete);
ldr.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR,thumbIoError);
ldr.load( new URLRequest(_urlList[i]), new LoaderContext(true));
}
_checkMark2.alpha = 1;
isReadyStep2 = true;
}
private function feedLoadError(e:RSSReadEvent):void{
_rssReader.removeEventListener(RSSReadEvent.SUCCESS, feedLoadComplete);
_rssReader.removeEventListener(RSSReadEvent.ERROR, feedLoadError);
//エラー
_step2State.text = "No photos found.";
changeColor(_step2State, 0xFF0000);
}
private function thumbLoadComplete(e:Event):void {
e.target.removeEventListener(Event.COMPLETE, thumbLoadComplete);
e.target.removeEventListener(IOErrorEvent.IO_ERROR,thumbIoError);
var bm:Bitmap = e.target.content as Bitmap;
bm.smoothing = true;
var max:Number = Math.min(bm.width, bm.height);
var scale:Number = 50 / max;
var mtx:Matrix = new Matrix();
mtx.translate( (max-bm.width)/2, (max-bm.height)/2 );
mtx.scale(scale, scale);
mtx.translate( (55*e.target.loader.x), 0 );
_thumbBm.bitmapData.draw(bm, mtx, null, null, null, true);
}
private function thumbIoError(e:IOErrorEvent):void {
e.target.removeEventListener(Event.COMPLETE, thumbLoadComplete);
e.target.removeEventListener(IOErrorEvent.IO_ERROR,thumbIoError);
}
/******************************************************/
private function clickPlayBtn(e:Event):void {
dispatchEvent( new PanelEvent(PanelEvent.COMPLETE, _faceList, _urlList) );
}
/******************************************************/
private function checkReady():void {
if(_isReadyStep1 && _isReadyStep2){
_dummyPlayBtn.visible = false;
}else{
_dummyPlayBtn.visible = true;
}
}
/******************************************************/
private function changeColor(obj:DisplayObject, color:Number):void {
var colorTransform:ColorTransform = obj.transform.colorTransform;
colorTransform.color = color;
obj.transform.colorTransform = colorTransform;
}
private function drawCheckMark(myX:Number=0, myY:Number=0):Sprite {
var spr:Sprite = new Sprite();
spr.graphics.beginFill (0x999999, 1);
spr.graphics.moveTo(0, 8);
spr.graphics.lineTo (8, 16);
spr.graphics.lineTo (21, 3);
spr.graphics.lineTo (18, 0);
spr.graphics.lineTo (8, 10);
spr.graphics.lineTo (3, 5);
spr.graphics.endFill();
spr.x = myX;
spr.y = myY;
return spr;
}
}
import flash.events.Event;
class PanelEvent extends Event {
public static const COMPLETE:String="complete";
public var faceList:Vector.<FaceObject>;
public var urlList:Array;
function PanelEvent(type:String, myFaceList:Vector.<FaceObject>, myUrlList:Array) {
super(type);
faceList = myFaceList;
urlList = myUrlList;
}
public override function clone():Event {
return new PanelEvent(type, faceList, urlList);
}
public override function toString():String {
return formatToString("PanelEvent","type","bubbles","cancelable","eventPhase","faceList","urlList");
}
}
import flash.display.*;
import flash.media.Camera;
import flash.media.Video;
import flash.events.Event;
import flash.geom.Matrix;
import com.bit101.components.*;
import org.libspark.betweenas3.BetweenAS3;
import org.libspark.betweenas3.easing.*;
import org.libspark.betweenas3.tweens.ITween;
import org.libspark.betweenas3.events.TweenEvent;
class WebCamPanel extends Sprite {
private var _captureContainer:Sprite;
private var _uiContainer:Sprite;
private var _camera:Camera;
private var _video:Video;
private var _photo:Sprite;
private var _bmd:BitmapData;
public function WebCamPanel() {
_captureContainer = new Sprite();
_uiContainer = new Sprite();
_photo = new Sprite();
addChild( _captureContainer );
addChild( _uiContainer );
_captureContainer.graphics.beginFill(0xD8D8D8, 1);
_captureContainer.graphics.drawRect(0, 0, 465, 465);
_captureContainer.graphics.beginFill(0xF3F3F3, 1);
_captureContainer.graphics.drawRect(1, 1, 463, 463);
_captureContainer.graphics.beginFill(0xCCCCCC, 1);
_captureContainer.graphics.drawRect(16, 16, 432, 432);
//
_uiContainer.graphics.lineStyle(1, 0x000000, .2);
_uiContainer.graphics.drawEllipse(130, 95, 207, 237);
_uiContainer.graphics.moveTo(130, 214);
_uiContainer.graphics.lineTo(130+207, 214);
_uiContainer.graphics.moveTo(233, 95);
_uiContainer.graphics.lineTo(233, 95+237);
_camera = Camera.getCamera();
_camera.setMode(445, 445, 10, false);
_video = new Video(_camera.width, _camera.height);
_video.attachCamera(_camera);
_video.scaleX = -1;
var margin:Number = Math.floor( (465-_camera.width)/2 );
_video.x = _camera.width + margin;
_video.y = margin;
_captureContainer.addChild(_video);
var shotBtn:PushButton = new PushButton(_uiContainer, 182, 380, "Shot!", clickShotBtn);
var closeBtn:PushButton = new PushButton(_uiContainer, 465-margin-21-5, margin+5, "X", clickCloseBtn);
closeBtn.setSize(21, 20);
}
private function clickShotBtn(e:Event):void {
_video.attachCamera(null); //カメラを停止
_bmd = new BitmapData(465, 465, false, 0x000000);
_bmd.draw(_captureContainer);
var bm:Bitmap = new Bitmap( _bmd );
bm.smoothing = true;
_photo.addChild( bm );
addChild( _photo );
var w:Number = _photo.width;
var pivot:Number = Math.random()*w;
var lr:Number = (pivot > w/2)? 1 : -1;
var rot:Number = Math.random()*60*lr;
_photo.x = pivot;
bm.x = -pivot;
var t:ITween = BetweenAS3.parallel(
BetweenAS3.tween(bm, {transform: {colorTransform: {redOffset: 0, greenOffset: 0, blueOffset: 0}}},
{transform: {colorTransform: {redOffset: 255, greenOffset: 255, blueOffset: 255}}},
1, Sine.easeOut),
BetweenAS3.delay( BetweenAS3.tween(_photo, {y:stage.stageHeight+bm.height, rotation:rot }, null, 1, Quart.easeIn), 2)
);
t.addEventListener(TweenEvent.COMPLETE, photoOutComplete);
t.play();
dispatchEvent( new WebCamEvent(WebCamEvent.COMPLETE, _bmd.clone()) );
//
removeChild( _uiContainer );
removeChild( _captureContainer );
}
private function photoOutComplete(e:TweenEvent):void {
removeChild( _photo );
}
private function clickCloseBtn(e:Event):void {
_video.attachCamera(null); //カメラを停止
parent.removeChild(this);
dispatchEvent( new WebCamEvent(WebCamEvent.CANCEL) );
}
}
import flash.events.Event;
import flash.display.BitmapData;
class WebCamEvent extends Event {
public static const COMPLETE:String = "complete";
//public static const SLIDE_COMPLETE = "slideComplete";
public static const CANCEL:String = "cancel";
public var bmd:BitmapData;
function WebCamEvent(type:String, myBmd:BitmapData=null) {
super(type);
bmd = myBmd;
}
public override function clone():Event {
return new WebCamEvent(type, bmd);
}
public override function toString():String {
return formatToString("WebCamEvent","type","bubbles","cancelable","eventPhase","bmd");
}
}
import flash.display.*;
import flash.events.*;
import com.adobe.images.JPGEncoder;
import flash.utils.ByteArray;
import flash.net.URLRequest;
import flash.net.URLLoader;
import flash.net.URLRequestMethod;
class FaceSearcher extends EventDispatcher {
private var _jpgEncoder:JPGEncoder;
private var _bmd:BitmapData;
private var _collageObjIndex:uint;
public function FaceSearcher() {
_jpgEncoder = new JPGEncoder( 80 );
}
public function detectFace( bmd:BitmapData, collageObjIndex:uint=0, localTestUrl:String=null ):void{
_bmd = bmd;
_collageObjIndex = collageObjIndex;
var ba:ByteArray = _jpgEncoder.encode( _bmd );
var req:URLRequest = new URLRequest();
req.data = ba;
req.url = "http://detectface.com/api/detect";
req.method = URLRequestMethod.POST;
req.contentType = "image/jpeg";
var urlLoader:URLLoader = new URLLoader();
urlLoader.addEventListener(Event.COMPLETE,onCompleteHandler);
urlLoader.addEventListener(IOErrorEvent.IO_ERROR,ioErrorHandler);
if(localTestUrl != null){
urlLoader.load(new URLRequest( localTestUrl ));
}else{
urlLoader.load(req);
}
}
private function onCompleteHandler(e:Event):void {
e.target.removeEventListener(Event.COMPLETE,onCompleteHandler);
e.target.removeEventListener(IOErrorEvent.IO_ERROR,ioErrorHandler);
if (e.target.data == "error") {
dispatchEvent( new DetectEvent(DetectEvent.ERROR) );
} else {
//認識成功
dispatchEvent( new DetectEvent(DetectEvent.SUCCESS, _bmd, e.target.data, _collageObjIndex) );
}
}
private function ioErrorHandler(e:IOErrorEvent):void {
e.target.removeEventListener(Event.COMPLETE,onCompleteHandler);
e.target.removeEventListener(IOErrorEvent.IO_ERROR,ioErrorHandler);
dispatchEvent( new DetectEvent(DetectEvent.ERROR) );
}
}
import flash.events.Event;
import flash.display.BitmapData;
class DetectEvent extends Event {
public static const SUCCESS:String="success";
public static const ERROR:String = "error";
public var bmd:BitmapData;
public var xmlObj:Object;
public var collageObjIndex:uint;
function DetectEvent(type:String, myBmd:BitmapData=null, myXmlObj:Object=null, myCollageObjIndex:uint=0) {
super(type);
bmd = myBmd;
xmlObj = myXmlObj;
collageObjIndex = myCollageObjIndex;
}
public override function clone():Event {
return new DetectEvent(type, bmd, xmlObj, collageObjIndex);
}
public override function toString():String {
return formatToString("DetectEvent","type","bubbles","cancelable","eventPhase","bmd","xmlObj", "collageObjIndex");
}
}
import flash.net.URLRequest;
import flash.events.*;
import flash.net.URLLoader;
class MediaRSSReader extends EventDispatcher {
private var media:Namespace = new Namespace("http://search.yahoo.com/mrss/");
public function MediaRSSReader() {}
public function loadFeed( tag:String ):void {
var feed:String = "http://api.flickr.com/services/feeds/photos_public.gne?tags="+ tag +"&format=rss_200";
var ldr:URLLoader = new URLLoader;
ldr.addEventListener(Event.COMPLETE, loadComplete);
ldr.addEventListener(IOErrorEvent.IO_ERROR,ioErrorHandler);
ldr.load(new URLRequest(feed));
}
private function loadComplete(e:Event):void {
e.target.removeEventListener(Event.COMPLETE, loadComplete);
e.target.removeEventListener(IOErrorEvent.IO_ERROR,ioErrorHandler);
var imageUriList:Array = XML(e.target.data)..media::thumbnail.@url.toXMLString().split('\n');
if(imageUriList[0] != ""){
dispatchEvent( new RSSReadEvent(RSSReadEvent.SUCCESS, imageUriList) );
}else{
dispatchEvent( new RSSReadEvent(RSSReadEvent.ERROR) );
}
}
private function ioErrorHandler(e:IOErrorEvent):void {
e.target.removeEventListener(Event.COMPLETE, loadComplete);
e.target.removeEventListener(IOErrorEvent.IO_ERROR,ioErrorHandler);
dispatchEvent( new RSSReadEvent(RSSReadEvent.ERROR) );
}
}
import flash.events.Event;
class RSSReadEvent extends Event {
public static const SUCCESS:String="success";
public static const ERROR:String = "error";
public var urlList:Array;
function RSSReadEvent(type:String, myList:Array=null) {
super(type);
urlList = myList;
}
public override function clone():Event {
return new RSSReadEvent(type, urlList);
}
public override function toString():String {
return formatToString("RSSReadEvent","type","bubbles","cancelable","eventPhase","urlList");
}
}
import flash.display.*;
import flash.geom.*;
import com.adobe.protocols.dict.events.MatchEvent;
class CollageMaker {
private static const MARGIN:Number = 1;
private static const CIR_MARGIN:Number = 1;
/* 顔を切り抜き */
public static function clipFace(myBmd:BitmapData, myXmlObj:Object):Vector.<FaceObject> {
var faceList:Vector.<FaceObject> = new Vector.<FaceObject>();
var xml:XML = XML(myXmlObj);
var bmd:BitmapData = myBmd;
var len:int = xml.face.length();
for(var i:uint=0; i<len; i++){
if(xml.face[i].features != undefined){
var ptList:Vector.<Point> = new Vector.<Point>();
var isStart:Boolean = true;
var oldX:Number;
var oldY:Number;
var featuresXl:XMLList = xml.face[i].features;
for(var pId:uint=1; pId<=10; pId++){
var xl:XMLList = featuresXl.point.(hasOwnProperty("@id") && @id == "F"+pId);
ptList.push( new Point(xl.@x, xl.@y) );
}
var lastPt:Point = ptList[ptList.length-1];
//マスク用に面を描画
var spr:Sprite = new Sprite();
spr.graphics.beginFill(0xFF0000, 1);
spr.graphics.moveTo(lastPt.x+(ptList[0].x-lastPt.x)/2, lastPt.y+(ptList[0].y-lastPt.y)/2);
for(var j:uint=0; j<ptList.length-1; j++){
var anchorX:Number = ptList[j].x+(ptList[j+1].x-ptList[j].x)/2;
var anchorY:Number = ptList[j].y+(ptList[j+1].y-ptList[j].y)/2;
spr.graphics.curveTo(ptList[j].x, ptList[j].y, anchorX, anchorY);
}
anchorX = lastPt.x+(ptList[0].x-lastPt.x)/2;
anchorY = lastPt.y+(ptList[0].y-lastPt.y)/2;
spr.graphics.curveTo(lastPt.x, lastPt.y, anchorX, anchorY);
spr.graphics.endFill();
var maskBmd:BitmapData = new BitmapData(bmd.width, bmd.height, true, 0x00000000);
maskBmd.draw(spr);
var diffRect:Rectangle = maskBmd.getColorBoundsRect(0xFFFFFFFF, 0x00000000, false);
var faceBmd:BitmapData = new BitmapData(diffRect.width+MARGIN*2, diffRect.height+MARGIN*2, true, 0x00000000);
faceBmd.copyPixels(bmd, diffRect, new Point(MARGIN, MARGIN), maskBmd, new Point(diffRect.x, diffRect.y));
//両目の位置からいろいろ算出
var resObj:Object = getRevision( xml.face[i] );
//
/* 詳細まで認識した場合用 */
var faceSpr:Sprite = new Sprite();
var faceBm:Bitmap = new Bitmap( faceBmd );
faceBm.smoothing = true;
//両目の中点を原点に移動
faceBm.rotation = -resObj.rad * (180/Math.PI);
//n度回転させた後のずれを算出
var faceDiffX:Number = diffRect.x-resObj.diffX -MARGIN;
var faceDiffY:Number = diffRect.y-resObj.diffY -MARGIN;
faceBm.x = Math.cos(-resObj.rad)*faceDiffX-Math.sin(-resObj.rad)*faceDiffY;
faceBm.y = Math.cos(-resObj.rad)*faceDiffY+Math.sin(-resObj.rad)*faceDiffX;
faceSpr.addChild( faceBm );
//
/* 大まかな位置しか認識されなかった場合用 */
var roughSpr:Sprite = new Sprite();
var roughBm:Bitmap = new Bitmap( faceBmd );
roughBm.smoothing = true;
roughBm.rotation = -resObj.rad * (180/Math.PI);
var roughX:Number = diffRect.x-resObj.diffX-MARGIN;
var roughY:Number = -spr.height/2-MARGIN;
roughBm.x = Math.cos(-resObj.rad)*roughX-Math.sin(-resObj.rad)*roughY;
roughBm.y = Math.cos(-resObj.rad)*roughY+Math.sin(-resObj.rad)*roughX;
roughSpr.addChild( roughBm );
//
/* 顔が見つからなかった場合用 */
var cirW:Number = diffRect.height/.73;
var cirH:Number = cirW*1.3;
var cirMaskSpr:Sprite = new Sprite();
cirMaskSpr.graphics.beginFill(0xFF0000, 1);
cirMaskSpr.graphics.drawEllipse(1, 1, cirW-2, cirH-2);
var mar:Number = 1;
var cirMaskBmd:BitmapData = new BitmapData(cirW, cirH, true, 0x00000000);
cirMaskBmd.draw( cirMaskSpr );
//
var cirBaseBmd:BitmapData = new BitmapData(cirW, cirH, false, 0xF3F3F3);
var diffBaseX:Number = -diffRect.x + (cirW-diffRect.width)/2;
var diffBaseY:Number = -diffRect.y + (cirH-diffRect.height)/2 - cirH*.05;
cirBaseBmd.draw(bmd, new Matrix(1, 0, 0, 1, diffBaseX, diffBaseY), null, null, null, true);
//
var cirFaceBmd:BitmapData = new BitmapData(cirW+CIR_MARGIN*2, cirH+CIR_MARGIN*2, true, 0x00000000);
cirFaceBmd.copyPixels(cirBaseBmd, new Rectangle(0, 0, cirW, cirH), new Point(CIR_MARGIN, CIR_MARGIN), cirMaskBmd, new Point(0, 0));
//
var cirFaceSpr:Sprite = new Sprite();
var cirBm:Bitmap = new Bitmap(cirFaceBmd/*cirMaskBmd*/);
cirBm.smoothing = true;
cirBm.x = -cirW/2 - CIR_MARGIN;
cirBm.y = -cirH/2 - CIR_MARGIN;
cirFaceSpr.addChild( cirBm );
//
faceList.push( new FaceObject( faceSpr, roughSpr, cirFaceSpr, resObj.eyeDist) );
}
}
return faceList;
}
/* 顔をコラージュ */
public static function createCollage(myBmd:BitmapData, myXmlObj:Object, faceList:Vector.<FaceObject>):Object {
var rate:Number = 1.5; //倍率(コラージュする写真の画質向上)
var resObj:Object = new Object();
var xml:XML = XML(myXmlObj);
var bmd:BitmapData = myBmd;
var collageBmd:BitmapData = new BitmapData(bmd.width*rate, bmd.height*rate, false, 0xFFFFFF);
var pivotList:Array = new Array();
//まずは元イメージを描画
collageBmd.draw( bmd, new Matrix(rate, 0, 0, rate, 0, 0), null, null, null, true);
//
var scale:Number;
var mtr:Matrix;
var faceObj:FaceObject;
//xmlをパースして見つかった顔の数だけコラージュ
var len:int = xml.face.length();
if(len == 0){
//1つも認識出来なかった場合
for(var k:uint=0; k<faceList.length; k++){
faceObj = faceList[k];
var cirSpr:Sprite = faceObj.circleSpr;
var minSize:Number = Math.min(collageBmd.width, collageBmd.height);
scale = minSize*.15 / cirSpr.height;
var margin:Number = cirSpr.width*scale/8;
mtr = new Matrix();
mtr.scale(scale, scale);
mtr.translate(cirSpr.width*scale/2 + margin*3 + ((cirSpr.width*scale)+margin)*k, cirSpr.height*scale/2 + margin*3);
//顔を描画
collageBmd.draw(cirSpr, mtr, null, null, null, true);
}
pivotList.push( {w:0, h:0, x:0, y:0});
}else if(xml.face.features.length() != 0){
//詳細まで認識されている顔がある場合、その顔のみ置き換え
for(var i:uint=0; i<len; i++){
if(xml.face[i].features != undefined){
//コラージュする顔をランダム選択
faceObj = faceList[ Math.floor(Math.random()*faceList.length) ];
var faceSpr:Sprite = faceObj.detailSpr;
//両目の位置からいろいろ算出
var res:Object = CollageMaker.getRevision( xml.face[i] );
scale = res.eyeDist / faceObj.eyeDist * rate;
mtr = new Matrix();
mtr.rotate( res.rad );
mtr.scale(scale, scale);
mtr.translate(res.diffX*rate, res.diffY*rate);
//顔を描画
collageBmd.draw(faceSpr, mtr, null, null, null, true);
pivotList.push( {w:faceSpr.width*scale, h:faceSpr.height*scale, x:res.diffX*rate, y:res.diffY*rate} );
}
}
}else{
//大まかな位置しか認識されなかった場合、認識したすべての顔を置き換え
for(var j:uint=0; j<len; j++){
//コラージュする顔をランダム選択
faceObj = faceList[ Math.floor(Math.random()*faceList.length) ];
var roughSpr:Sprite = faceObj.roughSpr;
//
scale = xml.face[j].bounds.@height / (roughSpr.height-MARGIN*2) *rate;
var myX:Number = Number(xml.face[j].bounds.@x) + (xml.face[j].bounds.@width)/2;
var myY:Number = Number(xml.face[j].bounds.@y) + (xml.face[j].bounds.@height)/2;
mtr = new Matrix();
mtr.scale(scale, scale);
mtr.translate(myX*rate, myY*rate);
//顔を描画
collageBmd.draw(roughSpr, mtr, null, null, null, true);
pivotList.push( {w:roughSpr.width*scale, h:roughSpr.height*scale, x:myX*rate, y:myY*rate} );
}
}
resObj.collageBmd = collageBmd;
resObj.pivotObj = pivotList[ Math.floor(Math.random()*pivotList.length) ];
return resObj;
}
/**
* 両目の位置からいろいろ算出
* eyeDist: 両目の距離
* rad: 両目の角度(ラジアン)
*/
public static function getRevision( myXl:XML ):Object {
var resObj:Object = new Object();
var xl:XMLList;
xl = myXl.features..point.(hasOwnProperty("@id") && @id == "PR");
var ptR:Point = new Point( xl.@x, xl.@y );
xl = myXl.features..point.(hasOwnProperty("@id") && @id == "PL");
var ptL:Point = new Point( xl.@x, xl.@y );
var dx:Number = ptR.x-ptL.x;
var dy:Number = ptR.y-ptL.y;
var vector:Point = ptL.subtract(ptR);
resObj.eyeDist = Math.sqrt(dx*dx + dy*dy);
resObj.diffX = ptR.x - dx/2;
resObj.diffY = ptR.y - dy/2;
resObj.rad = Math.atan2(vector.y, vector.x);
return resObj;
}
}
import flash.display.Sprite;
import flash.geom.Point;
import flash.display.Bitmap;
class FaceObject {
private var _faceSpr:Sprite; //切り抜いた顔
private var _roughSpr:Sprite;
private var _circleSpr:Sprite;
public var eyeDist:Number; //両目の距離
public function FaceObject(myFaceSpr:Sprite, myRoughSpr:Sprite, myCircleSpr:Sprite, myEyeDist:Number) {
_faceSpr = myFaceSpr;
_roughSpr = myRoughSpr;
_circleSpr = myCircleSpr;
eyeDist = myEyeDist;
}
public function get detailSpr():Sprite {
_faceSpr.scaleX = _faceSpr.scaleY = 1;
_faceSpr.x = _faceSpr.y = 0;
return _faceSpr;
}
public function get roughSpr():Sprite {
_roughSpr.scaleX = _roughSpr.scaleY = 1;
_roughSpr.x = _roughSpr.y = 0;
return _roughSpr;
}
public function get circleSpr():Sprite {
_circleSpr.scaleX = _circleSpr.scaleY = 1;
_circleSpr.x = _circleSpr.y = 0;
return _circleSpr;
}
}
import flash.display.BitmapData;
import flash.display.DisplayObject;
class CollageObject {
public var url:String;
public var bmd:BitmapData; //元写真
public var xmlObj:Object; //顔の位置情報
public var content:DisplayObject;
public function CollageObject( myUrl:String ) {
url = myUrl;
}
}