cv::matchTemplate は、画像の中から指定したテンプレート画像と最も類似する領域を検出する関数です。最小限の実装は次の通りです。
cv::matchTemplate(src, templ, result, cv::TM_CCOEFF_NORMED);
cv::minMaxLoc(result, nullptr, &maxVal, nullptr, &maxLoc);
動作環境
- OpenCV 4.x
- コンパイル例:
g++ -std=c++17 main.cpp $(pkg-config --cflags --libs opencv4) -o main
環境構築がまだの方は環境構築ガイドを先にどうぞ。
基本の使い方
#include <iostream>
#include <opencv2/opencv.hpp>
int main()
{
// 検索対象画像とテンプレート画像を読み込む
cv::Mat src = cv::imread("scene.png");
cv::Mat templ = cv::imread("template.png");
if (src.empty() || templ.empty()) {
std::cerr << "画像の読み込みに失敗しました" << std::endl;
return 1;
}
// マッチング結果マップを格納する Mat
// サイズ: (src.rows - templ.rows + 1) x (src.cols - templ.cols + 1)
cv::Mat result;
cv::matchTemplate(src, templ, result, cv::TM_CCOEFF_NORMED);
// 最大値の位置を取得(TM_CCOEFF_NORMED は値が大きいほど類似)
double maxVal;
cv::Point maxLoc;
cv::minMaxLoc(result, nullptr, &maxVal, nullptr, &maxLoc);
std::cout << "最大類似度: " << maxVal << std::endl;
std::cout << "検出位置 (左上): " << maxLoc << std::endl;
// 検出領域を矩形で描画
cv::Point br(maxLoc.x + templ.cols, maxLoc.y + templ.rows);
cv::rectangle(src, maxLoc, br, cv::Scalar(0, 0, 255), 2);
cv::imwrite("output.png", src);
std::cout << "output.png に保存しました" << std::endl;
return 0;
}
実行結果
最大類似度: 0.987342
検出位置 (左上): [124, 87]
output.png に保存しました
result の各ピクセルが「その位置を左上とした領域とテンプレートの類似度」を表します。minMaxLoc で最大値の座標を取り出し、そこからテンプレートサイズ分の矩形を描くだけでマッチング結果を可視化できます。
引数と戻り値
| 引数 | 型 | 説明 |
|---|---|---|
image |
InputArray |
検索対象の画像(8U または 32F) |
templ |
InputArray |
テンプレート画像(image と同じ型・チャンネル数) |
result |
OutputArray |
類似度マップ(32F、単チャンネル) |
method |
int |
マッチング手法(下表参照) |
mask |
InputArray |
マスク画像(省略可・一部手法のみ有効) |
戻り値はありません(void)。
method の選択肢
| 定数 | 概要 | 最良値 |
|---|---|---|
TM_SQDIFF |
差分二乗和 | 最小値 |
TM_SQDIFF_NORMED |
正規化差分二乗和 | 最小値 |
TM_CCORR |
相互相関 | 最大値 |
TM_CCORR_NORMED |
正規化相互相関 | 最大値 |
TM_CCOEFF |
相関係数 | 最大値 |
TM_CCOEFF_NORMED |
正規化相関係数 | 最大値 |
⚠️ TM_SQDIFF 系は最小値が最良一致になる点に注意してください。TM_CCOEFF_NORMED は −1〜1 に正規化されるため閾値設定が最も直感的で、実務上よく使われます。
実践例
複数の一致候補をすべて検出する
1つの画像に同じパターンが複数存在する場合、minMaxLoc では1箇所しか取れません。閾値処理と輪郭検出を組み合わせて全候補を取り出します。
#include <iostream>
#include <vector>
#include <opencv2/opencv.hpp>
int main()
{
cv::Mat src = cv::imread("scene.png");
cv::Mat templ = cv::imread("template.png");
if (src.empty() || templ.empty()) {
std::cerr << "画像の読み込みに失敗しました" << std::endl;
return 1;
}
cv::Mat result;
cv::matchTemplate(src, templ, result, cv::TM_CCOEFF_NORMED);
// 閾値以上の領域をバイナリ化して複数候補を抽出
const double threshold = 0.85;
cv::Mat mask;
cv::threshold(result, mask, threshold, 1.0, cv::THRESH_BINARY);
mask.convertTo(mask, CV_8U, 255);
// 連結成分ごとに外接矩形を取得
std::vector<std::vector<cv::Point>> contours;
cv::findContours(mask, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
std::cout << "検出数: " << contours.size() << std::endl;
for (const auto& c : contours) {
cv::Rect r = cv::boundingRect(c);
// result 座標系からテンプレートサイズに合わせる
cv::Rect detectedRegion(r.x, r.y, templ.cols, templ.rows);
cv::rectangle(src, detectedRegion, cv::Scalar(0, 255, 0), 2);
}
cv::imwrite("output_multi.png", src);
std::cout << "output_multi.png に保存しました" << std::endl;
return 0;
}
検出数: 3
output_multi.png に保存しました
✅ 閾値を高くするほど誤検出は減りますが、検出漏れも増えます。TM_CCOEFF_NORMED なら 0.80〜0.90 あたりが実務での出発点です。
つまずきポイント
⚠️ テンプレートが検索画像より大きいとクラッシュする
result のサイズは (src.rows - templ.rows + 1) × (src.cols - templ.cols + 1) になります。テンプレートが検索画像と同サイズ以上だとサイズが 0 以下になり、実行時に assertion エラーが発生します。テンプレートは必ず検索画像より小さくなるよう確認してください。
cv::error: (-215:Assertion failed) _templ.cols <= _img.cols ... in matchTemplate
⚠️ TM_SQDIFF 系は minMaxLoc の「最小値」を使う
TM_CCOEFF_NORMED に慣れていると、TM_SQDIFF 系を使うとき maxLoc を取ってしまい真逆の結果になります。手法によって minLoc/maxLoc の使い分けが変わる点は必ず意識してください。
// TM_SQDIFF / TM_SQDIFF_NORMED の場合
cv::minMaxLoc(result, &minVal, nullptr, &minLoc, nullptr);
// minLoc が最良一致
⚠️ カラー画像とグレースケール画像の混在
src と templ のチャンネル数が異なると assertion エラーになります。グレースケールで高速に処理したい場合は、両方を cvtColor で変換してから渡してください。
cv::error: (-215:Assertion failed) _img.type() == _templ.type() in matchTemplate
関連する関数
- cv::minMaxLoc —
matchTemplateの結果から最大・最小値と座標を取り出すセットで使う関数 - cv::rectangle — 検出領域の可視化に使用
- cv::findContours の使い方【OpenCV/C++】〜輪郭を検出する〜 — 複数候補の抽出に組み合わせる
- cv::boundingRect / cv::minAreaRect の使い方【OpenCV/C++】〜外接矩形を求める〜 — 検出領域の矩形化に使用
まとめ
cv::matchTemplateは類似度マップを返す関数で、位置の取得にはcv::minMaxLocを組み合わせる。- 手法は
TM_CCOEFF_NORMEDが扱いやすく、閾値設定も直感的で実務向き。 - 複数検出には閾値処理 +
findContours+boundingRectのパターンが定番。
🛠 画像処理のプロが開発するSDK/API
本ブログを運営するスワローインキュベートは、OpenCV ベースのなりすまし判定SDK/API(C++製・OpenCV 4.10)を開発しています。顔認証システムへの組み込み実績多数。

