About BBS

オプティカルフロー

作成者: 怡土順一, 最終変更者: 怡土順一, 最終変更リビジョン: 358, 最終変更日時: 2007-12-26 14:39:31 +0900 (水, 26 12月 2007)

■ オプティカルフロー

オプティカルフローとは,時間連続な画像列を利用して,画像の速度場(物体 の速度+カメラの速度)を求め,それをベクトル集合で表現したものである. 大別して,勾配法,ブロックマッチング法が存在する.
勾配法では,「オプティカルフロー拘束方程式」と呼ばれる, 輝度の時間/空間的微分(輝度勾配)の拘束方程式を用いて,これに制約条件 を付加することでフローを求める. 比較的高速に全画素についての速度場を計算できるが,前提条件に合わない個 所(急激な輝度変化,ノイズ)では,著しい誤差が発生する事がある.
ブロックマッチング法では,画像中のあるブロックをテンプレートとして,次 時間の画像中からマッチする個所を探索することでフローを求める. 適切な特徴を持つ画像においては,ロバストにフローを計算できるが, 比較的計算時間がかかる.また,回転,スケーリングに対するロバスト性は, テンプレートとなるブロックのサイズや画像の特徴に依存する.

サンプル


オプティカルフロー1 cvCalcOpticalFlowHS, cvCalcOpticalFlowLK

勾配法によるオプティカルフローの計算

サンプルコード

#include <cv.h> #include <highgui.h> int main (int argc, char **argv) { int i, j, dx, dy, rows, cols; IplImage *src_img1, *src_img2, *dst_img1, *dst_img2; CvMat *velx, *vely; CvTermCriteria criteria; if (argc != 3 || (src_img1 = cvLoadImage (argv[1], CV_LOAD_IMAGE_GRAYSCALE)) == 0 || (src_img2 = cvLoadImage (argv[2], CV_LOAD_IMAGE_GRAYSCALE)) == 0) return -1; dst_img1 = cvLoadImage (argv[2], CV_LOAD_IMAGE_COLOR); dst_img2 = (IplImage *) cvClone (dst_img1); // (1)速度ベクトルを格納する構造体の確保,等 cols = src_img1->width; rows = src_img1->height; velx = cvCreateMat (rows, cols, CV_32FC1); vely = cvCreateMat (rows, cols, CV_32FC1); cvSetZero (velx); cvSetZero (vely); criteria = cvTermCriteria (CV_TERMCRIT_ITER | CV_TERMCRIT_EPS, 64, 0.01); // (2)オプティカルフローを計算(HS) cvCalcOpticalFlowHS (src_img1, src_img2, 0, velx, vely, 100.0, criteria); // (3)オプティカルフローを描画(HS) for (i = 0; i < cols; i += 5) { for (j = 0; j < rows; j += 5) { dx = (int) cvGetReal2D (velx, j, i); dy = (int) cvGetReal2D (vely, j, i); cvLine (dst_img1, cvPoint (i, j), cvPoint (i + dx, j + dy), CV_RGB (255, 0, 0), 1, CV_AA, 0); } } // (4)オプティカルフローを計算(LK) cvCalcOpticalFlowLK (src_img1, src_img2, cvSize (15, 15), velx, vely); // (5)計算されたフローを描画(LK) for (i = 0; i < cols; i += 5) { for (j = 0; j < rows; j += 5) { dx = (int) cvGetReal2D (velx, j, i); dy = (int) cvGetReal2D (vely, j, i); cvLine (dst_img2, cvPoint (i, j), cvPoint (i + dx, j + dy), CV_RGB (255, 0, 0), 1, CV_AA, 0); } } // (6)オプティカルフローの表示 cvNamedWindow ("ImageHS", 1); cvShowImage ("ImageHS", dst_img1); cvNamedWindow ("ImageLK", 1); cvShowImage ("ImageLK", dst_img2); cvWaitKey (0); cvDestroyWindow ("ImageHS"); cvDestroyWindow ("ImageLK"); cvReleaseImage (&src_img1); cvReleaseImage (&src_img2); cvReleaseImage (&dst_img1); cvReleaseImage (&dst_img2); cvReleaseMat (&velx); cvReleaseMat (&vely); return 0; }

// (1)速度ベクトルを格納する構造体の確保,等
入力画像と同じサイズのCvArrを確保する.今回は,cvMatを利用しているが, IplImage等でも構わない. また,関数cvCalcOpticalFlowHS()で利用される終了条件構造体も確保する.

// (2)オプティカルフローを計算(HS)
Horn & Schunck アルゴリズムを用いてオプティカルフローを計算する.

// (3)オプティカルフローを描画(HS)
X方向,y方向共に5[pixel]毎に, 関数cvGetReal2D()を用いて,インデックス(座標)を指定して速度ベクトルを取り出し, それを入力画像上に描画する.

