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

forked from: 動体検知

まだまだ途中バージョン
* 作っている自分も良くわかってません。
* videoのサイズ大きいと負荷が高いのかPCのファンが高速回転汗
* 参考URL
* 動体検知:Flash8で動体検知
* http://faces.bascule.co.jp/motiondetection/
* bitmap操作関連:ActionScript 3.0 でラベリング (改)を勝手に添削
* http://void.heteml.jp/blog/archives/2007/10/as3_labeling.html
* 
* 一応完成
* バスキュールの動体検知ほぼそのままです。
* 無駄に長い気がする・・・as3なのだからas2のころより短く書けるはず・・・ですが自分では出来ません。
Get Adobe Flash player
by fumix 05 Jul 2009
// forked from fumix's 動体検知
// forked from fumix's forked from: 動体検知
// forked from ll_koba_ll's 動体検知
// write as3 code here..
/***
 * まだまだ途中バージョン
 * 作っている自分も良くわかってません。
 * videoのサイズ大きいと負荷が高いのかPCのファンが高速回転汗
 * 参考URL
 * 動体検知:Flash8で動体検知
 * http://faces.bascule.co.jp/motiondetection/
 * bitmap操作関連:ActionScript 3.0 でラベリング (改)を勝手に添削
 * http://void.heteml.jp/blog/archives/2007/10/as3_labeling.html
 * 
 * 一応完成
 * バスキュールの動体検知ほぼそのままです。
 * 無駄に長い気がする・・・as3なのだからas2のころより短く書けるはず・・・ですが自分では出来ません。
 
***/
package
{
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.BlendMode;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.filters.ConvolutionFilter;
	import flash.geom.ColorTransform;
	import flash.geom.Matrix;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import flash.media.Camera;
	import flash.media.Video;
	import flash.text.TextField;

	[SWF(width = "465", height = "465", frameRate = "30",backgroundColor="0x000000")]
	public class blend extends Sprite
	{
		private var camera:Camera;
		private var video:Video;
		private var now:BitmapData;
		private var before:BitmapData;
		private var bmd:BitmapData;
		private var rect:Rectangle;
		private var pt:Point;
		private var motion:MotionDetector;
		
		private var noiseReduction:ConvolutionFilter;
		
		
		private var size:int = 465;
		
		public function blend()
		{
			super();
			if (stage) init();
			else addEventListener(Event.ADDED_TO_STAGE, init);
		}
		/**
		 * 初期処理 
		 * @param e
		 * 
		 */
		private function init(e:Event = null):void 
		{
            removeEventListener(Event.ADDED_TO_STAGE, init);
            camera = Camera.getCamera();
            //カメラあり
            if(camera != null){
            	setUpCamera();
            //カメラ無し
            }else{
            	var txt:TextField = new TextField();
            	txt.text ='カメラ無し';
            	addChild(txt);
            }
            
			noiseReduction = new ConvolutionFilter(3, 3);
            noiseReduction.bias = -(0x1000 + 0x100 * 6);
            noiseReduction.matrix = [
                1,  1, 1,
                1, 16, 1,
                1,  1, 1
            ];            
            bmd = new BitmapData(size,size/camera.width * camera.height,false,0x000000);
            addChild(new Bitmap(bmd));
            //動体検知クラス
            motion = new MotionDetector();
            addEventListener(Event.ENTER_FRAME,loop);

/*            var btn:Sprite = new Sprite();
            btn.graphics.beginFill(0xFFFFFF);
            btn.graphics.drawRect(0,0,50,30);
            btn.graphics.endFill();
            btn.buttonMode = true;
            btn.addEventListener(MouseEvent.CLICK,loop);
            addChild(btn);
*/
            now = new BitmapData(camera.width,camera.height,false);
            before = new BitmapData(camera.width,camera.height,false);
            rect = new Rectangle(0, 0, camera.width,camera.height);
            pt = new Point;
		}
		/**
		 *カメラのセットアップ 
		 * 
		 */
		private function setUpCamera():void
		{
			camera.setMode(240,240,12);
			video = new Video(camera.width,camera.height);
			video.attachCamera(camera);
			addChild(video);
		}
		private function loop(e:Event=null):void
		{
			now.draw(video);
			now.draw(before,new Matrix(), new ColorTransform(),BlendMode.DIFFERENCE);
			var ret:uint = now.threshold(now,rect,pt,">",0xff111111,0xffffffff);
			//ノイズリダクション
			var ret2 = now.applyFilter(now, rect, pt, noiseReduction);
			before.draw(video);

			var drawMap:Sprite = new Sprite();
			var motionArray:Object = motion.Check(now);

			for(var idx:int=0; idx<motionArray.w.length; idx++){
				var tgt_rec:Rectangle = motionArray.w[idx].rect;
				if(tgt_rec.width * tgt_rec.height > 500){
				drawMap.graphics.beginFill(0xFF0000,0.5);
				drawMap.graphics.drawRect(tgt_rec.x*2,tgt_rec.y*2,tgt_rec.width*2,tgt_rec.height*2);
				drawMap.graphics.endFill();
				}
			}
			/*
			for(var idx=0; idx<motionArray.n.length; idx++){
				var tgt_rec:Rectangle = motionArray.n[idx].rect;
				drawMap.graphics.lineStyle(1,0x0000ff);
				drawMap.graphics.drawRect(tgt_rec.x,tgt_rec.y,tgt_rec.width,tgt_rec.height);							
			}
			*/
			//trace(lno);
			bmd.draw(video,new Matrix(2.0,0,0,2,0,0));
			bmd.draw(drawMap);
			
			
		}
	}
}
import flash.display.BitmapData;
import flash.geom.Rectangle;
import flash.geom.Point;

