opencv sift guide - mprg : 機械知覚&ロボティクス...

45
OpenCVによるSIFT プログラム解説 機械知覚&ロボティクスグループ 長谷川 昂宏

Upload: duongdang

Post on 07-Jun-2018

235 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: OpenCV SIFT Guide - MPRG : 機械知覚&ロボティクス ...tkhr/src/sift/OpenCV_SIFT...キーポイントと特徴量の配列宣言(main.cpp) 9 /* 出力画像 */ cv::Mat result

OpenCVによるSIFT プログラム解説

機械知覚&ロボティクスグループ 長谷川 昂宏

Page 2: OpenCV SIFT Guide - MPRG : 機械知覚&ロボティクス ...tkhr/src/sift/OpenCV_SIFT...キーポイントと特徴量の配列宣言(main.cpp) 9 /* 出力画像 */ cv::Mat result

プログラムについて•開発ツール :Microsoft Visual Studio 2010 • プログラミング言語 :C++ • 使用したライブラリ :OpenCV 2.4.9

• プログラムのダンロード - http://www.vision.cs.chubu.ac.jp/~tkhr/src/SIFT/SIFT_Detection_and_Description.zip - http://www.vision.cs.chubu.ac.jp/~tkhr/src/SIFT/SIFT_Correspondence_Search.zip

2

Page 3: OpenCV SIFT Guide - MPRG : 機械知覚&ロボティクス ...tkhr/src/sift/OpenCV_SIFT...キーポイントと特徴量の配列宣言(main.cpp) 9 /* 出力画像 */ cv::Mat result

OpenCVのSIFTを使用したプログラム•ファイル名:SIFT_Detection_and_Description - 1枚の画像からのキーポイント検出と特徴量計算

• ファイル名:SIFT_Correspondence_Search - 2枚の画像からのキーポイント検出と特徴量計算 - 2枚の画像から検出したキーポイントをマッチング

3

Page 4: OpenCV SIFT Guide - MPRG : 機械知覚&ロボティクス ...tkhr/src/sift/OpenCV_SIFT...キーポイントと特徴量の配列宣言(main.cpp) 9 /* 出力画像 */ cv::Mat result

OpenCVのSIFTを使用したプログラム•ファイル名:SIFT_Detection_and_Description - 1枚の画像からのキーポイント検出と特徴量計算

• ファイル名:SIFT_Correspondence_Search - 2枚の画像からのキーポイント検出と特徴量計算 - 2枚の画像から検出したキーポイントをマッチング

4

Page 5: OpenCV SIFT Guide - MPRG : 機械知覚&ロボティクス ...tkhr/src/sift/OpenCV_SIFT...キーポイントと特徴量の配列宣言(main.cpp) 9 /* 出力画像 */ cv::Mat result

SIFT_Detection_and_Description• プログラムの構成 - main.cpp • メイン関数

- draw.cpp • キーポイントの描画関数

- draw.h • キーポイントの描画関数のクラス定義

- header.h • ヘッダーファイルの読み込み

- common.h • 入出力画像のパス,パラメータの設定

5

Page 6: OpenCV SIFT Guide - MPRG : 機械知覚&ロボティクス ...tkhr/src/sift/OpenCV_SIFT...キーポイントと特徴量の配列宣言(main.cpp) 9 /* 出力画像 */ cv::Mat result

SIFT_Detection_and_Description

6

・画像の読み込み ・画像のグレースケール変換 ・OpenCVによるSIFTキーポイントの検出 ・OpenCVによるSIFT特徴量の抽出 ・キーポイントと特徴量をテキストに書き出し ・出力画像の表示と保存

main.cpp

・キーポイントの座標,スケール,  オリエンテーションを描画

draw.cpp

・検出したキーポイントの配列 ・出力用の画像配列

・キーポイントを描画した画像配列

Page 7: OpenCV SIFT Guide - MPRG : 機械知覚&ロボティクス ...tkhr/src/sift/OpenCV_SIFT...キーポイントと特徴量の配列宣言(main.cpp) 9 /* 出力画像 */ cv::Mat result

画像の読み込み(main.cpp)

7

/* 画像の読み込み(カラー) */ cv::Mat img = cv::imread(input_path, 1);

OpenCVの多次元配列変数 画像・行列の場合,2次元配列

OpenCVの画像入力関数 第1引数:入力画像のパス (文字列) 第2引数:画像の読み込みタイプ (0:グレースケールで読み込み,1:カラーで読み込み)

/* 画像が読み込めなかった場合は終了 */ if(img.empty()){

printf("Input Error (%s)\n", input_path); return EXIT_FAILURE; }

画像が正常に読み込めなかった場合 → img.empty() == true 画像が正常に読み込めた場合 → img.empty() == false

Page 8: OpenCV SIFT Guide - MPRG : 機械知覚&ロボティクス ...tkhr/src/sift/OpenCV_SIFT...キーポイントと特徴量の配列宣言(main.cpp) 9 /* 出力画像 */ cv::Mat result

グレースケール変換と画像の正規化(main.cpp)

8

/* グレースケール画像 */ cv::Mat gray_img;

