イメージ ポイント機能を使用したビデオの安定化
この例では、不安定なプラットフォームから撮影されたビデオを安定化する方法を説明します。映像安定化の 1 つの方法は、イメージ内の際立った特徴を追跡し、これをアンカー ポイントとして、それに対するすべての摂動を取り除くことです。ただし、この手続きは、最初のビデオ フレームのどこに際立った特徴があるかについての知識を用いてブートストラップしなければなりません。この例では、そのような "事前" 知識がなくても有効な、映像安定化の方法について説明します。この方法では、代わりにビデオ シーケンス内の "背景面" を自動的に探索し、その観測された歪みを使ってカメラの動きを補正します。
この安定化アルゴリズムには 2 つの手順があります。まず、2 つのイメージ間における点の対応にestgeotform2d
関数を適用して、ビデオ シーケンスにあるすべての隣接フレーム間のアフィン イメージ変換を決定します。次に、ビデオ フレームをワーピングさせてビデオを安定させます。
ムービー ファイルからのフレームの読み取り
安定化アルゴリズムに色は必要なく、グレースケール イメージを使用すると速度が改善されるため、ビデオ シーケンスの最初の 2 つのフレームを強度イメージとして読み取ります。両方のフレームを並べて表示し、赤とシアンのカラー合成を作成して両者間のピクセル単位の差異を示します。2 つのフレーム間の垂直オフセットと水平オフセットが大きいことに注意してください。
filename = "shaky_car.avi"; hVideoSrc = VideoReader(filename); imgA = im2gray(im2single(readFrame(hVideoSrc))); imgB = im2gray(im2single(readFrame(hVideoSrc))); figure; montage({imgA,imgB}); title(["Frame A",repmat(" ",[1 50]),"Frame B"]);
figure imshowpair(imgA,imgB,ColorChannels="red-cyan"); title("Color composite (frame A = red,frame B = cyan)");
各フレームからの際立った点の収集
ここでの目標は、2 つのフレーム間の歪みを補正する変換を決定することです。estgeotform2d
関数を使用すると、アフィン変換が返されるため、この作業が容易になります。この関数では、2 つのフレーム間における一連の点の対応関係を入力として与えなければなりません。これらの対応関係を求めるには、まず両方のフレームから対象となる点を収集し、それらの間に想定できそうな対応関係を選択します。
この手順では、フレームごとに候補点を作成します。もう片方のフレームで対応する点を見つける可能性を高めるには、イメージ内でコーナーなどの際立った特徴の周りの点に着目するのが有効です。これを行うには、コーナーを素早く検出できることで知られる detectFASTFeatures
関数を使用します。
ptThresh = 0.1; pointsA = detectFASTFeatures(imgA,MinContrast=ptThresh); pointsB = detectFASTFeatures(imgB,MinContrast=ptThresh);
下の図には、両方のフレームから検出された点が表示されています。それらの点の多くが、木立ちの輪郭に沿った点、大きな交通標識のコーナー、自動車のコーナーといった、イメージ内の同じ特徴をカバーしていることを確認してください。
figure imshow(imgA) hold on plot(pointsA) title("Corners in A")
figure imshow(imgB) hold on plot(pointsB) title("Corners in B")
点の対応関係の選択
導出された点の間の対応関係を選択します。各点について、それを中心とする FREAK (Fast Retina Keypoint) 記述子を抽出します。FREAK 記述子はバイナリであるため、点の間のマッチング コストにはハミング距離を使用します。フレーム A とフレーム B の点は一意性の制約なしで推定的にマッチされているため、フレーム B の点はフレーム A の複数の点に対応させることができます。
コーナーの FREAK 記述子を抽出します。
[featuresA,pointsA] = extractFeatures(imgA,pointsA); [featuresB,pointsB] = extractFeatures(imgB,pointsB);
現在のフレームと前のフレームで検出された特徴をマッチングします。FREAK 記述子はバイナリであるため、関数matchFeatures
はハミング距離を使って対応する点を探します。
indexPairs = matchFeatures(featuresA,featuresB); pointsA = pointsA(indexPairs(:,1),:); pointsB = pointsB(indexPairs(:,2),:);
このイメージは、先に提示したものと同じカラー合成を示していますが、フレーム A からの点が赤で、フレーム B からの点が緑で追加されています。点の間に描かれた黄色いラインは、この手続きで選択された対応関係を示しています。これらの対応関係の多くは正しいものですが、外れているものもかなりあります。
figure showMatchedFeatures(imgA,imgB,pointsA,pointsB) legend("A","B")
ノイズの多い対応関係からの変換の推定
前の手順で得られた点の対応関係の多くは正しくありません。しかし、RANSAC アルゴリズムのバリエーションである M-estimator SAmple Consensus (MSAC) アルゴリズムを使用して、2 つのイメージ間における幾何学的変換のロバスト推定を導出することは可能です。MSAC アルゴリズムは、estgeotform2d
関数に実装されています。この関数は、一連の点の対応関係から有効なインライアの対応関係を探索し、その関係を使用してアフィン変換を導出します。この変換は、最初の点のセットからのインライアの位置を、2 番目のセットからのインライアとできるだけ一致させます。結果として得られるアフィン変換は、3 行 3 列の行列で表されます。
[a_1 a_2 t_x; a_3 a_4 t_y; 0 0 1]
パラメーター は変換のスケール、回転、およびせん断効果を定義します。パラメーター は平行移動のパラメーターです。この変換を使用して、対応する特徴が同じイメージ位置に移るようイメージをワーピングさせることができます。
アフィン変換には、結像面だけしか変更できないという制限があります。したがって、走行中の自動車から撮影したこのビデオのような、3 次元のシーンから取った 2 つのフレーム間にある一般的な歪みを検出するタスクには適していません。ただし、この後で説明する特定条件の下であれば正しく機能します。
[tform,inlierIdx] = estgeotform2d(pointsB,pointsA,"affine");
pointsBm = pointsB(inlierIdx,:);
pointsAm = pointsA(inlierIdx,:);
imgBp = imwarp(imgB,tform,OutputView=imref2d(size(imgB)));
pointsBmp = transformPointsForward(tform,pointsBm.Location);
再投影されたフレーム B に重ね合わせたフレーム A を、再投影された点の対応関係と共に示すカラー合成を表示します。結果を見ると、インライアの対応関係がほぼ厳密に一致しています。イメージの中心部はともによく揃っており、赤とシアンのカラー合成がその領域ではほぼ純粋な白黒になっています。
インライアの対応関係はすべてイメージの背景にあり、前景にはないことに注意してください。前景自体は揃っていません。これは、背景の特徴が遠くにあり、無限遠の平面にあるかのように動作するためです。したがって、アフィン変換が結像面のみの変更に制限されていても、両方のイメージの背景面を揃えるにはそれで十分です。さらに、背景面がフレーム間で動かないか、大きく変化しないと仮定すれば、この変換は実際にはカメラの動きを捉えていることになります。したがって、この補正を行うとビデオが安定化することになります。この条件は、フレーム間のカメラの移動が十分に小さいか、あるいは逆に、ビデオのフレーム レートが十分に速いならば有効です。
figure showMatchedFeatures(imgA,imgBp,pointsAm,pointsBmp) legend('A','B')
変換の近似と平滑化
一連のビデオ フレーム が与えられたとき、上記の手続きを使ってすべてのフレーム と の間の歪みをアフィン変換 として推定します。したがって、最初のフレームに対するフレーム の累積歪みは、その前のすべてのフレーム間変換の積となります。すなわち、次のようになります。
上記アフィン変換の 6 つのパラメーターをすべて使用することもできますが、数値を簡素化し安定させるため、より簡潔な「スケール - 回転 - 平行移動」の変換として行列を再近似します。完全なアフィン変換には自由パラメーターが 6 つありますが、この変換の自由パラメーターは、倍率、角度、および 2 つの平行移動の 4 つだけです。この新しい変換行列の形式は次のとおりです。
[s*cos(ang) s*sin(ang) t_x; s*-sin(ang) s*cos(ang) t_y; 0 0 1]
この置き換え手続きを説明するため、上で取得した変換を等価な「スケール - 回転 - 平行移動」で近似します。変換の置き換えによる誤差が最小限であることを示すため、両方の変換を用いてフレーム B を再投影し、この 2 つのイメージを赤とシアンのカラー合成として下に表示します。イメージの見た目がほぼ白黒になっていますが、これは、異なる再投影間のピクセル単位の差が無視できるほど小さいことを明確に示しています。
スケールと回転を表す部分行列を抽出します。
tformAffine = tform.A; R = tformAffine(1:2,1:2);
2 つの有効な逆正接の平均からシータを計算します。
theta = mean([atan2(-R(3),R(1)) atan2(R(2),R(4))]);
2 つの安定した平均計算結果の平均からスケールを計算します。
scale = mean(R([1 4])/cos(theta));
シータを度に変換します。
thetad = rad2deg(theta);
平行移動は同じままです。
translation = tformAffine(1:2,3);
新しい s-R-t 変換を再構成し、simtform2d
オブジェクトに保存します。
tformSRT = simtform2d(scale,thetad,translation); imgBold = imwarp(imgB,tform,OutputView=imref2d(size(imgB))); imgBsRt = imwarp(imgB,tformSRT,OutputView=imref2d(size(imgB))); figure(2) clf imshowpair(imgBold,imgBsRt,"ColorChannels","red-cyan"),axis image; title("Color composite of affine and similarity outputs");
ビデオ全体への適用
上記の手順を適用してビデオ シーケンスを平滑化します。可読性を高めるため、2 つのイメージ間の変換を推定する上記の手続きは、MATLAB® 関数 cvexEstStabilizationTform
に含められています。関数 cvexTformToSRT
も、一般的なアフィン変換を相似 (スケール - 回転 - 平行移動) 変換に置き換えます。
各手順において、現在のフレーム間の変換が計算され、s-R-t 変換として当てはめられます。その後、これを最初のフレーム以降のカメラの動きをすべて記述する累積変換と組み合わせます。ビデオ プレーヤーには、平滑化されたビデオの最後の 2 フレームが赤とシアンの合成として表示されます。コードから早期終了条件を削除すると、ビデオ全体を処理できるようになります。
ビデオ ソースをファイルの先頭にリセットし、ビデオ プレーヤーを作成します。
read(hVideoSrc,1); hVPlayer = vision.VideoPlayer;
ビデオ内のすべてのフレームを処理します。
movMean = im2gray(im2single(readFrame(hVideoSrc))); imgB = movMean; imgBp = imgB; correctedMean = imgBp; ii = 2; cumulativeTform = simtform2d;
新しいフレームごとに、フレーム A からフレーム B への変換を推定し、それを s-R-t 変換として当てはめます。以前に修正したフレームとのカラー合成として表示します。release
オブジェクト関数は、開いているファイルをすべて閉じてメモリを解放します。
while hasFrame(hVideoSrc) && ii < 10 imgA = imgB; imgAp = imgBp; imgB = im2gray(im2single(readFrame(hVideoSrc))); movMean = movMean + imgB; tformAffine = cvexEstStabilizationTform(imgA,imgB); sRtTform = cvexTformToSRT(tformAffine); cumulativeTform = simtform2d(cumulativeTform.A * sRtTform.A); imgBp = imwarp(imgB,cumulativeTform,OutputView=imref2d(size(imgB))); step(hVPlayer,imfuse(imgAp,imgBp,ColorChannels="red-cyan")); correctedMean = correctedMean + imgBp; ii = ii+1; end correctedMean = correctedMean/(ii-2); movMean = movMean/(ii-2); release(hVPlayer);
計算中に、生のビデオ フレームおよび修正されたフレームの平均が算出されました。ディスプレイにこれらの平均を並べて表示します。左のイメージは生の入力フレームの平均を示しており、元のビデオに多量の歪みが含まれていたことが確認できます。一方、右のイメージは修正後のフレームの平均を示しており、イメージ中央部分で最小限の歪みしかありません。車が前に進んでいるため前景の詳細がぼやけているものの、これは安定化アルゴリズムが有効であることを示しています。
figure montage({movMean,correctedMean}) title(["Raw input mean",repmat(" ",[1 30]),"Corrected sequence mean"])
参考文献
[1] Tordoff, B; Murray, DW. "Guided sampling and consensus for motion estimation." European Conference n Computer Vision, 2002.
[2] Lee, KY; Chuang, YY; Chen, BY; Ouhyoung, M. "Video Stabilization using Robust Feature Trajectories." National Taiwan University, 2009.
[3] Litvin, A; Konrad, J; Karl, WC. "Probabilistic video stabilization using Kalman filtering and mosaicking." IS&T/SPIE Symposium on Electronic Imaging, Image and Video Communications and Proc., 2003.
[4] Matsushita, Y; Ofek, E; Tang, X; Shum, HY. "Full-frame Video Stabilization." Microsoft® Research Asia。CVPR 2005.