カメラの前で体を動かしたら、箱が動く・・・みたいな実験
パターンマッチングによるカメラの移動方向推定
映像を縮小し、全体をすこしずつ移動して直前のフレームと
重ねあわせ、その差を比較することで移動方向を推定。
SSDという手法があるらしいが、それに近いのかも。
CPU負荷とのたたかいなので、scaleSmall、search_count、threshold
の組み合わせでよいところを設定。
search_countを多くすると、速い動きにも対応できるが、
それだけCPUに負担
[注意]固定カメラで顔を動かしたりしても反応しません。
運動カメラの動きをとらえます。
/**
* Copyright narutohyper ( http://wonderfl.net/user/narutohyper )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/ehLi
*/
// forked from utabi's パターンマッチングによるカメラの移動方向推定
// パターンマッチングによるカメラの移動方向推定
//
// 映像を縮小し、全体をすこしずつ移動して直前のフレームと
// 重ねあわせ、その差を比較することで移動方向を推定。
// SSDという手法があるらしいが、それに近いのかも。
// CPU負荷とのたたかいなので、scaleSmall、search_count、threshold
// の組み合わせでよいところを設定。
// search_countを多くすると、速い動きにも対応できるが、
// それだけCPUに負担
//
// [注意]固定カメラで顔を動かしたりしても反応しません。
// 運動カメラの動きをとらえます。
package {
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.display.BlendMode;
import flash.events.Event;
import flash.filters.BlurFilter;
import flash.filters.ColorMatrixFilter;
import flash.filters.ConvolutionFilter;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.geom.ColorTransform;
import flash.media.Camera;
import flash.media.Video;
import net.hires.debug.*;
import flash.text.*;
import alternativ5.engine3d.materials.FillMaterial;
import alternativ5.engine3d.primitives.Box;
import alternativ5.engine3d.events.MouseEvent3D
import alternativ5.types.Point3D;
import flash.display.Sprite;
import flash.display.BlendMode;
[SWF(frameRate="30")]
Wonderfl.capture_delay( 20 );
public class SimpleDemo extends Sprite {
private var camera:Camera;
private var video:Video;
private var videoWidth:int = 465;
private var videoHeight:int = 465;
private var raw:BitmapData;
private var rect:Rectangle;
private var pt:Point = new Point(0,0);
private var xp:int,yp:int;
// Properties for Patch Search
private var now:BitmapData;
private var nowMedium:BitmapData;
private var nowSmall:BitmapData;
private var last:BitmapData;
private var lastMedium:BitmapData;
private var lastSmall:BitmapData;
private var scaleSmall:Number = .12;
private var scaleMedium:Number = .25;
private var mSmall:Matrix = new Matrix();
private var mMedium:Matrix = new Matrix();
// 検索の順番
// 7 8 1
// 6 0 2
// 5 4 3
private var compareX:Array = [ 0, 1, 1, 1, 0, -1, -1, -1, 0];
private var compareY:Array = [ 0, -1, 0, 1, 1, 1, 0, -1, -1];
private var dNow:int = 0;
private var dResult:Point = new Point(0,0);
private var score:Number;
private var scoreNow:Number;
private var scoreLast:Number;
private var count:int = 0;
private var t:Number;
private var ii:int;
private var aVec:Vector.<uint>;
private var bVec:Vector.<uint>;
private var scoreIndiv:Number;
private var scoreNowIndiv:uint;
private var current:int;
private var dif:int;
//Sprite
private var arrow:Sprite;
public function SimpleDemo() {
camera=Camera.getCamera();
if (camera==null) {
} else {
start();
}
}
private function start():void {
camera.setMode(640, 480,30);
video = new Video(videoWidth, videoHeight);
video.attachCamera(camera);
raw = new BitmapData(videoWidth,videoHeight);
rect = raw.rect;
//this.addChild(video);
//this.addChild(new Bitmap(raw));
arrow = new Sprite();
arrow.graphics.beginFill(0xff0000);
arrow.graphics.moveTo(0,-2);
arrow.graphics.lineTo(40,-2);
arrow.graphics.lineTo(40,-10);
arrow.graphics.lineTo(63,0);
arrow.graphics.lineTo(40,10);
arrow.graphics.lineTo(40,2);
arrow.graphics.lineTo(0,2);
arrow.graphics.lineTo(0,-2);
arrow.graphics.endFill();
arrow.x = videoWidth/2;
arrow.y = videoHeight/2- arrow.height/2;
//
mSmall.scale(scaleSmall,scaleSmall);
mMedium.scale(scaleMedium,scaleMedium);
now = new BitmapData(videoWidth,videoHeight,false);
nowMedium = new BitmapData(videoWidth*scaleMedium, videoHeight*scaleMedium, false);
nowSmall = new BitmapData(videoWidth*scaleSmall, videoHeight*scaleSmall, false);
last = new BitmapData(videoWidth,videoHeight,false);
lastMedium = new BitmapData(videoWidth*scaleMedium, videoHeight*scaleMedium, false);
lastSmall = new BitmapData(videoWidth*scaleSmall, videoHeight*scaleSmall, false);
// テンプレートを作成します
var template:BasicTemplate = new BasicTemplate();
addChild(template);
//this.addChild(arrow);
this.addChild(new Stats);
// プリミティブを作成します
var box:Box;
box = new Box(600, 600, 600);
box.cloneMaterialToAllSurfaces(new FillMaterial(0x0000FF,1,BlendMode.NORMAL,1,0x0000000));
// 3Dシーンのルートに追加します
template.scene.root.addChild(box);
template.cameraContoller.lookAt(new Point3D());
var dbg:TextField=new TextField()
dbg.selectable=true;
dbg.mouseEnabled=true;
dbg.height=100
var format:TextFormat=new TextFormat();
format.color=0x666666
format.size=12;
format.font='_ゴシック';
dbg.defaultTextFormat=format
dbg.x=300
addChild(dbg)
// Event.ENTER_FRAME 時に実行されるレンダリングのイベントです。
// レンダリング前に実行したい処理を記述します。
template.onPreRender = function():void {
// 立方体を回転させます (角度はラジアン)
raw.lock();
raw.draw(video);
var detected:Object = detectDirection(raw);
arrow.rotation = (Math.atan2(detected.point.x,detected.point.y)* -180 / Math.PI) +90;
if(arrow.rotation>0&&arrow.rotation<=90){
} else if(arrow.rotation==180){
dbg.appendText(String(arrow.rotation)+"\n");
box.rotationY += 2 * Math.PI / 180;
} else if(arrow.rotation==0){
dbg.appendText(String(arrow.rotation)+"\n");
box.rotationY -= 2 * Math.PI / 180;
} else {
dbg.appendText(String(arrow.rotation)+"\n");
}
//arrow.scaleX = detected.distance/25;
//arrow.scaleY = detected.distance/25;
}
}
private function detectDirection(bd:BitmapData):Object {
now.copyPixels(bd,rect,pt);
nowMedium.draw(bd,mMedium);
nowSmall.draw(bd,mSmall);
var p:Point = getDirection(new Point(0,0),nowSmall,lastSmall, 8, 0.1);
//var p2:Point = getDirection(new Point(p.x*scaleMedium/scaleSmall,p.y*scaleMedium/scaleSmall),nowMedium,lastMedium, 2, 0.05);
var d:Number = Point.distance(pt, p)*(1/scaleSmall);
//trace(p,Point.distance(pt, p)*(1/scaleSmall),Math.atan2(p.x,p.y));
last.copyPixels(bd,last.rect,pt);
lastMedium.copyPixels(nowMedium,lastMedium.rect,pt);
lastSmall.copyPixels(nowSmall,lastSmall.rect,pt);
return {point:p,distance:d};
}
private function getDirection(centerP:Point,a:BitmapData,b:BitmapData,search_count:int, threshold:Number):Point{
dNow = 0;
dResult = centerP;
score = 50000000;
scoreNow = 0;
scoreLast = 0;
count = 0;
t = (a.width-1) * (a.height-1) *threshold;
while(score > t){
dNow = 0;
for (ii = 0 ; ii < 9; ii++){
scoreNow = getScore(a, b, compareX[ii] + dResult.x, compareY[ii] + dResult.y);
if (scoreNow < score){
//前回のものよりスコアが低いなら、そっちを優先
dNow = ii;
score = scoreNow;
}
};
dResult = new Point(dResult.x + compareX[dNow],dResult.y+compareY[dNow]);
count ++;
if(count >= search_count){
// search_countまでに条件が満たなかったら中断
//trace("break");
break;
}
}
return dResult;
}
private function getScore(a:BitmapData,b:BitmapData,x:int,y:int):Number{
//x,yずらした画像の差分をベクトルで計算し、合計スコアを返す
aVec = a.getVector(a.rect);
bVec = b.getVector(b.rect);
scoreIndiv = 0;
scoreNowIndiv = 0;
current = 0;
dif = x + y*a.width;
for (yp = 1; yp < a.height-1 ; yp++){
for (xp = 1; xp < a.width-1 ; xp++){
current = yp*a.width + xp;
if( 0 <= current+dif && current+dif < aVec.length){
scoreNowIndiv = Math.abs(aVec[current] - bVec[current+dif]);
if ((0x00000000 < scoreNowIndiv ) && (scoreNowIndiv < 0x00ffffff)){
scoreIndiv += (scoreNowIndiv / 0xffffff);
}
}
}
}
return scoreIndiv;
}
}
}
import alternativ5.engine3d.controllers.CameraController;
import alternativ5.engine3d.core.Camera3D;
import alternativ5.engine3d.core.Object3D;
import alternativ5.engine3d.core.Scene3D;
import alternativ5.engine3d.display.View;
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageQuality;
import flash.display.StageScaleMode;
import flash.events.Event;
/**
* BasicTemplate for Alternativa3D
* Alternativa3Dを扱いやすくするためのテンプレートです
* @author Yasu
*/
class BasicTemplate extends Sprite{
/**
* シーンインスタンスです。
*/
public var scene:Scene3D;
/**
* ビューインスタンスです。
*/
public var view:View;
/**
* カメラインスタンスです。
*/
public var camera:Camera3D;
/**
* カメラコントローラーです。
*/
public var cameraContoller:CameraController;
private var _viewWidth:int;
private var _viewHeight:int;
private var _scaleToStage:Boolean;
/**
* 新しい BasicTemplate インスタンスを作成します。
* @param viewWidth
* @param viewHeight
* @param scaleToStage
*/
public function BasicTemplate(viewWidth:int=640, viewHeight:int=480, scaleToStage:Boolean = true) {
_viewWidth = viewWidth;
_viewHeight = viewHeight;
_scaleToStage = scaleToStage;
// Creating scene
scene = new Scene3D();
scene.splitAnalysis = false; // not analysis for performance
scene.root = new Object3D();
// Adding camera
camera = new Camera3D();
camera.z = -1000;
scene.root.addChild(camera);
// camera contoller
cameraContoller = new CameraController(this);
cameraContoller.camera = camera;
// set view
view = new View();
view.camera = camera;
addChild(view);
// stage
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
/**
* 初期化されたときに実行されるイベントです。
* 初期化時に実行したい処理をオーバーライドして記述します。
*/
protected function atInit():void {}
/**
* 初期化されたときに実行されるイベントです。
* 初期化時に実行したい処理を記述します。
*/
private var _onInit:Function = function():void { };
public function get onInit():Function { return _onInit; }
public function set onInit(value:Function):void {
_onInit = value;
}
/**
* Event.ENTER_FRAME 時に実行されるレンダリングのイベントです。
* レンダリング前に実行したい処理をオーバーライドして記述します。
*/
protected function atPreRender():void {}
/**
* Event.ENTER_FRAME 時に実行されるレンダリングのイベントです。
* レンダリング前に実行したい処理を記述します。
*/
private var _onPreRender:Function = function():void{};
public function get onPreRender():Function { return _onPreRender; }
public function set onPreRender(value:Function):void {
_onPreRender = value;
}
/**
* Event.ENTER_FRAME 時に実行されるレンダリングのイベントです。
* レンダリング後に実行したい処理をオーバーライドして記述します。
*/
protected function atPostRender():void {
}
/**
* Event.ENTER_FRAME 時に実行されるレンダリングのイベントです。
* レンダリング後に実行したい処理を記述します。
*/
protected var _onPostRender:Function = function():void{};
public function get onPostRender():Function { return _onPostRender; }
public function set onPostRender(value:Function):void {
_onPostRender = value;
}
/**
* レンダリングを開始します。
*/
public function startRendering():void {
addEventListener(Event.ENTER_FRAME, onRenderTick);
}
/**
* レンダリングを停止します。
*/
public function stopRendering():void {
removeEventListener(Event.ENTER_FRAME, onRenderTick);
}
/**
* シングルレンダリング(レンダリングを一回だけ)を実行します。
*/
public function singleRender():void {
onRenderTick();
}
private function init(e:Event = null):void {
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.align = StageAlign.TOP_LEFT;
stage.quality = StageQuality.HIGH;
// resize
stage.addEventListener(Event.RESIZE, onResize);
onResize(null);
// render
startRendering();
atInit();
_onInit();
}
private function onRenderTick(e:Event = null):void {
atPostRender();
_onPostRender();
scene.calculate();
atPreRender();
_onPreRender();
}
private function onResize(event:Event = null):void {
if (_scaleToStage) {
view.width = stage.stageWidth;
view.height = stage.stageHeight;
}else {
view.width = _viewWidth;
view.height = _viewHeight;
}
}
}