/* カラー画像をグレースケール画像に変換 */ cv::cvtColor(img, gray_img, CV_BGR2GRAY);OpenCVによる画像の色空間を変換する関数 第1引数:変換前の画像 第2引数:変換後の画像 第3引数:変換の種類

CV_BGR2GRAY:RGB → Gray CV_BGR2HSV :RGB → HSV CV_BGR2LAB : RGB → L*a*b*

/* グレースケール画像を正規化 */ cv::normalize(gray_img, gray_img, 0, 255, cv::NORM_MINMAX);OpenCVによる画像の正規化関数 第1引数:正規化前の画像 第2引数:正規化後の画像 第3引数:正規化後の下限値 第4引数:正規化後の上限値 第5引数:正規化の種類

Page 9: OpenCV SIFT Guide - MPRG : 機械知覚&ロボティクス ...tkhr/src/sift/OpenCV_SIFT...キーポイントと特徴量の配列宣言(main.cpp) 9 /* 出力画像 */ cv::Mat result

キーポイントと特徴量の配列宣言(main.cpp)

9

/* 出力画像 */ cv::Mat result = img.clone();出力画像配列に入力画像をそのままコピー

/* 検出したキーポイントを入れる配列 */ std::vector<cv::KeyPoint> keypoints;C++の動的配列宣言 cv::KeyPoint:キーポイントデータを格納するためのクラス int型の配列を宣言したい場合:std::vector<int>

/* 抽出した特徴量を入れる配列 */ cv::Mat features;特徴量の配列はMat型で宣言

Page 10: OpenCV SIFT Guide - MPRG : 機械知覚&ロボティクス ...tkhr/src/sift/OpenCV_SIFT...キーポイントと特徴量の配列宣言(main.cpp) 9 /* 出力画像 */ cv::Mat result

処理時間計測(main.cpp)

10

double tic(){ return (double)cv::getTickCount();

}

int main(){ /* 処理時間計測開始 */ t = tic();

/* 処理時間計測終了 */ t = toc(t);

}

tic()とtoc()の間の処理時間を計測 tには計測した時間(秒)が代入される

double toc(double t){ return ((double)cv::getTickCount() - t) / cv::getTickFrequency();

}

Page 11: OpenCV SIFT Guide - MPRG : 機械知覚&ロボティクス ...tkhr/src/sift/OpenCV_SIFT...キーポイントと特徴量の配列宣言(main.cpp) 9 /* 出力画像 */ cv::Mat result

SIFTによるキーポイント検出と特徴量記述(main.cpp)

11

/* SIFTによるキーポイントの検出 */ cv::Mat SiftFeatureDetector detector(

         param.n_features,          param.n_octave_layers, param.contrast_threshold, param.edge_threshold, param.sigma);

detector.detect(gray_img, keypoints);

OpenCVによるSIFTキーポイント検出の変数を定義 第1引数:DoGのスコアをソートして上位n個を出力 (0の場合全てのキーポイントを出力) 第2引数:1オクターブあたりのピラミッドの層 第3引数:コントラストによるキーポイントの絞り込みの閾値 第4引数:エッジ上のキーポイントの絞り込みの閾値 第5引数:最初の画像に適用するガウシアンフィルタのσ

SIFTによるキーポイント検出関数 第1引数:グレースケール画像 第2引数:キーポイント配列

/* SIFTによる特徴量の抽出 */ cv::SiftDescriptorExtractor extractor;

extractor.compute(gray_img, keypoints, features);OpenCVによるSIFT特徴量記述の変数を定義

SIFTによる特徴量記述関数 第1引数:グレースケール画像 第2引数:キーポイント配列 第3引数:特徴量配列

Page 12: OpenCV SIFT Guide - MPRG : 機械知覚&ロボティクス ...tkhr/src/sift/OpenCV_SIFT...キーポイントと特徴量の配列宣言(main.cpp) 9 /* 出力画像 */ cv::Mat result

キーポイントの描画(main.cpp)

12

/* 検出したキーポイントを出力画像に描画 */ draw drawing;

drawing.drawKeypoints(result, keypoints);検出したキーポイントを描画する関数 第1引数:キーポイントを描画する画像 第2引数:キーポイント配列

drawクラスによる変数の定義

Page 13: OpenCV SIFT Guide - MPRG : 機械知覚&ロボティクス ...tkhr/src/sift/OpenCV_SIFT...キーポイントと特徴量の配列宣言(main.cpp) 9 /* 出力画像 */ cv::Mat result

キーポイントの描画(draw.cpp)

13

int draw::drawKeypoints(cv::Mat &result, std::vector<cv::KeyPoint> keypoints){

for(size_t i = 0; i < keypoints.size(); i++){

}

return EXIT_SUCCESS; }

keypoinsの配列の要素数 (= 検出したキーポイントの数)

検出したキーポイントを描画する関数 第1引数:キーポイントを描画する画像 (参照渡し) 第2引数:キーポイント配列

for文の中で検出したキーポイントを1点ずつ画像に描画

Page 14: OpenCV SIFT Guide - MPRG : 機械知覚&ロボティクス ...tkhr/src/sift/OpenCV_SIFT...キーポイントと特徴量の配列宣言(main.cpp) 9 /* 出力画像 */ cv::Mat result

