1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131 | #include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include <iostream>
#include <math.h>
int main(int argc, char** argv)
{
cv::Mat image = cv::imread("flower.jpg", cv::IMREAD_COLOR);
cv::Mat templ = cv::imread("flower_templ.jpg", cv::IMREAD_COLOR);
if (image.empty() || templ.empty())
{
std::cout << "Can't read one of the images" << std::endl;
return EXIT_FAILURE;
}
int matchMethodEnum[] = { cv::TM_SQDIFF, cv::TM_SQDIFF_NORMED, cv::TM_CCORR, cv::TM_CCORR_NORMED, cv::TM_CCOEFF, cv::TM_CCOEFF_NORMED };
std::string matchMethodName[] = { "01_TM_SQDIFF", "02_TM_SQDIFF_NORMED", "03_TM_CCORR", "04_TM_CCORR_NORMED", "05_TM_CCOEFF", "06_TM_CCOEFF_NORMED" };
for (int i = 0; i < 6; i++) {
int match_method = matchMethodEnum[i];
// 類似度マップ結果格納領域の確保(入力画像からテンプレート画像を引いて1足したサイズになるので注意)
int result_cols = image.cols - templ.cols + 1;
int result_rows = image.rows - templ.rows + 1;
cv::Mat result;
result.create(result_rows, result_cols, CV_32FC1);
// 入力画像と類似度マップの高さ、幅の割合を出しておく
double ratio_w = (double)result_cols / (double)image.cols;
double ratio_h = (double)result_rows / (double)image.rows;
// マッチング結果の画像を作成する
cv::matchTemplate(image, templ, result, match_method);
// pngファイル保存用に0-255で正規化したものも準備している(この処理は物体検出処理と関係ない)
cv::Mat resultBackup = result.clone();
cv::normalize(resultBackup, resultBackup, 0, 255.0, cv::NORM_MINMAX, -1, cv::Mat());
// 結果を正規化する
if (match_method == cv::TM_SQDIFF || match_method == cv::TM_CCORR || match_method == cv::TM_CCOEFF || match_method == cv::TM_CCOEFF_NORMED) {
cv::normalize(result, result, 0.0, 1.0, cv::NORM_MINMAX, -1, cv::Mat());
}
// 結果内の最大値、最小値、それらのポジションを取得する
double minVal;
double maxVal;
cv::Point minLoc;
cv::Point maxLoc;
cv::Point matchLoc;
cv::minMaxLoc(result, &minVal, &maxVal, &minLoc, &maxLoc, cv::Mat());
// 差の二乗和は0に近いほうが一致度が高い。他は逆
if (match_method == cv::TM_SQDIFF || match_method == cv::TM_SQDIFF_NORMED)
{
matchLoc = minLoc;
}
else
{
matchLoc = maxLoc;
}
// 入力画像を出力用にコピーしておく(枠などを書くため)
cv::Mat tmp;
image.copyTo(tmp);
// 念のため10回ぐらいでbreak
for (int j = 0; j < 10; j++) {
double minVal = 0.0;
double maxVal = 0.0;
cv::Point minLoc;
cv::Point maxLoc;
cv::Point matchLoc;
minMaxLoc(result, &minVal, &maxVal, &minLoc, &maxLoc, cv::Mat());
if (match_method == cv::TM_SQDIFF || match_method == cv::TM_SQDIFF_NORMED) {
// TM_SQDIFFとTM_SQDIFF_NORMEDは値が0に近いほど精度が高いのでminValが正解
if (minVal > 0.05) {
break;
}
matchLoc = minLoc;
// 座標はあくまで入力画像における座標であり、一回り小さい類似度マップの座標とは違う
// よって、その割合の分だけ、座標をずらす必要がある。
double _x = ratio_w * (double)matchLoc.x;
double _y = ratio_h * (double)matchLoc.y;
// 塗りつぶす。この時注意しないといけないのは、TM_SQDIFFやTM_SQDIFF_NORMEDは値が0に近いほど正解、色でいうと黒に近いほど正解なので白で塗りつぶす点。
// なお、実際にはちょっとはみ出るように塗りつぶしといたほうが良い。最も良い点の周辺も当然類似度が高いので
cv::rectangle(result, cv::Point((int)_x, (int)_y), cv::Point((int)(_x + (templ.cols * ratio_w)), (int)(_y + (templ.rows * ratio_h))), cv::Scalar::all(255), -1);
// 以下はpngファイル用。物体検出処理そのものとは無関係
cv::rectangle(resultBackup, cv::Point((int)_x, (int)_y), cv::Point((int)(_x + (templ.cols * ratio_w)), (int)(_y + (templ.rows * ratio_h))), cv::Scalar::all(255), -1);
}
else {
// TM_CCORRやTM_CCOEFFは値が1に近いほど精度が高いのでmaxValが正解
if (maxVal < 0.95) {
break;
}
matchLoc = maxLoc;
// 座標はあくまで入力画像における座標であり、一回り小さい類似度マップの座標とは違う
// よって、その割合の分だけ、座標をずらす必要がある
double _x = ratio_w * (double)matchLoc.x;
double _y = ratio_h * (double)matchLoc.y;
// 塗りつぶす。この時注意しないといけないのは、TM_CCORRやTM_CCOEFFは値が1に近いほど正解、色でいうと白に近いほど正解なので黒で塗りつぶす点。
// なお、実際にはちょっとはみ出るように塗りつぶしといたほうが良い。最も良い点の周辺も当然類似度が高いので
cv::rectangle(result, cv::Point((int)_x, (int)_y), cv::Point((int)(_x + (templ.cols * ratio_w)), (int)(_y + (templ.rows * ratio_h))), cv::Scalar::all(0), -1);
// 以下はpngファイル用。物体検出処理そのものとは無関係
cv::rectangle(resultBackup, cv::Point((int)_x, (int)_y), cv::Point((int)(_x + (templ.cols * ratio_w)), (int)(_y + (templ.rows * ratio_h))), cv::Scalar::all(0), -1);
}
cv::rectangle(tmp, matchLoc, cv::Point(matchLoc.x + templ.cols, matchLoc.y + templ.rows), cv::Scalar(0, 0, 255), 5, 8, 0);
// 表示
cv::imshow(matchMethodName[i], tmp);
cv::imshow(matchMethodName[i] + "_result", result);
// ファイルとして保存
cv::imwrite(matchMethodName[i] + "_flower.png", tmp);
cv::imwrite(matchMethodName[i] + "_flower_result.png", resultBackup); // resultを用いると正しく絵が出ない
}
}
cv::waitKey();
return 0;
}
|