Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Tetragramm
GitHub Repository: Tetragramm/opencv
Path: blob/master/modules/video/src/ecc.cpp
16337 views
1
/*M///////////////////////////////////////////////////////////////////////////////////////
2
//
3
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
4
//
5
// By downloading, copying, installing or using the software you agree to this license.
6
// If you do not agree to this license, do not download, install,
7
// copy or use the software.
8
//
9
//
10
// Intel License Agreement
11
// For Open Source Computer Vision Library
12
//
13
// Copyright (C) 2000, Intel Corporation, all rights reserved.
14
// Third party copyrights are property of their respective owners.
15
//
16
// Redistribution and use in source and binary forms, with or without modification,
17
// are permitted provided that the following conditions are met:
18
//
19
// * Redistribution's of source code must retain the above copyright notice,
20
// this list of conditions and the following disclaimer.
21
//
22
// * Redistribution's in binary form must reproduce the above copyright notice,
23
// this list of conditions and the following disclaimer in the documentation
24
// and/or other materials provided with the distribution.
25
//
26
// * The name of Intel Corporation may not be used to endorse or promote products
27
// derived from this software without specific prior written permission.
28
//
29
// This software is provided by the copyright holders and contributors "as is" and
30
// any express or implied warranties, including, but not limited to, the implied
31
// warranties of merchantability and fitness for a particular purpose are disclaimed.
32
// In no event shall the Intel Corporation or contributors be liable for any direct,
33
// indirect, incidental, special, exemplary, or consequential damages
34
// (including, but not limited to, procurement of substitute goods or services;
35
// loss of use, data, or profits; or business interruption) however caused
36
// and on any theory of liability, whether in contract, strict liability,
37
// or tort (including negligence or otherwise) arising in any way out of
38
// the use of this software, even if advised of the possibility of such damage.
39
//
40
//M*/
41
42
#include "precomp.hpp"
43
44
45
/****************************************************************************************\
46
* Image Alignment (ECC algorithm) *
47
\****************************************************************************************/
48
49
using namespace cv;
50
51
static void image_jacobian_homo_ECC(const Mat& src1, const Mat& src2,
52
const Mat& src3, const Mat& src4,
53
const Mat& src5, Mat& dst)
54
{
55
56
57
CV_Assert(src1.size() == src2.size());
58
CV_Assert(src1.size() == src3.size());
59
CV_Assert(src1.size() == src4.size());
60
61
CV_Assert( src1.rows == dst.rows);
62
CV_Assert(dst.cols == (src1.cols*8));
63
CV_Assert(dst.type() == CV_32FC1);
64
65
CV_Assert(src5.isContinuous());
66
67
68
const float* hptr = src5.ptr<float>(0);
69
70
const float h0_ = hptr[0];
71
const float h1_ = hptr[3];
72
const float h2_ = hptr[6];
73
const float h3_ = hptr[1];
74
const float h4_ = hptr[4];
75
const float h5_ = hptr[7];
76
const float h6_ = hptr[2];
77
const float h7_ = hptr[5];
78
79
const int w = src1.cols;
80
81
82
//create denominator for all points as a block
83
Mat den_ = src3*h2_ + src4*h5_ + 1.0;//check the time of this! otherwise use addWeighted
84
85
//create projected points
86
Mat hatX_ = -src3*h0_ - src4*h3_ - h6_;
87
divide(hatX_, den_, hatX_);
88
Mat hatY_ = -src3*h1_ - src4*h4_ - h7_;
89
divide(hatY_, den_, hatY_);
90
91
92
//instead of dividing each block with den,
93
//just pre-devide the block of gradients (it's more efficient)
94
95
Mat src1Divided_;
96
Mat src2Divided_;
97
98
divide(src1, den_, src1Divided_);
99
divide(src2, den_, src2Divided_);
100
101
102
//compute Jacobian blocks (8 blocks)
103
104
dst.colRange(0, w) = src1Divided_.mul(src3);//1
105
106
dst.colRange(w,2*w) = src2Divided_.mul(src3);//2
107
108
Mat temp_ = (hatX_.mul(src1Divided_)+hatY_.mul(src2Divided_));
109
dst.colRange(2*w,3*w) = temp_.mul(src3);//3
110
111
hatX_.release();
112
hatY_.release();
113
114
dst.colRange(3*w, 4*w) = src1Divided_.mul(src4);//4
115
116
dst.colRange(4*w, 5*w) = src2Divided_.mul(src4);//5
117
118
dst.colRange(5*w, 6*w) = temp_.mul(src4);//6
119
120
src1Divided_.copyTo(dst.colRange(6*w, 7*w));//7
121
122
src2Divided_.copyTo(dst.colRange(7*w, 8*w));//8
123
}
124
125
static void image_jacobian_euclidean_ECC(const Mat& src1, const Mat& src2,
126
const Mat& src3, const Mat& src4,
127
const Mat& src5, Mat& dst)
128
{
129
130
CV_Assert( src1.size()==src2.size());
131
CV_Assert( src1.size()==src3.size());
132
CV_Assert( src1.size()==src4.size());
133
134
CV_Assert( src1.rows == dst.rows);
135
CV_Assert(dst.cols == (src1.cols*3));
136
CV_Assert(dst.type() == CV_32FC1);
137
138
CV_Assert(src5.isContinuous());
139
140
const float* hptr = src5.ptr<float>(0);
141
142
const float h0 = hptr[0];//cos(theta)
143
const float h1 = hptr[3];//sin(theta)
144
145
const int w = src1.cols;
146
147
//create -sin(theta)*X -cos(theta)*Y for all points as a block -> hatX
148
Mat hatX = -(src3*h1) - (src4*h0);
149
150
//create cos(theta)*X -sin(theta)*Y for all points as a block -> hatY
151
Mat hatY = (src3*h0) - (src4*h1);
152
153
154
//compute Jacobian blocks (3 blocks)
155
dst.colRange(0, w) = (src1.mul(hatX))+(src2.mul(hatY));//1
156
157
src1.copyTo(dst.colRange(w, 2*w));//2
158
src2.copyTo(dst.colRange(2*w, 3*w));//3
159
}
160
161
162
static void image_jacobian_affine_ECC(const Mat& src1, const Mat& src2,
163
const Mat& src3, const Mat& src4,
164
Mat& dst)
165
{
166
167
CV_Assert(src1.size() == src2.size());
168
CV_Assert(src1.size() == src3.size());
169
CV_Assert(src1.size() == src4.size());
170
171
CV_Assert(src1.rows == dst.rows);
172
CV_Assert(dst.cols == (6*src1.cols));
173
174
CV_Assert(dst.type() == CV_32FC1);
175
176
177
const int w = src1.cols;
178
179
//compute Jacobian blocks (6 blocks)
180
181
dst.colRange(0,w) = src1.mul(src3);//1
182
dst.colRange(w,2*w) = src2.mul(src3);//2
183
dst.colRange(2*w,3*w) = src1.mul(src4);//3
184
dst.colRange(3*w,4*w) = src2.mul(src4);//4
185
src1.copyTo(dst.colRange(4*w,5*w));//5
186
src2.copyTo(dst.colRange(5*w,6*w));//6
187
}
188
189
190
static void image_jacobian_translation_ECC(const Mat& src1, const Mat& src2, Mat& dst)
191
{
192
193
CV_Assert( src1.size()==src2.size());
194
195
CV_Assert( src1.rows == dst.rows);
196
CV_Assert(dst.cols == (src1.cols*2));
197
CV_Assert(dst.type() == CV_32FC1);
198
199
const int w = src1.cols;
200
201
//compute Jacobian blocks (2 blocks)
202
src1.copyTo(dst.colRange(0, w));
203
src2.copyTo(dst.colRange(w, 2*w));
204
}
205
206
207
static void project_onto_jacobian_ECC(const Mat& src1, const Mat& src2, Mat& dst)
208
{
209
/* this functions is used for two types of projections. If src1.cols ==src.cols
210
it does a blockwise multiplication (like in the outer product of vectors)
211
of the blocks in matrices src1 and src2 and dst
212
has size (number_of_blcks x number_of_blocks), otherwise dst is a vector of size
213
(number_of_blocks x 1) since src2 is "multiplied"(dot) with each block of src1.
214
215
The number_of_blocks is equal to the number of parameters we are lloking for
216
(i.e. rtanslation:2, euclidean: 3, affine: 6, homography: 8)
217
218
*/
219
CV_Assert(src1.rows == src2.rows);
220
CV_Assert((src1.cols % src2.cols) == 0);
221
int w;
222
223
float* dstPtr = dst.ptr<float>(0);
224
225
if (src1.cols !=src2.cols){//dst.cols==1
226
w = src2.cols;
227
for (int i=0; i<dst.rows; i++){
228
dstPtr[i] = (float) src2.dot(src1.colRange(i*w,(i+1)*w));
229
}
230
}
231
232
else {
233
CV_Assert(dst.cols == dst.rows); //dst is square (and symmetric)
234
w = src2.cols/dst.cols;
235
Mat mat;
236
for (int i=0; i<dst.rows; i++){
237
238
mat = Mat(src1.colRange(i*w, (i+1)*w));
239
dstPtr[i*(dst.rows+1)] = (float) pow(norm(mat),2); //diagonal elements
240
241
for (int j=i+1; j<dst.cols; j++){ //j starts from i+1
242
dstPtr[i*dst.cols+j] = (float) mat.dot(src2.colRange(j*w, (j+1)*w));
243
dstPtr[j*dst.cols+i] = dstPtr[i*dst.cols+j]; //due to symmetry
244
}
245
}
246
}
247
}
248
249
250
static void update_warping_matrix_ECC (Mat& map_matrix, const Mat& update, const int motionType)
251
{
252
CV_Assert (map_matrix.type() == CV_32FC1);
253
CV_Assert (update.type() == CV_32FC1);
254
255
CV_Assert (motionType == MOTION_TRANSLATION || motionType == MOTION_EUCLIDEAN ||
256
motionType == MOTION_AFFINE || motionType == MOTION_HOMOGRAPHY);
257
258
if (motionType == MOTION_HOMOGRAPHY)
259
CV_Assert (map_matrix.rows == 3 && update.rows == 8);
260
else if (motionType == MOTION_AFFINE)
261
CV_Assert(map_matrix.rows == 2 && update.rows == 6);
262
else if (motionType == MOTION_EUCLIDEAN)
263
CV_Assert (map_matrix.rows == 2 && update.rows == 3);
264
else
265
CV_Assert (map_matrix.rows == 2 && update.rows == 2);
266
267
CV_Assert (update.cols == 1);
268
269
CV_Assert( map_matrix.isContinuous());
270
CV_Assert( update.isContinuous() );
271
272
273
float* mapPtr = map_matrix.ptr<float>(0);
274
const float* updatePtr = update.ptr<float>(0);
275
276
277
if (motionType == MOTION_TRANSLATION){
278
mapPtr[2] += updatePtr[0];
279
mapPtr[5] += updatePtr[1];
280
}
281
if (motionType == MOTION_AFFINE) {
282
mapPtr[0] += updatePtr[0];
283
mapPtr[3] += updatePtr[1];
284
mapPtr[1] += updatePtr[2];
285
mapPtr[4] += updatePtr[3];
286
mapPtr[2] += updatePtr[4];
287
mapPtr[5] += updatePtr[5];
288
}
289
if (motionType == MOTION_HOMOGRAPHY) {
290
mapPtr[0] += updatePtr[0];
291
mapPtr[3] += updatePtr[1];
292
mapPtr[6] += updatePtr[2];
293
mapPtr[1] += updatePtr[3];
294
mapPtr[4] += updatePtr[4];
295
mapPtr[7] += updatePtr[5];
296
mapPtr[2] += updatePtr[6];
297
mapPtr[5] += updatePtr[7];
298
}
299
if (motionType == MOTION_EUCLIDEAN) {
300
double new_theta = updatePtr[0];
301
new_theta += asin(mapPtr[3]);
302
303
mapPtr[2] += updatePtr[1];
304
mapPtr[5] += updatePtr[2];
305
mapPtr[0] = mapPtr[4] = (float) cos(new_theta);
306
mapPtr[3] = (float) sin(new_theta);
307
mapPtr[1] = -mapPtr[3];
308
}
309
}
310
311
312
double cv::findTransformECC(InputArray templateImage,
313
InputArray inputImage,
314
InputOutputArray warpMatrix,
315
int motionType,
316
TermCriteria criteria,
317
InputArray inputMask)
318
{
319
320
321
Mat src = templateImage.getMat();//template iamge
322
Mat dst = inputImage.getMat(); //input image (to be warped)
323
Mat map = warpMatrix.getMat(); //warp (transformation)
324
325
CV_Assert(!src.empty());
326
CV_Assert(!dst.empty());
327
328
// If the user passed an un-initialized warpMatrix, initialize to identity
329
if(map.empty()) {
330
int rowCount = 2;
331
if(motionType == MOTION_HOMOGRAPHY)
332
rowCount = 3;
333
334
warpMatrix.create(rowCount, 3, CV_32FC1);
335
map = warpMatrix.getMat();
336
map = Mat::eye(rowCount, 3, CV_32F);
337
}
338
339
if( ! (src.type()==dst.type()))
340
CV_Error( Error::StsUnmatchedFormats, "Both input images must have the same data type" );
341
342
//accept only 1-channel images
343
if( src.type() != CV_8UC1 && src.type()!= CV_32FC1)
344
CV_Error( Error::StsUnsupportedFormat, "Images must have 8uC1 or 32fC1 type");
345
346
if( map.type() != CV_32FC1)
347
CV_Error( Error::StsUnsupportedFormat, "warpMatrix must be single-channel floating-point matrix");
348
349
CV_Assert (map.cols == 3);
350
CV_Assert (map.rows == 2 || map.rows ==3);
351
352
CV_Assert (motionType == MOTION_AFFINE || motionType == MOTION_HOMOGRAPHY ||
353
motionType == MOTION_EUCLIDEAN || motionType == MOTION_TRANSLATION);
354
355
if (motionType == MOTION_HOMOGRAPHY){
356
CV_Assert (map.rows ==3);
357
}
358
359
CV_Assert (criteria.type & TermCriteria::COUNT || criteria.type & TermCriteria::EPS);
360
const int numberOfIterations = (criteria.type & TermCriteria::COUNT) ? criteria.maxCount : 200;
361
const double termination_eps = (criteria.type & TermCriteria::EPS) ? criteria.epsilon : -1;
362
363
int paramTemp = 6;//default: affine
364
switch (motionType){
365
case MOTION_TRANSLATION:
366
paramTemp = 2;
367
break;
368
case MOTION_EUCLIDEAN:
369
paramTemp = 3;
370
break;
371
case MOTION_HOMOGRAPHY:
372
paramTemp = 8;
373
break;
374
}
375
376
377
const int numberOfParameters = paramTemp;
378
379
const int ws = src.cols;
380
const int hs = src.rows;
381
const int wd = dst.cols;
382
const int hd = dst.rows;
383
384
Mat Xcoord = Mat(1, ws, CV_32F);
385
Mat Ycoord = Mat(hs, 1, CV_32F);
386
Mat Xgrid = Mat(hs, ws, CV_32F);
387
Mat Ygrid = Mat(hs, ws, CV_32F);
388
389
float* XcoPtr = Xcoord.ptr<float>(0);
390
float* YcoPtr = Ycoord.ptr<float>(0);
391
int j;
392
for (j=0; j<ws; j++)
393
XcoPtr[j] = (float) j;
394
for (j=0; j<hs; j++)
395
YcoPtr[j] = (float) j;
396
397
repeat(Xcoord, hs, 1, Xgrid);
398
repeat(Ycoord, 1, ws, Ygrid);
399
400
Xcoord.release();
401
Ycoord.release();
402
403
Mat templateZM = Mat(hs, ws, CV_32F);// to store the (smoothed)zero-mean version of template
404
Mat templateFloat = Mat(hs, ws, CV_32F);// to store the (smoothed) template
405
Mat imageFloat = Mat(hd, wd, CV_32F);// to store the (smoothed) input image
406
Mat imageWarped = Mat(hs, ws, CV_32F);// to store the warped zero-mean input image
407
Mat imageMask = Mat(hs, ws, CV_8U); // to store the final mask
408
409
Mat inputMaskMat = inputMask.getMat();
410
//to use it for mask warping
411
Mat preMask;
412
if(inputMask.empty())
413
preMask = Mat::ones(hd, wd, CV_8U);
414
else
415
threshold(inputMask, preMask, 0, 1, THRESH_BINARY);
416
417
//gaussian filtering is optional
418
src.convertTo(templateFloat, templateFloat.type());
419
GaussianBlur(templateFloat, templateFloat, Size(5, 5), 0, 0);
420
421
Mat preMaskFloat;
422
preMask.convertTo(preMaskFloat, CV_32F);
423
GaussianBlur(preMaskFloat, preMaskFloat, Size(5, 5), 0, 0);
424
// Change threshold.
425
preMaskFloat *= (0.5/0.95);
426
// Rounding conversion.
427
preMaskFloat.convertTo(preMask, preMask.type());
428
preMask.convertTo(preMaskFloat, preMaskFloat.type());
429
430
dst.convertTo(imageFloat, imageFloat.type());
431
GaussianBlur(imageFloat, imageFloat, Size(5, 5), 0, 0);
432
433
// needed matrices for gradients and warped gradients
434
Mat gradientX = Mat::zeros(hd, wd, CV_32FC1);
435
Mat gradientY = Mat::zeros(hd, wd, CV_32FC1);
436
Mat gradientXWarped = Mat(hs, ws, CV_32FC1);
437
Mat gradientYWarped = Mat(hs, ws, CV_32FC1);
438
439
440
// calculate first order image derivatives
441
Matx13f dx(-0.5f, 0.0f, 0.5f);
442
443
filter2D(imageFloat, gradientX, -1, dx);
444
filter2D(imageFloat, gradientY, -1, dx.t());
445
446
gradientX = gradientX.mul(preMaskFloat);
447
gradientY = gradientY.mul(preMaskFloat);
448
449
// matrices needed for solving linear equation system for maximizing ECC
450
Mat jacobian = Mat(hs, ws*numberOfParameters, CV_32F);
451
Mat hessian = Mat(numberOfParameters, numberOfParameters, CV_32F);
452
Mat hessianInv = Mat(numberOfParameters, numberOfParameters, CV_32F);
453
Mat imageProjection = Mat(numberOfParameters, 1, CV_32F);
454
Mat templateProjection = Mat(numberOfParameters, 1, CV_32F);
455
Mat imageProjectionHessian = Mat(numberOfParameters, 1, CV_32F);
456
Mat errorProjection = Mat(numberOfParameters, 1, CV_32F);
457
458
Mat deltaP = Mat(numberOfParameters, 1, CV_32F);//transformation parameter correction
459
Mat error = Mat(hs, ws, CV_32F);//error as 2D matrix
460
461
const int imageFlags = INTER_LINEAR + WARP_INVERSE_MAP;
462
const int maskFlags = INTER_NEAREST + WARP_INVERSE_MAP;
463
464
465
// iteratively update map_matrix
466
double rho = -1;
467
double last_rho = - termination_eps;
468
for (int i = 1; (i <= numberOfIterations) && (fabs(rho-last_rho)>= termination_eps); i++)
469
{
470
471
// warp-back portion of the inputImage and gradients to the coordinate space of the templateImage
472
if (motionType != MOTION_HOMOGRAPHY)
473
{
474
warpAffine(imageFloat, imageWarped, map, imageWarped.size(), imageFlags);
475
warpAffine(gradientX, gradientXWarped, map, gradientXWarped.size(), imageFlags);
476
warpAffine(gradientY, gradientYWarped, map, gradientYWarped.size(), imageFlags);
477
warpAffine(preMask, imageMask, map, imageMask.size(), maskFlags);
478
}
479
else
480
{
481
warpPerspective(imageFloat, imageWarped, map, imageWarped.size(), imageFlags);
482
warpPerspective(gradientX, gradientXWarped, map, gradientXWarped.size(), imageFlags);
483
warpPerspective(gradientY, gradientYWarped, map, gradientYWarped.size(), imageFlags);
484
warpPerspective(preMask, imageMask, map, imageMask.size(), maskFlags);
485
}
486
487
Scalar imgMean, imgStd, tmpMean, tmpStd;
488
meanStdDev(imageWarped, imgMean, imgStd, imageMask);
489
meanStdDev(templateFloat, tmpMean, tmpStd, imageMask);
490
491
subtract(imageWarped, imgMean, imageWarped, imageMask);//zero-mean input
492
templateZM = Mat::zeros(templateZM.rows, templateZM.cols, templateZM.type());
493
subtract(templateFloat, tmpMean, templateZM, imageMask);//zero-mean template
494
495
const double tmpNorm = std::sqrt(countNonZero(imageMask)*(tmpStd.val[0])*(tmpStd.val[0]));
496
const double imgNorm = std::sqrt(countNonZero(imageMask)*(imgStd.val[0])*(imgStd.val[0]));
497
498
// calculate jacobian of image wrt parameters
499
switch (motionType){
500
case MOTION_AFFINE:
501
image_jacobian_affine_ECC(gradientXWarped, gradientYWarped, Xgrid, Ygrid, jacobian);
502
break;
503
case MOTION_HOMOGRAPHY:
504
image_jacobian_homo_ECC(gradientXWarped, gradientYWarped, Xgrid, Ygrid, map, jacobian);
505
break;
506
case MOTION_TRANSLATION:
507
image_jacobian_translation_ECC(gradientXWarped, gradientYWarped, jacobian);
508
break;
509
case MOTION_EUCLIDEAN:
510
image_jacobian_euclidean_ECC(gradientXWarped, gradientYWarped, Xgrid, Ygrid, map, jacobian);
511
break;
512
}
513
514
// calculate Hessian and its inverse
515
project_onto_jacobian_ECC(jacobian, jacobian, hessian);
516
517
hessianInv = hessian.inv();
518
519
const double correlation = templateZM.dot(imageWarped);
520
521
// calculate enhanced correlation coefficiont (ECC)->rho
522
last_rho = rho;
523
rho = correlation/(imgNorm*tmpNorm);
524
if (cvIsNaN(rho)) {
525
CV_Error(Error::StsNoConv, "NaN encountered.");
526
}
527
528
// project images into jacobian
529
project_onto_jacobian_ECC( jacobian, imageWarped, imageProjection);
530
project_onto_jacobian_ECC(jacobian, templateZM, templateProjection);
531
532
533
// calculate the parameter lambda to account for illumination variation
534
imageProjectionHessian = hessianInv*imageProjection;
535
const double lambda_n = (imgNorm*imgNorm) - imageProjection.dot(imageProjectionHessian);
536
const double lambda_d = correlation - templateProjection.dot(imageProjectionHessian);
537
if (lambda_d <= 0.0)
538
{
539
rho = -1;
540
CV_Error(Error::StsNoConv, "The algorithm stopped before its convergence. The correlation is going to be minimized. Images may be uncorrelated or non-overlapped");
541
542
}
543
const double lambda = (lambda_n/lambda_d);
544
545
// estimate the update step delta_p
546
error = lambda*templateZM - imageWarped;
547
project_onto_jacobian_ECC(jacobian, error, errorProjection);
548
deltaP = hessianInv * errorProjection;
549
550
// update warping matrix
551
update_warping_matrix_ECC( map, deltaP, motionType);
552
553
554
}
555
556
// return final correlation coefficient
557
return rho;
558
}
559
560
561
/* End of file. */
562
563