// (4)オプティカルフローを計算(LK)
Lucas & Kanade アルゴリズムを用いてオプティカルフローを計算する.

// (5)計算されたフローを描画(LK)
X方向,y方向共に5[pixel]毎に, 関数cvGetReal2D()を用いて,インデックス(座標)を指定して速度ベクトルを取り出し, それを入力画像上に描画する.

// (6)オプティカルフローの表示
上記の二つの手法により計算されたオプティカルフローを,入力画像上に描画 したものを実際に表示する. 何かキーが押されるまで待つ.

実行結果例

入力画像1 入力画像2 結果画像(HS) 結果画像(LK)


オプティカルフロー2 cvCalcOpticalFlowBM

ブロックマッチングによるオプティカルフローの計算

サンプルコード

#include <cv.h> #include <highgui.h> int main (int argc, char **argv) { int i, j, dx, dy, rows, cols; int block_size = 10; int shift_size = 1; CvMat *velx, *vely; CvSize block = cvSize (block_size, block_size); CvSize shift = cvSize (shift_size, shift_size); CvSize max_range = cvSize (50, 50); IplImage *src_img1, *src_img2, *dst_img; if (argc != 3 || (src_img1 = cvLoadImage (argv[1], CV_LOAD_IMAGE_GRAYSCALE)) == 0 || (src_img2 = cvLoadImage (argv[2], CV_LOAD_IMAGE_GRAYSCALE)) == 0) return -1; dst_img = cvLoadImage (argv[2], CV_LOAD_IMAGE_COLOR); // (1)速度ベクトルを格納する構造体の確保 rows = int (ceil (double (src_img1->height) / block_size)); cols = int (ceil (double (src_img1->width) / block_size)); velx = cvCreateMat (rows, cols, CV_32FC1); vely = cvCreateMat (rows, cols, CV_32FC1); cvSetZero (velx); cvSetZero (vely); // (2)オプティカルフローの計算 cvCalcOpticalFlowBM (src_img1, src_img2, block, shift, max_range, 0, velx, vely); // (3)計算されたフローを描画 for (i = 0; i < velx->width; i++) { for (j = 0; j < vely->height; j++) { dx = (int) cvGetReal2D (velx, j, i); dy = (int) cvGetReal2D (vely, j, i); cvLine (dst_img, cvPoint (i * block_size, j * block_size), cvPoint (i * block_size + dx, j * block_size + dy), CV_RGB (255, 0, 0), 1, CV_AA, 0); } } cvNamedWindow ("Image", 1); cvShowImage ("Image", dst_img); cvWaitKey (0); cvDestroyWindow ("Image"); cvReleaseImage (&src_img1); cvReleaseImage (&src_img2); cvReleaseImage (&dst_img); cvReleaseMat (&velx); cvReleaseMat (&vely); return 0; }

// (1)速度ベクトルを格納する構造体の確保
サイズ ceil(src.width/block_size)×ceil(src.height/block_size), 32ビット,シングルチャンネルの,速度ベクトルを格納する構造体を確保する.

// (2)オプティカルフローの計算
ブロックマッチングにより,オプティカルフローを計算する. 3番目の引数は,ブロック(テンプレート)サイズを表す. 4番目の引数は,5番目の引数で表される一つのブロックに対する探索範囲内を, どれだけずらされながらマッチングを行うかを表す.

// (3)計算されたフローを描画
関数cvGetReal2D()を用いて,インデックス(座標)を指定して速度ベクトルを取り出し, それを入力画像上に描画する.

実行結果例

入力画像1 入力画像2 結果画像


オプティカルフロー3 cvCalcOpticalFlowPyrLK

疎な特徴に対するオプティカルフローの計算

サンプルコード

