OpenCVでのORBアルゴリズムによる特徴点抽出とマッチングの処理についての2回目です。前回はVisual Studio Community 2013のVisual C++コンソールアプリでORBによる特徴点抽出の基本的と思われる処理を作成しました。今回はGUIを使ってマッチング処理を試してみました。
処理としては、1枚の画像を最大10枚の画像とORBによる特徴点抽出とマッチングを行い、10枚の中で有効なマッチングの数が多かった画像とのマッチング画像を表示します。
今回テストした内容です。数字の画像(以下は例として2の画像)を用意しました。0から9までの数字で、MS Pゴシックのサイズ250のフォントです。
これで同じ画像の2を0から9までと比較してみます。もちろん同じ2の画像で最大の有効なマッチングの個数が得られます。今回のプログラムで以下のように実行します。
まず、0から9までの画像を同じディレクトリに用意します。ここでは、C:\image配下に用意しました。
プログラムを以下のように実行していきます。まず起動直後の画面です。
0から9までの画像の場所を指定します。ここでは初期設定のC:\imageのままです。画像の種類も初期設定のJPEGです。
元画像を読み込みます。ここで元画像というのは0.jpgから9.jpgの画像のことです。
対象画像を読み込みます。ここでは2ですが、もちろんどれでもいいです。画面に表示されます。
有効なマッチング数が最大のマッチング画像が表示されます。もちろん同じ画像の2です。
実際のマッチングの結果
実際のマッチングの結果はデバッグ画面に表示するようにしています。デバッガを起動して以下のように出力画面に表示されます。
実際の数値は以下です。それぞれ70個の特徴点が抽出され、ハミング距離がしきい値内の個数が以下となりました。
2 – 70
3 – 35
8 – 35
0 – 34
6 – 33
9 – 30
5 – 27
7 – 16
1 – 19
4 – 6
しきい値を1.0にしてみました。より似ていると評価される点に絞るという意味合いです。
2 – 70
3 – 23
8 – 21
0 – 20
6 – 16
9 – 15
5 – 9
7 – 7
1 – 7
4 – 1
しきい値を3.0にしてみました。より多くの特徴点を有効にしています。順番が変わりました。
2 – 70
3 – 49
0 – 42
9 – 41
6 – 39
5 – 38
8 – 37
1 – 27
7 – 20
4 – 11
この数字が何を意味しているかというと、このフォントでこの大きさで2とどれが似ているかということだと思うのですが、いかがでしょうか。確かに3や8の方が1や4と比較して似ているとは思います。
縮小も試しました。3を90%にして試したのですが、0,2,8,9 あたりと同じような特徴になるようです。
以上、何とか特徴点を捉えたかなという結果です。
ここからプログラムの説明です。
このプログラム一式は以下です。(※用途に関わらずご利用頂いて問題ありませんが、一切無保証です。不具合等の責任は負いかねます。)
プログラム一式
OpenCVを含むため、33M程度のサイズがあります。
プログラムとしては、前回の処理を以下のGUIプログラムで実行しているということです。OpenCVをNuGetで組み込んで、前回の処理をこのGUI上で実行しています。
Visual Studio Community 2013のVisual C++でOpenCVを使う
プログラムの主な部分の説明です。まず最大10枚分の画像を読む処理です。元画像読み込みボタンで実行されます。指定されたディレクトリの配下の0.jpgまたは0.pngから順に9.jpgまたは9.pngまで読み込みます。必ず0から読みますが、9までのファイルがなければループを抜けます。
変数の宣言部分等全体はお手数ですが、プログラム一式をダウンロードして参照して下さい。
/*---------------------------------------------- マッチング元画像の読み込み -----------------------------------------------*/ void readImagesForMatch() { int idx = 0; string imgPath = ""; imagesFromFiles.clear(); //画像形式 hCombo = GetDlgItem(hwMain, IDC_COMBO1); comNo = (UINT)SendMessage(hCombo, CB_GETCURSEL, 0, 0); char imgExt[10]; if (comNo == 0){ strcpy(imgExt, ".jpg"); } else { strcpy(imgExt, ".png"); } //ディレクトリ名 GetDlgItemText(hwMain, IDC_EDIT1, (LPTSTR)szBufEdit1, (int)sizeof(szBufEdit1)); imgDirPath = szBufEdit1; imgDirPath += "\\"; //0.jpgから9.jpgまで順に読み込み for (idx = 0; idx < 10; idx++){ imgPath = imgDirPath + to_string(idx) + imgExt; //ファイル存在チェック ifstream file(imgPath.c_str()); if (!file){ break; } Mat img = imread(imgPath); imagesFromFiles.push_back(img); } }
マッチング処理は以下です。対象画像読み込みボタンで画面上の読み込んだ画像と、上記で読み込んだ最大10枚の画像を順にマッチングして、有効な特徴点の数が多い画像とのマッチング結果を表示します。ORBの部分は、前回を参照して下さい。
/*---------------------------------------------- マッチング実行 -----------------------------------------------*/ void matching() { // FeatureDetectorオブジェクトの生成 Ptr<FeatureDetector> detector = new ORB(80, 1.25f, 4, 7, 0, 2, 0, 7); Ptr<DescriptorExtractor> extractor = new ORB(80, 1.25f, 4, 7, 0, 2, 0, 7); // 特徴点情報 vector<KeyPoint> keypoints1; vector<KeyPoint> keypoints2; // 対象画像の処理 Mat descriptor1; detector->detect(imgMatRead, keypoints1); extractor->compute(imgMatRead, keypoints1, descriptor1); //個数が最大の番号 int maxIdx = -1; //カウンタ int count = 0; int matchCount = 0; int matchCountMax = -1; //しきい値内の個数 vector<DMatch> dmatchOK; //各画像でマッチング int idx = 0; for (idx = 0; idx < (int)imagesFromFiles.size(); idx++){ detector->detect(imagesFromFiles[idx], keypoints2); // 特徴情報 Mat descriptor2; // 特徴記述の計算 extractor->compute(imagesFromFiles[idx], keypoints2, descriptor2); // DescriptorMatcher生成 Ptr<DescriptorMatcher> matcher = DescriptorMatcher::create("BruteForce-Hamming"); // 特徴点のマッチング情報 vector<DMatch> dmatch; // 特徴点マッチング matcher->match(descriptor1, descriptor2, dmatch); count = dmatch.size(); matchCount = 0; // 最小距離の取得 double min_dist = DBL_MAX; for (int i = 0; i < (int)dmatch.size(); i++){ double dist = dmatch[i].distance; if (dist < min_dist){ min_dist = dist; } } if (min_dist < 1.0) { min_dist = 1.0; } // しきい値を設定する double dThrs = (double)Pos / (double)10.0; const double threshold = dThrs * min_dist; for (int i = 0; i < (int)dmatch.size(); i++){ if (dmatch[i].distance < threshold){ matchCount++; } } //デバッグ用出力 OutputDebugStringForThis("%d - %d\n", idx, count); OutputDebugStringForThis("%d - %d\n", idx, matchCount); //しきい値以内の個数のカウント if (matchCount > matchCountMax){ maxIdx = idx; matchCountMax = matchCount; for (int i = 0; i < (int)dmatch.size(); i++){ if (dmatch[i].distance < threshold){ dmatchOK.push_back(dmatch[i]); } } } } // しきい値内の結果で最大の個数であったマッチング結果画像の作成 Mat result; drawMatches(imgMatRead, keypoints1, imagesFromFiles[maxIdx], keypoints2, dmatchOK, result); imshow("matching", result); }
以上、今回は、ORBアルゴリズムでの特徴点抽出でした。OpenCVはまだまだいろいろとテーマがあるのでこれからも書けたらと思います。
関連サイト、書籍
OpenCVとVisual C++による画像処理と認識(10) —– いろいろな特徴検出法を試し、道路標識に応用する —–
OpenCVとVisual C++による画像処理と認識(11)—– ORBを用いて特徴点のマッチングを行う —–
上記サイトの著者の石立喬先生の書籍です。Kindle版です。
(受注)書籍版、PDF版もあります。