mxlib
c++ tools for analyzing astronomical data and other tasks by Jared R. Males. [git repo]
imageXCorrDiscrete.hpp
Go to the documentation of this file.
1 /** \file imageXCorrDiscrete.hpp
2  * \brief A class to register images using the discrete cross correlation.
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 imageXCorrDiscrete_hpp
28 #define imageXCorrDiscrete_hpp
29 
30 #include "../mxError.hpp"
31 #include "../math/fit/fitGaussian.hpp"
32 #include "imageUtils.hpp"
33 
34 namespace mx
35 {
36 namespace improc
37 {
38 
39 enum class xcorrPeakMethod{ centroid, gaussfit, interp };
40 
41 /// Find the optimum shift to align two images using the discrete cross correlation.
42 /** The reference image must be smaller than the target image. Both the reference image and the section of
43  * target image being analyzed are mean subtracted and variance normalized. An optional mask can be supplied,
44  * which limits the pixels used for the mean and variance calculation.
45  *
46  * Typical usage will be to set the mask, then the reference, then repeatedly call operator() to
47  * determine the shifts for a sequence of imaages. No new heap allocations take place on these calls
48  * to operator(), and the reference image is not re-normalized on each call.
49  *
50  * The shift is reported in pixels such that if the mxlib imageShift function is used
51  * to shift the input image by the negative of the shifts, it will align with the
52  * reference at the center of the array.
53  *
54  * Three peak finding methods are provided. xcorrPeakMethod::centroid uses center of light,
55  * xcorrPeakMethod::centroid uses Gaussian centroiding, and xcorrPeakMethod::interp uses interpolation
56  * to find the peak to a given tolerance.
57  *
58  * \tparam _ccImT is the Eigen-like array type used for image processing. See typedefs.
59  *
60  * \ingroup image_reg
61  */
62 template<class _ccImT>
64 {
65 public:
66  typedef _ccImT ccImT; ///< the Eigen-like array type used for image processing
67  typedef typename _ccImT::Scalar Scalar; ///< the scalar type of the image type
68 
69 protected:
70 public:
71  /** \name Working Memory
72  * @{
73  */
74 
75  ccImT m_refIm; ///< The normalized reference image.
76 
77  ccImT m_maskIm; ///< Mask image to use, may be needed for proper normalization even if refIm has 0 mask applied.
78 
79  bool m_haveMask {false};
80 
81  ccImT m_normIm; ///< The normalized image.
82 
83  ccImT m_ccIm; ///< The cross-correlation image
84 
85  ccImT m_magIm; ///< The magnified image, used if m_peakMethod == xcorrPeakMethod::interp
86 
87  ///@}
88 
90 
91  int m_maxLag {0}; ///< The maximum lag to consider in the initial cross-correlation.
92 
93  Scalar m_tol {0.1}; ///< The tolerance of the interpolated-magnified image, in pixels.
94 
95  Scalar m_magSize {0}; ///< Magnified size of the ccIm when using interp. Set as function of m_tol and m_maxLag.
96 
97 public:
98  xcorrPeakMethod m_peakMethod {xcorrPeakMethod::centroid};
99 
100 public:
101 
102  /// Default c'tor
104 
105  /// Construct seeting maxLag.
106  explicit imageXCorrDiscrete(int maxLag);
107 
108  /// Get the current maximum lag
109  /**
110  * \returns the current value of m_maxLag
111  */
112  int maxLag();
113 
114  /// Set the maximum lag
115  void maxLag( int ml /**< [in] the new maximum lag */);
116 
117  /// Get the tolerance of the interpolated-magnified image, in pixels.
118  /**
119  * \returns the current value of m_tol.
120  */
121  Scalar tol();
122 
123  /// Set the tolerance of the interpolated-magnified image, in pixels.
124  void tol( Scalar nt /**< [in] The new value of the interpolation tolerance. */ );
125 
126  /// Set the size of the cross-correlation images.
127  /** This resizes all working memory.
128  *
129  * \returns 0 on success
130  * \returns -1 on error
131  */
132  int resize( int nrows, ///< [in] the number of rows in the images to register
133  int ncols ///< [in] the number of columns in the images to register
134  );
135 
136  /// Set the mask image
137  /**
138  * \returns 0 on success
139  * \returns -1 on error
140  */
141  int maskIm( const ccImT & mask /**< [in] the new mask image */ );
142 
143  /// Get a reference to the mask image.
144  /**
145  * \returns a const referance to the mask image.
146  */
147  const ccImT & maskIm();
148 
149  /// Set the reference image
150  /** Normalizes the reference image by mean subtraction and variance division. Applies
151  * the mask first if supplied.
152  *
153  * \returns 0 on success
154  * \returns -1 on error
155  */
156  int refIm( const ccImT & im0 );
157 
158  /// Get a reference to the reference image.
159  /**
160  * \returns a const referent to m_refIm.
161  */
162  const ccImT& refIm();
163 
164  /// Get a reference to the normalized image.
165  /**
166  * \returns a const referent to m_normIm.
167  */
168  const ccImT & normIm();
169 
170  /// Get a reference to the cross correlation image.
171  /**
172  * \returns a const referent to m_ccIm.
173  */
174  const ccImT & ccIm();
175 
176  /// Get a reference to the magnified image.
177  /**
178  * \returns a const referent to m_magIm.
179  */
180  const ccImT & magIm();
181 
182  /// Conduct the cross correlation to a specified tolerance
183  /**
184  * \returns 0 on success
185  * \returns -1 on error
186  */
187  template<class imT>
188  int operator()( Scalar & xShift, ///< [out] the x shift of im w.r.t. im0, in pixels
189  Scalar & yShift, ///< [out] the y shift of im w.r.t. im0, in pixels
190  const imT & im ///< [in] the image to cross-correlate with the reference
191  );
192 };
193 
194 template< class ccImT>
196 {
197 }
198 
199 template< class ccImT>
201 {
202  maxLag(mL);
203 }
204 
205 template< class ccImT>
207 {
208  return m_maxLag;
209 }
210 
211 template< class ccImT>
213 {
214  m_maxLag = ml;
215  tol(m_tol);
216 }
217 
218 template< class ccImT>
219 typename ccImT::Scalar imageXCorrDiscrete<ccImT>::tol()
220 {
221  return m_tol;
222 }
223 
224 
225 template< class ccImT>
227 {
228 
229  m_magSize = ceil(((2.*m_maxLag + 1) - 1.0)/ nt)+1;
230 
231  Scalar mag = (m_magSize-1.0)/((2.*m_maxLag + 1) - 1.0);
232 
233  m_tol = 1.0/mag;
234 }
235 
236 template< class ccImT>
238 {
239  m_maskIm = mask;
240  m_haveMask = true;
241 
242  return 0;
243 }
244 
245 template< class ccImT>
247 {
248  return m_maskIm;
249 }
250 
251 template< class ccImT>
253 {
254  ccImT im0;
255  if(m_haveMask)
256  {
257  if(im.rows()!=m_maskIm.rows() && im.cols() != m_maskIm.cols())
258  {
259  mxError("imageXCorFit::setReference", MXE_SIZEERR, "reference and mask are not the same size");
260  return -1;
261  }
262 
263  im0 = im*m_maskIm;
264  }
265  else
266  {
267  im0 = im;
268  }
269 
270  Scalar m = imageMean(im0);
271  Scalar v = imageVariance(im0, m);
272  m_refIm = (im0 - m)/sqrt(v);
273 
274  return 0;
275 }
276 
277 template< class ccImT>
279 {
280  return m_refIm;
281 }
282 
283 template< class ccImT>
285 {
286  return m_normIm;
287 }
288 
289 template< class ccImT>
291 {
292  return m_ccIm;
293 }
294 
295 template< class ccImT>
297 {
298  return m_magIm;
299 }
300 
301 template< class ccImT>
302 template< class imT>
304  Scalar & yShift,
305  const imT & im
306  )
307 {
308  if( im.rows() <= m_refIm.rows() )
309  {
310  mxError("imageXCorrDiscrete", MXE_SIZEERR, "reference must be smaller than target image (rows)");
311  return -1;
312  }
313 
314  if( im.cols() <= m_refIm.cols() )
315  {
316  mxError("imageXCorrDiscrete", MXE_SIZEERR, "reference must be smaller than target image (cols)");
317  return -1;
318  }
319  // 16/4 15/4 16/3 15/3
320  int maxLag_r = (im.rows()-m_refIm.rows()); // 6 5 6 6
321  int maxLag_c = (im.cols()-m_refIm.cols());
322 
323 
324  if(maxLag_r > m_maxLag && m_maxLag != 0) maxLag_r = m_maxLag;
325  if(maxLag_c > m_maxLag && m_maxLag != 0) maxLag_c = m_maxLag;
326 
327  m_ccIm.resize(2*maxLag_r + 1, 2*maxLag_c + 1);
328 
329 
330  for( int rL = -maxLag_r; rL <= maxLag_r; ++rL)
331  {
332  for( int cL = -maxLag_c; cL <= maxLag_c; ++cL)
333  {
334  // 16/4 15/4 16/3 15/3
335  int r0 = 0.5*im.rows() + rL-0.5*m_refIm.rows(); // 0-15 0-13 0-14 0-14
336  int c0 = 0.5*im.cols() + cL-0.5*m_refIm.cols();
337 
338 
339  if(m_haveMask)
340  {
341  m_normIm = im.block(r0,c0, m_refIm.rows(), m_refIm.cols())*m_maskIm;
342  }
343  else
344  {
345  m_normIm = im.block(r0,c0, m_refIm.rows(), m_refIm.cols());
346  }
347 
348  Scalar m = imageMean(m_normIm);
349  Scalar sv = sqrt(imageVariance(m_normIm,m));
350  if(sv <= 0)
351  {
352  m_ccIm(maxLag_r + rL, maxLag_c + cL) = 0;
353  }
354  else
355  {
356  m_normIm = (m_normIm-m)/sv;
357  m_ccIm(maxLag_r + rL, maxLag_c + cL) = (m_normIm*m_refIm).sum();
358  //ds9Interface ds9( m_normIm, 1);
359  }
360 
361 
362  }
363  }
364 
365  if(m_peakMethod == xcorrPeakMethod::gaussfit)
366  {
367  int xLag0,yLag0;
368  Scalar pk = m_ccIm.maxCoeff(&xLag0, &yLag0);
369  Scalar mn = m_ccIm.minCoeff();
370  m_fitter.setArray(m_ccIm.data(), m_ccIm.rows(), m_ccIm.cols());
371  m_fitter.setGuess(mn, pk, xLag0, yLag0, 0.2*m_ccIm.rows(), 0.2*m_ccIm.cols(), 0);
372  m_fitter.fit();
373 
374  xShift = m_fitter.x0() - maxLag_r;
375  yShift = m_fitter.y0() - maxLag_c;
376  }
377  else if(m_peakMethod == xcorrPeakMethod::interp)
378  {
379  m_magIm.resize(m_magSize, m_magSize);
380  imageMagnify(m_magIm, m_ccIm, cubicConvolTransform<Scalar>());
381  int x, y;
382  m_magIm.maxCoeff(&x,&y);
383  xShift = x*m_tol - maxLag_r;
384  yShift = y*m_tol - maxLag_c;
385  }
386  else
387  {
388  Scalar x,y;
389  imageCenterOfLight(x,y,m_ccIm);
390  xShift = x - maxLag_r;
391  yShift = y - maxLag_c;
392  }
393 
394  return 0;
395 
396 }
397 
398 } //improc
399 } //mx
400 
401 #endif //imageXCorrDiscrete_hpp
Find the optimum shift to align two images using the discrete cross correlation.
ccImT m_normIm
The normalized image.
_ccImT::Scalar Scalar
the scalar type of the image type
const ccImT & refIm()
Get a reference to the reference image.
int m_maxLag
The maximum lag to consider in the initial cross-correlation.
ccImT m_maskIm
Mask image to use, may be needed for proper normalization even if refIm has 0 mask applied.
ccImT m_magIm
The magnified image, used if m_peakMethod == xcorrPeakMethod::interp.
int maxLag()
Get the current maximum lag.
ccImT m_ccIm
The cross-correlation image.
Scalar m_tol
The tolerance of the interpolated-magnified image, in pixels.
Scalar m_magSize
Magnified size of the ccIm when using interp. Set as function of m_tol and m_maxLag.
_ccImT ccImT
the Eigen-like array type used for image processing
ccImT m_refIm
The normalized reference image.
Scalar tol()
Get the tolerance of the interpolated-magnified image, in pixels.
int operator()(Scalar &xShift, Scalar &yShift, const imT &im)
Conduct the cross correlation to a specified tolerance.
const ccImT & maskIm()
Get a reference to the mask image.
const ccImT & magIm()
Get a reference to the magnified image.
const ccImT & normIm()
Get a reference to the normalized image.
int resize(int nrows, int ncols)
Set the size of the cross-correlation images.
const ccImT & ccIm()
Get a reference to the cross correlation image.
Class to manage fitting a 2D Gaussian to data via the levmarInterface.
@ imageMean
The mean of each image (within the search region) is subtracted from itself.
void imageMagnify(arrOutT &transim, const arrInT &im, transformT trans)
Magnify an image.
int imageCenterOfLight(typename imageT::Scalar &x, typename imageT::Scalar &y, const imageT &im)
Calculate the center of light of an image.
Definition: imageUtils.hpp:163
imageT::Scalar imageVariance(imageT &im, typename imageT::Scalar mean)
Calculate the variance of an image w.r.t. a given value.
Definition: imageUtils.hpp:139
Header for the image processing utilities.
The mxlib c++ namespace.
Definition: mxError.hpp:107
Transformation by cubic convolution interpolation.