被写体の移動方向推定

被写体の移動方向推定
カメラの中心に写っているものが、どの方向に動いているかを推定。

▼Wonderfl

直前のフレームの中心の3×3ピクセルを、
次のフレームの中心付近の3×3ピクセル達と比較して、
一番近いものを取り出すことによって、
移動方向を推定している。
カメラの画像そのままだと、情報量が多すぎてなんもできないので、
ちょっとぼかして、色数を落として(rgb各色4段階)いる。

ゆっくり動かすぶんには、なんかそれっぽくなったような。

パターンマッチングによるカメラの移動方向推定
http://wonderfl.net/code/3095974b264ca9ab00750977c426c30df0b2d201
みたいに(たぶん)洗練された方法じゃなくて、
まずは、一番最初に思いつく方法で極単純化して自分なりに実装してみた。
各種フィルターを使うような最適化も全然していない。

色数をもうちょっと増やしたり、
最適化して、そのぶんパターンマッチングに
CPU資源を割り振ったりしたら、
もっと精度上がるかな。
▼ActionScript AS3(FP9)
[sourcecode language=”as3″]
/*
被写体の移動方向推定
カメラの中心に写っているものが、どの方向に動いているかを推定。

直前のフレームの中心の3×3ピクセルを、
次のフレームの中心付近の3×3ピクセル達と比較して、
一番近いものを取り出すことによって、
移動方向を推定している。
カメラの画像そのままだと、情報量が多すぎてなんもできないので、
ちょっとぼかして、色数を落として(rgb各色4段階)いる。

ゆっくり動かすぶんには、なんかそれっぽくなったような。

パターンマッチングによるカメラの移動方向推定
http://wonderfl.net/code/3095974b264ca9ab00750977c426c30df0b2d201
みたいに(たぶん)洗練された方法じゃなくて、
まずは、一番最初に思いつく方法で極単純化して自分なりに実装してみた。
各種フィルターを使うような最適化も全然していない。

色数をもうちょっと増やしたり、
最適化して、そのぶんパターンマッチングに
CPU資源を割り振ったりしたら、
もっと精度上がるかな。

*/
package {
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.*;
import flash.media.Camera;
import flash.media.Video;
import flash.utils.Timer;

public class CameraEx extends Sprite {

private var video:Video;
private var btn:Sprite;
private var _bitmapProcess:BitmapProcess;

public function CameraEx() {

stage.scaleMode = StageScaleMode.NO_SCALE;
stage.align = StageAlign.TOP_LEFT;

// camera
var camera:Camera = Camera.getCamera();

if (camera != null) {
video = new Video(160, 120);
video.attachCamera(camera);
addChild(video);
} else {
trace("You need a camera.");
}

_bitmapProcess = new BitmapProcess();
_bitmapProcess.x = 160;
this.addChild(_bitmapProcess);

// button
btn = new Sprite();
btn.buttonMode = true
btn.graphics.beginFill(0x333333);
btn.graphics.drawRect(0,0,100,30);
btn.y = 120 + 10;
addChild(btn);
btn.addEventListener(MouseEvent.MOUSE_UP, btnUp);

var timer:Timer = new Timer(100,9999);
timer.addEventListener(TimerEvent.TIMER,onTime);
timer.start();
}
private function onTime(e:TimerEvent):void {
_bitmapProcess.setBitmap(video);
}

private function btnUp(e:MouseEvent):void {
_bitmapProcess.setBitmap(video);
}
}
}

import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.filters.BitmapFilter;
import flash.filters.BlurFilter;
import flash.filters.ColorMatrixFilter;
import flash.filters.ConvolutionFilter;
import flash.geom.ColorTransform;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.media.Video;

