cv::ORB の使い方【OpenCV/C++】〜ORB特徴点の検出とマッチング〜

OpenCV for C++
📌 準備: OpenCV の環境構築がまだの方はこちら → C++ 環境構築ガイド / C#(OpenCvSharp)セットアップ

cv::ORB の使い方【OpenCV/C++】〜ORB特徴点の検出とマッチング〜

cv::ORB は、スケール・回転に頑健な特徴点を高速に検出・記述できる OpenCV の代表的なアルゴリズムです。SIFT/SURF と異なりライセンスフリーで商用利用可能なため、製品組み込みでも広く採用されています。最小の呼び出しは次のとおりです。

auto orb = cv::ORB::create();
orb->detectAndCompute(img, cv::noArray(), keypoints, descriptors);

動作環境

項目 内容
OpenCV 4.x
言語 C++17
モジュール features2d(追加モジュール不要)
ビルドコマンド g++ -std=c++17 main.cpp \pkg-config –cflags –libs opencv4“

環境構築がまだの方は環境構築ガイドを先にどうぞ。


基本の使い方

まず1枚の画像から特徴点を検出し、結果をウィンドウに表示するプログラムです。

#include <opencv2/opencv.hpp>
#include <iostream>

int main()
{
    // 画像読み込み(グレースケールでもカラーでも可)
    cv::Mat img = cv::imread("sample.jpg", cv::IMREAD_GRAYSCALE);
    if (img.empty()) {
        std::cerr << "画像を読み込めませんでした\n";
        return 1;
    }

    // ORB インスタンス生成(デフォルト: 500点)
    auto orb = cv::ORB::create(500);

    // 特徴点とディスクリプタを同時に計算
    std::vector<cv::KeyPoint> keypoints;
    cv::Mat descriptors;
    orb->detectAndCompute(img, cv::noArray(), keypoints, descriptors);

    std::cout << "検出した特徴点数: " << keypoints.size() << "\n";

    // 特徴点を描画して保存
    cv::Mat output;
    cv::drawKeypoints(img, keypoints, output,
                      cv::Scalar::all(-1),
                      cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);

    cv::imwrite("orb_keypoints.jpg", output);
    std::cout << "orb_keypoints.jpg に保存しました\n";
    return 0;
}

実行結果:

検出した特徴点数: 487
orb_keypoints.jpg に保存しました

検出された特徴点が円形(スケール)と向き(回転角)付きで描画された画像が保存されます。円の大きさがスケール、線分が主方向を表します。

各処理の要点

行 説明
cv::ORB::create(500) 最大検出点数を指定してインスタンス生成
detectAndCompute(...) 検出と記述を1回で実行。マスク不要なら cv::noArray()
descriptors CV_8UC1 のバイナリ記述子(32バイト×N行)
DRAW_RICH_KEYPOINTS スケール・方向も可視化するフラグ

引数と戻り値

cv::ORB::create()

引数 型 デフォルト 説明
nfeatures int 500 検出する特徴点の最大数
scaleFactor float 1.2f スケールピラミッドの倍率(1より大きい値)
nlevels int 8 スケールピラミッドの階層数
edgeThreshold int 31 画像端の除外マージン(px)
firstLevel int 0 ピラミッドの開始レベル
WTA_K int 2 BRIEF記述子生成時の比較点数(2 or 3 or 4)
scoreType int ORB::HARRIS_SCORE FAST または Harris でのキーポイントランキング
patchSize int 31 BRIEF記述子を計算するパッチサイズ
fastThreshold int 20 FAST コーナー検出の閾値

detectAndCompute()

引数 型 説明
image InputArray 入力画像(グレースケール推奨)
mask InputArray 検出対象領域のマスク(不要なら cv::noArray())
keypoints std::vector<cv::KeyPoint>& 出力: 検出した特徴点
descriptors OutputArray 出力: 各特徴点の記述子
useProvidedKeypoints bool true にすると keypoints を入力として再利用

実践例

2枚の画像をマッチングする

物体認識や画像位置合わせの基本となる、2枚間の特徴点マッチングです。

#include <opencv2/opencv.hpp>
#include <iostream>
#include <algorithm>

int main()
{
    cv::Mat img1 = cv::imread("object.jpg", cv::IMREAD_GRAYSCALE);
    cv::Mat img2 = cv::imread("scene.jpg",  cv::IMREAD_GRAYSCALE);
    if (img1.empty() || img2.empty()) {
        std::cerr << "画像を読み込めませんでした\n";
        return 1;
    }

    auto orb = cv::ORB::create(1000);

    std::vector<cv::KeyPoint> kp1, kp2;
    cv::Mat desc1, desc2;
    orb->detectAndCompute(img1, cv::noArray(), kp1, desc1);
    orb->detectAndCompute(img2, cv::noArray(), kp2, desc2);

    // ORBはバイナリ記述子なので Hamming 距離でマッチング
    cv::BFMatcher matcher(cv::NORM_HAMMING, true); // crossCheck=true
    std::vector<cv::DMatch> matches;
    matcher.match(desc1, desc2, matches);

    // 距離でソートして上位50件を取得
    std::sort(matches.begin(), matches.end(),
              [](const cv::DMatch& a, const cv::DMatch& b){ return a.distance < b.distance; });
    int goodCount = std::min(50, static_cast<int>(matches.size()));
    std::vector<cv::DMatch> goodMatches(matches.begin(), matches.begin() + goodCount);

    std::cout << "マッチング数 (上位): " << goodMatches.size() << "\n";

    // 結果描画
    cv::Mat result;
    cv::drawMatches(img1, kp1, img2, kp2, goodMatches, result);
    cv::imwrite("orb_matches.jpg", result);
    std::cout << "orb_matches.jpg に保存しました\n";
    return 0;
}

