cv::SIFT の使い方【OpenCV/C++】〜SIFT特徴点の検出と記述子抽出〜
cv::SIFT は、スケール・回転に不変な特徴点を検出し、128次元の記述子を生成するクラスです。物体認識・画像マッチング・パノラマ合成など、実務で広く使われます。最小構成は以下のとおりです。
auto sift = cv::SIFT::create();
sift->detectAndCompute(gray, cv::noArray(), keypoints, descriptors);
動作環境
- OpenCV 4.x(4.4以降を推奨)
- C++17
- ビルド:
g++ -std=c++17 main.cpp $(pkg-config --cflags --libs opencv4)
⚠️ OpenCV 4.4 以降、SIFTは特許期限切れにより
opencv_contribなしのメインモジュールに移動しています。それ以前のバージョンではopencv_contribのxfeatures2dモジュールが必要です。
環境構築がまだの方は環境構築ガイドを先にどうぞ。
基本の使い方
#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/features2d.hpp>
int main()
{
// 画像読み込み
cv::Mat img = cv::imread("input.jpg");
if (img.empty()) {
std::cerr << "画像の読み込みに失敗しました" << std::endl;
return 1;
}
// グレースケール変換(SIFTはグレースケール画像を入力とする)
cv::Mat gray;
cv::cvtColor(img, gray, cv::COLOR_BGR2GRAY);
// SIFTオブジェクトを生成
auto sift = cv::SIFT::create();
// 特徴点と記述子を同時に検出
std::vector<cv::KeyPoint> keypoints;
cv::Mat descriptors;
sift->detectAndCompute(gray, cv::noArray(), keypoints, descriptors);
std::cout << "検出された特徴点数: " << keypoints.size() << std::endl;
std::cout << "記述子サイズ: " << descriptors.rows << " x " << descriptors.cols << std::endl;
// 特徴点を元画像に描画
cv::Mat output;
cv::drawKeypoints(img, keypoints, output,
cv::Scalar::all(-1),
cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
cv::imwrite("sift_keypoints.jpg", output);
std::cout << "sift_keypoints.jpg に保存しました" << std::endl;
return 0;
}
実行結果:
検出された特徴点数: 1247
記述子サイズ: 1247 x 128
sift_keypoints.jpg に保存しました
検出点数は画像の内容によって変わります。各特徴点は位置・スケール・方向を持ち、DRAW_RICH_KEYPOINTS を指定することでスケールと向きを円と線で可視化できます。
コードの要点
| 行の内容 | 説明 |
|---|---|
cv::SIFT::create() |
SIFTインスタンスを生成。引数で特徴点数の上限などを調整できる |
cv::noArray() |
マスクなし。特定領域に限定したい場合は8ビット1チャンネルのマスクを渡す |
descriptors |
CV_32F・128列の行列。各行が1特徴点の記述子 |
DRAW_RICH_KEYPOINTS |
スケール・向きを円で描画するフラグ |
引数と戻り値
cv::SIFT::create()
| 引数 | 型 | デフォルト | 説明 |
|---|---|---|---|
nfeatures |
int |
0 |
保持する特徴点の最大数(0=制限なし) |
nOctaveLayers |
int |
3 |
オクターブあたりのレイヤー数 |
contrastThreshold |
double |
0.04 |
低コントラスト点を除去する閾値 |
edgeThreshold |
double |
10 |
エッジ上の点を除去する閾値 |
sigma |
double |
1.6 |
入力画像に適用するガウシアンのσ |
enable_precise_upscale |
bool |
false |
高精度スケールアップ(4.8以降) |
detectAndCompute()
| 引数 | 型 | 説明 |
|---|---|---|
image |
cv::InputArray |
入力画像(グレースケール推奨) |
mask |
cv::InputArray |
検出マスク(不要なら cv::noArray()) |
keypoints |
std::vector<cv::KeyPoint>& |
出力: 検出された特徴点 |
descriptors |
cv::OutputArray |
出力: 128次元記述子(CV_32F) |
useProvidedKeypoints |
bool |
true にすると既存のkeypointsを使い記述子のみ計算 |
実践例
2枚の画像をSIFTでマッチングする
物体認識や位置合わせでよく使うパターンです。
#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/features2d.hpp>
int main()
{
cv::Mat img1 = cv::imread("scene1.jpg");
cv::Mat img2 = cv::imread("scene2.jpg");
if (img1.empty() || img2.empty()) {
std::cerr << "画像の読み込みに失敗しました" << std::endl;
return 1;
}
cv::Mat gray1, gray2;
cv::cvtColor(img1, gray1, cv::COLOR_BGR2GRAY);
cv::cvtColor(img2, gray2, cv::COLOR_BGR2GRAY);
// 特徴点数を上限500に絞ってSIFTを生成
auto sift = cv::SIFT::create(500);
std::vector<cv::KeyPoint> kp1, kp2;
cv::Mat desc1, desc2;
sift->detectAndCompute(gray1, cv::noArray(), kp1, desc1);
sift->detectAndCompute(gray2, cv::noArray(), kp2, desc2);
// BFMatcherでL2距離マッチング(SIFTはL2が適切)
cv::BFMatcher matcher(cv::NORM_L2);
std::vector<std::vector<cv::DMatch>> knnMatches;
matcher.knnMatch(desc1, desc2, knnMatches, 2);
// Lowe's ratio testで良いマッチのみ残す
std::vector<cv::DMatch> goodMatches;
for (const auto& m : knnMatches) {
if (m[0].distance < 0.75f * m[1].distance) {
goodMatches.push_back(m[0]);
}
}
std::cout << "良いマッチ数: " << goodMatches.size() << std::endl;
// マッチング結果を描画して保存
cv::Mat matchImg;
cv::drawMatches(img1, kp1, img2, kp2, goodMatches, matchImg);
cv::imwrite("sift_matches.jpg", matchImg);
std::cout << "sift_matches.jpg に保存しました" << std::endl;
return 0;
}
実行結果:
良いマッチ数: 87
sift_matches.jpg に保存しました
Lowe’s ratio test(0.75f の部分)は精度とマッチ数のトレードオフです。値を下げると精度が上がりマッチ数が減ります。実務では 0.7〜0.8 を試してチューニングしてください。
特定領域だけで特徴点を検出する(マスク使用)
#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/features2d.hpp>
int main()
{
cv::Mat img = cv::imread("input.jpg");
if (img.empty()) {
std::cerr << "画像の読み込みに失敗しました" << std::endl;
return 1;
}
cv::Mat gray;
cv::cvtColor(img, gray, cv::COLOR_BGR2GRAY);
// 画像中央半分のみを有効にするマスクを作成
cv::Mat mask = cv::Mat::zeros(gray.size(), CV_8UC1);
cv::Rect roi(gray.cols / 4, gray.rows / 4,
gray.cols / 2, gray.rows / 2);
mask(roi) = 255;
auto sift = cv::SIFT::create();
std::vector<cv::KeyPoint> keypoints;
cv::Mat descriptors;
sift->detectAndCompute(gray, mask, keypoints, descriptors);
std::cout << "ROI内の特徴点数: " << keypoints.size() << std::endl;
cv::Mat output;
cv::drawKeypoints(img, keypoints, output,
cv::Scalar(0, 255, 0),
cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
cv::rectangle(output, roi, cv::Scalar(0, 0, 255), 2);
cv::imwrite("sift_masked.jpg", output);
return 0;
}
つまずきポイント
✅ OpenCV 4.4未満では xfeatures2d が必要
OpenCV 4.4 未満では SIFT は opencv_contrib の xfeatures2d モジュールにありました。その場合はヘッダが <opencv2/xfeatures2d.hpp>、クラスが cv::xfeatures2d::SIFT になります。現行のOpenCV 4.4以降なら <opencv2/features2d.hpp> を使い、cv::SIFT で直接アクセスできます。バージョンを確認してからコードを書きましょう。
// OpenCV 4.4未満の場合(参考)
#include <opencv2/xfeatures2d.hpp>
auto sift = cv::xfeatures2d::SIFT::create();
⚠️ 記述子の型は CV_32F・NORM_L2 でマッチング
SIFTの記述子は CV_32F(32ビット浮動小数点)です。BFMatcher を使う際は cv::NORM_L2 を指定してください。ORBやBRIEFで使う cv::NORM_HAMMING を誤って指定するとアサーションエラーになります。
// ✅ 正しい
cv::BFMatcher matcher(cv::NORM_L2);
// ❌ SIFTに NORM_HAMMING は使えない
cv::BFMatcher matcher(cv::NORM_HAMMING);
⚠️ カラー画像をそのまま渡すと結果が変わる場合がある
detectAndCompute にカラー画像(CV_8UC3)を渡しても内部でグレースケール変換されますが、実装の挙動に依存します。明示的に cv::cvtColor でグレースケール変換してから渡すのが安全です。また日本語パスを cv::imread に渡すと環境によって空の Mat が返るため、パスは ASCII のみにしてください。
関連する関数
cv::drawKeypoints— 特徴点を画像に描画するcv::drawMatches— 2画像間のマッチング結果を並べて描画するcv::BFMatcher— 総当たりマッチャー。SIFTとよく組み合わせて使うcv::FlannBasedMatcher— 大量の特徴点を高速にマッチングしたい場合に有効cv::ORB— SIFTより高速な代替手法。バイナリ記述子で組込向きに使われる- cv::CascadeClassifier の使い方【OpenCV/C++】〜Haar Cascadeで顔検出〜 — 別アプローチの物体検出手法として参照
まとめ
cv::SIFT は OpenCV 4.4以降メインモジュールに統合され、cv::SIFT::create() と detectAndCompute() の2ステップで使えます。マッチングには cv::BFMatcher + NORM_L2 + Lowe’s ratio test の組み合わせが定番です。特徴点数の上限(nfeatures)やコントラスト閾値(contrastThreshold)を用途に合わせて調整することで、精度と速度のバランスをとれます。
🛠 画像処理のプロが開発するSDK/API
本ブログを運営するスワローインキュベートは、OpenCV ベースのなりすまし判定SDK/API(C++製・OpenCV 4.10)を開発しています。顔認証システムへの組み込み実績多数。
→ 顔認証のなりすまし対策 設計チェックリスト(当ブログ)
→ なりすまし判定SDK/APIの詳細を見る
→ API仕様書・サンプルコード

