/* 被写体の移動方向推定 カメラの中心に写っているものが、どの方向に動いているかを推定。 直前のフレームの中心の3x3ピクセルを、 次のフレームの中心付近の3x3ピクセル達と比較して、 一番近いものを取り出すことによって、 移動方向を推定している。 カメラの画像そのままだと、情報量が多すぎてなんもできないので、 ちょっとぼかして、色数を落として(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; } } 被写体の移動方向推定