#はじめに
cv::matchTemplate()
のサンプルコードとして
cv::matchTemplate(search_img, tmp_img, result_img, cv::TM_CCOEFF_NORMED);
cv::Point max_pt;
double maxVal;
cv::minMaxLoc(result_img, NULL, &maxVal, NULL, &max_pt);
がありますが、cv::minMaxLoc()
は最大値を求めるので1箇所しか検出することができません。
1つの画像から複数箇所を検出したい場合、Pythonではnp.where()
などがありますが、C++にはないので自分で実装する必要があります。
Googleで[ opencv テンプレートマッチング 複数 c++ ]と検索すると
OpenCV のテンプレートマッチを使って駒を検出
[[OpenCV][C++]テンプレートマッチングを使って複数検出してみた]
(https://qiita.com/satsukiya/items/c8828f48be7673007007
"[OpenCV][C++]テンプレートマッチングを使って複数検出してみた")
これらの記事がよく見られていますが、自分で書いたコードが違う感じになったので
記事に残しておこうと思います。
#使用するデータ
チュートリアルではマリオが使われているのでそれに習って画像を用意しました
#サンプルコード
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#define THRESHOLD 0.7
#define FIND_TARGET_NUM 30
int main(int argc, char** argv){
//元画像と検出画像の追加
cv::Mat src = cv::imread("../picture/src.png", 1);
cv::Mat tmp = cv::imread("../picture/template.png", 1);
//マッチング結果の画像と出力用画像の宣言
cv::Mat result, dist;
src.copyTo(dist);
//テンプレートマッチングの実行
cv::matchTemplate(src, tmp, result, cv::TM_CCOEFF_NORMED);
//最大値を持つ画素の検索
double val;
cv::Point loc;
cv::minMaxLoc(result,NULL,&val,NULL,&loc);
for(int i=0;i<FIND_TARGET_NUM && fabs(val) > THRESHOLD; ++i){
//検出部分のマスク
cv::rectangle(result, cv::Point(loc.x-tmp.cols/2,loc.y-tmp.rows/2), cv::Point(loc.x+tmp.cols/2, loc.y+tmp.rows/2), cv::Scalar(0), -1);
//出力用画像への描画
cv::rectangle(dist, cv::Point(loc.x,loc.y), cv::Point(loc.x+tmp.cols, loc.y+tmp.rows), cv::Scalar(0,0,255), 2);
//再度最大値を持つ画素の検索
cv::minMaxLoc(result,NULL,&val,NULL,&loc);
}
//結果画像の出力
cv::imwrite("../picture/output.png", dist);
return 0;
}
#まとめ
今回使用したコードは以下のリポジトリにおいておくのでご自由に使用してください
pictureディレクトリの画像を変更すれば汎用的に使用できます
find_multi_template