ここしばらくは、目で見て楽しめるようなモノをPapervision3Dなどを使ってお手軽に作っていたので、
今回はひさしぶりに地味で取っつきにくいことをやりたいと思います。
位相限定相関 (POC : Phase Only Correlation)
難しそうな名前ですが実際にやることは簡単です。
前回 作成した位相画像、これは画像の周波数成分の位相特性を表したものでした。
POCでは位相特性のみの相関を取って類似度を測ります。名前そのまま。
デザイナー寄りのFlasherさんでも、”パターンマッチング”という言葉なら知ってる人は多いかもしれません。
この手法はサブピクセルレベルの高い精度で位置ずれ量を検出できるため、
ステレオ画像の対応点探索などに用いられています。
また、バイオメトリクス分野においては虹彩・指紋認証など多くの応用分野があります。
POC関連の論文はWeb上でたくさん見つかりますが、簡単な概念は以下のサイトがわかりやすいです。
位相限定相関法の原理とは – 視ることと識ること3
・処理のフロー
相関のピーク位置がそのまま画像のずれを表しているので、その位置を探すだけです。
今回は意図的に位置だけずらした画像を用いて、その位置ずれ量を検出します。
FFT演算用クラスはこれまで利用してきたものと同じものです → FFT.as
(FFT.asのスペクトル位置の並び替え部分は今回不要なので省略できます)
・位相限定相関法による画像の位置ずれ量検出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
package { import __AS3__.vec.Vector; import flash.display.*; import flash.geom.Point; import flash.utils.getTimer; [SWF(width = "514", height = "300", backgroundColor = "#000000", frameRate = "12")] public class Test extends Sprite { [Embed(source = './assets/lenagray1.jpg')] private var EmbedImage:Class; [Embed(source = './assets/lenagray2.jpg')] private var EmbedImage2:Class; private var srcBmp:Bitmap; private var srcBmp2:Bitmap; private var imgData:BitmapData; private var imgData2:BitmapData; private var re:Vector.<Number>; private var im:Vector.<Number>; private var re2:Vector.<Number>; private var im2:Vector.<Number>; private var rePOC:Vector.<Number>; private var imPOC:Vector.<Number>; private var w:int = 0; private var h:int = 0; public function Test() { srcBmp = new EmbedImage() as Bitmap; imgData = srcBmp.bitmapData.clone(); srcBmp2 = new EmbedImage2() as Bitmap; imgData2 = srcBmp2.bitmapData.clone(); w = imgData.width; h = imgData.height; re = new Vector.<Number>(w*h, true); im = re.concat(); re2 = re.concat(); im2 = re.concat(); rePOC = re.concat(); imPOC = re.concat(); var ts:int = getTimer(); // 画像データの直列化 serializeData(re, imgData); serializeData(re2, imgData2); // 位相限定相関 phaseOnlyCorrelation(re, im, re2, im2, rePOC, imPOC); var te:int = getTimer(); trace("time: " + (te - ts) + " ms"); // ピーク値と座標の探索 var peak:Number = 0.0, spectrum:Number = 0.0; var pos:Point = new Point(); for(var y:int=0; y<h; y++) { for(var x:int=0; x<w; x++) { //spectrum = Math.sqrt(rePOC[x + y*w]*rePOC[x + y*w] + imPOC[x + y*w]*imPOC[x + y*w]); spectrum = rePOC[x + y*w] + Math.abs(imPOC[x + y*w]); if(spectrum > peak){ peak = spectrum; pos.x = x; pos.y = y; } } } trace("peak: " + peak + ", position: " + pos); addChild(srcBmp); addChild(srcBmp2); srcBmp2.x += w + 2; } // 位相限定相関 private function phaseOnlyCorrelation(srcRe1:Vector.<Number>, srcIm1:Vector.<Number>, srcRe2:Vector.<Number>, srcIm2:Vector.<Number>, dstRe:Vector.<Number>, dstIm:Vector.<Number>):void { var fft:FFT = new FFT(w); var spectrum:Number = 0.0; // 2D-FFT fft.fft2d(srcRe1, srcIm1); fft.fft2d(srcRe2, srcIm2); for(var i:int=0; i<w*h; i++) { // 位相を算出 (振幅で正規化) spectrum = Math.sqrt(srcRe1[i]*srcRe1[i] + srcIm1[i]*srcIm1[i]); srcRe1[i] /= spectrum; srcIm1[i] /= spectrum; spectrum = Math.sqrt(srcRe2[i]*srcRe2[i] + srcIm2[i]*srcIm2[i]); srcRe2[i] /= spectrum; srcIm2[i] /= spectrum; // 相互相関 dstRe[i] = srcRe1[i]*srcRe2[i]; dstIm[i] = srcIm1[i]*(-srcIm2[i]); } // 2D-IFFT fft.fft2d(dstRe, dstIm, true); } // 画像データの直列化 private function serializeData(data:Vector.<Number>, img:BitmapData):void { for(var y:int=0; y<h; y++) { for(var x:int=0; x<w; x++) { data[x + y*w] = img.getPixel(x, y) & 0xff; } } } } } |
・出力結果例
1 2 |
time: 1582 ms peak: 0.44894899867857474, position: (x=86, y=45) |
この結果は、画像Bは画像AからX軸方向に86px、Y軸方向に45pxずれていることを示しています。
処理時間は256×256pxの画像で約1.6秒なので、Flashならまぁそんなもんなのかなといったところ。
あと、真面目に解析するんなら複素行列とかのクラスを新たに作った方がいいです。後悔;;
Flashでステレオ画像処理とかできたらいいなと思ってます。
ただ、その場合はユーザー側にいろいろ条件を課すことになりそうですが。。
ステレオマッチング、、すごくめんどくさそう;
・関連記事
位相画像
ハイパス/ローパスフィルタ
二次元離散フーリエ変換 – AS3.0
離散フーリエ変換 – AS3.0
ステレオ画像処理