/* キーポイントの座標点を描画 */ cv::circle(result, keypoints[i].pt, POINT_RADIUS, POINT_COLOR, POINT_THICKNESS, POINT_TYPE);

キーポイントの座標点の描画(draw.cpp)

14

OpenCVによる円を描画する関数 第1引数:出力画像 第2引数:キーポイントの座標 (keypoints[i].pt:i番目のキーポイントの座標) 第3引数:描画する円の半径 第4引数:描画する円の色 第5引数:描画する円の太さ (負の数の場合は円の中を塗りつぶし) 第6引数:描画の種類

キーポイントの座標点の描画結果

Page 15: OpenCV SIFT Guide - MPRG : 機械知覚&ロボティクス ...tkhr/src/sift/OpenCV_SIFT...キーポイントと特徴量の配列宣言(main.cpp) 9 /* 出力画像 */ cv::Mat result

/* キーポイントのスケールを描画 */ cv::circle(result, keypoints[i].pt, keypoints[i].size, SCALE_COLOR, SCALE_THICKNESS, SCALE_TYPE);

キーポイントのスケールを描画(draw.cpp)

15

OpenCVによる円を描画する関数 第1引数:出力画像 第2引数:キーポイントの座標 (keypoints[i].pt:i番目のキーポイントの座標) 第3引数:キーポイントのスケールサイズ (keypoints[i].size:i番目のキーポイントのスケール) 第4引数:描画する円の色 第5引数:描画する円の太さ (負の数の場合は円の中を塗りつぶし) 第6引数:描画の種類

キーポイントの座標点とスケールの描画結果

Page 16: OpenCV SIFT Guide - MPRG : 機械知覚&ロボティクス ...tkhr/src/sift/OpenCV_SIFT...キーポイントと特徴量の配列宣言(main.cpp) 9 /* 出力画像 */ cv::Mat result

/* オリエンテーションとスケールが交わるx座標を計算 */ cpt.x = keypoints[i].pt.x + cos(keypoints[i].angle * DEG2RAD) * keypoints[i].size;

キーポイントのオリエンテーションを描画(draw.cpp)

16

i番目のキーポイントのx座標 i番目のキーポイントの オリエンテーション

DegreeからRadianに 変換するための係数

この点のx座標を計算

/* オリエンテーションとスケールが交わるy座標を計算 */ cpt.y = keypoints[i].pt.y + sin(keypoints[i].angle * DEG2RAD) * keypoints[i].size;

i番目のキーポイントのy座標

この点のy座標を計算

Page 17: OpenCV SIFT Guide - MPRG : 機械知覚&ロボティクス ...tkhr/src/sift/OpenCV_SIFT...キーポイントと特徴量の配列宣言(main.cpp) 9 /* 出力画像 */ cv::Mat result

キーポイントのオリエンテーションを描画(draw.cpp)

17キーポイントの座標点,スケール,オリエンテーションの描画結果

/* キーポイントのオリエンテーションを描画 */ cv::line(result, keypoints[i].pt, cpt, ORIENTATION_COLOR, ORIENTATION_THICKNESS, ORIENTATION_TYPE);OpenCVによる直線を描画する関数 第1引数:出力画像 第2引数:キーポイントの座標 (直線の始点) 第3引数:キーポイントのスケールとオリエンテーションが交わる円周上の座標 (直線の終点) 第4引数:描画する直線の色 第5引数:描画する直線の太さ 第6引数:描画の種類

Page 18: OpenCV SIFT Guide - MPRG : 機械知覚&ロボティクス ...tkhr/src/sift/OpenCV_SIFT...キーポイントと特徴量の配列宣言(main.cpp) 9 /* 出力画像 */ cv::Mat result

キーポイントと特徴量の書き出し(main.cpp)

18

