cv::matchTemplate の使い方【OpenCV/C++】〜テンプレートマッチングで物体位置を検出する〜

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

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 が最良一致

⚠️ カラー画像とグレースケール画像の混在

srctempl のチャンネル数が異なると assertion エラーになります。グレースケールで高速に処理したい場合は、両方を cvtColor で変換してから渡してください。

cv::error: (-215:Assertion failed) _img.type() == _templ.type() in matchTemplate

関連する関数


まとめ

  • cv::matchTemplate は類似度マップを返す関数で、位置の取得には cv::minMaxLoc を組み合わせる。
  • 手法は TM_CCOEFF_NORMED が扱いやすく、閾値設定も直感的で実務向き。
  • 複数検出には閾値処理 + findContours + boundingRect のパターンが定番。

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

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

→ なりすまし判定SDK/APIの詳細を見る
→ API仕様書・サンプルコード

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