class BitmapProcess extends Sprite{
private var _bitmap0:Bitmap;
private var _bitmap1:Bitmap;
private var _bitmap2:Bitmap;
private var _bitmap3:Bitmap;
private var _bitmap4:Bitmap;
private var _bitmap5:Bitmap;
private var _bitmapData0:BitmapData;
private var _bitmapData1:BitmapData;
private var _bitmapData2:BitmapData;
private var _tempBitmapData:BitmapData;
private var _arrow:Sprite;
public function BitmapProcess(){
_bitmapData0 = new BitmapData(160,120,false,0x000000);
_bitmap0 = new Bitmap(_bitmapData0);
_bitmap0.scaleX = _bitmap0.scaleY = 2;
this.addChild(_bitmap0);

_bitmapData1 = new BitmapData(160,120,false,0x000000);
_bitmap1 = new Bitmap(_bitmapData1);
_bitmap1.scaleX = _bitmap1.scaleY = 2;
_bitmap1.y = 240;
this.addChild(_bitmap1);

_bitmapData2 = new BitmapData(160,120,false,0x000000);
_bitmap2 = new Bitmap(_bitmapData2);
_bitmap2.scaleX = _bitmap2.scaleY = 2;
_bitmap2.y = 500;
//this.addChild(_bitmap2);

_bitmap3 = new Bitmap(new BitmapData(160,120,false,0x000000));
_bitmap3.scaleX = _bitmap3.scaleY = 2;
_bitmap3.x = 330;
//this.addChild(_bitmap3);

_bitmap5 = new Bitmap(new BitmapData(160,120,false,0x000000));
_bitmap5.scaleX = _bitmap5.scaleY = 2;
_bitmap5.x = 330;
_bitmap5.y = 500;
//this.addChild(_bitmap5);

_arrow = new Sprite();
_arrow.graphics.beginFill(0xFF0000,0.5);
_arrow.graphics.moveTo(8,8);
_arrow.graphics.lineTo(8,8);
_arrow.graphics.lineTo(8,1);
_arrow.graphics.lineTo(16,1);
_arrow.graphics.lineTo(16,-1);
_arrow.graphics.lineTo(8,-1);
_arrow.graphics.lineTo(8,-8);
_arrow.graphics.lineTo(0,0);
_arrow.graphics.endFill();
_arrow.x = _bitmap1.x + _bitmap1.width/2;
_arrow.y = _bitmap1.y + _bitmap1.height/2;
_arrow.visible = false;
this.addChild(_arrow);

_tempBitmapData = new BitmapData(160,120,false,0x000000);
}
public function setBitmap(video:Video):void{
_bitmap0.bitmapData = _bitmap2.bitmapData.clone();
_tempBitmapData.draw(video);
_bitmap2.bitmapData = _tempBitmapData.clone();
_bitmap2.bitmapData.applyFilter(_bitmap2.bitmapData,_bitmap2.bitmapData.rect,new Point(0,0),new BlurFilter(2,2));
_bitmap2.bitmapData = posterization(_bitmap2.bitmapData);
_bitmap1.bitmapData.draw(video);

_bitmap3.bitmapData = _bitmap0.bitmapData.clone();
_bitmap5.bitmapData = _bitmap2.bitmapData.clone();

var colors3:Array = [];
colors3[0] = _bitmap3.bitmapData.getPixel(79,59);
colors3[1] = _bitmap3.bitmapData.getPixel(80,59);
colors3[2] = _bitmap3.bitmapData.getPixel(81,59);
colors3[3] = _bitmap3.bitmapData.getPixel(79,60);
colors3[4] = _bitmap3.bitmapData.getPixel(80,60);
colors3[5] = _bitmap3.bitmapData.getPixel(81,60);
colors3[6] = _bitmap3.bitmapData.getPixel(79,61);
colors3[7] = _bitmap3.bitmapData.getPixel(80,61);
colors3[8] = _bitmap3.bitmapData.getPixel(81,61);

var colors5:Array = [9];
var nears:Array = [];
var focus:Array = [[0,0],[1,0],[0,1],[-1,0],[0,-1],[1,1],[-1,1],[-1,-1],[1,-1],
[2,0],[0,2],[-2,0],[0,-2],[2,1],[1,2],[-1,2],[-2,1],[-2,-1],[-1,-2],[1,-2],[2,-1],[2,2],[-2,2],[-2,-2],[2,-2],
[3,0],[0,3],[-3,0],[0,-3],[3,1],[3,2],[2,3],[1,3],[-1,3],[-2,3],[-3,2],[-3,1],[-3,-1],[-3,-2],
[-2,-3],[-1,-3],[1,-3],[2,-3],[3,-2],[3,-1],[3,3],[-3,3],[-3,-3],[3,-3]];
var score:int;
for (var i:int = 0; i < focus.length; i++) {
var tx:int = 79+focus[i][0];
var ty:int = 59+focus[i][1];
colors5[0] = _bitmap5.bitmapData.getPixel(tx,ty);
colors5[1] = _bitmap5.bitmapData.getPixel(tx+1,ty);
colors5[2] = _bitmap5.bitmapData.getPixel(tx+2,ty);
colors5[3] = _bitmap5.bitmapData.getPixel(tx,ty+1);
colors5[4] = _bitmap5.bitmapData.getPixel(tx+1,ty+1);
colors5[5] = _bitmap5.bitmapData.getPixel(tx+2,ty+1);
colors5[6] = _bitmap5.bitmapData.getPixel(tx,ty+2);
colors5[7] = _bitmap5.bitmapData.getPixel(tx+1,ty+2);
colors5[8] = _bitmap5.bitmapData.getPixel(tx+2,ty+2);
score = nearArray(colors3,colors5);
nears.push(score);
if(score == 0){
break;
}
}

var nearPoz:int = nears.sort(Array.RETURNINDEXEDARRAY|Array.NUMERIC)[0];
// _bitmap3.bitmapData.lock();
// _bitmap3.bitmapData.setPixel(78,58,0xFF0000);
// _bitmap3.bitmapData.setPixel(82,58,0xFF0000);
// _bitmap3.bitmapData.setPixel(78,62,0xFF0000);
// _bitmap3.bitmapData.setPixel(82,62,0xFF0000);
// _bitmap3.bitmapData.unlock();
//
// _bitmap5.bitmapData.lock();
// _bitmap5.bitmapData.setPixel(78,58,0xFF0000);
// _bitmap5.bitmapData.setPixel(82,58,0xFF0000);
// _bitmap5.bitmapData.setPixel(78,62,0xFF0000);
// _bitmap5.bitmapData.setPixel(82,62,0xFF0000);
if(nears[nearPoz] < 9 && nearPoz !=0){
// _bitmap5.bitmapData.setPixel(focus[nearPoz][0]+79+1,focus[nearPoz][1]+59+1,0x0000FF);
_arrow.visible = true;
_arrow.rotation = Math.atan2(focus[nearPoz][1],focus[nearPoz][0])*180/Math.PI+180;
}else{
_arrow.visible = false;
}
// _bitmap5.bitmapData.unlock();

}
private function nearArray(a:Array,b:Array):int
{
var score:int = 0;
for (var i:int = 0; i < 9; i++) {
score += near(a[i],b[i]);
}
return score;
}
private function near(rgb1:uint,rgb2:uint):int
{
var r1:int = rgb1 >> 16 & 0xFF;
var g1:int = rgb1 >> 8 & 0xFF;
var b1:int = rgb1 & 0xFF;
var r2:int = rgb2 >> 16 & 0xFF;
var g2:int = rgb2 >> 8 & 0xFF;
var b2:int = rgb2 & 0xFF;
return Math.abs(r1-r2)/64+Math.abs(g1-g2)/64+Math.abs(b1-b2)/64;
}

private function outline(bd:BitmapData):BitmapData
{
var matrix:Array = [ -30, 30, 0,
-30, 30, 0,
-30, 30, 0];
var filter:BitmapFilter = new ConvolutionFilter(3,3, matrix, 9);
var filters:Array = [];
bd.applyFilter(bd,bd.rect, new Point(0, 0), filter)
return bd;
}

public function posterization(bitmapData:BitmapData):BitmapData{
var n:int = bitmapData.height;
var m:int = bitmapData.width;
for (var i:int = 0; i < n; i++) {
for (var j:int = 0; j < m; j++) {
var rgb:uint = bitmapData.getPixel(j,i);
var r:int = rgb >> 16 & 0xFF;
var g:int = rgb >> 8 & 0xFF;
var b:int = rgb & 0xFF;
r = Math.floor(r/64)*64;
g = Math.floor(g/64)*64;
b = Math.floor(b/64)*64;
bitmapData.setPixel(j,i,r*256*256+g*256+b);
}
}
return bitmapData;
}
public function getGray(rgb:uint):int{
var r:int = rgb >> 16 & 0xFF;
var g:int = rgb >> 8 & 0xFF;
var b:int = rgb & 0xFF;
return (r+g+b)/3;
}
}

[/sourcecode]