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
image_jacobian_homo_ECC(const Mat & src1,const Mat & src2,const Mat & src3,const Mat & src4,const Mat & src5,Mat & dst)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-divide 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
image_jacobian_euclidean_ECC(const Mat & src1,const Mat & src2,const Mat & src3,const Mat & src4,const Mat & src5,Mat & dst)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
image_jacobian_affine_ECC(const Mat & src1,const Mat & src2,const Mat & src3,const Mat & src4,Mat & dst)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
image_jacobian_translation_ECC(const Mat & src1,const Mat & src2,Mat & dst)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
project_onto_jacobian_ECC(const Mat & src1,const Mat & src2,Mat & dst)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
update_warping_matrix_ECC(Mat & map_matrix,const Mat & update,const int motionType)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 /** Function that computes enhanced corelation coefficient from Georgios et.al. 2008
313 * See https://github.com/opencv/opencv/issues/12432
314 */
computeECC(InputArray templateImage,InputArray inputImage,InputArray inputMask)315 double cv::computeECC(InputArray templateImage, InputArray inputImage, InputArray inputMask)
316 {
317 CV_Assert(!templateImage.empty());
318 CV_Assert(!inputImage.empty());
319
320 if( ! (templateImage.type()==inputImage.type()))
321 CV_Error( Error::StsUnmatchedFormats, "Both input images must have the same data type" );
322
323 Scalar meanTemplate, sdTemplate;
324
325 int active_pixels = inputMask.empty() ? templateImage.size().area() : countNonZero(inputMask);
326 int type = templateImage.type();
327 meanStdDev(templateImage, meanTemplate, sdTemplate, inputMask);
328 Mat templateImage_zeromean = Mat::zeros(templateImage.size(), templateImage.type());
329 Mat templateMat = templateImage.getMat();
330 Mat inputMat = inputImage.getMat();
331
332 /*
333 * For unsigned ints, when the mean is computed and subtracted, any values less than the mean
334 * will be set to 0 (since there are no negatives values). This impacts the norm and dot product, which
335 * ultimately results in an incorrect ECC. To circumvent this problem, if unsigned ints are provided,
336 * we convert them to a signed ints with larger resolution for the subtraction step.
337 */
338 if(type == CV_8U || type == CV_16U) {
339 int newType = type == CV_8U ? CV_16S : CV_32S;
340 Mat templateMatConverted, inputMatConverted;
341 templateMat.convertTo(templateMatConverted, newType);
342 cv::swap(templateMat, templateMatConverted);
343 inputMat.convertTo(inputMatConverted, newType);
344 cv::swap(inputMat, inputMatConverted);
345 }
346 subtract(templateMat, meanTemplate, templateImage_zeromean, inputMask);
347 double templateImagenorm = std::sqrt(active_pixels*sdTemplate.val[0]*sdTemplate.val[0]);
348
349 Scalar meanInput, sdInput;
350
351 Mat inputImage_zeromean = Mat::zeros(inputImage.size(), inputImage.type());
352 meanStdDev(inputImage, meanInput, sdInput, inputMask);
353 subtract(inputMat, meanInput, inputImage_zeromean, inputMask);
354 double inputImagenorm = std::sqrt(active_pixels*sdInput.val[0]*sdInput.val[0]);
355
356 return templateImage_zeromean.dot(inputImage_zeromean)/(templateImagenorm*inputImagenorm);
357 }
358
359
findTransformECC(InputArray templateImage,InputArray inputImage,InputOutputArray warpMatrix,int motionType,TermCriteria criteria,InputArray inputMask,int gaussFiltSize)360 double cv::findTransformECC(InputArray templateImage,
361 InputArray inputImage,
362 InputOutputArray warpMatrix,
363 int motionType,
364 TermCriteria criteria,
365 InputArray inputMask,
366 int gaussFiltSize)
367 {
368
369
370 Mat src = templateImage.getMat();//template image
371 Mat dst = inputImage.getMat(); //input image (to be warped)
372 Mat map = warpMatrix.getMat(); //warp (transformation)
373
374 CV_Assert(!src.empty());
375 CV_Assert(!dst.empty());
376
377 // If the user passed an un-initialized warpMatrix, initialize to identity
378 if(map.empty()) {
379 int rowCount = 2;
380 if(motionType == MOTION_HOMOGRAPHY)
381 rowCount = 3;
382
383 warpMatrix.create(rowCount, 3, CV_32FC1);
384 map = warpMatrix.getMat();
385 map = Mat::eye(rowCount, 3, CV_32F);
386 }
387
388 if( ! (src.type()==dst.type()))
389 CV_Error( Error::StsUnmatchedFormats, "Both input images must have the same data type" );
390
391 //accept only 1-channel images
392 if( src.type() != CV_8UC1 && src.type()!= CV_32FC1)
393 CV_Error( Error::StsUnsupportedFormat, "Images must have 8uC1 or 32fC1 type");
394
395 if( map.type() != CV_32FC1)
396 CV_Error( Error::StsUnsupportedFormat, "warpMatrix must be single-channel floating-point matrix");
397
398 CV_Assert (map.cols == 3);
399 CV_Assert (map.rows == 2 || map.rows ==3);
400
401 CV_Assert (motionType == MOTION_AFFINE || motionType == MOTION_HOMOGRAPHY ||
402 motionType == MOTION_EUCLIDEAN || motionType == MOTION_TRANSLATION);
403
404 if (motionType == MOTION_HOMOGRAPHY){
405 CV_Assert (map.rows ==3);
406 }
407
408 CV_Assert (criteria.type & TermCriteria::COUNT || criteria.type & TermCriteria::EPS);
409 const int numberOfIterations = (criteria.type & TermCriteria::COUNT) ? criteria.maxCount : 200;
410 const double termination_eps = (criteria.type & TermCriteria::EPS) ? criteria.epsilon : -1;
411
412 int paramTemp = 6;//default: affine
413 switch (motionType){
414 case MOTION_TRANSLATION:
415 paramTemp = 2;
416 break;
417 case MOTION_EUCLIDEAN:
418 paramTemp = 3;
419 break;
420 case MOTION_HOMOGRAPHY:
421 paramTemp = 8;
422 break;
423 }
424
425
426 const int numberOfParameters = paramTemp;
427
428 const int ws = src.cols;
429 const int hs = src.rows;
430 const int wd = dst.cols;
431 const int hd = dst.rows;
432
433 Mat Xcoord = Mat(1, ws, CV_32F);
434 Mat Ycoord = Mat(hs, 1, CV_32F);
435 Mat Xgrid = Mat(hs, ws, CV_32F);
436 Mat Ygrid = Mat(hs, ws, CV_32F);
437
438 float* XcoPtr = Xcoord.ptr<float>(0);
439 float* YcoPtr = Ycoord.ptr<float>(0);
440 int j;
441 for (j=0; j<ws; j++)
442 XcoPtr[j] = (float) j;
443 for (j=0; j<hs; j++)
444 YcoPtr[j] = (float) j;
445
446 repeat(Xcoord, hs, 1, Xgrid);
447 repeat(Ycoord, 1, ws, Ygrid);
448
449 Xcoord.release();
450 Ycoord.release();
451
452 Mat templateZM = Mat(hs, ws, CV_32F);// to store the (smoothed)zero-mean version of template
453 Mat templateFloat = Mat(hs, ws, CV_32F);// to store the (smoothed) template
454 Mat imageFloat = Mat(hd, wd, CV_32F);// to store the (smoothed) input image
455 Mat imageWarped = Mat(hs, ws, CV_32F);// to store the warped zero-mean input image
456 Mat imageMask = Mat(hs, ws, CV_8U); // to store the final mask
457
458 Mat inputMaskMat = inputMask.getMat();
459 //to use it for mask warping
460 Mat preMask;
461 if(inputMask.empty())
462 preMask = Mat::ones(hd, wd, CV_8U);
463 else
464 threshold(inputMask, preMask, 0, 1, THRESH_BINARY);
465
466 //gaussian filtering is optional
467 src.convertTo(templateFloat, templateFloat.type());
468 GaussianBlur(templateFloat, templateFloat, Size(gaussFiltSize, gaussFiltSize), 0, 0);
469
470 Mat preMaskFloat;
471 preMask.convertTo(preMaskFloat, CV_32F);
472 GaussianBlur(preMaskFloat, preMaskFloat, Size(gaussFiltSize, gaussFiltSize), 0, 0);
473 // Change threshold.
474 preMaskFloat *= (0.5/0.95);
475 // Rounding conversion.
476 preMaskFloat.convertTo(preMask, preMask.type());
477 preMask.convertTo(preMaskFloat, preMaskFloat.type());
478
479 dst.convertTo(imageFloat, imageFloat.type());
480 GaussianBlur(imageFloat, imageFloat, Size(gaussFiltSize, gaussFiltSize), 0, 0);
481
482 // needed matrices for gradients and warped gradients
483 Mat gradientX = Mat::zeros(hd, wd, CV_32FC1);
484 Mat gradientY = Mat::zeros(hd, wd, CV_32FC1);
485 Mat gradientXWarped = Mat(hs, ws, CV_32FC1);
486 Mat gradientYWarped = Mat(hs, ws, CV_32FC1);
487
488
489 // calculate first order image derivatives
490 Matx13f dx(-0.5f, 0.0f, 0.5f);
491
492 filter2D(imageFloat, gradientX, -1, dx);
493 filter2D(imageFloat, gradientY, -1, dx.t());
494
495 gradientX = gradientX.mul(preMaskFloat);
496 gradientY = gradientY.mul(preMaskFloat);
497
498 // matrices needed for solving linear equation system for maximizing ECC
499 Mat jacobian = Mat(hs, ws*numberOfParameters, CV_32F);
500 Mat hessian = Mat(numberOfParameters, numberOfParameters, CV_32F);
501 Mat hessianInv = Mat(numberOfParameters, numberOfParameters, CV_32F);
502 Mat imageProjection = Mat(numberOfParameters, 1, CV_32F);
503 Mat templateProjection = Mat(numberOfParameters, 1, CV_32F);
504 Mat imageProjectionHessian = Mat(numberOfParameters, 1, CV_32F);
505 Mat errorProjection = Mat(numberOfParameters, 1, CV_32F);
506
507 Mat deltaP = Mat(numberOfParameters, 1, CV_32F);//transformation parameter correction
508 Mat error = Mat(hs, ws, CV_32F);//error as 2D matrix
509
510 const int imageFlags = INTER_LINEAR + WARP_INVERSE_MAP;
511 const int maskFlags = INTER_NEAREST + WARP_INVERSE_MAP;
512
513
514 // iteratively update map_matrix
515 double rho = -1;
516 double last_rho = - termination_eps;
517 for (int i = 1; (i <= numberOfIterations) && (fabs(rho-last_rho)>= termination_eps); i++)
518 {
519
520 // warp-back portion of the inputImage and gradients to the coordinate space of the templateImage
521 if (motionType != MOTION_HOMOGRAPHY)
522 {
523 warpAffine(imageFloat, imageWarped, map, imageWarped.size(), imageFlags);
524 warpAffine(gradientX, gradientXWarped, map, gradientXWarped.size(), imageFlags);
525 warpAffine(gradientY, gradientYWarped, map, gradientYWarped.size(), imageFlags);
526 warpAffine(preMask, imageMask, map, imageMask.size(), maskFlags);
527 }
528 else
529 {
530 warpPerspective(imageFloat, imageWarped, map, imageWarped.size(), imageFlags);
531 warpPerspective(gradientX, gradientXWarped, map, gradientXWarped.size(), imageFlags);
532 warpPerspective(gradientY, gradientYWarped, map, gradientYWarped.size(), imageFlags);
533 warpPerspective(preMask, imageMask, map, imageMask.size(), maskFlags);
534 }
535
536 Scalar imgMean, imgStd, tmpMean, tmpStd;
537 meanStdDev(imageWarped, imgMean, imgStd, imageMask);
538 meanStdDev(templateFloat, tmpMean, tmpStd, imageMask);
539
540 subtract(imageWarped, imgMean, imageWarped, imageMask);//zero-mean input
541 templateZM = Mat::zeros(templateZM.rows, templateZM.cols, templateZM.type());
542 subtract(templateFloat, tmpMean, templateZM, imageMask);//zero-mean template
543
544 const double tmpNorm = std::sqrt(countNonZero(imageMask)*(tmpStd.val[0])*(tmpStd.val[0]));
545 const double imgNorm = std::sqrt(countNonZero(imageMask)*(imgStd.val[0])*(imgStd.val[0]));
546
547 // calculate jacobian of image wrt parameters
548 switch (motionType){
549 case MOTION_AFFINE:
550 image_jacobian_affine_ECC(gradientXWarped, gradientYWarped, Xgrid, Ygrid, jacobian);
551 break;
552 case MOTION_HOMOGRAPHY:
553 image_jacobian_homo_ECC(gradientXWarped, gradientYWarped, Xgrid, Ygrid, map, jacobian);
554 break;
555 case MOTION_TRANSLATION:
556 image_jacobian_translation_ECC(gradientXWarped, gradientYWarped, jacobian);
557 break;
558 case MOTION_EUCLIDEAN:
559 image_jacobian_euclidean_ECC(gradientXWarped, gradientYWarped, Xgrid, Ygrid, map, jacobian);
560 break;
561 }
562
563 // calculate Hessian and its inverse
564 project_onto_jacobian_ECC(jacobian, jacobian, hessian);
565
566 hessianInv = hessian.inv();
567
568 const double correlation = templateZM.dot(imageWarped);
569
570 // calculate enhanced correlation coefficient (ECC)->rho
571 last_rho = rho;
572 rho = correlation/(imgNorm*tmpNorm);
573 if (cvIsNaN(rho)) {
574 CV_Error(Error::StsNoConv, "NaN encountered.");
575 }
576
577 // project images into jacobian
578 project_onto_jacobian_ECC( jacobian, imageWarped, imageProjection);
579 project_onto_jacobian_ECC(jacobian, templateZM, templateProjection);
580
581
582 // calculate the parameter lambda to account for illumination variation
583 imageProjectionHessian = hessianInv*imageProjection;
584 const double lambda_n = (imgNorm*imgNorm) - imageProjection.dot(imageProjectionHessian);
585 const double lambda_d = correlation - templateProjection.dot(imageProjectionHessian);
586 if (lambda_d <= 0.0)
587 {
588 rho = -1;
589 CV_Error(Error::StsNoConv, "The algorithm stopped before its convergence. The correlation is going to be minimized. Images may be uncorrelated or non-overlapped");
590
591 }
592 const double lambda = (lambda_n/lambda_d);
593
594 // estimate the update step delta_p
595 error = lambda*templateZM - imageWarped;
596 project_onto_jacobian_ECC(jacobian, error, errorProjection);
597 deltaP = hessianInv * errorProjection;
598
599 // update warping matrix
600 update_warping_matrix_ECC( map, deltaP, motionType);
601
602
603 }
604
605 // return final correlation coefficient
606 return rho;
607 }
608
findTransformECC(InputArray templateImage,InputArray inputImage,InputOutputArray warpMatrix,int motionType,TermCriteria criteria,InputArray inputMask)609 double cv::findTransformECC(InputArray templateImage, InputArray inputImage,
610 InputOutputArray warpMatrix, int motionType,
611 TermCriteria criteria,
612 InputArray inputMask)
613 {
614 // Use default value of 5 for gaussFiltSize to maintain backward compatibility.
615 return findTransformECC(templateImage, inputImage, warpMatrix, motionType, criteria, inputMask, 5);
616 }
617
618 /* End of file. */
619