/* キーポイントと特徴量をテキストに書き出すためのファイルポインタ */ FILE *det_fp, *des_fp; det_fp = fopen(keypoints_path, “w");

des_fp = fopen(features_path, "w");キーポイントを書き出すテキストファイル

特徴量を書き出すテキストファイル

/* キーポイントをテキストに書き出し */ for(size_t i = 0; i < keypoints.size(); i++){ /* x座標, y座標, スケールサイズ, オリエンテーション(deg), DoGの出力値 */ fprintf(det_fp, "%.2f, %.2f, %.2f, %.2f, %.2f\n", keypoints[i].pt.x, keypoints[i].pt.y, keypoints[i].size, keypoints[i].angle, keypoints[i].response);

}

キーポイント配列のデータをテキストに書き出し keypoints[i].pt.x :i番目のキーポイントのx座標 keypoints[i].pt.y :i番目のキーポイントのy座標 keypoints[i].size :i番目のキーポイントのスケールサイズ keypoints[i].angle :i番目のキーポイントのオリエンテーション (deg) keypoints[i].response :i番目のキーポイントのDoG出力値

Page 19: OpenCV SIFT Guide - MPRG : 機械知覚&ロボティクス ...tkhr/src/sift/OpenCV_SIFT...キーポイントと特徴量の配列宣言(main.cpp) 9 /* 出力画像 */ cv::Mat result

キーポイントと特徴量の書き出し(main.cpp)

19

/* 特徴量をテキストに書き出し */ for(size_t j = 0; j < features.rows; j++){

for(size_t i = 0; i < features.cols; i++){

fprintf(des_fp, "%.2f ", features.at<float>(j, i));

} fprintf(des_fp, "\n");

}

特徴量配列 (キーポイント数 × 特徴量次元数)の行数

特徴量配列 (キーポイント数 × 特徴量次元数)の列数

j行 i列の配列要素へアクセス

0.00 39.00 110.00 0.00 0.0013.000.002.002.00

6.00 1.00 1.00 0.00107.00 128.00 0.00 0.000.00 0.00 0.00 2.0035.00 122.00 68.00 8.00

0.0043.000.002.000.00

103.00 6.00 10.00 0.00 22.00 88.00

· · ·

···

· · ·· · ·· · ·· · ·

· · ·

···

···

···

···

··· ···

キーポイント1キーポイント2キーポイント3キーポイント4キーポイント5

キーポイントn

1次元 2次元 3次元 4次元 5次元 128次元· · ·

···

特徴量の配列構造

Page 20: OpenCV SIFT Guide - MPRG : 機械知覚&ロボティクス ...tkhr/src/sift/OpenCV_SIFT...キーポイントと特徴量の配列宣言(main.cpp) 9 /* 出力画像 */ cv::Mat result

出力画像の表示と保存(main.cpp)

20

/* 出力ウィンドウの用意 */ cv::namedWindow(“SIFT Result”, CV_WINDOW_AUTOSIZE);

/* 出力画像をウィンドウで表示 */ cv::imshow(“SIFT Result”, result);

/* 出力画像の保存 */ cv::imwrite(output_img, result);

/* キーを押すまで待機 */ cv::waitKey();

OpenCVによる出力ウィンドウの定義 第1引数:ウィンドウの名前 第2引数:ウィンドウの種類

CV_WINDOW_AOUTOSIZE:画像に合わせてウィンドウサイズが決まる CV_WINDOW_FREERATIO :アスペクト比を保たずにウィンドウの調整が可能 CV_WINDOW_KEEPRATIO :アスペクト比を保つようにウィンドウの調整が可能 CV_WINDOW_NORMAL :ユーザがウィンドウサイズを変更可能

OpenCVによる画像の表示関数 第1引数:ウィンドウの名前 第2引数:表示する画像

OpenCVによる画像の保存 第1引数:出力画像のパス 第2引数:保存する画像

OpenCVによる待機関数 引数が0の場合はキーが押されるまで待機 引数が正の整数なら引数の時間(ms)待機

Page 21: OpenCV SIFT Guide - MPRG : 機械知覚&ロボティクス ...tkhr/src/sift/OpenCV_SIFT...キーポイントと特徴量の配列宣言(main.cpp) 9 /* 出力画像 */ cv::Mat result

OpenCVのSIFTを使用したプログラム•ファイル名:SIFT_Detection_and_Description - 1枚の画像からのキーポイント検出と特徴量計算

• ファイル名:SIFT_Correspondence_Search - 2枚の画像からのキーポイント検出と特徴量計算 - 2枚の画像から検出したキーポイントをマッチング

21

Page 22: OpenCV SIFT Guide - MPRG : 機械知覚&ロボティクス ...tkhr/src/sift/OpenCV_SIFT...キーポイントと特徴量の配列宣言(main.cpp) 9 /* 出力画像 */ cv::Mat result

SIFT_Correspondence_Search• プログラムの構成 - main.cpp • メイン関数

- search.cpp • 対応点探索の関数

- search.h • 対応点探索関数のクラス定義

- draw.cpp • 対応点の描画関数

- draw.h • 対応点の描画関数のクラス定義

- header.h • ヘッダーファイルの読み込み

- common.h • 入出力画像のパス,パラメータの設定 22

Page 23: OpenCV SIFT Guide - MPRG : 機械知覚&ロボティクス ...tkhr/src/sift/OpenCV_SIFT...キーポイントと特徴量の配列宣言(main.cpp) 9 /* 出力画像 */ cv::Mat result

SIFT_Correspondence_Search

23

・画像の読み込み ・画像のグレースケール変換 ・OpenCVによるSIFTキーポイントの検出 ・OpenCVによるSIFT特徴量の抽出 ・対応点の座標をテキストに書き出し ・出力画像の表示と保存

main.cpp

・対応点同士を直線で結ぶ ・キーポイント数,対応点数,処理時間を画像に描画

draw.cpp

・2画像間のSIFT特徴量の  ユークリッド距離を計算 ・Top 1とTop 2のユークリッド距離を  比較して対応点を探索

search.cpp

・検出したキーポイントの配列 ・記述した特徴量の配列 ・対応点座標を格納する配列

・対応点座標を格納した配列

・出力用の画像配列 ・検出したキーポイントの配列 ・記述した特徴量の配列 ・対応点座標を格納した配列

・対応点を描画した画像配列

Page 24: OpenCV SIFT Guide - MPRG : 機械知覚&ロボティクス ...tkhr/src/sift/OpenCV_SIFT...キーポイントと特徴量の配列宣言(main.cpp) 9 /* 出力画像 */ cv::Mat result

画像の読み込み(main.cpp)

24

/* 画像の読み込み(カラー) */ cv::Mat img1 = cv::imread(input_path1, 1); cv::Mat img2 = cv::imread(input_path2, 1); /* 画像が読み込めなかった場合は終了 */ if(img1.empty()){ printf("Input Error (%s)\n", input_path1); return EXIT_FAILURE; }

if(img2.empty()){ printf("Input Error (%s)\n", input_path2); return EXIT_FAILURE; }

「SIFT_Detection_and_Description」と同じ処理 今回は2枚の画像をマッチングするため読み込む画像は2枚

Page 25: OpenCV SIFT Guide - MPRG : 機械知覚&ロボティクス ...tkhr/src/sift/OpenCV_SIFT...キーポイントと特徴量の配列宣言(main.cpp) 9 /* 出力画像 */ cv::Mat result

グレースケール変換と画像の正規化(main.cpp)

25

/* グレースケール画像 */ cv::Mat gray_img1, gray_img2;

/* カラー画像をグレースケール画像に変換 */ cv::cvtColor(img1, gray_img1, CV_BGR2GRAY); cv::cvtColor(img2, gray_img2, CV_BGR2GRAY);

/* グレースケール画像を正規化 */ cv::normalize(gray_img1, gray_img1, 0, 255, cv::NORM_MINMAX); cv::normalize(gray_img2, gray_img2, 0, 255, cv::NORM_MINMAX);

「SIFT_Detection_and_Description」と同じ処理

Page 26: OpenCV SIFT Guide - MPRG : 機械知覚&ロボティクス ...tkhr/src/sift/OpenCV_SIFT...キーポイントと特徴量の配列宣言(main.cpp) 9 /* 出力画像 */ cv::Mat result

出力画像の生成(main.cpp)

26

/* 出力画像の生成 */ cv::Mat result(cv::Size(img1.cols + img2.cols, img1.rows + img2.rows), CV_8UC3, BACKGROUND_COLOR);

cv::Mat part1(result, cv::Rect(0, 0, img1.cols, img1.rows));

cv::Mat part2(result, cv::Rect(img1.cols, img1.rows, img2.cols, img2.rows));

img1.copyTo(part1);

img2.copyTo(part2);

出力画像の定義 第1引数:OpenCVによるサイズ指定 (画像1の幅 + 画像2の幅,画像1の高さ + 画像2の高さ) 第2引数:Mat型配列のタイプ 第3引数:初期値 (画像の場合初期の色)

CV_8UC3 :unsigned char型, 3チャンネル (カラー画像) CV_8UC1 :unsigned char型, 1チャンネル (グレースケール画像) CV_32FC1:float型, 1チャンネル CV_64FC1:double型, 1チャンネル

出力画像(result)の一部を矩形領域で定義 第1引数:出力用画像配列 第2引数:出力画像内の矩形領域を指定 (左上のx, y座標, 右下のx, y座標)

出力画像(result)の一部を矩形領域で定義 第1引数:出力用画像配列 第2引数:出力画像内の矩形領域を指定 (左上のx, y座標, 右下のx, y座標)

画像1(img1)を上記で指定した出力用画像の矩形領域(part1)へコピー

画像2(img2)を上記で指定した出力用画像の矩形領域(part2)へコピー

Page 27: OpenCV SIFT Guide - MPRG : 機械知覚&ロボティクス ...tkhr/src/sift/OpenCV_SIFT...キーポイントと特徴量の配列宣言(main.cpp) 9 /* 出力画像 */ cv::Mat result

配列(キーポイント, 特徴量, 対応点)の宣言(main.cpp)

27

/* 検出したキーポイントを入れる配列 */ std::vector<cv::KeyPoint> keypoints1; std::vector<cv::KeyPoint> keypoints2;

/* 抽出した特長量を入れる配列 */ cv::Mat features1; cv::Mat features2;

/* 対応点のインデックスを入れる配列 */ std::vector<long int> cp_idx;

「SIFT_Detection_and_Description」と同じ処理 画像2枚分のキーポイント配列と特徴量配列を用意

キーポイント配列1(keypoints1)に対応するキーポイント配列2(keypoints2)のインデックスを格納する配列

Page 28: OpenCV SIFT Guide - MPRG : 機械知覚&ロボティクス ...tkhr/src/sift/OpenCV_SIFT...キーポイントと特徴量の配列宣言(main.cpp) 9 /* 出力画像 */ cv::Mat result

SIFTによるキーポイント検出と特徴量記述(main.cpp)

28

/* SIFTによるキーポイントの検出 */ cv::Mat SiftFeatureDetector detector(

         param.n_features,          param.n_octave_layers, param.contrast_threshold, param.edge_threshold, param.sigma);

detector.detect(gray_img1, keypoints1); detector.detect(gray_img2, keypoints2);

/* SIFTによる特徴量の抽出 */ cv::SiftDescriptorExtractor extractor; extractor.compute(gray_img1, keypoints1, features1); extractor.compute(gray_img2, keypoints2, features2);

「SIFT_Detection_and_Description」と同じ処理 2枚のグレースケール画像(gray_img1, gray_img2)からキーポイントと特徴量を計算

Page 29: OpenCV SIFT Guide - MPRG : 機械知覚&ロボティクス ...tkhr/src/sift/OpenCV_SIFT...キーポイントと特徴量の配列宣言(main.cpp) 9 /* 出力画像 */ cv::Mat result

対応点の探索(main.cpp)

29

/* 対応点の探索 */ search searching;

matches = searching.correspondenceSearch(features1, features2, cp_idx);

searchクラスによる変数の定義

2画像間の対応点を探索する関数 第1引数:画像1の特徴量配列 第2引数:画像2の特徴量配列 第3引数:対応点となる特徴量(キーポイント)配列のインデックスを入れる配列

対応点数(戻り値)

Page 30: OpenCV SIFT Guide - MPRG : 機械知覚&ロボティクス ...tkhr/src/sift/OpenCV_SIFT...キーポイントと特徴量の配列宣言(main.cpp) 9 /* 出力画像 */ cv::Mat result

対応点の探索(search.cpp)

30

/* 特徴量間の距離を入れる配列 */ std::vector<search> feature_dists(features2.rows);

/* 特徴量の差の2乗和 */ double ssd = 0.0;

/* 対応点の数 */ size_t num_of_matches = 0;

特徴量間の距離とインデックスを入れる配列 (要素数は画像2の特徴量数で定義) 型はクラスで定義

Page 31: OpenCV SIFT Guide - MPRG : 機械知覚&ロボティクス ...tkhr/src/sift/OpenCV_SIFT...キーポイントと特徴量の配列宣言(main.cpp) 9 /* 出力画像 */ cv::Mat result

対応点探索のループ文(search.cpp)

31

/* 距離が近い特徴量を全探索 */ for(size_t i = 0; i < features1.rows; i++){

for(size_t j = 0; j < features2.rows; j++){

for(size_t k = 0; k < features1.cols; k++){

} }

}

3つ目のfor文の中で各次元の特徴量の差の2乗を計算して和をとる

特徴量配列1の行数 (キーポイント数)ループ

特徴量配列1の列数 (特徴量の次元数)ループ

特徴量配列2の行数 (キーポイント数)ループ

Page 32: OpenCV SIFT Guide - MPRG : 機械知覚&ロボティクス ...tkhr/src/sift/OpenCV_SIFT...キーポイントと特徴量の配列宣言(main.cpp) 9 /* 出力画像 */ cv::Mat result

特徴量同士の計算(search.cpp)

32

for(size_t k = 0; k < features1.cols; k++){

/* 特徴量の差の2乗和を計算 */ float diff = features1.at<float>(i, k) - features2.at<float>(j, k);

ssd += (diff * diff);

}

/* 各特徴量間のユークリッド距離を代入 */ feature_dists[j].disntance = sqrt(ssd);

/* 各特長量間のインデックスを代入 */ feature_dists[j].index = j;

ssd = 0.0;

特徴量1(features1)のi番目, k次元目と特徴量2(features2)のj番目, k次元目の差分値

特徴量同士の差分値の2乗の和を計算

特徴量間のユークリッド距離を代入

特徴量2のインデックスを代入

Page 33: OpenCV SIFT Guide - MPRG : 機械知覚&ロボティクス ...tkhr/src/sift/OpenCV_SIFT...キーポイントと特徴量の配列宣言(main.cpp) 9 /* 出力画像 */ cv::Mat result

対応点の判定(search.cpp)

33

/* 各特徴量間のユークリッド距離をソート */ sortDistance(feature_dists);

/* (1番目に近い距離) < (2番目に近い距離 * 閾値)の場合対応点とする */ if(feature_dists[0].disntance < feature_dists[1].disntance * DIST_RATIO){

/* 対応点の数をカウント */ num_of_matches++;

/* 対応点のインデックスを代入 */ cp_idx.push_back(feature_dists[0].index);

} else{ /* 対応点でない場合は-1を代入 */ cp_idx.push_back(-1);

}

特徴量1と特徴量2のユークリッド距離を昇順にソートする関数

対応点か否かの判定 [Top1 < Top2 * 閾値] が成り立つ場合,対応点とする (0 < 閾値 <= 1)

対応点の場合,特徴量2(キーポイント2)のインデックスを配列に代入

対応点でない場合,-1を配列に代入

Page 34: OpenCV SIFT Guide - MPRG : 機械知覚&ロボティクス ...tkhr/src/sift/OpenCV_SIFT...キーポイントと特徴量の配列宣言(main.cpp) 9 /* 出力画像 */ cv::Mat result

対応点の描画(main.cpp)

34

/* 対応点同士の直線を出力画像に描画 */ draw drawing;

drawing.drawCorrespondence(result, keypoints1, keypoints2, cp_idx, matches, img1.cols, img1.rows, t);対応点の座標を結んだ直線を描画する関数 第1引数:対応点を描画する画像配列 第2引数:キーポイント配列1 第3引数:キーポイント配列2 第4引数:対応点のインデックス配列 第5引数:対応点数 第6引数:画像1の幅 第7引数:画像1の高さ 第8引数:処理時間

drawクラスによる変数の定義

Page 35: OpenCV SIFT Guide - MPRG : 機械知覚&ロボティクス ...tkhr/src/sift/OpenCV_SIFT...キーポイントと特徴量の配列宣言(main.cpp) 9 /* 出力画像 */ cv::Mat result

対応点の描画(draw.cpp)

35

/* 描画する対応点の座標 */ cv::Point pt1; cv::Point pt2;

/* 乱数パターンの初期化 */ srand((unsigned int)time(NULL));

/* 対応点を描画 */ for(size_t i = 0; i < cp_idx.size(); i++){

/* 配列の要素が-1以外のときは対応点 */ if(cp_idx[i] != -1){

} }

インデックス配列の要素数 (= キーポイント数)

ここのif文の中で対応点同士を直線で結び,画像に描画

Page 36: OpenCV SIFT Guide - MPRG : 機械知覚&ロボティクス ...tkhr/src/sift/OpenCV_SIFT...キーポイントと特徴量の配列宣言(main.cpp) 9 /* 出力画像 */ cv::Mat result

対応点を直線で結ぶ(draw.cpp)

36

/* R, G, Bの値を乱数で決定 */ int red = rand() % 256; int green = rand() % 256; int blue = rand() % 256;

/* 乱数によるカラー */ cv::Scalar rand_color(blue, green, red);

/* 画像1の対応点座標 */ pt1.x = keypoints1[i].pt.x; pt1.y = keypoints1[i].pt.y;

/* 画像2の対応点座標 */ pt2.x = keypoints2[cp_idx[i]].pt.x + width; pt2.y = keypoints2[cp_idx[i]].pt.y + height;

cp_idx[i]はkeypoints1[i]に対応するkeypoints2の配列番号 2枚の画像を斜めに配置しているためkeypoints2のx, y座標にそれぞれ画像1の幅と高さを足す

Page 37: OpenCV SIFT Guide - MPRG : 機械知覚&ロボティクス ...tkhr/src/sift/OpenCV_SIFT...キーポイントと特徴量の配列宣言(main.cpp) 9 /* 出力画像 */ cv::Mat result

対応点を直線で結ぶ(draw.cpp)

37

/* 画像1と画像2の座標同士を線で結ぶ */ cv::line(result, pt1, pt2, rand_color, LINE_THICKNESS, LINE_TYPE);OpenCVによる直線を描画する関数 第1引数:出力画像 第2引数:キーポイント1の対応点座標 (直線の始点) 第3引数:キーポイント2の対応点座標 (直線の終点) 第4引数:描画する直線の色 第5引数:描画する直線の太さ 第6引数:描画の種類

Page 38: OpenCV SIFT Guide - MPRG : 機械知覚&ロボティクス ...tkhr/src/sift/OpenCV_SIFT...キーポイントと特徴量の配列宣言(main.cpp) 9 /* 出力画像 */ cv::Mat result

文字列の描画(draw.cpp)

38

/* OpenCVのフォント */ int face[] = {

cv::FONT_HERSHEY_SIMPLEX, cv::FONT_HERSHEY_PLAIN, cv::FONT_HERSHEY_DUPLEX, cv::FONT_HERSHEY_COMPLEX, cv::FONT_HERSHEY_TRIPLEX, cv::FONT_HERSHEY_COMPLEX_SMALL, cv::FONT_HERSHEY_SCRIPT_SIMPLEX, cv::FONT_HERSHEY_SCRIPT_COMPLEX, cv::FONT_ITALIC

};

/* 画像に描画するテキストを入れる配列 */ char keys1[128], keys2[128], corresp[128], time[128];

OpenCVで扱える文字のフォントの種類

Page 39: OpenCV SIFT Guide - MPRG : 機械知覚&ロボティクス ...tkhr/src/sift/OpenCV_SIFT...キーポイントと特徴量の配列宣言(main.cpp) 9 /* 出力画像 */ cv::Mat result

文字列の描画(draw.cpp)

39

/* 各配列にテキストを代入 */ sprintf(keys1, "Image1: %d", keypoints1.size());

sprintf(keys2, "Image2: %d", keypoints2.size());

sprintf(corresp, "Corresponding points: %d", matches);

sprintf(time, "Computational time: %.2f[s]", t);

画像1のキーポイント数の文字列

画像2のキーポイント数の文字列

対応点数の文字列

SIFTによるキーポイントマッチングの処理時間の文字列

Page 40: OpenCV SIFT Guide - MPRG : 機械知覚&ロボティクス ...tkhr/src/sift/OpenCV_SIFT...キーポイントと特徴量の配列宣言(main.cpp) 9 /* 出力画像 */ cv::Mat result

文字列の描画(draw.cpp)

40

/* 出力画像にテキストを描画 */ cv::putText(result, keys1, cv::Point(50, result.rows - 60), face[CHARACTER_FONT], CHARACTER_SCALE, CHARACTER_COLOR, CHARACTER_THICKNESS, CHARACTER_TYPE);

cv::putText(result, keys2, cv::Point(50, result.rows - 20), face[CHARACTER_FONT], CHARACTER_SCALE, CHARACTER_COLOR, CHARACTER_THICKNESS, CHARACTER_TYPE);

cv::putText(result, corresp, cv::Point(width + 50, 50), face[CHARACTER_FONT], CHARACTER_SCALE, CHARACTER_COLOR, CHARACTER_THICKNESS, CHARACTER_TYPE);

cv::putText(result, time, cv::Point(width + 50, 100), face[CHARACTER_FONT], CHARACTER_SCALE, CHARACTER_COLOR, CHARACTER_THICKNESS, CHARACTER_TYPE); OpenCVによる文字列の描画関数 第1引数:描画する画像,第2引数:描画する文字列,第3引数:描画する文字列の位置,第4引数:文字のフォント 第5引数:文字のスケール,第6引数:文字の色,第7引数:文字の太さ,第8引数:文字の描画方法

Page 41: OpenCV SIFT Guide - MPRG : 機械知覚&ロボティクス ...tkhr/src/sift/OpenCV_SIFT...キーポイントと特徴量の配列宣言(main.cpp) 9 /* 出力画像 */ cv::Mat result

対応点の描画結果

41

画像1

画像2

Page 42: OpenCV SIFT Guide - MPRG : 機械知覚&ロボティクス ...tkhr/src/sift/OpenCV_SIFT...キーポイントと特徴量の配列宣言(main.cpp) 9 /* 出力画像 */ cv::Mat result

対応点座標の書き出し(main.cpp)

42

/* 対応点座標をテキストに書き出すための変数 */ std::ofstream ofs(corresp_path);

/* 画像1と画像2の対応点座標 */ cv::Point2f pt1; cv::Point2f pt2;

出力テキストファイルの宣言 fopen()のC++バージョン

Page 43: OpenCV SIFT Guide - MPRG : 機械知覚&ロボティクス ...tkhr/src/sift/OpenCV_SIFT...キーポイントと特徴量の配列宣言(main.cpp) 9 /* 出力画像 */ cv::Mat result

対応点座標の書き出し(main.cpp)

43

/* 対応点の座標をテキストに書き出し */ for(size_t i = 0; i < cp_idx.size(); i++){

/* 配列の要素が-1以外のときは対応点 */ if(cp_idx[i] != -1){

/* 画像1の対応点座標 */ pt1.x = keypoints1[i].pt.x; pt1.y = keypoints1[i].pt.y;

/* 画像2の対応点座標 */ pt2.x = keypoints2[cp_idx[i]].pt.x; pt2.y = keypoints2[cp_idx[i]].pt.y;

/* 対応点の座標をテキストファイルに書き出し */ ofs << pt1 << " " << pt2 << std::endl;

} }

画像1の対応点座標(pt1)と画像2の対応点座標(pt2)をファイルに書き出し

Page 44: OpenCV SIFT Guide - MPRG : 機械知覚&ロボティクス ...tkhr/src/sift/OpenCV_SIFT...キーポイントと特徴量の配列宣言(main.cpp) 9 /* 出力画像 */ cv::Mat result

出力画像の表示と保存(main.cpp)

44

/* 出力画像が大きすぎる場合はコメントを解除してダウンサンプリング */ //cv::resize(result, result, cv::Size(), 0.5, 0.5, cv::INTER_AREA);

/* 出力ウィンドウの用意 */ cv::namedWindow("SIFT Matching", CV_WINDOW_AUTOSIZE); /* 出力画像をウィンドウで表示 */ cv::imshow("SIFT Matching", result); /* 出力画像の保存 */ cv::imwrite(output_img_path, result); /* キーを押すまで待機 */ cv::waitKey(0);

OpenCVによる画像の拡大・縮小関数 第1引数:拡大・縮小前の画像 第2引数:拡大・縮小後の画像 第3引数:拡大・縮小後の画像サイズ      (0の場合はx, y方向のスケール係数で自動的に決定) 第4引数:x方向のスケール係数      (0の場合は拡大・縮小後の画像サイズで自動的に決定) 第5引数:y方向のスケール係数      (0は拡大・縮小後の画像サイズで自動的に決定) 第6引数:拡大・縮小の方法

「SIFT_Detection_and_Description」と同じ処理

cv::INTER_NEAREST :最近傍補間 cv::INTER_LINEAR :バイリニア補間(デフォルト) cv::INTER_AREA :ピクセル領域の関係を利用したサンプリング cv::INTER_CUBIC :4×4の近傍領域を利用するバイキュービック補間 cv::INTER_LANCZOS4:8×8の近傍を利用するLanczos法の補間

Page 45: OpenCV SIFT Guide - MPRG : 機械知覚&ロボティクス ...tkhr/src/sift/OpenCV_SIFT...キーポイントと特徴量の配列宣言(main.cpp) 9 /* 出力画像 */ cv::Mat result

プログラムについて

45

プログラムの不明な点,バグの発見等がありましたらご連絡ください。藤吉研究室 D1 長谷川 昂宏 [email protected]

本資料のダウンロード http://www.vision.cs.chubu.ac.jp/~tkhr/src/SIFT/

OpenCV_SIFT_Guide.pdf