実行結果:

マッチング数 (上位): 50
orb_matches.jpg に保存しました

2枚の画像の対応点が線で結ばれたマッチング結果画像が保存されます。crossCheck=true を指定することで双方向に整合するマッチングのみ残り、誤対応を大幅に削減できます。

knnMatch + Lowe’s ratio test で精度向上

より精密なフィルタリングが必要な場合は knnMatch を使います。

#include <opencv2/opencv.hpp>
#include <iostream>

int main()
{
    cv::Mat img1 = cv::imread("object.jpg", cv::IMREAD_GRAYSCALE);
    cv::Mat img2 = cv::imread("scene.jpg",  cv::IMREAD_GRAYSCALE);
    if (img1.empty() || img2.empty()) {
        std::cerr << "画像を読み込めませんでした\n";
        return 1;
    }

    auto orb = cv::ORB::create(1000);

    std::vector<cv::KeyPoint> kp1, kp2;
    cv::Mat desc1, desc2;
    orb->detectAndCompute(img1, cv::noArray(), kp1, desc1);
    orb->detectAndCompute(img2, cv::noArray(), kp2, desc2);

    // crossCheck=false にして knnMatch を使う
    cv::BFMatcher matcher(cv::NORM_HAMMING, false);
    std::vector<std::vector<cv::DMatch>> knnMatches;
    matcher.knnMatch(desc1, desc2, knnMatches, 2); // 上位2件

    // Lowe's ratio test(ORBのバイナリ記述子は 0.75 前後が目安)
    std::vector<cv::DMatch> goodMatches;
    for (const auto& m : knnMatches) {
        if (m.size() == 2 && m[0].distance < 0.75f * m[1].distance)
            goodMatches.push_back(m[0]);
    }

    std::cout << "良好なマッチング数: " << goodMatches.size() << "\n";

    cv::Mat result;
    cv::drawMatches(img1, kp1, img2, kp2, goodMatches, result);
    cv::imwrite("orb_ratio_matches.jpg", result);
    return 0;
}

つまずきポイント

⚠️ ディスクリプタ型の取り違えによる assertion エラー

ORB のディスクリプタは CV_8UC1(バイナリ記述子)です。cv::NORM_L2 を指定する cv::BFMatcher や cv::FlannBasedMatcher(デフォルト設定)とは型不一致となり、実行時に assertion が発生します。

# 誤(SIFT向けの設定をそのままコピペすると落ちる)
cv::BFMatcher matcher(cv::NORM_L2);

# 正(ORBはHamming距離)
cv::BFMatcher matcher(cv::NORM_HAMMING);

FlannBasedMatcher を使いたい場合は cv::flann::LshIndexParams を指定する必要があります。

⚠️ desc が empty() のまま match() を呼ぶとクラッシュ

画像が小さすぎる・マスクが全黒・edgeThreshold が画像サイズより大きいなどの場合、特徴点が1件も検出されず descriptors が空になります。matcher.match() に空の Mat を渡すと実行時エラーになるため、必ず検出数を確認してから処理に進んでください。

if (desc1.empty() || desc2.empty()) {
    std::cerr << "ディスクリプタが空です。画像サイズや設定を確認してください\n";
    return 1;
}

⚠️ WTA_K と NORM_HAMMING2 の対応

cv::ORB::create() の WTA_K を 3 または 4 に変更した場合、マッチャーの距離尺度も cv::NORM_HAMMING2 に変更する必要があります。デフォルト(WTA_K=2)のまま使う分には NORM_HAMMING で問題ありません。


関連する関数

  • cv::AKAZE ― ORB と同様にライセンスフリーで利用できるバイナリ記述子。浮動小数点記述子も選択可
  • cv::BFMatcher ― ブルートフォースマッチャー。ORB との組み合わせが基本
  • cv::drawKeypoints ― 特徴点の可視化
  • cv::drawMatches ― マッチング結果の可視化
  • cv::CascadeClassifier の使い方【OpenCV/C++】〜Haar Cascadeで顔検出〜 ― 物体検出の別アプローチとして参照

まとめ

cv::ORB は商用利用可能・追加モジュール不要で使える高速な特徴点アルゴリズムです。マッチャーには必ず cv::NORM_HAMMING を指定し、ディスクリプタが空でないことを確認してから match() を呼ぶのが実装の鉄則です。crossCheck や ratio test を組み合わせることで精度を大幅に向上させられます。

🛠 画像処理のプロが開発するSDK/API

本ブログを運営するスワローインキュベートは、OpenCV ベースのなりすまし判定SDK/API(C++製・OpenCV 4.10)を開発しています。顔認証システムへの組み込み実績多数。

→ 顔認証のなりすまし対策 設計チェックリスト(当ブログ)
→ なりすまし判定SDK/APIの詳細を見る
→ API仕様書・サンプルコード

タイトルとURLをコピーしました