Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Tetragramm
GitHub Repository: Tetragramm/opencv
Path: blob/master/samples/cpp/image_alignment.cpp
16337 views
1
/*
2
* This sample demonstrates the use of the function
3
* findTransformECC that implements the image alignment ECC algorithm
4
*
5
*
6
* The demo loads an image (defaults to ../data/fruits.jpg) and it artificially creates
7
* a template image based on the given motion type. When two images are given,
8
* the first image is the input image and the second one defines the template image.
9
* In the latter case, you can also parse the warp's initialization.
10
*
11
* Input and output warp files consist of the raw warp (transform) elements
12
*
13
* Authors: G. Evangelidis, INRIA, Grenoble, France
14
* M. Asbach, Fraunhofer IAIS, St. Augustin, Germany
15
*/
16
#include <opencv2/imgcodecs.hpp>
17
#include <opencv2/highgui.hpp>
18
#include <opencv2/video.hpp>
19
#include <opencv2/imgproc.hpp>
20
#include <opencv2/core/utility.hpp>
21
22
#include <stdio.h>
23
#include <string>
24
#include <time.h>
25
#include <iostream>
26
#include <fstream>
27
28
using namespace cv;
29
using namespace std;
30
31
static void help(void);
32
static int readWarp(string iFilename, Mat& warp, int motionType);
33
static int saveWarp(string fileName, const Mat& warp, int motionType);
34
static void draw_warped_roi(Mat& image, const int width, const int height, Mat& W);
35
36
#define HOMO_VECTOR(H, x, y)\
37
H.at<float>(0,0) = (float)(x);\
38
H.at<float>(1,0) = (float)(y);\
39
H.at<float>(2,0) = 1.;
40
41
#define GET_HOMO_VALUES(X, x, y)\
42
(x) = static_cast<float> (X.at<float>(0,0)/X.at<float>(2,0));\
43
(y) = static_cast<float> (X.at<float>(1,0)/X.at<float>(2,0));
44
45
46
const std::string keys =
47
"{@inputImage | ../data/fruits.jpg | input image filename }"
48
"{@templateImage | | template image filename (optional)}"
49
"{@inputWarp | | input warp (matrix) filename (optional)}"
50
"{n numOfIter | 50 | ECC's iterations }"
51
"{e epsilon | 0.0001 | ECC's convergence epsilon }"
52
"{o outputWarp | outWarp.ecc | output warp (matrix) filename }"
53
"{m motionType | affine | type of motion (translation, euclidean, affine, homography) }"
54
"{v verbose | 1 | display initial and final images }"
55
"{w warpedImfile | warpedECC.png | warped input image }"
56
"{h help | | print help message }"
57
;
58
59
60
static void help(void)
61
{
62
63
cout << "\nThis file demonstrates the use of the ECC image alignment algorithm. When one image"
64
" is given, the template image is artificially formed by a random warp. When both images"
65
" are given, the initialization of the warp by command line parsing is possible. "
66
"If inputWarp is missing, the identity transformation initializes the algorithm. \n" << endl;
67
68
cout << "\nUsage example (one image): \n./ecc ../data/fruits.jpg -o=outWarp.ecc "
69
"-m=euclidean -e=1e-6 -N=70 -v=1 \n" << endl;
70
71
cout << "\nUsage example (two images with initialization): \n./ecc yourInput.png yourTemplate.png "
72
"yourInitialWarp.ecc -o=outWarp.ecc -m=homography -e=1e-6 -N=70 -v=1 -w=yourFinalImage.png \n" << endl;
73
74
}
75
76
static int readWarp(string iFilename, Mat& warp, int motionType){
77
78
// it reads from file a specific number of raw values:
79
// 9 values for homography, 6 otherwise
80
CV_Assert(warp.type()==CV_32FC1);
81
int numOfElements;
82
if (motionType==MOTION_HOMOGRAPHY)
83
numOfElements=9;
84
else
85
numOfElements=6;
86
87
int i;
88
int ret_value;
89
90
ifstream myfile(iFilename.c_str());
91
if (myfile.is_open()){
92
float* matPtr = warp.ptr<float>(0);
93
for(i=0; i<numOfElements; i++){
94
myfile >> matPtr[i];
95
}
96
ret_value = 1;
97
}
98
else {
99
cout << "Unable to open file " << iFilename.c_str() << endl;
100
ret_value = 0;
101
}
102
return ret_value;
103
}
104
105
static int saveWarp(string fileName, const Mat& warp, int motionType)
106
{
107
// it saves the raw matrix elements in a file
108
CV_Assert(warp.type()==CV_32FC1);
109
110
const float* matPtr = warp.ptr<float>(0);
111
int ret_value;
112
113
ofstream outfile(fileName.c_str());
114
if( !outfile ) {
115
cerr << "error in saving "
116
<< "Couldn't open file '" << fileName.c_str() << "'!" << endl;
117
ret_value = 0;
118
}
119
else {//save the warp's elements
120
outfile << matPtr[0] << " " << matPtr[1] << " " << matPtr[2] << endl;
121
outfile << matPtr[3] << " " << matPtr[4] << " " << matPtr[5] << endl;
122
if (motionType==MOTION_HOMOGRAPHY){
123
outfile << matPtr[6] << " " << matPtr[7] << " " << matPtr[8] << endl;
124
}
125
ret_value = 1;
126
}
127
return ret_value;
128
129
}
130
131
132
static void draw_warped_roi(Mat& image, const int width, const int height, Mat& W)
133
{
134
Point2f top_left, top_right, bottom_left, bottom_right;
135
136
Mat H = Mat (3, 1, CV_32F);
137
Mat U = Mat (3, 1, CV_32F);
138
139
Mat warp_mat = Mat::eye (3, 3, CV_32F);
140
141
for (int y = 0; y < W.rows; y++)
142
for (int x = 0; x < W.cols; x++)
143
warp_mat.at<float>(y,x) = W.at<float>(y,x);
144
145
//warp the corners of rectangle
146
147
// top-left
148
HOMO_VECTOR(H, 1, 1);
149
gemm(warp_mat, H, 1, 0, 0, U);
150
GET_HOMO_VALUES(U, top_left.x, top_left.y);
151
152
// top-right
153
HOMO_VECTOR(H, width, 1);
154
gemm(warp_mat, H, 1, 0, 0, U);
155
GET_HOMO_VALUES(U, top_right.x, top_right.y);
156
157
// bottom-left
158
HOMO_VECTOR(H, 1, height);
159
gemm(warp_mat, H, 1, 0, 0, U);
160
GET_HOMO_VALUES(U, bottom_left.x, bottom_left.y);
161
162
// bottom-right
163
HOMO_VECTOR(H, width, height);
164
gemm(warp_mat, H, 1, 0, 0, U);
165
GET_HOMO_VALUES(U, bottom_right.x, bottom_right.y);
166
167
// draw the warped perimeter
168
line(image, top_left, top_right, Scalar(255));
169
line(image, top_right, bottom_right, Scalar(255));
170
line(image, bottom_right, bottom_left, Scalar(255));
171
line(image, bottom_left, top_left, Scalar(255));
172
}
173
174
int main (const int argc, const char * argv[])
175
{
176
177
CommandLineParser parser(argc, argv, keys);
178
parser.about("ECC demo");
179
180
parser.printMessage();
181
help();
182
183
string imgFile = parser.get<string>(0);
184
string tempImgFile = parser.get<string>(1);
185
string inWarpFile = parser.get<string>(2);
186
187
int number_of_iterations = parser.get<int>("n");
188
double termination_eps = parser.get<double>("e");
189
string warpType = parser.get<string>("m");
190
int verbose = parser.get<int>("v");
191
string finalWarp = parser.get<string>("o");
192
string warpedImFile = parser.get<string>("w");
193
if (!parser.check())
194
{
195
parser.printErrors();
196
return -1;
197
}
198
if (!(warpType == "translation" || warpType == "euclidean"
199
|| warpType == "affine" || warpType == "homography"))
200
{
201
cerr << "Invalid motion transformation" << endl;
202
return -1;
203
}
204
205
int mode_temp;
206
if (warpType == "translation")
207
mode_temp = MOTION_TRANSLATION;
208
else if (warpType == "euclidean")
209
mode_temp = MOTION_EUCLIDEAN;
210
else if (warpType == "affine")
211
mode_temp = MOTION_AFFINE;
212
else
213
mode_temp = MOTION_HOMOGRAPHY;
214
215
Mat inputImage = imread(imgFile,0);
216
if (inputImage.empty())
217
{
218
cerr << "Unable to load the inputImage" << endl;
219
return -1;
220
}
221
222
Mat target_image;
223
Mat template_image;
224
225
if (tempImgFile!="") {
226
inputImage.copyTo(target_image);
227
template_image = imread(tempImgFile,0);
228
if (template_image.empty()){
229
cerr << "Unable to load the template image" << endl;
230
return -1;
231
}
232
233
}
234
else{ //apply random warp to input image
235
resize(inputImage, target_image, Size(216, 216), 0, 0, INTER_LINEAR_EXACT);
236
Mat warpGround;
237
RNG rng(getTickCount());
238
double angle;
239
switch (mode_temp) {
240
case MOTION_TRANSLATION:
241
warpGround = (Mat_<float>(2,3) << 1, 0, (rng.uniform(10.f, 20.f)),
242
0, 1, (rng.uniform(10.f, 20.f)));
243
warpAffine(target_image, template_image, warpGround,
244
Size(200,200), INTER_LINEAR + WARP_INVERSE_MAP);
245
break;
246
case MOTION_EUCLIDEAN:
247
angle = CV_PI/30 + CV_PI*rng.uniform((double)-2.f, (double)2.f)/180;
248
249
warpGround = (Mat_<float>(2,3) << cos(angle), -sin(angle), (rng.uniform(10.f, 20.f)),
250
sin(angle), cos(angle), (rng.uniform(10.f, 20.f)));
251
warpAffine(target_image, template_image, warpGround,
252
Size(200,200), INTER_LINEAR + WARP_INVERSE_MAP);
253
break;
254
case MOTION_AFFINE:
255
256
warpGround = (Mat_<float>(2,3) << (1-rng.uniform(-0.05f, 0.05f)),
257
(rng.uniform(-0.03f, 0.03f)), (rng.uniform(10.f, 20.f)),
258
(rng.uniform(-0.03f, 0.03f)), (1-rng.uniform(-0.05f, 0.05f)),
259
(rng.uniform(10.f, 20.f)));
260
warpAffine(target_image, template_image, warpGround,
261
Size(200,200), INTER_LINEAR + WARP_INVERSE_MAP);
262
break;
263
case MOTION_HOMOGRAPHY:
264
warpGround = (Mat_<float>(3,3) << (1-rng.uniform(-0.05f, 0.05f)),
265
(rng.uniform(-0.03f, 0.03f)), (rng.uniform(10.f, 20.f)),
266
(rng.uniform(-0.03f, 0.03f)), (1-rng.uniform(-0.05f, 0.05f)),(rng.uniform(10.f, 20.f)),
267
(rng.uniform(0.0001f, 0.0003f)), (rng.uniform(0.0001f, 0.0003f)), 1.f);
268
warpPerspective(target_image, template_image, warpGround,
269
Size(200,200), INTER_LINEAR + WARP_INVERSE_MAP);
270
break;
271
}
272
}
273
274
275
const int warp_mode = mode_temp;
276
277
// initialize or load the warp matrix
278
Mat warp_matrix;
279
if (warpType == "homography")
280
warp_matrix = Mat::eye(3, 3, CV_32F);
281
else
282
warp_matrix = Mat::eye(2, 3, CV_32F);
283
284
if (inWarpFile!=""){
285
int readflag = readWarp(inWarpFile, warp_matrix, warp_mode);
286
if ((!readflag) || warp_matrix.empty())
287
{
288
cerr << "-> Check warp initialization file" << endl << flush;
289
return -1;
290
}
291
}
292
else {
293
294
printf("\n ->Performance Warning: Identity warp ideally assumes images of "
295
"similar size. If the deformation is strong, the identity warp may not "
296
"be a good initialization. \n");
297
298
}
299
300
if (number_of_iterations > 200)
301
cout << "-> Warning: too many iterations " << endl;
302
303
if (warp_mode != MOTION_HOMOGRAPHY)
304
warp_matrix.rows = 2;
305
306
// start timing
307
const double tic_init = (double) getTickCount ();
308
double cc = findTransformECC (template_image, target_image, warp_matrix, warp_mode,
309
TermCriteria (TermCriteria::COUNT+TermCriteria::EPS,
310
number_of_iterations, termination_eps));
311
312
if (cc == -1)
313
{
314
cerr << "The execution was interrupted. The correlation value is going to be minimized." << endl;
315
cerr << "Check the warp initialization and/or the size of images." << endl << flush;
316
}
317
318
// end timing
319
const double toc_final = (double) getTickCount ();
320
const double total_time = (toc_final-tic_init)/(getTickFrequency());
321
if (verbose){
322
cout << "Alignment time (" << warpType << " transformation): "
323
<< total_time << " sec" << endl << flush;
324
// cout << "Final correlation: " << cc << endl << flush;
325
326
}
327
328
// save the final warp matrix
329
saveWarp(finalWarp, warp_matrix, warp_mode);
330
331
if (verbose){
332
cout << "\nThe final warp has been saved in the file: " << finalWarp << endl << flush;
333
}
334
335
// save the final warped image
336
Mat warped_image = Mat(template_image.rows, template_image.cols, CV_32FC1);
337
if (warp_mode != MOTION_HOMOGRAPHY)
338
warpAffine (target_image, warped_image, warp_matrix, warped_image.size(),
339
INTER_LINEAR + WARP_INVERSE_MAP);
340
else
341
warpPerspective (target_image, warped_image, warp_matrix, warped_image.size(),
342
INTER_LINEAR + WARP_INVERSE_MAP);
343
344
//save the warped image
345
imwrite(warpedImFile, warped_image);
346
347
// display resulting images
348
if (verbose)
349
{
350
351
cout << "The warped image has been saved in the file: " << warpedImFile << endl << flush;
352
353
namedWindow ("image", WINDOW_AUTOSIZE);
354
namedWindow ("template", WINDOW_AUTOSIZE);
355
namedWindow ("warped image", WINDOW_AUTOSIZE);
356
namedWindow ("error (black: no error)", WINDOW_AUTOSIZE);
357
358
moveWindow ("image", 20, 300);
359
moveWindow ("template", 300, 300);
360
moveWindow ("warped image", 600, 300);
361
moveWindow ("error (black: no error)", 900, 300);
362
363
// draw boundaries of corresponding regions
364
Mat identity_matrix = Mat::eye(3,3,CV_32F);
365
366
draw_warped_roi (target_image, template_image.cols-2, template_image.rows-2, warp_matrix);
367
draw_warped_roi (template_image, template_image.cols-2, template_image.rows-2, identity_matrix);
368
369
Mat errorImage;
370
subtract(template_image, warped_image, errorImage);
371
double max_of_error;
372
minMaxLoc(errorImage, NULL, &max_of_error);
373
374
// show images
375
cout << "Press any key to exit the demo (you might need to click on the images before)." << endl << flush;
376
377
imshow ("image", target_image);
378
waitKey (200);
379
imshow ("template", template_image);
380
waitKey (200);
381
imshow ("warped image", warped_image);
382
waitKey(200);
383
imshow ("error (black: no error)", abs(errorImage)*255/max_of_error);
384
waitKey(0);
385
386
}
387
388
// done
389
return 0;
390
}
391
392