class MotionDetector
{
	private var _watchingRectArray:Array;
	private var _nowRectArray:Array;
	private var idCounter:int;
	/**
	 *コンストラクタ 
	 * 
	 */
	public function MotionDetector()
	{
		_watchingRectArray = new Array();
		idCounter = 0;
	}
	
	/**
	 * 動体の矩形の入った配列を返す 
	 * @param bmd チェックしたい画像(フィルタリング済み)
	 * @return 矩形情報の配列
	 * 
	 */
	public function Check(bmd:BitmapData):Object
	{
		var dst:BitmapData = bmd.clone();
		var temp:BitmapData = new BitmapData(dst.width,1,false,0x000000);
		var lno:int = 0;
		var zero:Point = new Point();

		_nowRectArray = new Array();
		var _tempRectArray:Array = new Array();
		
		//ブロック化
		var rect:Rectangle = dst.getColorBoundsRect(0xffffff,0xffffff,true);
		var area:Rectangle = new Rectangle(0,0,dst.width,1);

		while( !rect.isEmpty()){
			area.y = rect.top;
			temp.copyPixels(dst,area,zero);
			rect = temp.getColorBoundsRect(0xffffff,0xffffff,true);
			dst.floodFill(rect.x,area.y,0xff00ff);
			var br:Rectangle = dst.getColorBoundsRect(0xFFFFFF, 0xFF00FF);
			br.inflate(4,4);
			dst.fillRect(br, 0x0000ff);
			rect = dst.getColorBoundsRect( 0xffffff, 0xffffff, true );
			_nowRectArray.push(new ExRectangle(br));
		}
		//rectangleが重なってたら、同じものとみなして、数を絞る
		for(var idx:int=0; idx<_nowRectArray.length; idx++){
			var idx_rec:Rectangle = _nowRectArray[idx].rect;
			
			for(var i:int=idx+1; i<_nowRectArray.length; i++){
				var tgt_rec:Rectangle = _nowRectArray[i].rect;
				if( idx_rec!=tgt_rec && idx_rec.intersects(tgt_rec) ){
					_nowRectArray.push(new ExRectangle(idx_rec.union(tgt_rec)));
					_nowRectArray.splice(Number(i), 1);
					_nowRectArray.splice(Number(idx), 1);
					idx --;
					break;
				}
			}
		}

		//今回検出分とwatchしてる分の重なりを検証
		for(var i:int=0; i<_nowRectArray.length; i++){
			var rec_ref:ExRectangle = _nowRectArray[i];
			if(rec_ref.rect.width*rec_ref.rect.height >= bmd.width*bmd.height*0.7){
				//画面全体が変化している場合(カメラの自動露出調整、オートホワイトバランス調整など)は無視。
				_nowRectArray.splice(Number(i), 1);
				i--;
			}else{
				rec_ref.isects = new Array();
				for(var j in _watchingRectArray){
					var rec_watch:ExRectangle = _watchingRectArray[j];
					if(rec_watch.rect.intersects(rec_ref.rect)){
						rec_watch.isects.push(rec_ref);
						rec_ref.isects.push(rec_watch);
					}
				}
			}
		}
		//今回検出分がそれぞれ、いくつのwatch分に重なってるか
		for(i=0; i<_nowRectArray.length; i++){
			var rec_ref:ExRectangle = _nowRectArray[i];
			var isectsNum:int = rec_ref.isects.length;
			
			//1つもwatch中のrectとintersectしてない → 新規追加
			if(isectsNum == 0){
				if(rec_ref.rect.width*rec_ref.rect.height <= 50){
					//結構小さい → 廃棄
					//trace("tooSmall"+(rec_ref.width*rec_ref.height)+":"+(original_width*original_height*resolution_scale*_min_size_new_rect_rate));
					_nowRectArray.splice(Number(i), 1);
					i--;
				}else{
					//新規生成
					rec_ref.event = "create";
					rec_ref.life = 5;
					rec_ref.id = ++idCounter;
					_tempRectArray.push(rec_ref);
				}
			
			//複数のwatch中のrectとintersectしている → watch中のrectと最もたくさんintersectしているものに統合
			}
			else if(isectsNum >= 2){
				var rect_integrate:ExRectangle = null;
				for(var j=0; j<isectsNum-1; j++){
					var rec_now:ExRectangle = rec_ref.isects[j];
					var rec_next:ExRectangle = rec_ref.isects[j+1];
					rect_integrate = (rec_now.isects.length >= rec_next.isects.length) ? rec_now : rec_next;
				} 
				for(var j=0; j<isectsNum; j++){
					if(rect_integrate != rec_ref.isects[j]){
						var rec_disappear:ExRectangle = rec_ref.isects[j];
						//rec_disappear.event = "integrate";
						//rec_disappear.integarateTarget =  rect_integrate;
						//recs_disappeared.push(rec_disappear);
						//recs_watchingから統合されたrectを削除
						for(var k=0; k<_watchingRectArray.length; k++){
							if(_watchingRectArray[k] == rec_disappear){
								_watchingRectArray.splice(k, 1);
								break;
							}
						}
					}
				}				
			}
		}
		//重なりデータを下に、新規watch候補を選定
		for(var k=0; k<_watchingRectArray.length; k++){
			var rec_watch:ExRectangle = _watchingRectArray[k];
			var isectsNum = rec_watch.isects.length;
			if(isectsNum == 0){
				//0個の場合 → LIFE分継続
				//trace("watch_未intersect: id:" + rec_watch.id + " life:"+ rec_watch.life + " event: " + rec_watch.event);
				rec_watch.life -- ;
				rec_watch.event = "stay";
				_tempRectArray.push(rec_watch);
			}else if(isectsNum == 1){
				//一つの場合 → 維持
				//trace("watch_1つintersect: " + rec_watch.id  + " life:" + rec_watch.life);
				rec_watch.isects[0].id = rec_watch.id;
				rec_watch.isects[0].event = "move";
				rec_watch.isects[0].life = 5;
				rec_watch.isects[0].isects = new Array();
				_tempRectArray.push(rec_watch.isects[0]);
				
			}else{
				//複数個の場合 → union
				//それぞれの距離を測ったほうがいいかも
				//あるいは、一つのrectの面積の上限を決めるとか
				//(2つのrectが一旦unionされると以降つながり続ける→すれ違った人が、ずっと同じ人間だと認識されてしまうだろう)
				//計算面倒だなあ
				//trace("watch_複数にintersect: id:" + rec_watch.id + " 個数:" + rec_watch.isects.length + " life:" + rec_watch.life);
				while(rec_watch.isects.length > 1){
					rec_watch.isects[0].rect = rec_watch.isects[0].rect.union(rec_watch.isects[1].rect);
					rec_watch.isects.splice(1, 1);
				}
				rec_watch.isects[0].id = rec_watch.id;
				rec_watch.isects[0].event = "move";
				rec_watch.isects[0].life = 5;
				_tempRectArray.push(rec_watch.isects[0]);
			}
		}
		_watchingRectArray = new Array();
		
		//候補から、判定
		for(var i=0; i<_tempRectArray.length; i++){
			var rec_item:ExRectangle = _tempRectArray[i];
			var infratex = (rec_item.rect.width - 4 <= 4) ? 0 : 4;
			var infratey = (rec_item.rect.height - 4 <= 4) ? 0 : 4;
			rec_item.rect.inflate(-infratex, -infratey);
			
			//誰ともintersectしてない寿命切れのrectを削除
			if(rec_item.event == "stay"){
				if(rec_item.life <= 0){
					//trace("disappear : " + rec_item.id);
					//rec_item.event = "disappear";
					//recs_disappeared.push(rec_item);
				}else{
					_watchingRectArray.push(rec_item);
				}
			//移動、統合物
			}else if(rec_item.event == "move"){
				_watchingRectArray.push(rec_item);
			
			//新規
			}else if(rec_item.event == "create"){
				_watchingRectArray.push(rec_item);
			}
		}
		//trace("item:"+_watchingRectArray.length);
		return {w:_watchingRectArray,n:_nowRectArray};
	}

	public function get watchingRectArray():Array
	{
		return _watchingRectArray;
	}

	public function get nowRectArray():Array
	{
		return _nowRectArray;
	}
}

class ExRectangle
{
	public var rect:Rectangle;
	public var isects:Array;
	public var event:String;
	public var life:int;
	public var id:int;
	public function ExRectangle(r:Rectangle)
	{
		isects = new Array();
		rect = r;
		event = '';
		life = 0;
		id = 0;
	}	
}