27#ifndef imageXCorrFFT_hpp
28#define imageXCorrFFT_hpp
30#include "../mxlib.hpp"
31#include "../math/ft/fftT.hpp"
32#include "../math/ft/mftT.hpp"
33#include "../math/ft/ftUtils.hpp"
34#include "../math/ft/fftwEnvironment.hpp"
36#include "../math/fit/fitGaussian.hpp"
79template <
class _realImageT>
85 typedef typename _realImageT::Scalar
realT;
94 typedef realT fftOutT;
526 template <
class im0T,
class imT>
537template <
class realImageT>
543template <
class realImageT>
549template <
class realImageT>
555template <
class realImageT>
564 if( m_rows > 0 && m_cols > 0 )
566 return resize( m_rows, m_cols, os );
575template <
class realImageT>
581template <
class realImageT>
587template <
class realImageT>
593template <
class realImageT>
597 if( m_rows == nrows && m_cols == ncols && m_padFactor == padFactor )
612 m_padFactor = padFactor;
620 if( m_rows == 0 || m_cols == 0 )
622 m_ftIm0.resize( 0, 0 );
623 m_ftWork.resize( 0, 0 );
625 m_ftWorkIn.resize( 0, 0 );
629 m_ccIm.resize( 0, 0 );
630 m_ftWorkPadded.resize( 0, 0 );
631 m_mtWorkPadded.resize( 0, 0 );
632 m_ftWorkPaddedOut.resize( 0, 0 );
639 m_ftIm0.resize( m_rows, m_cols );
641 m_ftWork.resize( m_rows, m_cols );
643 m_ftWorkIn.resize( m_rows, m_cols );
647 m_ftIm0.resize( (
int)( 0.5 * m_rows ) + 1, m_cols );
649 m_ftWork.resize( (
int)( 0.5 * m_rows ) + 1, m_cols );
656 m_rowsPadded = ceil( m_rows * m_padFactor );
657 m_colsPadded = ceil( m_cols * m_padFactor );
659 m_padFactorR =
static_cast<realT>( m_rowsPadded ) /
static_cast<realT>( m_rows );
660 m_padFactorC =
static_cast<realT>( m_colsPadded ) /
static_cast<realT>( m_cols );
662 m_ccIm.resize( m_rowsPadded, m_colsPadded );
666 m_ftWorkPadded.resize( m_rowsPadded, m_colsPadded );
667 m_ftWorkPaddedOut.resize( m_rowsPadded, m_colsPadded );
671 m_ftWorkPadded.resize( (
int)( 0.5 * m_rowsPadded ) + 1, m_colsPadded );
673 if( m_peakMethod == xcorrPeakMethod::mftOversamp )
675 m_ftWorkPaddedOut.resize( m_rowsPadded, m_colsPadded );
679 m_ftWorkPaddedOut.resize( 0, 0 );
684 if( m_peakMethod == xcorrPeakMethod::mftOversamp )
686 m_mtWorkPadded.resize( m_rowsPadded, m_colsPadded );
690 m_mtWorkPadded.resize( 0, 0 );
698template <
class realImageT>
701 return resize( nrows, ncols, m_padFactor );
704template <
class realImageT>
708 m_haveRefMask =
true;
715template <
class realImageT>
721template <
class realImageT>
724 return m_haveRefMask;
727template <
class realImageT>
736template <
class realImageT>
742template <
class realImageT>
748template <
class realImageT>
752 m_haveRefWindow =
true;
759template <
class realImageT>
765template <
class realImageT>
768 return m_haveRefWindow;
771template <
class realImageT>
780template <
class realImageT>
786template <
class realImageT>
792template <
class realImageT>
795 if( no != m_normalize )
802template <
class realImageT>
808template <
class realImageT>
816 if( im.rows() != m_refMaskIm.rows() && im.cols() != m_refMaskIm.cols() )
825 realT m = imageMean<realT>( im, m_refMaskIm );
826 realT v = imageVariance<realT>( im, m, m_refMaskIm );
827 im0 = ( ( im - m ) * m_refMaskIm ) / sqrt( v );
831 im0 = im * m_refMaskIm;
839 realT m = imageMean<realT>( im );
840 realT v = imageVariance<realT>( im, m );
841 im0 = ( im - m ) / sqrt( v );
850 if( m_haveRefWindow )
852 if( im0.rows() != m_refWinIm.rows() && im.cols() != m_refWinIm.cols() )
868 resize( im0.rows(), im0.cols(), padFactor );
871 imageShiftWP( im0, m_refIm, 0.5 * m_rows + 1, 0.5 * m_cols + 1 );
876 m_ftWork = im0.template cast<complexT>();
878 m_fft_fwd( m_ftIm0.data(), m_ftWork.data() );
880 m_fft_fwd( m_ftIm0.data(), im0.data() );
884 for(
int c = 0; c < m_ftIm0.cols(); ++c )
886 for(
int r = 0; r < m_ftIm0.rows(); ++r )
888 complexT val = std::conj( m_ftIm0( r, c ) );
889 val /= ( m_rows * m_cols );
890 m_ftIm0( r, c ) = val;
900 operator()( m_refX0, m_refY0, m_refPeak, m_refIm );
907template <
class realImageT>
910 return refIm( im, m_padFactor );
913template <
class realImageT>
919template <
class realImageT>
925template <
class realImageT>
932template <
class realImageT>
938template <
class realImageT>
944template <
class realImageT>
949 m_magSize = ceil( ( ( 2. * m_maxLag + 1 ) - 1.0 ) / nt ) + 1;
951 m_mag = ( m_magSize - 1.0 ) / ( ( 2. * m_maxLag + 1 ) - 1.0 );
958template <
class realImageT>
961 if( xpm != m_peakMethod )
971template <
class realImageT>
977template <
class realImageT>
983template <
class realImageT>
989template <
class realImageT>
995template <
class realImageT>
1001template <
class realImageT>
1007template <
class realImageT>
1013template <
class realImageT>
1023 realT pk = m_ccIm.maxCoeff( &xLag0, &yLag0 );
1024 realT mn = m_ccIm.minCoeff();
1026 if( xLag0 - m_maxLag < 0 )
1030 if( xLag0 + 2 * m_maxLag + 1 >= m_ccIm.rows() )
1032 m_maxLag = ( m_ccIm.rows() - 1 - xLag0 ) / 2;
1034 if( yLag0 - m_maxLag < 0 )
1038 if( yLag0 + 2 * m_maxLag + 1 >= m_ccIm.cols() )
1040 m_maxLag = ( m_ccIm.cols() - 1 - yLag0 ) / 2;
1043 realT x0 = xLag0 - m_maxLag;
1044 realT y0 = yLag0 - m_maxLag;
1046 if( m_peakMethod == xcorrPeakMethod::gaussFit )
1048 m_magIm = m_ccIm.block( x0, y0, 2 * m_maxLag + 1, 2 * m_maxLag + 1 );
1050 m_fitter.setArray( m_magIm.data(), m_magIm.rows(), m_magIm.cols() );
1051 m_magIm.maxCoeff( &xLag0, &yLag0 );
1053 m_fitter.setGuess( mn, pk - mn, m_maxLag, m_maxLag, 3, 3, 0 );
1056 x0 = ( m_fitter.x0() + x0 );
1057 y0 = ( m_fitter.y0() + y0 );
1058 xcPeak = m_fitter.G();
1060 else if( m_peakMethod == xcorrPeakMethod::interpPeak )
1063 m_magIm.
resize( m_magSize, m_magSize );
1066 m_ccIm.block( x0, y0, 2 * m_maxLag + 1, 2 * m_maxLag + 1 ),
1069 xcPeak = m_magIm.maxCoeff( &x, &y );
1070 x0 = ( x * ( 1.0 / m_mag ) + x0 );
1071 y0 = ( y * ( 1.0 / m_mag ) + y0 );
1073 else if( m_peakMethod == xcorrPeakMethod::centerOfLight )
1077 m_magIm = m_ccIm.block( x0, y0, 2 * m_maxLag + 1, 2 * m_maxLag + 1 );
1078 m_magIm -= m_magIm.minCoeff();
1079 xcPeak = m_magIm.maxCoeff();
1085 else if( m_peakMethod == xcorrPeakMethod::mftOversamp )
1088 m_ccIm.maxCoeff( &x, &y );
1090 if( m_refX0 == 0 && m_refY0 == 0 )
1097 x = x / m_padFactorR - m_refX0;
1098 y = y / m_padFactorC - m_refY0;
1100 realT mag = 1.0 / m_tol;
1102 realT tmagR = mag * m_padFactorR;
1103 realT tmagC = mag * m_padFactorC;
1105 realT shiftx0 = -1 * ( m_refX0 * m_padFactorR ) * ( mag - 1 ) - x * tmagR;
1106 realT shifty0 = -1 * ( m_refY0 * m_padFactorC ) * ( mag - 1 ) - y * tmagC;
1112 m_mft_bwd( m_ftWorkPaddedOut, m_ftWorkPadded );
1116 m_mtWorkPadded.resize( m_rowsPadded, m_colsPadded );
1117 m_mtWorkPadded.setZero();
1120 m_mft_bwd( m_ftWorkPaddedOut, m_mtWorkPadded );
1124 m_magIm = m_ftWorkPaddedOut.real();
1127 xcPeak = m_magIm.maxCoeff( &xf, &yf );
1129 xShift = x + ( xf - m_refX0 * m_padFactorR ) / tmagR;
1130 yShift = y + ( yf - m_refY0 * m_padFactorC ) / tmagC;
1135 else if( m_peakMethod == xcorrPeakMethod::none )
1138 xcPeak = m_ccIm.maxCoeff( &x, &y );
1148 xShift = x0 / m_padFactorR - m_refX0;
1149 yShift = y0 / m_padFactorC - m_refY0;
1152template <
class realImageT>
1161 if( im.rows() != m_rows )
1166 if( im.cols() != m_cols )
1176 realT m = imageMean<realT>( im, m_maskIm );
1177 realT v = imageVariance<realT>( im, m, m_maskIm );
1179 m_normIm = ( im - m ) * m_maskIm / sqrt( v );
1183 m_normIm = im * m_maskIm;
1191 realT m = imageMean<realT>( im );
1192 realT v = imageVariance<realT>( im, m );
1194 m_normIm = ( im - m ) / sqrt( v );
1204 m_normIm *= m_winIm;
1208 m_ftWorkIn = m_normIm.template cast<complexT>();
1210 m_fft_fwd( m_ftWork.data(), m_ftWorkIn.data() );
1212 m_fft_fwd( m_ftWork.data(), m_normIm.data() );
1217 m_ftWork *= m_ftIm0;
1219 m_ftWorkPadded.setZero();
1225 m_fft_bwd( m_ftWorkPaddedOut.data(), m_ftWorkPadded.data() );
1227 m_ccIm = m_ftWorkPaddedOut.real();
1233 m_fft_bwd( m_ccIm, m_ftWorkPadded );
1237 findPeak( xShift, yShift, xcPeak );
1242template <
class realImageT>
1243template <
class im0T,
class imT>
1246 setReference( im0 );
1247 return operator()( xShift, yShift, xcPeak, im );
Augments an exception with the source file and line.
Find the optimum shift to align two images using the FFT cross correlation.
realT padFactor()
Get the padding factor.
int m_cols
The number of columns in the images.
realImageT m_refACIm
The auto-correlation image of the reference.
int m_rowsPadded
The number of rows in the padded CC.
int colsPadded()
Get the number of rows in the padded cross-correlation.
eigenImage< complexT > complexArrayT
Complex eigen array type with Scalar==complexT.
int m_maxLag
The maximum lag to consider in the initial cross-correlation. Default is 5.
void findPeak(realT &xShift, realT &yShift, realT &xcPeak)
complexArrayT m_mtWorkPadded
Working memory for the MFT for xcorrPeakMethod::mftOversamp.
bool haveMask()
Get the value of the haveMask flag.
bool haveWindow()
Get the value of the haveWindow flag.
int m_rows
The number of rows in the images.
math::ft::fftT< fftOutT, fftInT, 2 > m_fft_fwd
FFT object for the forward transform.
const realImageT & maskIm()
Get a reference to the mask image.
const realImageT & refIm()
Get a reference to the reference image.
realImageT m_refIm
The normalized reference image.
bool haveRefMask()
Get the value of the haveRefMask flag.
int resize(int nrows, int ncols, realT padFactor)
Set the size of the cross-correlation problem based on input image size.
int maxLag()
Get the current maximum lag.
realImageT m_magIm
The magnified image, used if m_peakMethod == xcorrPeakMethod::interpPeak.
std::complex< realT > complexT
Complex floating point type.
realT m_padFactor
The padding factor for the CC.
int operator()(realT &xShift, realT &yShift, realT &xcPeak, const imT &im)
Conduct the cross correlation to a specified tolerance.
bool m_haveMask
Flag indicating that a mask has been provided.
complexArrayT m_ftWorkPadded
Working memory for the padded inverse FFT input.
complexArrayT m_ftIm0
Working memory for the FT of the reference image.
complexArrayT m_ftWork
Working memory for the FFT.
const realImageT & refWinIm()
Get a reference to the reference window image.
bool haveRefWindow()
Get the value of the haveRefWindow flag.
math::ft::mftT< complexT, complexT, 2 > m_mft_bwd
const realImageT & refACIm()
Get a reference to the reference auto-correlation image.
const realImageT & ccIm()
Get a reference to the cross correlation image.
complexArrayT m_ftWorkPaddedOut
Working memory for the FFT and MFT output.
int m_colsPadded
The number of columns in the padded CC.
int rowsPadded()
Get the number of rows in the padded cross-correlation.
realT refX0()
Get the x-shift of the reference image to itself.
imageXCorrFFT()
Default c'tor.
bool refValid()
Get the value of the reference valid flag.
math::ft::fftT< fftInT, fftOutT, 2 > m_fft_bwd
FFT object for the backward transform.
bool normalize()
Get the current value of the normalize flag.
realImageT m_refWinIm
Window image to use for the reference.
realT tol()
Get the tolerance of the interpolated-magnified image, in pixels.
const realImageT & refMaskIm()
Get a reference to the reference mask image.
bool m_haveRefWindow
Flag indicating that a window has been provided for the reference.
const realImageT & magIm()
Get a reference to the magnified image.
realT refY0()
Get the y-shift of the reference image to itself.
_realImageT::Scalar realT
the scalar type of the image type
bool m_haveRefMask
Flag indicating that a referece mask has been provided.
realImageT m_ccIm
The cross-correlation image.
realImageT m_winIm
Window image for the target image.
realImageT m_normIm
The normalized image.
int rows()
Get the number of rows in the input images.
bool m_haveWindow
Flag indicating that a window has been provided.
const realImageT & winIm()
Get a reference to the window image.
const realImageT & normIm()
Get a reference to the normalized image.
xcorrPeakMethod m_peakMethod
The peak finding method to use.
int cols()
Get the number of columns in the input images.
_realImageT realImageT
the Eigen-like array type used for image processing
Class to manage fitting a 2D Gaussian to data via the levmarInterface.
Tools for using the eigen library for image processing.
int imageCenterOfLight(typename imageT::Scalar &x, typename imageT::Scalar &y, const imageT &im)
Calculate the center of light of an image.
Eigen::Array< scalarT, -1, -1 > eigenImage
Definition of the eigenImage type, which is an alias for Eigen::Array.
@ sizeerr
A size was invalid or calculated incorrectly.
@ invalidconfig
A config setting was invalid.
@ invalidarg
An argument was invalid.
error_t mxlib_error_report(const error_t &code, const std::string &expl, const std::source_location &loc=std::source_location::current())
Print a report to stderr given an mxlib error_t code and explanation and return the code.
void augmentR2CFFTOutput(eigenArrOutT &aout, eigenArrInT &ain)
void padR2CFFTOutput(eigenArrOutT &aout, eigenArrInT &ain)
Add padding to the output of a real-to-complex (R2C) FFT.
void padC2CFFTOutput(eigenArrOutT &aout, eigenArrInT &ain)
Add padding to the output of a complex-to-complex (C2C) FFT.
@ backward
Specifies the backward transform.
@ forward
Specifies the forward transform.
xcorrPeakMethod
Methods for finding the cross-correlation peak.
Header for the image processing utilities.
CRTP base class to register images.