#include <cv.h> #include <highgui.h> int main (int argc, char **argv) { char *status; int i, corner_count = 150; CvPoint2D32f *corners1, *corners2; CvTermCriteria criteria; IplImage *src_img1, *src_img2, *dst_img; IplImage *eig_img, *temp_img; IplImage *prev_pyramid, *curr_pyramid; if (argc != 3 || (src_img1 = cvLoadImage (argv[1], CV_LOAD_IMAGE_GRAYSCALE)) == 0 || (src_img2 = cvLoadImage (argv[2], CV_LOAD_IMAGE_GRAYSCALE)) == 0) return -1; dst_img = cvLoadImage (argv[2], CV_LOAD_IMAGE_COLOR); // (1)必要な構造体の確保 eig_img = cvCreateImage (cvGetSize (src_img1), IPL_DEPTH_32F, 1); temp_img = cvCreateImage (cvGetSize (src_img1), IPL_DEPTH_32F, 1); corners1 = (CvPoint2D32f *) cvAlloc (corner_count * sizeof (CvPoint2D32f)); corners2 = (CvPoint2D32f *) cvAlloc (corner_count * sizeof (CvPoint2D32f)); prev_pyramid = cvCreateImage (cvSize (src_img1->width + 8, src_img1->height / 3), IPL_DEPTH_8U, 1); curr_pyramid = cvCreateImage (cvSize (src_img1->width + 8, src_img1->height / 3), IPL_DEPTH_8U, 1); status = (char *) cvAlloc (corner_count); criteria = cvTermCriteria (CV_TERMCRIT_ITER | CV_TERMCRIT_EPS, 64, 0.01); // (2)疎な特徴点を検出 cvGoodFeaturesToTrack (src_img1, eig_img, temp_img, corners1, &corner_count, 0.001, 5, NULL); // (3)オプティカルフローを計算 cvCalcOpticalFlowPyrLK (src_img1, src_img2, prev_pyramid, curr_pyramid, corners1, corners2, corner_count, cvSize (10, 10), 4, status, NULL, criteria, 0); // (4)計算されたフローを描画 for (i = 0; i < corner_count; i++) { if (status[i]) cvLine (dst_img, cvPointFrom32f (corners1[i]), cvPointFrom32f (corners2[i]), CV_RGB (255, 0, 0), 1, CV_AA, 0); } cvNamedWindow ("Image", 1); cvShowImage ("Image", dst_img); cvWaitKey (0); cvDestroyWindow ("Image"); cvReleaseImage (&src_img1); cvReleaseImage (&src_img2); cvReleaseImage (&dst_img); cvReleaseImage (&eig_img); cvReleaseImage (&temp_img); cvReleaseImage (&prev_pyramid); cvReleaseImage (&curr_pyramid); return 0; }

// (1)必要な構造体の確保
関数cvGoodFeaturesToTrack()および,関数cvCalcOpticalFlowPyrLK()に必要 な構造体を確保する.

// (2)疎な特徴点を検出
関数cvGoodFeaturesToTrack()を用いて,画像中で大きな固有値を持つコーナー (はっきりとした特徴点)を見つける. 検出されたコーナーは,4番目の引数で示される配列に格納され,その個数は, 5番目の引数で示される変数に格納される. 6番目,および7番目の引数はそれぞれ,特徴点の質と特徴点同士の最低距離を表しており, これらの値が小さくなると検出される特徴点が増える. 詳細については,リファレンスを参照すること.

// (3)オプティカルフローを計算
先の関数により検出された疎な特徴点に関して,オプティカルフローを計算する.

// (4)計算されたフローを描画
フローが見付かった点に対してのみ,そのフローを描画する.

実行結果例

入力画像1 入力画像2 結果画像

OpenCV-1.0 リファレンス マニュアル
OpenCV-1.1pre リファレンス マニュアル
OpenCVサンプルコード


画素値の直接操作
部分画像のシャッフル
画像の連結
画像のコピー
画像形状の変形
タイリング
画像の反転
逆行列(擬似逆行列)の計算
色空間の写像
離散フーリエ変換
階層構造を持つ輪郭の座標取得
図形の描画
ポリゴンの描画
凸ポリゴンの描画
テキストの描画
IplImage構造体情報の保存
マップのシーケンスを保存
IplImage構造体情報の読み込み
マップのシーケンスを読み込む
K-means法によるクラスタリング
クラスタリングによる減色処理
エッジの検出
コーナーの検出
並進移動のためのピクセルサンプリング
回転移動のためのピクセルサンプリング
画像のサイズ変更
画像のアフィン変換(1)
画像のアフィン変換(2)
画像の透視投影変換
全方位画像の透視投影変換
モルフォロジー変換
平滑化
ユーザ定義フィルタ
境界線の作成
画像の二値化
画像の二値化(大津の手法)
画像ピラミッドの作成
画像ピラミッドを用いた画像の領域分割
平均値シフト法による画像のセグメント化
Watershedアルゴリズムによる画像の領域分割
輪郭の検出と描画
画像のモーメントを計算
ハフ変換による直線検出
ハフ変換による円検出
距離変換とその可視化
不要オブジェクトの除去
ヒストグラムの描画
ヒストグラム間の距離
二次元のヒストグラム
バックプロジェクションパッチ
ヒストグラムの均一化
テンプレートマッチング
形状のマッチング
点列を包含する矩形
輪郭領域の面積と輪郭の長さ
二つの矩形を包含する矩形
楕円のフィッティング
点列を包含する図形
動的背景更新による物体検出
snakeによる輪郭追跡(静止画)
オプティカルフロー1
オプティカルフロー2
オプティカルフロー3
Condensation
顔の検出
カメラキャリブレーション
歪み補正
マップを利用した歪み補正
サポートベクターマシン
画像の各ピクセル値を特徴ベクトルとしたSVMの学習
画像の各ピクセル値を特徴ベクトルとしたSVMによる物体検出
マウスイベントの取得
トラックバーの利用
カメラからの画像キャプチャ
動画としてファイルへ書き出す
ラベリング