-
Notifications
You must be signed in to change notification settings - Fork 45
/
fftm.cpp
276 lines (241 loc) · 8.13 KB
/
fftm.cpp
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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
#include "opencv2/core.hpp"
#include "opencv2/opencv.hpp"
using namespace std;
using namespace cv;
//----------------------------------------------------------
// Recombinate image quaters
//----------------------------------------------------------
void Recomb(Mat &src, Mat &dst)
{
int cx = src.cols >> 1;
int cy = src.rows >> 1;
Mat tmp;
tmp.create(src.size(), src.type());
src(Rect(0, 0, cx, cy)).copyTo(tmp(Rect(cx, cy, cx, cy)));
src(Rect(cx, cy, cx, cy)).copyTo(tmp(Rect(0, 0, cx, cy)));
src(Rect(cx, 0, cx, cy)).copyTo(tmp(Rect(0, cy, cx, cy)));
src(Rect(0, cy, cx, cy)).copyTo(tmp(Rect(cx, 0, cx, cy)));
dst = tmp;
}
//----------------------------------------------------------
// 2D Forward FFT
//----------------------------------------------------------
void ForwardFFT(Mat &Src, Mat *FImg, bool do_recomb = true)
{
int M = getOptimalDFTSize(Src.rows);
int N = getOptimalDFTSize(Src.cols);
Mat padded;
copyMakeBorder(Src, padded, 0, M - Src.rows, 0, N - Src.cols, BORDER_CONSTANT, Scalar::all(0));
Mat planes[] = { Mat_<float>(padded), Mat::zeros(padded.size(), CV_32F) };
Mat complexImg;
merge(planes, 2, complexImg);
dft(complexImg, complexImg);
split(complexImg, planes);
planes[0] = planes[0](Rect(0, 0, planes[0].cols & -2, planes[0].rows & -2));
planes[1] = planes[1](Rect(0, 0, planes[1].cols & -2, planes[1].rows & -2));
if (do_recomb)
{
Recomb(planes[0], planes[0]);
Recomb(planes[1], planes[1]);
}
planes[0] /= float(M*N);
planes[1] /= float(M*N);
FImg[0] = planes[0].clone();
FImg[1] = planes[1].clone();
}
//----------------------------------------------------------
// 2D inverse FFT
//----------------------------------------------------------
void InverseFFT(Mat *FImg, Mat &Dst, bool do_recomb = true)
{
if (do_recomb)
{
Recomb(FImg[0], FImg[0]);
Recomb(FImg[1], FImg[1]);
}
Mat complexImg;
merge(FImg, 2, complexImg);
idft(complexImg, complexImg);
split(complexImg, FImg);
Dst = FImg[0].clone();
}
//-----------------------------------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------------------------------
void highpass(Size sz, Mat& dst)
{
Mat a = Mat(sz.height, 1, CV_32FC1);
Mat b = Mat(1, sz.width, CV_32FC1);
float step_y = CV_PI / sz.height;
float val = -CV_PI*0.5;
for (int i = 0; i < sz.height; ++i)
{
a.at<float>(i) = cos(val);
val += step_y;
}
val = -CV_PI*0.5;
float step_x = CV_PI / sz.width;
for (int i = 0; i < sz.width; ++i)
{
b.at<float>(i) = cos(val);
val += step_x;
}
Mat tmp = a*b;
dst = (1.0 - tmp).mul(2.0 - tmp);
}
//-----------------------------------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------------------------------
float logpolar(Mat& src, Mat& dst)
{
float radii = src.cols;
float angles = src.rows;
Point2f center(src.cols / 2, src.rows / 2);
float d = norm(Vec2f(src.cols - center.x, src.rows - center.y));
float log_base = pow(10.0, log10(d) / radii);
float d_theta = CV_PI / (float)angles;
float theta = CV_PI / 2.0;
float radius = 0;
Mat map_x(src.size(), CV_32FC1);
Mat map_y(src.size(), CV_32FC1);
for (int i = 0; i < angles; ++i)
{
for (int j = 0; j < radii; ++j)
{
radius = pow(log_base, float(j));
float x = radius * sin(theta) + center.x;
float y = radius * cos(theta) + center.y;
map_x.at<float>(i, j) = x;
map_y.at<float>(i, j) = y;
}
theta += d_theta;
}
remap(src, dst, map_x, map_y, cv::INTER_LINEAR, BORDER_CONSTANT, Scalar(0, 0, 0));
return log_base;
}
//-----------------------------------------------------------------------------------------------------
// As input we need equal sized images, with the same aspect ratio,
// scale difference should not exceed 1.8 times.
//-----------------------------------------------------------------------------------------------------
RotatedRect LogPolarFFTTemplateMatch(Mat& im0, Mat& im1, double canny_threshold1, double canny_threshold2)
{
// Accept 1 or 3 channel CV_8U, CV_32F or CV_64F images.
CV_Assert((im0.type() == CV_8UC1) || (im0.type() == CV_8UC3) ||
(im0.type() == CV_32FC1) || (im0.type() == CV_32FC3) ||
(im0.type() == CV_64FC1) || (im0.type() == CV_64FC3));
CV_Assert(im0.rows == im1.rows && im0.cols == im1.cols);
CV_Assert(im0.channels() == 1 || im0.channels() == 3 || im0.channels() == 4);
CV_Assert(im1.channels() == 1 || im1.channels() == 3 || im1.channels() == 4);
Mat im0_tmp = im0.clone();
Mat im1_tmp = im1.clone();
if (im0.channels() == 3)
{
cvtColor(im0, im0, cv::COLOR_BGR2GRAY);
}
if (im0.channels() == 4)
{
cvtColor(im0, im0, cv::COLOR_BGRA2GRAY);
}
if (im1.channels() == 3)
{
cvtColor(im1, im1, cv::COLOR_BGR2GRAY);
}
if (im1.channels() == 4)
{
cvtColor(im1, im1, cv::COLOR_BGRA2GRAY);
}
if (im0.type() == CV_32FC1)
{
im0.convertTo(im0, CV_8UC1, 255.0);
}
if (im1.type() == CV_32FC1)
{
im1.convertTo(im1, CV_8UC1, 255.0);
}
if (im0.type() == CV_64FC1)
{
im0.convertTo(im0, CV_8UC1, 255.0);
}
if (im1.type() == CV_64FC1)
{
im1.convertTo(im1, CV_8UC1, 255.0);
}
// Canny(im0, im0, canny_threshold1, canny_threshold2); // you can change this
// Canny(im1, im1, canny_threshold1, canny_threshold2);
// Ensure both images are of CV_32FC1 type
im0.convertTo(im0, CV_32FC1, 1.0 / 255.0);
im1.convertTo(im1, CV_32FC1, 1.0 / 255.0);
Mat F0[2], F1[2];
Mat f0, f1;
ForwardFFT(im0, F0);
ForwardFFT(im1, F1);
magnitude(F0[0], F0[1], f0);
magnitude(F1[0], F1[1], f1);
// Create filter
Mat h;
highpass(f0.size(), h);
// Apply it in freq domain
f0 = f0.mul(h);
f1 = f1.mul(h);
/*
cv::Mat mask = cv::Mat::zeros(f0.size(), CV_8UC1);
cv::rectangle(mask, cv::Point(f0.cols / 2, f0.rows / 2), cv::Point(f0.cols, 0), Scalar::all(255), -1);
mask = 255 - mask;
f0.setTo(0, mask);
f1.setTo(0, mask);
*/
cv::normalize(f0, f0, 0, 1, NORM_MINMAX);
cv::normalize(f1, f1, 0, 1, NORM_MINMAX);
//cv::imshow("f0", f0);
//cv::imshow("f1", f1);
float log_base;
Mat f0lp, f1lp;
log_base = logpolar(f0, f0lp);
log_base = logpolar(f1, f1lp);
cv::Mat dbgImg= cv::Mat::zeros(f0lp.size(), CV_32FC3);
cv::Mat Z = cv::Mat::zeros(f0lp.size(), CV_32FC1);
//std::vector<cv::Mat> ch = {f0lp,f1lp,Z};
//cv::merge(ch, dbgImg);
//cv::imshow("dbgImg", dbgImg);
// Find rotation and scale
Point2d rotation_and_scale = cv::phaseCorrelate(f1lp, f0lp);
float angle = 180.0 * rotation_and_scale.y / f0lp.rows;
float scale = pow(log_base, rotation_and_scale.x);
// --------------
if (scale > 1.8)
{
rotation_and_scale = cv::phaseCorrelate(f1lp, f0lp);
angle = -180.0 * rotation_and_scale.y / f0lp.rows;
scale = 1.0 / pow(log_base, rotation_and_scale.x);
if (scale > 1.8)
{
cout << "Images are not compatible. Scale change > 1.8" << endl;
return RotatedRect();
}
}
// --------------
if (angle < -90.0)
{
angle += 180.0;
}
else if (angle > 90.0)
{
angle -= 180.0;
}
// Now rotate and scale fragment back, then find translation
Mat rot_mat = getRotationMatrix2D(Point(im1.cols / 2, im1.rows / 2), angle, 1.0 / scale);
// rotate and scale
Mat im1_rs;
warpAffine(im1, im1_rs, rot_mat, im1.size());
// find translation
Point2d tr = cv::phaseCorrelate(im1_rs, im0);
// compute rotated rectangle parameters
RotatedRect rr;
rr.center = tr + Point2d(im0.cols / 2, im0.rows / 2);
rr.angle = -angle;
rr.size.width = im1.cols / scale;
rr.size.height = im1.rows / scale;
im0 = im0_tmp.clone();
im1 = im1_tmp.clone();
return rr;
}