LCOV - code coverage report
Current view: top level - improc - imageXCorrFFT.hpp (source / functions) Coverage Total Hit
Test: mxlib Lines: 65.4 % 188 123
Test Date: 2026-02-19 16:58:26 Functions: 100.0 % 9 9

            Line data    Source code
       1              : /** \file imageXCorrFFT.hpp
       2              :  * \brief A class to register images using the Fourier cross correlation with a peak fit.
       3              :  * \ingroup image_processing_files
       4              :  * \author Jared R. Males (jaredmales@gmail.com)
       5              :  *
       6              :  */
       7              : 
       8              : //***********************************************************************//
       9              : // Copyright 2020 Jared R. Males (jaredmales@gmail.com)
      10              : //
      11              : // This file is part of mxlib.
      12              : //
      13              : // mxlib is free software: you can redistribute it and/or modify
      14              : // it under the terms of the GNU General Public License as published by
      15              : // the Free Software Foundation, either version 3 of the License, or
      16              : // (at your option) any later version.
      17              : //
      18              : // mxlib is distributed in the hope that it will be useful,
      19              : // but WITHOUT ANY WARRANTY; without even the implied warranty of
      20              : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      21              : // GNU General Public License for more details.
      22              : //
      23              : // You should have received a copy of the GNU General Public License
      24              : // along with mxlib.  If not, see <http://www.gnu.org/licenses/>.
      25              : //***********************************************************************//
      26              : 
      27              : #ifndef imageXCorrFFT_hpp
      28              : #define imageXCorrFFT_hpp
      29              : 
      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"
      35              : 
      36              : #include "../math/fit/fitGaussian.hpp"
      37              : 
      38              : #include "eigenImage.hpp"
      39              : #include "imageUtils.hpp"
      40              : #include "imageTransforms.hpp"
      41              : #include "imageXCorr.hpp"
      42              : 
      43              : namespace mx
      44              : {
      45              : namespace improc
      46              : {
      47              : 
      48              : /// Find the optimum shift to align two images using the FFT cross correlation.
      49              : /** The reference image must be the same size as the target image.  Both the reference image and the
      50              :  * target image being analyzed can be masked, normalized by mean subtraction and variance division,
      51              :  * and windowed.  Seperate masks and windows for the reference and target image are used.
      52              :  *
      53              :  * The reference mask, reference window, and normalization flag must be set before setting the reference
      54              :  * (unless using the defaults).  If the reference mask, reference window, or normalization flag are changed
      55              :  * then the reference must be set again. After configuring the reference, then operator() can be called
      56              :  * repeatedly to determine the shifts for a sequence of imaages.  The mask and window can be changed between
      57              :  * calls without requiring resetting the reference. No new heap allocations take place on these calls
      58              :  * to operator(), and the reference image is not re-normalized on each call.
      59              :  *
      60              :  * The shift is reported in pixels such that if the mxlib imageShift function is used
      61              :  * to shift the input image by the negative of the shifts, it will align with the
      62              :  * reference at the center of the array.
      63              :  *
      64              :  * Several peak finding methods are provided:
      65              :  *  - xcorrPeakMethod::centerOfLight uses center of light
      66              :  *  - xcorrPeakMethod::gaussFit uses Gaussian centroiding
      67              :  *  - xcorrPeakMethod::interpPeak uses interpolation to find the peak to a given tolerance.
      68              :  *  - xcorrPeakMethod::none will just return the coordinate of the max pixel
      69              :  *
      70              :  * This class uses the real-to-complex transforms of FFTW by default.  This should offer
      71              :  * improved speed and memory use.  For testing the complex-to-complex transforms can be
      72              :  * used by defining `XCFFT_C2C` before including this file.
      73              :  *
      74              :  * \tparam _realImageT is the Eigen-like array type used for image processing.  Must have
      75              :  *                     a real floating point Scalar type (float, double, etc.). See typedefs.
      76              :  *
      77              :  * \ingroup image_reg
      78              :  */
      79              : template <class _realImageT>
      80              : class imageXCorrFFT
      81              : {
      82              :   public:
      83              :     typedef _realImageT realImageT;             ///< the Eigen-like array type used for image processing
      84              : 
      85              :     typedef typename _realImageT::Scalar realT; ///< the scalar type of the image type
      86              : 
      87              :     typedef std::complex<realT> complexT;       ///< Complex floating point type.
      88              : 
      89              : // defaut is the real-to-complex.  Only use C2C if defined:
      90              : #ifdef XCFFT_C2C
      91              :     typedef complexT fftOutT;
      92              :     typedef complexT fftInT;
      93              : #else
      94              :     typedef realT fftOutT;
      95              :     typedef complexT fftInT;
      96              : #endif
      97              : 
      98              :     typedef eigenImage<complexT> complexArrayT; ///< Complex eigen array type with Scalar==complexT
      99              : 
     100              :   protected:
     101              :     /** \name Configuration Member Data
     102              :      * @{
     103              :      */
     104              :     int m_rows{ 0 };               ///< The number of rows in the images
     105              : 
     106              :     int m_cols{ 0 };               ///< The number of columns in the images
     107              : 
     108              :     realT m_padFactor{ 1 };        ///< The padding factor for the CC
     109              : 
     110              :     realT m_padFactorR{ 1 };       /**< The actual padding factor for rows of the CC
     111              :                                         set based on the resultant array sizes.*/
     112              : 
     113              :     realT m_padFactorC{ 1 };       /**< The actual padding factor for cols of the CC
     114              :                                         set based on the resultant array sizes.*/
     115              : 
     116              :     int m_rowsPadded{ 0 };         ///< The number of rows in the padded CC
     117              : 
     118              :     int m_colsPadded{ 0 };         ///< The number of columns in the padded CC
     119              : 
     120              :     realImageT m_refMaskIm;        /**< Mask image to use for the reference, may be needed for proper normalization
     121              :                                    even if refIm has 0 mask applied.*/
     122              : 
     123              :     bool m_haveRefMask{ false };   ///< Flag indicating that a referece mask has been provided.
     124              : 
     125              :     realImageT m_maskIm;           /**< Mask image to use, may be needed for proper normalization even if
     126              :                                    refIm has 0 mask applied.*/
     127              : 
     128              :     bool m_haveMask{ false };      ///< Flag indicating that a mask has been provided.
     129              : 
     130              :     realImageT m_refWinIm;         ///< Window image to use for the reference
     131              : 
     132              :     bool m_haveRefWindow{ false }; ///< Flag indicating that a window has been provided for the reference
     133              : 
     134              :     realImageT m_winIm;            ///< Window image for the target image.
     135              : 
     136              :     bool m_haveWindow{ false };    ///< Flag indicating that a window has been provided.
     137              : 
     138              :     bool m_normalize{ true };      /**< Flag specifying whether images should be normalized prior to performing
     139              :                                         the cross-correlation*/
     140              : 
     141              :     realImageT m_refIm;            ///< The normalized reference image.
     142              : 
     143              :     bool m_refValid{ false };      /**< Flag indicating whether the reference is valid.  It can be invalidated
     144              :                                         if a mask, window, or normalize is set after the reference is set.*/
     145              : 
     146              :     int m_maxLag{ 5 };             ///< The maximum lag to consider in the initial cross-correlation.  Default is 5.
     147              : 
     148              :     realT m_tol{ 0.1 };            /**< For xcorrPeakMethod::interpPeak, the tolerance of the
     149              :                                         interpolated-magnified image, in pixels.*/
     150              : 
     151              :     realT m_magSize{ 0 };          /**< Magnified size of the ccIm when using interp.
     152              :                                         Set as function of m_tol and m_maxLag.*/
     153              : 
     154              :     realT m_mag{ 1 };              /**< The magnification, in principle is 1/m_tol but will vary
     155              :                                         slightly based on integer array sizes*/
     156              : 
     157              :     /// The peak finding method to use
     158              :     xcorrPeakMethod m_peakMethod{ xcorrPeakMethod::interpPeak };
     159              : 
     160              :     ///@}
     161              : 
     162              :     /** \name Working Memory Member Data
     163              :      * @{
     164              :      */
     165              : 
     166              :     realImageT m_refACIm;  ///< The auto-correlation image of the reference.
     167              : 
     168              :     realT m_refX0{ 0 };    /**< The x-shift of the reference image to itself using the selected
     169              :                                 algorithm, used as coordinate origin*/
     170              : 
     171              :     realT m_refY0{ 0 };    /**< The x-shift of the reference image to itself using the selected
     172              :                                 algorithm, used as coordinate origin*/
     173              : 
     174              :     realT m_refPeak{ 0 };  /**< the x-corr peak of the reference shift to itself */
     175              : 
     176              :     realImageT m_normIm;   ///< The normalized image.
     177              : 
     178              :     realImageT m_ccIm;     ///< The cross-correlation image
     179              : 
     180              :     realImageT m_magIm;    ///< The magnified image, used if m_peakMethod == xcorrPeakMethod::interpPeak
     181              : 
     182              :     complexArrayT m_ftIm0; ///< Working memory for the FT of the reference image.
     183              : 
     184              :   public:
     185              :     complexArrayT m_ftWork; ///< Working memory for the FFT.
     186              : 
     187              : #ifdef XCFFT_C2C
     188              :     complexArrayT m_ftWorkIn; ///< Working memory for the FFT input.
     189              : #endif
     190              : 
     191              :     complexArrayT m_ftWorkPadded;                    ///< Working memory for the padded inverse FFT input.
     192              : 
     193              :     complexArrayT m_ftWorkPaddedOut;                 ///< Working memory for the FFT and MFT output.
     194              : 
     195              :     complexArrayT m_mtWorkPadded;                    ///< Working memory for the MFT for xcorrPeakMethod::mftOversamp
     196              : 
     197              :     math::ft::fftT<fftOutT, fftInT, 2> m_fft_fwd;    ///< FFT object for the forward transform.
     198              : 
     199              :     math::ft::fftT<fftInT, fftOutT, 2> m_fft_bwd;    ///< FFT object for the backward transform.
     200              : 
     201              :     math::ft::mftT<complexT, complexT, 2> m_mft_bwd; /**< MFT object used to oversample the
     202              :                                                        peak for xcorrPeakMethod::mftOversamp*/
     203              : 
     204              :     ///@}
     205              : 
     206              :     /** \name Peak Finding Tools
     207              :      * @{
     208              :      */
     209              :     math::fit::fitGaussian2D<mx::math::fit::gaussian2D_gen_fitter<realT>> m_fitter;
     210              : 
     211              :     ///@}
     212              : 
     213              :   public:
     214              :     /** \name Construction and Destruction
     215              :      * @{
     216              :      */
     217              : 
     218              :     /// Default c'tor
     219              :     imageXCorrFFT();
     220              : 
     221              :     ///@}
     222              : 
     223              :     /** \name Configuration Member Access
     224              :      * @{
     225              :      */
     226              : 
     227              :     /// Get the number of rows in the input images
     228              :     /** This can only be set by a call to resize() or refIm().
     229              :      *
     230              :      * \returns the current value of m_rows
     231              :      */
     232              :     int rows();
     233              : 
     234              :     /// Get the number of columns in the input images
     235              :     /** This can only be set by a call to resize() or refIm().
     236              :      *
     237              :      * \returns the current value of m_cols
     238              :      */
     239              :     int cols();
     240              : 
     241              :     /// Set the padding factor
     242              :     /** This can also be set by a call to resize() or refIm().
     243              :      *  The padding factor must be greater than or equal to 1.
     244              :      *
     245              :      *  If m_rows and m_cols are both greater than 0 this calls resize(int, int, realT).
     246              :      *
     247              :      */
     248              :     int padFactor( realT os /**< [in] the new padding factor*/ );
     249              : 
     250              :     /// Get the padding factor
     251              :     /**
     252              :      * \returns the current value of m_padFactor
     253              :      */
     254              :     realT padFactor();
     255              : 
     256              :     /// Get the number of rows in the padded cross-correlation
     257              :     /** This can only be set by a call to resize() or refIm().
     258              :      *
     259              :      * \returns the current value of m_rowsPadded
     260              :      */
     261              :     int rowsPadded();
     262              : 
     263              :     /// Get the number of rows in the padded cross-correlation
     264              :     /** This can only be set by a call to resize() or refIm().
     265              :      *
     266              :      * \returns the current value of m_colsPadded
     267              :      */
     268              :     int colsPadded();
     269              : 
     270              :     /// Set the size of the cross-correlation problem based on input image size.
     271              :     /** This resizes all working memory and conducts fftw planning.
     272              :      *  If there are no changes to size or padding, this returns immediately
     273              :      *  making no changes.
     274              :      *
     275              :      *  If there are changes to size or padding the reference is invalidated.
     276              :      *
     277              :      *  If nrows or ncols is 0, this resizes all working memory to 0.
     278              :      *
     279              :      * \returns 0 on success
     280              :      * \returns -1 on error
     281              :      */
     282              :     int resize( int nrows, /**< [in] the number of rows in the images to register */
     283              :                 int ncols, /**< [in] the number of columns in the images to register */
     284              :                 realT padFactor /**< [in] the padding factor.  Must be `>=` 1. */ );
     285              : 
     286              :     /// Set the size of the cross-correlation problem based on input image size.
     287              :     /** Calls resize(int, int, realT) with the current value of m_padFactor.
     288              :      *
     289              :      * \overload
     290              :      *
     291              :      * \returns 0 on success
     292              :      * \returns -1 on error
     293              :      */
     294              :     int resize( int nrows, /**< [in] the number of rows in the images to register */
     295              :                 int ncols /**< [in] the number of columns in the images to register */ );
     296              : 
     297              :     /// Set the reference mask image
     298              :     /** This invalidates the reference
     299              :      * \returns 0 on success
     300              :      * \returns -1 on error
     301              :      */
     302              :     int refMaskIm( const realImageT &mask /**< [in] the new reference mask image */ );
     303              : 
     304              :     /// Get a reference to the reference mask image.
     305              :     /**
     306              :      * \returns a const reference to the reference mask image.
     307              :      */
     308              :     const realImageT &refMaskIm();
     309              : 
     310              :     /// Get the value of the haveRefMask flag
     311              :     /**
     312              :      * \returns the current value of m_haveRefMask
     313              :      */
     314              :     bool haveRefMask();
     315              : 
     316              :     /// Set the mask image
     317              :     /** This does not invalidate the reference
     318              :      *
     319              :      * \returns 0 on success
     320              :      * \returns -1 on error
     321              :      */
     322              :     int maskIm( const realImageT &mask /**< [in] the new mask image */ );
     323              : 
     324              :     /// Get a reference to the mask image.
     325              :     /**
     326              :      * \returns a const referance to the mask image.
     327              :      */
     328              :     const realImageT &maskIm();
     329              : 
     330              :     /// Get the value of the haveMask flag
     331              :     /**
     332              :      * \returns the current value of m_haveMask
     333              :      */
     334              :     bool haveMask();
     335              : 
     336              :     /// Set the reference window image
     337              :     /** This invalidates the reference.
     338              :      *
     339              :      * \returns 0 on success
     340              :      * \returns -1 on error
     341              :      */
     342              :     int refWinIm( const realImageT &win /**< [in] the new reference window image */ );
     343              : 
     344              :     /// Get a reference to the reference window image.
     345              :     /**
     346              :      * \returns a const referance to the reference window image.
     347              :      */
     348              :     const realImageT &refWinIm();
     349              : 
     350              :     /// Get the value of the haveRefWindow flag
     351              :     /**
     352              :      * \returns the current value of m_haveRefWindow
     353              :      */
     354              :     bool haveRefWindow();
     355              : 
     356              :     /// Set the window image
     357              :     /** This does not invalidate the reference.
     358              :      *
     359              :      * \returns 0 on success
     360              :      * \returns -1 on error
     361              :      */
     362              :     int winIm( const realImageT &win /**< [in] the new window image */ );
     363              : 
     364              :     /// Get a reference to the window image.
     365              :     /**
     366              :      * \returns a const referance to the window image.
     367              :      */
     368              :     const realImageT &winIm();
     369              : 
     370              :     /// Get the value of the haveWindow flag
     371              :     /**
     372              :      * \returns the current value of m_haveWindow
     373              :      */
     374              :     bool haveWindow();
     375              : 
     376              :     /// Set the normalize flag
     377              :     /** Changing this invalidates the reference.
     378              :      */
     379              :     void normalize( bool no /**< [in] the new normalize flag value */ );
     380              : 
     381              :     /// Get the current value of the normalize flag
     382              :     /**
     383              :      * \returns the current value of m_normalize
     384              :      */
     385              :     bool normalize();
     386              : 
     387              :     /// Set the reference image
     388              :     /** Performs the following steps
     389              :      *    - Applies the mask if supplied.
     390              :      *    - If desired, normalizes the reference image by mean subtraction and variance division.
     391              :      *    - Applies the window if supplied.
     392              :      *    - Stores the result as m_refIm
     393              :      *    - Resizes working memory with a call to resize()
     394              :      *    - Shifts to FFT order and Fourier transforms and conjugates the result.
     395              :      *    - Calls operator() to measure the reference shifts
     396              :      *    - Stores the resultant auto-correlation in m_refACIm
     397              :      *
     398              :      * \returns 0 on success
     399              :      * \returns -1 on error
     400              :      */
     401              :     int refIm( const realImageT &im0, /**< [in] The new reference image */
     402              :                realT padFactor /**< [in] [optional] the padding factor to use */ );
     403              : 
     404              :     /// Set the reference image
     405              :     /** Calls refIm(const realImageT &, realT) with the current value of m_padFactor.
     406              :      *
     407              :      *  \overload
     408              :      *
     409              :      * \returns 0 on success
     410              :      * \returns -1 on error
     411              :      */
     412              :     int refIm( const realImageT &im0 /**< [in] The new reference image */ );
     413              : 
     414              :     /// Get a reference to the reference image.
     415              :     /**
     416              :      * \returns a const referent to m_refIm.
     417              :      */
     418              :     const realImageT &refIm();
     419              : 
     420              :     /// Get the value of the reference valid flag
     421              :     /**
     422              :      * \returns the value of m_refValid
     423              :      */
     424              :     bool refValid();
     425              : 
     426              :     /// Set the maximum lag
     427              :     void maxLag( int ml /**< [in] the new maximum lag */ );
     428              : 
     429              :     /// Get the current maximum lag
     430              :     /**
     431              :      * \returns the current value of m_maxLag
     432              :      */
     433              :     int maxLag();
     434              : 
     435              :     /// Set the tolerance of the interpolated-magnified image, in pixels.
     436              :     void tol( realT nt /**< [in] The new value of the interpolation tolerance. */ );
     437              : 
     438              :     /// Get the tolerance of the interpolated-magnified image, in pixels.
     439              :     /**
     440              :      * \returns the current value of m_tol.
     441              :      */
     442              :     realT tol();
     443              : 
     444              :     void peakMethod( xcorrPeakMethod xpm );
     445              : 
     446              :     xcorrPeakMethod peakMethod();
     447              : 
     448              :     ///@}
     449              : 
     450              :     /** \name Working Memory Member Access
     451              :      * @{
     452              :      */
     453              : 
     454              :     /// Get a reference to the reference auto-correlation image.
     455              :     /**
     456              :      * \returns a const referent to m_refACIm.
     457              :      */
     458              :     const realImageT &refACIm();
     459              : 
     460              :     /// Get the x-shift of the reference image to itself.
     461              :     /**
     462              :      * \returns m_refX0
     463              :      */
     464              :     realT refX0();
     465              : 
     466              :     /// Get the y-shift of the reference image to itself.
     467              :     /**
     468              :      * \returns m_refY0
     469              :      */
     470              :     realT refY0();
     471              : 
     472              :     /// Get a reference to the normalized image.
     473              :     /**
     474              :      * \returns a const referent to m_normIm.
     475              :      */
     476              :     const realImageT &normIm();
     477              : 
     478              :     /// Get a reference to the cross correlation image.
     479              :     /**
     480              :      * \returns a const referent to m_ccIm.
     481              :      */
     482              :     const realImageT &ccIm();
     483              : 
     484              :     /// Get a reference to the magnified image.
     485              :     /**
     486              :      * \returns a const referent to m_magIm.
     487              :      */
     488              :     const realImageT &magIm();
     489              : 
     490              :     ///@}
     491              : 
     492              :     /** \name Peak Finding
     493              :      * @{
     494              :      */
     495              :   protected:
     496              :     void findPeak( realT &xShift, /**< [out] the x shift of im w.r.t. im0, in pixels */
     497              :                    realT &yShift, /**< [out] the y shift of im w.r.t. im0, in pixels */
     498              :                    realT &xcPeak  /**< [out] the x-corr peak */
     499              :     );
     500              : 
     501              :     ///@}
     502              : 
     503              :     /** \name Cross Correlation Operator
     504              :      * @{
     505              :      */
     506              : 
     507              :   public:
     508              :     /// Conduct the cross correlation to a specified tolerance
     509              :     /**
     510              :      * \returns 0 on success
     511              :      * \returns -1 on error
     512              :      */
     513              :     template <class imT>
     514              :     int operator()( realT &xShift, ///< [out] the x shift of im w.r.t. im0, in pixels
     515              :                     realT &yShift, ///< [out] the y shift of im w.r.t. im0, in pixels
     516              :                     realT &xcPeak, ///< [out] the xc peak
     517              :                     const imT &im /**< [in] the image to cross-correlate with the reference*/ );
     518              : 
     519              :     /// Conduct the cross correlation to a specified tolerance
     520              :     /**
     521              :      * \overload
     522              :      *
     523              :      * \returns 0 on success
     524              :      * \returns -1 on error
     525              :      */
     526              :     template <class im0T, class imT>
     527              :     int operator()( realT &xShift, ///< [out] the x shift of im w.r.t. im0, in pixels
     528              :                     realT &yShift, ///< [out] the y shift of im w.r.t. im0, in pixels
     529              :                     realT &xcPeak, ///< [out] the xc peak
     530              :                     im0T &im0,     ///< [in] a new reference image
     531              :                     imT &im        ///< [in] the image to cross-correlate with the reference
     532              :     );
     533              : 
     534              :     ///@}
     535              : };
     536              : 
     537              : template <class realImageT>
     538           12 : imageXCorrFFT<realImageT>::imageXCorrFFT()
     539              : {
     540           12 :     maxLag( m_maxLag ); // this sets up m_magSize.
     541           12 : }
     542              : 
     543              : template <class realImageT>
     544              : int imageXCorrFFT<realImageT>::rows()
     545              : {
     546              :     return m_rows;
     547              : }
     548              : 
     549              : template <class realImageT>
     550              : int imageXCorrFFT<realImageT>::cols()
     551              : {
     552              :     return m_cols;
     553              : }
     554              : 
     555              : template <class realImageT>
     556              : int imageXCorrFFT<realImageT>::padFactor( realT os )
     557              : {
     558              :     if( os < 1 )
     559              :     {
     560              :         internal::mxlib_error_report( error_t::invalidarg, "padding factor can't be less than 1" );
     561              :         return -1;
     562              :     }
     563              : 
     564              :     if( m_rows > 0 && m_cols > 0 )
     565              :     {
     566              :         return resize( m_rows, m_cols, os );
     567              :     }
     568              :     else
     569              :     {
     570              :         m_padFactor = os;
     571              :         return 0;
     572              :     }
     573              : }
     574              : 
     575              : template <class realImageT>
     576              : typename imageXCorrFFT<realImageT>::realT imageXCorrFFT<realImageT>::padFactor()
     577              : {
     578              :     return m_padFactor;
     579              : }
     580              : 
     581              : template <class realImageT>
     582              : int imageXCorrFFT<realImageT>::rowsPadded()
     583              : {
     584              :     return m_rowsPadded;
     585              : }
     586              : 
     587              : template <class realImageT>
     588              : int imageXCorrFFT<realImageT>::colsPadded()
     589              : {
     590              :     return m_colsPadded;
     591              : }
     592              : 
     593              : template <class realImageT>
     594           12 : int imageXCorrFFT<realImageT>::resize( int nrows, int ncols, realT padFactor )
     595              : {
     596              :     // No-op if no change
     597           12 :     if( m_rows == nrows && m_cols == ncols && m_padFactor == padFactor )
     598              :     {
     599            0 :         return 0;
     600              :     }
     601              : 
     602           12 :     if( padFactor < 1 )
     603              :     {
     604            0 :         internal::mxlib_error_report( error_t::invalidarg, "padding factor can't be less than 1" );
     605            0 :         return -1;
     606              :     }
     607              : 
     608           12 :     m_rows = nrows;
     609              : 
     610           12 :     m_cols = ncols;
     611              : 
     612           12 :     m_padFactor = padFactor;
     613              : 
     614              :     /* Any change here invalidates the reference.
     615              :      * This is trivially true for rows or cols.  Also must be
     616              :      * handled for padding which will change the reference shifts.
     617              :      */
     618           12 :     m_refValid = false;
     619              : 
     620           12 :     if( m_rows == 0 || m_cols == 0 )
     621              :     {
     622            0 :         m_ftIm0.resize( 0, 0 );
     623            0 :         m_ftWork.resize( 0, 0 );
     624              : #ifdef XCFFT_C2C
     625              :         m_ftWorkIn.resize( 0, 0 );
     626              : #endif
     627            0 :         m_rowsPadded = 0;
     628            0 :         m_colsPadded = 0;
     629            0 :         m_ccIm.resize( 0, 0 );
     630            0 :         m_ftWorkPadded.resize( 0, 0 );
     631            0 :         m_mtWorkPadded.resize( 0, 0 );
     632            0 :         m_ftWorkPaddedOut.resize( 0, 0 );
     633              : 
     634            0 :         return 0;
     635              :     }
     636              : 
     637              : #ifdef XCFFT_C2C
     638              : 
     639              :     m_ftIm0.resize( m_rows, m_cols );
     640              : 
     641              :     m_ftWork.resize( m_rows, m_cols );
     642              : 
     643              :     m_ftWorkIn.resize( m_rows, m_cols );
     644              : 
     645              : #else
     646              : 
     647           12 :     m_ftIm0.resize( (int)( 0.5 * m_rows ) + 1, m_cols );
     648              : 
     649           12 :     m_ftWork.resize( (int)( 0.5 * m_rows ) + 1, m_cols );
     650              : 
     651              : #endif
     652              : 
     653              :     // fftw is row-major, eigen defaults to column-major
     654           12 :     m_fft_fwd.plan( m_cols, m_rows, math::ft::dir::forward, false );
     655              : 
     656           12 :     m_rowsPadded = ceil( m_rows * m_padFactor );
     657           12 :     m_colsPadded = ceil( m_cols * m_padFactor );
     658              : 
     659           12 :     m_padFactorR = static_cast<realT>( m_rowsPadded ) / static_cast<realT>( m_rows );
     660           12 :     m_padFactorC = static_cast<realT>( m_colsPadded ) / static_cast<realT>( m_cols );
     661              : 
     662           12 :     m_ccIm.resize( m_rowsPadded, m_colsPadded );
     663              : 
     664              : #ifdef XCFFT_C2C
     665              : 
     666              :     m_ftWorkPadded.resize( m_rowsPadded, m_colsPadded );
     667              :     m_ftWorkPaddedOut.resize( m_rowsPadded, m_colsPadded );
     668              : 
     669              : #else
     670              : 
     671           12 :     m_ftWorkPadded.resize( (int)( 0.5 * m_rowsPadded ) + 1, m_colsPadded );
     672              : 
     673           12 :     if( m_peakMethod == xcorrPeakMethod::mftOversamp )
     674              :     {
     675            0 :         m_ftWorkPaddedOut.resize( m_rowsPadded, m_colsPadded );
     676              :     }
     677              :     else
     678              :     {
     679           12 :         m_ftWorkPaddedOut.resize( 0, 0 );
     680              :     }
     681              : 
     682              : #endif
     683              : 
     684           12 :     if( m_peakMethod == xcorrPeakMethod::mftOversamp )
     685              :     {
     686            0 :         m_mtWorkPadded.resize( m_rowsPadded, m_colsPadded );
     687              :     }
     688              :     else
     689              :     {
     690           12 :         m_mtWorkPadded.resize( 0, 0 );
     691              :     }
     692              : 
     693           12 :     m_fft_bwd.plan( m_colsPadded, m_rowsPadded, math::ft::dir::backward, false );
     694              : 
     695           12 :     return 0;
     696              : } // resize(int, int, realT)
     697              : 
     698              : template <class realImageT>
     699              : int imageXCorrFFT<realImageT>::resize( int nrows, int ncols )
     700              : {
     701              :     return resize( nrows, ncols, m_padFactor );
     702              : }
     703              : 
     704              : template <class realImageT>
     705              : int imageXCorrFFT<realImageT>::refMaskIm( const realImageT &mask )
     706              : {
     707              :     m_refMaskIm = mask;
     708              :     m_haveRefMask = true;
     709              : 
     710              :     m_refValid = false;
     711              : 
     712              :     return 0;
     713              : }
     714              : 
     715              : template <class realImageT>
     716              : const realImageT &imageXCorrFFT<realImageT>::refMaskIm()
     717              : {
     718              :     return m_refMaskIm;
     719              : }
     720              : 
     721              : template <class realImageT>
     722              : bool imageXCorrFFT<realImageT>::haveRefMask()
     723              : {
     724              :     return m_haveRefMask;
     725              : }
     726              : 
     727              : template <class realImageT>
     728              : int imageXCorrFFT<realImageT>::maskIm( const realImageT &mask )
     729              : {
     730              :     m_maskIm = mask;
     731              :     m_haveMask = true;
     732              : 
     733              :     return 0;
     734              : }
     735              : 
     736              : template <class realImageT>
     737              : const realImageT &imageXCorrFFT<realImageT>::maskIm()
     738              : {
     739              :     return m_maskIm;
     740              : }
     741              : 
     742              : template <class realImageT>
     743              : bool imageXCorrFFT<realImageT>::haveMask()
     744              : {
     745              :     return m_haveMask;
     746              : }
     747              : 
     748              : template <class realImageT>
     749              : int imageXCorrFFT<realImageT>::refWinIm( const realImageT &win )
     750              : {
     751              :     m_refWinIm = win;
     752              :     m_haveRefWindow = true;
     753              : 
     754              :     m_refValid = false;
     755              : 
     756              :     return 0;
     757              : }
     758              : 
     759              : template <class realImageT>
     760              : const realImageT &imageXCorrFFT<realImageT>::refWinIm()
     761              : {
     762              :     return m_refWinIm;
     763              : }
     764              : 
     765              : template <class realImageT>
     766              : bool imageXCorrFFT<realImageT>::haveRefWindow()
     767              : {
     768              :     return m_haveRefWindow;
     769              : }
     770              : 
     771              : template <class realImageT>
     772              : int imageXCorrFFT<realImageT>::winIm( const realImageT &win )
     773              : {
     774              :     m_winIm = win;
     775              :     m_haveWindow = true;
     776              : 
     777              :     return 0;
     778              : }
     779              : 
     780              : template <class realImageT>
     781              : const realImageT &imageXCorrFFT<realImageT>::winIm()
     782              : {
     783              :     return m_winIm;
     784              : }
     785              : 
     786              : template <class realImageT>
     787              : bool imageXCorrFFT<realImageT>::haveWindow()
     788              : {
     789              :     return m_haveWindow;
     790              : }
     791              : 
     792              : template <class realImageT>
     793              : void imageXCorrFFT<realImageT>::normalize( bool no )
     794              : {
     795              :     if( no != m_normalize )
     796              :     {
     797              :         m_refValid = false;
     798              :         m_normalize = no;
     799              :     }
     800              : }
     801              : 
     802              : template <class realImageT>
     803              : bool imageXCorrFFT<realImageT>::normalize()
     804              : {
     805              :     return m_normalize;
     806              : }
     807              : 
     808              : template <class realImageT>
     809           12 : int imageXCorrFFT<realImageT>::refIm( const realImageT &im, realT padFactor )
     810              : {
     811           12 :     realImageT im0;
     812              : 
     813              :     // Mask if needed
     814           12 :     if( m_haveRefMask )
     815              :     {
     816            0 :         if( im.rows() != m_refMaskIm.rows() && im.cols() != m_refMaskIm.cols() )
     817              :         {
     818            0 :             internal::mxlib_error_report( error_t::sizeerr, "reference and reference mask are not the same size" );
     819            0 :             return -1;
     820              :         }
     821              : 
     822              :         // Now normalize
     823            0 :         if( m_normalize )
     824              :         {
     825            0 :             realT m = imageMean<realT>( im, m_refMaskIm );
     826            0 :             realT v = imageVariance<realT>( im, m, m_refMaskIm );
     827            0 :             im0 = ( ( im - m ) * m_refMaskIm ) / sqrt( v );
     828              :         }
     829              :         else
     830              :         {
     831            0 :             im0 = im * m_refMaskIm;
     832              :         }
     833              :     }
     834              :     else
     835              :     {
     836              :         // Normalize
     837           12 :         if( m_normalize )
     838              :         {
     839           12 :             realT m = imageMean<realT>( im );
     840           12 :             realT v = imageVariance<realT>( im, m );
     841           12 :             im0 = ( im - m ) / sqrt( v );
     842              :         }
     843              :         else
     844              :         {
     845            0 :             im0 = im;
     846              :         }
     847              :     }
     848              : 
     849              :     // Window if needed
     850           12 :     if( m_haveRefWindow )
     851              :     {
     852            0 :         if( im0.rows() != m_refWinIm.rows() && im.cols() != m_refWinIm.cols() )
     853              :         {
     854            0 :             internal::mxlib_error_report( error_t::sizeerr, "reference and reference window are not the same size" );
     855            0 :             return -1;
     856              :         }
     857              : 
     858            0 :         im0 *= m_refWinIm;
     859              :     }
     860              : 
     861              :     // We save refIm as the un-shifted version
     862           12 :     m_refIm = im0;
     863              : 
     864              :     /* Setup the FFTW space
     865              :      * Note: we don't do this earlier in case we ever implement something (e.g. a filter)
     866              :      * that changes size before this point
     867              :      */
     868           12 :     resize( im0.rows(), im0.cols(), padFactor );
     869              : 
     870              :     // Now shift so center pixel is 0,0
     871           12 :     imageShiftWP( im0, m_refIm, 0.5 * m_rows + 1, 0.5 * m_cols + 1 );
     872              : 
     873              : // Then FT
     874              : #ifdef XCFFT_C2C
     875              : 
     876              :     m_ftWork = im0.template cast<complexT>();
     877              : 
     878              :     m_fft_fwd( m_ftIm0.data(), m_ftWork.data() );
     879              : #else
     880           12 :     m_fft_fwd( m_ftIm0.data(), im0.data() );
     881              : #endif
     882              : 
     883              :     // Conjugate and normalize for FFTW scaling.
     884          786 :     for( int c = 0; c < m_ftIm0.cols(); ++c )
     885              :     {
     886        26316 :         for( int r = 0; r < m_ftIm0.rows(); ++r )
     887              :         {
     888        25542 :             complexT val = std::conj( m_ftIm0( r, c ) );
     889        25542 :             val /= ( m_rows * m_cols );
     890        25542 :             m_ftIm0( r, c ) = val;
     891              :         }
     892              :     }
     893              : 
     894              :     // And finally find the reference shift
     895           12 :     m_refX0 = 0;
     896           12 :     m_refY0 = 0;
     897           12 :     m_refPeak = 0;
     898           12 :     m_refValid = true;
     899              : 
     900           12 :     operator()( m_refX0, m_refY0, m_refPeak, m_refIm );
     901              : 
     902           12 :     m_refACIm = m_ccIm;
     903              : 
     904           12 :     return 0;
     905           12 : }
     906              : 
     907              : template <class realImageT>
     908           12 : int imageXCorrFFT<realImageT>::refIm( const realImageT &im )
     909              : {
     910           12 :     return refIm( im, m_padFactor );
     911              : }
     912              : 
     913              : template <class realImageT>
     914              : const realImageT &imageXCorrFFT<realImageT>::refIm()
     915              : {
     916              :     return m_refIm;
     917              : }
     918              : 
     919              : template <class realImageT>
     920              : bool imageXCorrFFT<realImageT>::refValid()
     921              : {
     922              :     return m_refValid;
     923              : }
     924              : 
     925              : template <class realImageT>
     926           23 : void imageXCorrFFT<realImageT>::maxLag( int ml )
     927              : {
     928           23 :     m_maxLag = ml;
     929           23 :     tol( m_tol );
     930           23 : }
     931              : 
     932              : template <class realImageT>
     933              : int imageXCorrFFT<realImageT>::maxLag()
     934              : {
     935              :     return m_maxLag;
     936              : }
     937              : 
     938              : template <class realImageT>
     939              : typename imageXCorrFFT<realImageT>::realT imageXCorrFFT<realImageT>::tol()
     940              : {
     941              :     return m_tol;
     942              : }
     943              : 
     944              : template <class realImageT>
     945           24 : void imageXCorrFFT<realImageT>::tol( realT nt )
     946              : {
     947           24 :     m_tol = nt;
     948              : 
     949           24 :     m_magSize = ceil( ( ( 2. * m_maxLag + 1 ) - 1.0 ) / nt ) + 1;
     950              : 
     951           24 :     m_mag = ( m_magSize - 1.0 ) / ( ( 2. * m_maxLag + 1 ) - 1.0 );
     952              : 
     953           24 :     m_refValid = false;
     954              : 
     955           24 :     return;
     956              : }
     957              : 
     958              : template <class realImageT>
     959           12 : void imageXCorrFFT<realImageT>::peakMethod( xcorrPeakMethod xpm )
     960              : {
     961           12 :     if( xpm != m_peakMethod )
     962              :     {
     963            8 :         m_peakMethod = xpm;
     964              : 
     965            8 :         m_refValid = false;
     966              :     }
     967              : 
     968           12 :     return;
     969              : }
     970              : 
     971              : template <class realImageT>
     972              : xcorrPeakMethod imageXCorrFFT<realImageT>::peakMethod()
     973              : {
     974              :     return m_peakMethod;
     975              : }
     976              : 
     977              : template <class realImageT>
     978              : const realImageT &imageXCorrFFT<realImageT>::refACIm()
     979              : {
     980              :     return m_refACIm;
     981              : }
     982              : 
     983              : template <class realImageT>
     984              : typename imageXCorrFFT<realImageT>::realT imageXCorrFFT<realImageT>::refX0()
     985              : {
     986              :     return m_refX0;
     987              : }
     988              : 
     989              : template <class realImageT>
     990              : typename imageXCorrFFT<realImageT>::realT imageXCorrFFT<realImageT>::refY0()
     991              : {
     992              :     return m_refY0;
     993              : }
     994              : 
     995              : template <class realImageT>
     996              : const realImageT &imageXCorrFFT<realImageT>::normIm()
     997              : {
     998              :     return m_normIm;
     999              : }
    1000              : 
    1001              : template <class realImageT>
    1002              : const realImageT &imageXCorrFFT<realImageT>::ccIm()
    1003              : {
    1004              :     return m_ccIm;
    1005              : }
    1006              : 
    1007              : template <class realImageT>
    1008              : const realImageT &imageXCorrFFT<realImageT>::magIm()
    1009              : {
    1010              :     return m_magIm;
    1011              : }
    1012              : 
    1013              : template <class realImageT>
    1014           24 : void imageXCorrFFT<realImageT>::findPeak( realT &xShift, realT &yShift, realT &xcPeak )
    1015              : {
    1016              :     int xLag0, yLag0;
    1017              : 
    1018              :     /*realImageT sm = m_ccIm;
    1019              :     filterImage(sm, m_ccIm, gaussKernel<eigenImage<realT>, 2>(3));
    1020              :     m_ccIm = sm;*/
    1021              : 
    1022              :     // m_ccIm = m_ccIm.pow(3);
    1023           24 :     realT pk = m_ccIm.maxCoeff( &xLag0, &yLag0 );
    1024           24 :     realT mn = m_ccIm.minCoeff();
    1025              : 
    1026           24 :     if( xLag0 - m_maxLag < 0 )
    1027              :     {
    1028            2 :         m_maxLag = xLag0;
    1029              :     }
    1030           24 :     if( xLag0 + 2 * m_maxLag + 1 >= m_ccIm.rows() )
    1031              :     {
    1032           14 :         m_maxLag = ( m_ccIm.rows() - 1 - xLag0 ) / 2;
    1033              :     }
    1034           24 :     if( yLag0 - m_maxLag < 0 )
    1035              :     {
    1036            0 :         m_maxLag = yLag0;
    1037              :     }
    1038           24 :     if( yLag0 + 2 * m_maxLag + 1 >= m_ccIm.cols() )
    1039              :     {
    1040           14 :         m_maxLag = ( m_ccIm.cols() - 1 - yLag0 ) / 2;
    1041              :     }
    1042              : 
    1043           24 :     realT x0 = xLag0 - m_maxLag;
    1044           24 :     realT y0 = yLag0 - m_maxLag;
    1045              : 
    1046           24 :     if( m_peakMethod == xcorrPeakMethod::gaussFit )
    1047              :     {
    1048            8 :         m_magIm = m_ccIm.block( x0, y0, 2 * m_maxLag + 1, 2 * m_maxLag + 1 );
    1049              : 
    1050            8 :         m_fitter.setArray( m_magIm.data(), m_magIm.rows(), m_magIm.cols() );
    1051            8 :         m_magIm.maxCoeff( &xLag0, &yLag0 );
    1052              : 
    1053            8 :         m_fitter.setGuess( mn, pk - mn, m_maxLag, m_maxLag, 3, 3, 0 );
    1054            8 :         m_fitter.fit();
    1055              : 
    1056            8 :         x0 = ( m_fitter.x0() + x0 );
    1057            8 :         y0 = ( m_fitter.y0() + y0 );
    1058            8 :         xcPeak = m_fitter.G();
    1059              :     }
    1060           16 :     else if( m_peakMethod == xcorrPeakMethod::interpPeak )
    1061              :     {
    1062              :         int x, y;
    1063            8 :         m_magIm.resize( m_magSize, m_magSize );
    1064              : 
    1065            8 :         imageMagnify( m_magIm,
    1066            8 :                       m_ccIm.block( x0, y0, 2 * m_maxLag + 1, 2 * m_maxLag + 1 ),
    1067            8 :                       cubicConvolTransform<realT>() );
    1068              : 
    1069            8 :         xcPeak = m_magIm.maxCoeff( &x, &y );
    1070            8 :         x0 = ( x * ( 1.0 / m_mag ) + x0 );
    1071            8 :         y0 = ( y * ( 1.0 / m_mag ) + y0 );
    1072              :     }
    1073            8 :     else if( m_peakMethod == xcorrPeakMethod::centerOfLight )
    1074              :     {
    1075              :         realT x, y;
    1076              : 
    1077            8 :         m_magIm = m_ccIm.block( x0, y0, 2 * m_maxLag + 1, 2 * m_maxLag + 1 );
    1078            8 :         m_magIm -= m_magIm.minCoeff(); // Must sum to > 0.
    1079            8 :         xcPeak = m_magIm.maxCoeff();
    1080            8 :         imageCenterOfLight( x, y, m_magIm );
    1081              : 
    1082            8 :         x0 = ( x + x0 );
    1083            8 :         y0 = ( y + y0 );
    1084              :     }
    1085            0 :     else if( m_peakMethod == xcorrPeakMethod::mftOversamp )
    1086              :     {
    1087              :         int x, y;
    1088            0 :         m_ccIm.maxCoeff( &x, &y );
    1089              : 
    1090            0 :         if( m_refX0 == 0 && m_refY0 == 0 ) // reference finding
    1091              :         {
    1092            0 :             x0 = x;
    1093            0 :             y0 = y;
    1094              :         }
    1095              :         else
    1096              :         {
    1097            0 :             x = x / m_padFactorR - m_refX0;
    1098            0 :             y = y / m_padFactorC - m_refY0;
    1099              : 
    1100            0 :             realT mag = 1.0 / m_tol;          // m_mag is the effective mag for interpPeak...
    1101              : 
    1102            0 :             realT tmagR = mag * m_padFactorR; // the total mag
    1103            0 :             realT tmagC = mag * m_padFactorC; // the total mag
    1104              : 
    1105            0 :             realT shiftx0 = -1 * ( m_refX0 * m_padFactorR ) * ( mag - 1 ) - x * tmagR;
    1106            0 :             realT shifty0 = -1 * ( m_refY0 * m_padFactorC ) * ( mag - 1 ) - y * tmagC;
    1107              : 
    1108            0 :             m_mft_bwd.plan( m_rowsPadded, m_colsPadded, math::ft::dir::backward, shiftx0, shifty0, mag );
    1109              : 
    1110              : #ifdef XCFFT_C2C
    1111              : 
    1112              :             m_mft_bwd( m_ftWorkPaddedOut, m_ftWorkPadded );
    1113              : 
    1114              : #else
    1115              : 
    1116            0 :             m_mtWorkPadded.resize( m_rowsPadded, m_colsPadded );
    1117            0 :             m_mtWorkPadded.setZero();
    1118            0 :             math::ft::augmentR2CFFTOutput( m_mtWorkPadded, m_ftWork );
    1119              : 
    1120            0 :             m_mft_bwd( m_ftWorkPaddedOut, m_mtWorkPadded );
    1121              : 
    1122              : #endif
    1123              : 
    1124            0 :             m_magIm = m_ftWorkPaddedOut.real();
    1125              : 
    1126              :             int xf, yf;
    1127            0 :             xcPeak = m_magIm.maxCoeff( &xf, &yf );
    1128              : 
    1129            0 :             xShift = x + ( xf - m_refX0 * m_padFactorR ) / tmagR;
    1130            0 :             yShift = y + ( yf - m_refY0 * m_padFactorC ) / tmagC;
    1131              : 
    1132            0 :             return;
    1133              :         }
    1134              :     }
    1135            0 :     else if( m_peakMethod == xcorrPeakMethod::none )
    1136              :     {
    1137              :         int x, y;
    1138            0 :         xcPeak = m_ccIm.maxCoeff( &x, &y );
    1139            0 :         x0 = x;
    1140            0 :         y0 = y;
    1141              :     }
    1142              :     else
    1143              :     {
    1144            0 :         throw( mx::exception( error_t::invalidconfig, "unknown peak finding method" ) );
    1145              :     }
    1146              : 
    1147              :     //--> unpad here, scaling the shifts
    1148           24 :     xShift = x0 / m_padFactorR - m_refX0;
    1149           24 :     yShift = y0 / m_padFactorC - m_refY0;
    1150              : }
    1151              : 
    1152              : template <class realImageT>
    1153              : template <class imT>
    1154           24 : int imageXCorrFFT<realImageT>::operator()( realT &xShift, realT &yShift, realT &xcPeak, const imT &im )
    1155              : {
    1156           24 :     if( !m_refValid )
    1157              :     {
    1158            0 :         throw( mx::exception( error_t::invalidconfig, "reference image is not valid" ) );
    1159              :     }
    1160              : 
    1161           24 :     if( im.rows() != m_rows )
    1162              :     {
    1163            0 :         throw( mx::exception( error_t::sizeerr, "image must be same size as reference (rows)" ) );
    1164              :     }
    1165              : 
    1166           24 :     if( im.cols() != m_cols )
    1167              :     {
    1168            0 :         throw( mx::exception( error_t::sizeerr, "image must be same size as reference (rows)" ) );
    1169              :     }
    1170              : 
    1171              :     // Mask and normalize as needed
    1172           24 :     if( m_haveMask )
    1173              :     {
    1174            0 :         if( m_normalize )
    1175              :         {
    1176            0 :             realT m = imageMean<realT>( im, m_maskIm );
    1177            0 :             realT v = imageVariance<realT>( im, m, m_maskIm );
    1178              : 
    1179            0 :             m_normIm = ( im - m ) * m_maskIm / sqrt( v );
    1180              :         }
    1181              :         else
    1182              :         {
    1183            0 :             m_normIm = im * m_maskIm;
    1184              :         }
    1185              :     }
    1186              :     else
    1187              :     {
    1188              : 
    1189           24 :         if( m_normalize )
    1190              :         {
    1191           24 :             realT m = imageMean<realT>( im );
    1192           24 :             realT v = imageVariance<realT>( im, m );
    1193              : 
    1194           24 :             m_normIm = ( im - m ) / sqrt( v );
    1195              :         }
    1196              :         else
    1197              :         {
    1198            0 :             m_normIm = im;
    1199              :         }
    1200              :     }
    1201              : 
    1202           24 :     if( m_haveWindow )
    1203              :     {
    1204            0 :         m_normIm *= m_winIm;
    1205              :     }
    1206              : 
    1207              : #ifdef XCFFT_C2C
    1208              :     m_ftWorkIn = m_normIm.template cast<complexT>();
    1209              : 
    1210              :     m_fft_fwd( m_ftWork.data(), m_ftWorkIn.data() );
    1211              : #else
    1212           24 :     m_fft_fwd( m_ftWork.data(), m_normIm.data() );
    1213              : #endif
    1214              : 
    1215              :     // m_ftIm0 is the conjugated, fftw-normalized, reference image in the Fourier domain
    1216              :     // So this is the FT of the cross-correlation:
    1217           24 :     m_ftWork *= m_ftIm0;
    1218              : 
    1219           24 :     m_ftWorkPadded.setZero();
    1220              : 
    1221              : #ifdef XCFFT_C2C
    1222              : 
    1223              :     math::ft::padC2CFFTOutput( m_ftWorkPadded, m_ftWork );
    1224              : 
    1225              :     m_fft_bwd( m_ftWorkPaddedOut.data(), m_ftWorkPadded.data() );
    1226              : 
    1227              :     m_ccIm = m_ftWorkPaddedOut.real();
    1228              : 
    1229              : #else
    1230              : 
    1231           24 :     math::ft::padR2CFFTOutput( m_ftWorkPadded, m_ftWork );
    1232              : 
    1233           24 :     m_fft_bwd( m_ccIm, m_ftWorkPadded );
    1234              : 
    1235              : #endif
    1236              : 
    1237           24 :     findPeak( xShift, yShift, xcPeak );
    1238              : 
    1239           24 :     return 0;
    1240              : }
    1241              : 
    1242              : template <class realImageT>
    1243              : template <class im0T, class imT>
    1244              : int imageXCorrFFT<realImageT>::operator()( realT &xShift, realT &yShift, realT &xcPeak, im0T &im0, imT &im )
    1245              : {
    1246              :     setReference( im0 );
    1247              :     return operator()( xShift, yShift, xcPeak, im );
    1248              : }
    1249              : 
    1250              : } // namespace improc
    1251              : } // namespace mx
    1252              : 
    1253              : #endif // imageXCorrFFT_hpp
        

Generated by: LCOV version 2.0-1