/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// Intel License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2000, Intel Corporation, all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * The name of Intel Corporation may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/
# include "precomp.hpp"
/****************************************************************************************\
* Image Alignment ( ECC algorithm ) *
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
using namespace cv ;
static void image_jacobian_homo_ECC ( const Mat & src1 , const Mat & src2 ,
const Mat & src3 , const Mat & src4 ,
const Mat & src5 , Mat & dst )
{
CV_Assert ( src1 . size ( ) = = src2 . size ( ) ) ;
CV_Assert ( src1 . size ( ) = = src3 . size ( ) ) ;
CV_Assert ( src1 . size ( ) = = src4 . size ( ) ) ;
CV_Assert ( src1 . rows = = dst . rows ) ;
CV_Assert ( dst . cols = = ( src1 . cols * 8 ) ) ;
CV_Assert ( dst . type ( ) = = CV_32FC1 ) ;
CV_Assert ( src5 . isContinuous ( ) ) ;
const float * hptr = src5 . ptr < float > ( 0 ) ;
const float h0_ = hptr [ 0 ] ;
const float h1_ = hptr [ 3 ] ;
const float h2_ = hptr [ 6 ] ;
const float h3_ = hptr [ 1 ] ;
const float h4_ = hptr [ 4 ] ;
const float h5_ = hptr [ 7 ] ;
const float h6_ = hptr [ 2 ] ;
const float h7_ = hptr [ 5 ] ;
const int w = src1 . cols ;
//create denominator for all points as a block
Mat den_ = src3 * h2_ + src4 * h5_ + 1.0 ; //check the time of this! otherwise use addWeighted
//create projected points
Mat hatX_ = - src3 * h0_ - src4 * h3_ - h6_ ;
divide ( hatX_ , den_ , hatX_ ) ;
Mat hatY_ = - src3 * h1_ - src4 * h4_ - h7_ ;
divide ( hatY_ , den_ , hatY_ ) ;
//instead of dividing each block with den,
Fix modules/ typos
Found using `codespell -q 3 -S ./3rdparty -L activ,amin,ang,atleast,childs,dof,endwhile,halfs,hist,iff,nd,od,uint`
5 years ago
//just pre-divide the block of gradients (it's more efficient)
Mat src1Divided_ ;
Mat src2Divided_ ;
divide ( src1 , den_ , src1Divided_ ) ;
divide ( src2 , den_ , src2Divided_ ) ;
//compute Jacobian blocks (8 blocks)
dst . colRange ( 0 , w ) = src1Divided_ . mul ( src3 ) ; //1
dst . colRange ( w , 2 * w ) = src2Divided_ . mul ( src3 ) ; //2
Mat temp_ = ( hatX_ . mul ( src1Divided_ ) + hatY_ . mul ( src2Divided_ ) ) ;
dst . colRange ( 2 * w , 3 * w ) = temp_ . mul ( src3 ) ; //3
hatX_ . release ( ) ;
hatY_ . release ( ) ;
dst . colRange ( 3 * w , 4 * w ) = src1Divided_ . mul ( src4 ) ; //4
dst . colRange ( 4 * w , 5 * w ) = src2Divided_ . mul ( src4 ) ; //5
dst . colRange ( 5 * w , 6 * w ) = temp_ . mul ( src4 ) ; //6
src1Divided_ . copyTo ( dst . colRange ( 6 * w , 7 * w ) ) ; //7
src2Divided_ . copyTo ( dst . colRange ( 7 * w , 8 * w ) ) ; //8
}
static void image_jacobian_euclidean_ECC ( const Mat & src1 , const Mat & src2 ,
const Mat & src3 , const Mat & src4 ,
const Mat & src5 , Mat & dst )
{
CV_Assert ( src1 . size ( ) = = src2 . size ( ) ) ;
CV_Assert ( src1 . size ( ) = = src3 . size ( ) ) ;
CV_Assert ( src1 . size ( ) = = src4 . size ( ) ) ;
CV_Assert ( src1 . rows = = dst . rows ) ;
CV_Assert ( dst . cols = = ( src1 . cols * 3 ) ) ;
CV_Assert ( dst . type ( ) = = CV_32FC1 ) ;
CV_Assert ( src5 . isContinuous ( ) ) ;
const float * hptr = src5 . ptr < float > ( 0 ) ;
const float h0 = hptr [ 0 ] ; //cos(theta)
const float h1 = hptr [ 3 ] ; //sin(theta)
const int w = src1 . cols ;
//create -sin(theta)*X -cos(theta)*Y for all points as a block -> hatX
Mat hatX = - ( src3 * h1 ) - ( src4 * h0 ) ;
//create cos(theta)*X -sin(theta)*Y for all points as a block -> hatY
Mat hatY = ( src3 * h0 ) - ( src4 * h1 ) ;
//compute Jacobian blocks (3 blocks)
dst . colRange ( 0 , w ) = ( src1 . mul ( hatX ) ) + ( src2 . mul ( hatY ) ) ; //1
src1 . copyTo ( dst . colRange ( w , 2 * w ) ) ; //2
src2 . copyTo ( dst . colRange ( 2 * w , 3 * w ) ) ; //3
}
static void image_jacobian_affine_ECC ( const Mat & src1 , const Mat & src2 ,
const Mat & src3 , const Mat & src4 ,
Mat & dst )
{
CV_Assert ( src1 . size ( ) = = src2 . size ( ) ) ;
CV_Assert ( src1 . size ( ) = = src3 . size ( ) ) ;
CV_Assert ( src1 . size ( ) = = src4 . size ( ) ) ;
CV_Assert ( src1 . rows = = dst . rows ) ;
CV_Assert ( dst . cols = = ( 6 * src1 . cols ) ) ;
CV_Assert ( dst . type ( ) = = CV_32FC1 ) ;
const int w = src1 . cols ;
//compute Jacobian blocks (6 blocks)
dst . colRange ( 0 , w ) = src1 . mul ( src3 ) ; //1
dst . colRange ( w , 2 * w ) = src2 . mul ( src3 ) ; //2
dst . colRange ( 2 * w , 3 * w ) = src1 . mul ( src4 ) ; //3
dst . colRange ( 3 * w , 4 * w ) = src2 . mul ( src4 ) ; //4
src1 . copyTo ( dst . colRange ( 4 * w , 5 * w ) ) ; //5
src2 . copyTo ( dst . colRange ( 5 * w , 6 * w ) ) ; //6
}
static void image_jacobian_translation_ECC ( const Mat & src1 , const Mat & src2 , Mat & dst )
{
CV_Assert ( src1 . size ( ) = = src2 . size ( ) ) ;
CV_Assert ( src1 . rows = = dst . rows ) ;
CV_Assert ( dst . cols = = ( src1 . cols * 2 ) ) ;
CV_Assert ( dst . type ( ) = = CV_32FC1 ) ;
const int w = src1 . cols ;
//compute Jacobian blocks (2 blocks)
src1 . copyTo ( dst . colRange ( 0 , w ) ) ;
src2 . copyTo ( dst . colRange ( w , 2 * w ) ) ;
}
static void project_onto_jacobian_ECC ( const Mat & src1 , const Mat & src2 , Mat & dst )
{
/* this functions is used for two types of projections. If src1.cols ==src.cols
it does a blockwise multiplication ( like in the outer product of vectors )
of the blocks in matrices src1 and src2 and dst
has size ( number_of_blcks x number_of_blocks ) , otherwise dst is a vector of size
( number_of_blocks x 1 ) since src2 is " multiplied " ( dot ) with each block of src1 .
The number_of_blocks is equal to the number of parameters we are lloking for
( i . e . rtanslation : 2 , euclidean : 3 , affine : 6 , homography : 8 )
*/
CV_Assert ( src1 . rows = = src2 . rows ) ;
CV_Assert ( ( src1 . cols % src2 . cols ) = = 0 ) ;
int w ;
float * dstPtr = dst . ptr < float > ( 0 ) ;
if ( src1 . cols ! = src2 . cols ) { //dst.cols==1
w = src2 . cols ;
for ( int i = 0 ; i < dst . rows ; i + + ) {
dstPtr [ i ] = ( float ) src2 . dot ( src1 . colRange ( i * w , ( i + 1 ) * w ) ) ;
}
}
else {
CV_Assert ( dst . cols = = dst . rows ) ; //dst is square (and symmetric)
w = src2 . cols / dst . cols ;
Mat mat ;
for ( int i = 0 ; i < dst . rows ; i + + ) {
mat = Mat ( src1 . colRange ( i * w , ( i + 1 ) * w ) ) ;
dstPtr [ i * ( dst . rows + 1 ) ] = ( float ) pow ( norm ( mat ) , 2 ) ; //diagonal elements
for ( int j = i + 1 ; j < dst . cols ; j + + ) { //j starts from i+1
dstPtr [ i * dst . cols + j ] = ( float ) mat . dot ( src2 . colRange ( j * w , ( j + 1 ) * w ) ) ;
dstPtr [ j * dst . cols + i ] = dstPtr [ i * dst . cols + j ] ; //due to symmetry
}
}
}
}
static void update_warping_matrix_ECC ( Mat & map_matrix , const Mat & update , const int motionType )
{
CV_Assert ( map_matrix . type ( ) = = CV_32FC1 ) ;
CV_Assert ( update . type ( ) = = CV_32FC1 ) ;
CV_Assert ( motionType = = MOTION_TRANSLATION | | motionType = = MOTION_EUCLIDEAN | |
motionType = = MOTION_AFFINE | | motionType = = MOTION_HOMOGRAPHY ) ;
if ( motionType = = MOTION_HOMOGRAPHY )
CV_Assert ( map_matrix . rows = = 3 & & update . rows = = 8 ) ;
else if ( motionType = = MOTION_AFFINE )
CV_Assert ( map_matrix . rows = = 2 & & update . rows = = 6 ) ;
else if ( motionType = = MOTION_EUCLIDEAN )
CV_Assert ( map_matrix . rows = = 2 & & update . rows = = 3 ) ;
else
CV_Assert ( map_matrix . rows = = 2 & & update . rows = = 2 ) ;
CV_Assert ( update . cols = = 1 ) ;
CV_Assert ( map_matrix . isContinuous ( ) ) ;
CV_Assert ( update . isContinuous ( ) ) ;
float * mapPtr = map_matrix . ptr < float > ( 0 ) ;
const float * updatePtr = update . ptr < float > ( 0 ) ;
if ( motionType = = MOTION_TRANSLATION ) {
mapPtr [ 2 ] + = updatePtr [ 0 ] ;
mapPtr [ 5 ] + = updatePtr [ 1 ] ;
}
if ( motionType = = MOTION_AFFINE ) {
mapPtr [ 0 ] + = updatePtr [ 0 ] ;
mapPtr [ 3 ] + = updatePtr [ 1 ] ;
mapPtr [ 1 ] + = updatePtr [ 2 ] ;
mapPtr [ 4 ] + = updatePtr [ 3 ] ;
mapPtr [ 2 ] + = updatePtr [ 4 ] ;
mapPtr [ 5 ] + = updatePtr [ 5 ] ;
}
if ( motionType = = MOTION_HOMOGRAPHY ) {
mapPtr [ 0 ] + = updatePtr [ 0 ] ;
mapPtr [ 3 ] + = updatePtr [ 1 ] ;
mapPtr [ 6 ] + = updatePtr [ 2 ] ;
mapPtr [ 1 ] + = updatePtr [ 3 ] ;
mapPtr [ 4 ] + = updatePtr [ 4 ] ;
mapPtr [ 7 ] + = updatePtr [ 5 ] ;
mapPtr [ 2 ] + = updatePtr [ 6 ] ;
mapPtr [ 5 ] + = updatePtr [ 7 ] ;
}
if ( motionType = = MOTION_EUCLIDEAN ) {
double new_theta = updatePtr [ 0 ] ;
new_theta + = asin ( mapPtr [ 3 ] ) ;
mapPtr [ 2 ] + = updatePtr [ 1 ] ;
mapPtr [ 5 ] + = updatePtr [ 2 ] ;
mapPtr [ 0 ] = mapPtr [ 4 ] = ( float ) cos ( new_theta ) ;
mapPtr [ 3 ] = ( float ) sin ( new_theta ) ;
mapPtr [ 1 ] = - mapPtr [ 3 ] ;
}
}
/** Function that computes enhanced corelation coefficient from Georgios et.al. 2008
* See https : //github.com/opencv/opencv/issues/12432
*/
double cv : : computeECC ( InputArray templateImage , InputArray inputImage , InputArray inputMask )
{
CV_Assert ( ! templateImage . empty ( ) ) ;
CV_Assert ( ! inputImage . empty ( ) ) ;
if ( ! ( templateImage . type ( ) = = inputImage . type ( ) ) )
CV_Error ( Error : : StsUnmatchedFormats , " Both input images must have the same data type " ) ;
Scalar meanTemplate , sdTemplate ;
int active_pixels = inputMask . empty ( ) ? templateImage . size ( ) . area ( ) : countNonZero ( inputMask ) ;
meanStdDev ( templateImage , meanTemplate , sdTemplate , inputMask ) ;
Mat templateImage_zeromean = Mat : : zeros ( templateImage . size ( ) , templateImage . type ( ) ) ;
subtract ( templateImage , meanTemplate , templateImage_zeromean , inputMask ) ;
double templateImagenorm = std : : sqrt ( active_pixels * sdTemplate . val [ 0 ] * sdTemplate . val [ 0 ] ) ;
Scalar meanInput , sdInput ;
Mat inputImage_zeromean = Mat : : zeros ( inputImage . size ( ) , inputImage . type ( ) ) ;
meanStdDev ( inputImage , meanInput , sdInput , inputMask ) ;
subtract ( inputImage , meanInput , inputImage_zeromean , inputMask ) ;
double inputImagenorm = std : : sqrt ( active_pixels * sdInput . val [ 0 ] * sdInput . val [ 0 ] ) ;
return templateImage_zeromean . dot ( inputImage_zeromean ) / ( templateImagenorm * inputImagenorm ) ;
}
double cv : : findTransformECC ( InputArray templateImage ,
InputArray inputImage ,
InputOutputArray warpMatrix ,
int motionType ,
TermCriteria criteria ,
InputArray inputMask ,
int gaussFiltSize )
{
Mat src = templateImage . getMat ( ) ; //template image
Mat dst = inputImage . getMat ( ) ; //input image (to be warped)
Mat map = warpMatrix . getMat ( ) ; //warp (transformation)
CV_Assert ( ! src . empty ( ) ) ;
CV_Assert ( ! dst . empty ( ) ) ;
// If the user passed an un-initialized warpMatrix, initialize to identity
if ( map . empty ( ) ) {
int rowCount = 2 ;
if ( motionType = = MOTION_HOMOGRAPHY )
rowCount = 3 ;
warpMatrix . create ( rowCount , 3 , CV_32FC1 ) ;
map = warpMatrix . getMat ( ) ;
map = Mat : : eye ( rowCount , 3 , CV_32F ) ;
}
if ( ! ( src . type ( ) = = dst . type ( ) ) )
CV_Error ( Error : : StsUnmatchedFormats , " Both input images must have the same data type " ) ;
//accept only 1-channel images
if ( src . type ( ) ! = CV_8UC1 & & src . type ( ) ! = CV_32FC1 )
CV_Error ( Error : : StsUnsupportedFormat , " Images must have 8uC1 or 32fC1 type " ) ;
if ( map . type ( ) ! = CV_32FC1 )
CV_Error ( Error : : StsUnsupportedFormat , " warpMatrix must be single-channel floating-point matrix " ) ;
CV_Assert ( map . cols = = 3 ) ;
CV_Assert ( map . rows = = 2 | | map . rows = = 3 ) ;
CV_Assert ( motionType = = MOTION_AFFINE | | motionType = = MOTION_HOMOGRAPHY | |
motionType = = MOTION_EUCLIDEAN | | motionType = = MOTION_TRANSLATION ) ;
if ( motionType = = MOTION_HOMOGRAPHY ) {
CV_Assert ( map . rows = = 3 ) ;
}
CV_Assert ( criteria . type & TermCriteria : : COUNT | | criteria . type & TermCriteria : : EPS ) ;
const int numberOfIterations = ( criteria . type & TermCriteria : : COUNT ) ? criteria . maxCount : 200 ;
const double termination_eps = ( criteria . type & TermCriteria : : EPS ) ? criteria . epsilon : - 1 ;
int paramTemp = 6 ; //default: affine
switch ( motionType ) {
case MOTION_TRANSLATION :
paramTemp = 2 ;
break ;
case MOTION_EUCLIDEAN :
paramTemp = 3 ;
break ;
case MOTION_HOMOGRAPHY :
paramTemp = 8 ;
break ;
}
const int numberOfParameters = paramTemp ;
const int ws = src . cols ;
const int hs = src . rows ;
const int wd = dst . cols ;
const int hd = dst . rows ;
Mat Xcoord = Mat ( 1 , ws , CV_32F ) ;
Mat Ycoord = Mat ( hs , 1 , CV_32F ) ;
Mat Xgrid = Mat ( hs , ws , CV_32F ) ;
Mat Ygrid = Mat ( hs , ws , CV_32F ) ;
float * XcoPtr = Xcoord . ptr < float > ( 0 ) ;
float * YcoPtr = Ycoord . ptr < float > ( 0 ) ;
int j ;
for ( j = 0 ; j < ws ; j + + )
XcoPtr [ j ] = ( float ) j ;
for ( j = 0 ; j < hs ; j + + )
YcoPtr [ j ] = ( float ) j ;
repeat ( Xcoord , hs , 1 , Xgrid ) ;
repeat ( Ycoord , 1 , ws , Ygrid ) ;
Xcoord . release ( ) ;
Ycoord . release ( ) ;
Mat templateZM = Mat ( hs , ws , CV_32F ) ; // to store the (smoothed)zero-mean version of template
Mat templateFloat = Mat ( hs , ws , CV_32F ) ; // to store the (smoothed) template
Mat imageFloat = Mat ( hd , wd , CV_32F ) ; // to store the (smoothed) input image
Mat imageWarped = Mat ( hs , ws , CV_32F ) ; // to store the warped zero-mean input image
Mat imageMask = Mat ( hs , ws , CV_8U ) ; // to store the final mask
Mat inputMaskMat = inputMask . getMat ( ) ;
//to use it for mask warping
Mat preMask ;
if ( inputMask . empty ( ) )
preMask = Mat : : ones ( hd , wd , CV_8U ) ;
else
threshold ( inputMask , preMask , 0 , 1 , THRESH_BINARY ) ;
//gaussian filtering is optional
src . convertTo ( templateFloat , templateFloat . type ( ) ) ;
GaussianBlur ( templateFloat , templateFloat , Size ( gaussFiltSize , gaussFiltSize ) , 0 , 0 ) ;
Mat preMaskFloat ;
preMask . convertTo ( preMaskFloat , CV_32F ) ;
GaussianBlur ( preMaskFloat , preMaskFloat , Size ( gaussFiltSize , gaussFiltSize ) , 0 , 0 ) ;
// Change threshold.
preMaskFloat * = ( 0.5 / 0.95 ) ;
// Rounding conversion.
preMaskFloat . convertTo ( preMask , preMask . type ( ) ) ;
preMask . convertTo ( preMaskFloat , preMaskFloat . type ( ) ) ;
dst . convertTo ( imageFloat , imageFloat . type ( ) ) ;
GaussianBlur ( imageFloat , imageFloat , Size ( gaussFiltSize , gaussFiltSize ) , 0 , 0 ) ;
// needed matrices for gradients and warped gradients
Mat gradientX = Mat : : zeros ( hd , wd , CV_32FC1 ) ;
Mat gradientY = Mat : : zeros ( hd , wd , CV_32FC1 ) ;
Mat gradientXWarped = Mat ( hs , ws , CV_32FC1 ) ;
Mat gradientYWarped = Mat ( hs , ws , CV_32FC1 ) ;
// calculate first order image derivatives
Matx13f dx ( - 0.5f , 0.0f , 0.5f ) ;
filter2D ( imageFloat , gradientX , - 1 , dx ) ;
filter2D ( imageFloat , gradientY , - 1 , dx . t ( ) ) ;
gradientX = gradientX . mul ( preMaskFloat ) ;
gradientY = gradientY . mul ( preMaskFloat ) ;
// matrices needed for solving linear equation system for maximizing ECC
Mat jacobian = Mat ( hs , ws * numberOfParameters , CV_32F ) ;
Mat hessian = Mat ( numberOfParameters , numberOfParameters , CV_32F ) ;
Mat hessianInv = Mat ( numberOfParameters , numberOfParameters , CV_32F ) ;
Mat imageProjection = Mat ( numberOfParameters , 1 , CV_32F ) ;
Mat templateProjection = Mat ( numberOfParameters , 1 , CV_32F ) ;
Mat imageProjectionHessian = Mat ( numberOfParameters , 1 , CV_32F ) ;
Mat errorProjection = Mat ( numberOfParameters , 1 , CV_32F ) ;
Mat deltaP = Mat ( numberOfParameters , 1 , CV_32F ) ; //transformation parameter correction
Mat error = Mat ( hs , ws , CV_32F ) ; //error as 2D matrix
const int imageFlags = INTER_LINEAR + WARP_INVERSE_MAP ;
const int maskFlags = INTER_NEAREST + WARP_INVERSE_MAP ;
// iteratively update map_matrix
double rho = - 1 ;
double last_rho = - termination_eps ;
for ( int i = 1 ; ( i < = numberOfIterations ) & & ( fabs ( rho - last_rho ) > = termination_eps ) ; i + + )
{
// warp-back portion of the inputImage and gradients to the coordinate space of the templateImage
if ( motionType ! = MOTION_HOMOGRAPHY )
{
warpAffine ( imageFloat , imageWarped , map , imageWarped . size ( ) , imageFlags ) ;
warpAffine ( gradientX , gradientXWarped , map , gradientXWarped . size ( ) , imageFlags ) ;
warpAffine ( gradientY , gradientYWarped , map , gradientYWarped . size ( ) , imageFlags ) ;
warpAffine ( preMask , imageMask , map , imageMask . size ( ) , maskFlags ) ;
}
else
{
warpPerspective ( imageFloat , imageWarped , map , imageWarped . size ( ) , imageFlags ) ;
warpPerspective ( gradientX , gradientXWarped , map , gradientXWarped . size ( ) , imageFlags ) ;
warpPerspective ( gradientY , gradientYWarped , map , gradientYWarped . size ( ) , imageFlags ) ;
warpPerspective ( preMask , imageMask , map , imageMask . size ( ) , maskFlags ) ;
}
Scalar imgMean , imgStd , tmpMean , tmpStd ;
meanStdDev ( imageWarped , imgMean , imgStd , imageMask ) ;
meanStdDev ( templateFloat , tmpMean , tmpStd , imageMask ) ;
subtract ( imageWarped , imgMean , imageWarped , imageMask ) ; //zero-mean input
templateZM = Mat : : zeros ( templateZM . rows , templateZM . cols , templateZM . type ( ) ) ;
subtract ( templateFloat , tmpMean , templateZM , imageMask ) ; //zero-mean template
const double tmpNorm = std : : sqrt ( countNonZero ( imageMask ) * ( tmpStd . val [ 0 ] ) * ( tmpStd . val [ 0 ] ) ) ;
const double imgNorm = std : : sqrt ( countNonZero ( imageMask ) * ( imgStd . val [ 0 ] ) * ( imgStd . val [ 0 ] ) ) ;
// calculate jacobian of image wrt parameters
switch ( motionType ) {
case MOTION_AFFINE :
image_jacobian_affine_ECC ( gradientXWarped , gradientYWarped , Xgrid , Ygrid , jacobian ) ;
break ;
case MOTION_HOMOGRAPHY :
image_jacobian_homo_ECC ( gradientXWarped , gradientYWarped , Xgrid , Ygrid , map , jacobian ) ;
break ;
case MOTION_TRANSLATION :
image_jacobian_translation_ECC ( gradientXWarped , gradientYWarped , jacobian ) ;
break ;
case MOTION_EUCLIDEAN :
image_jacobian_euclidean_ECC ( gradientXWarped , gradientYWarped , Xgrid , Ygrid , map , jacobian ) ;
break ;
}
// calculate Hessian and its inverse
project_onto_jacobian_ECC ( jacobian , jacobian , hessian ) ;
hessianInv = hessian . inv ( ) ;
const double correlation = templateZM . dot ( imageWarped ) ;
// calculate enhanced correlation coefficient (ECC)->rho
last_rho = rho ;
rho = correlation / ( imgNorm * tmpNorm ) ;
if ( cvIsNaN ( rho ) ) {
CV_Error ( Error : : StsNoConv , " NaN encountered. " ) ;
}
// project images into jacobian
project_onto_jacobian_ECC ( jacobian , imageWarped , imageProjection ) ;
project_onto_jacobian_ECC ( jacobian , templateZM , templateProjection ) ;
// calculate the parameter lambda to account for illumination variation
imageProjectionHessian = hessianInv * imageProjection ;
const double lambda_n = ( imgNorm * imgNorm ) - imageProjection . dot ( imageProjectionHessian ) ;
const double lambda_d = correlation - templateProjection . dot ( imageProjectionHessian ) ;
if ( lambda_d < = 0.0 )
{
rho = - 1 ;
CV_Error ( Error : : StsNoConv , " The algorithm stopped before its convergence. The correlation is going to be minimized. Images may be uncorrelated or non-overlapped " ) ;
}
const double lambda = ( lambda_n / lambda_d ) ;
// estimate the update step delta_p
error = lambda * templateZM - imageWarped ;
project_onto_jacobian_ECC ( jacobian , error , errorProjection ) ;
deltaP = hessianInv * errorProjection ;
// update warping matrix
update_warping_matrix_ECC ( map , deltaP , motionType ) ;
}
// return final correlation coefficient
return rho ;
}
double cv : : findTransformECC ( InputArray templateImage , InputArray inputImage ,
InputOutputArray warpMatrix , int motionType ,
TermCriteria criteria ,
InputArray inputMask )
{
// Use default value of 5 for gaussFiltSize to maintain backward compatibility.
return findTransformECC ( templateImage , inputImage , warpMatrix , motionType , criteria , inputMask , 5 ) ;
}
/* End of file. */