mxlib
c++ tools for analyzing astronomical data and other tasks by Jared R. Males. [git repo]
imageTransforms.hpp
Go to the documentation of this file.
1 /** \file imageTransforms.hpp
2  * \author Jared R. Males
3  * \brief Image interpolation and transformation
4  * \ingroup image_processing_files
5  *
6  */
7 
8 //***********************************************************************//
9 // Copyright 2015, 2016, 2017, 2018 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 improc_imageTransforms_hpp
28 #define improc_imageTransforms_hpp
29 
30 #include <cstddef>
31 #include <cmath>
32 
33 #include <iostream>
34 #include <limits>
35 
36 
37 namespace mx
38 {
39 namespace improc
40 {
41 
42 ///Transformation by bi-linear interpolation
43 /** \ingroup image_transforms
44  */
45 template<typename _arithT>
47 {
48  typedef _arithT arithT;
49 
50  static const size_t width = 2;
51  static const size_t lbuff = 0;
52 
53  template<typename arrT, typename arithT>
54  void operator()(arrT & kern, arithT x, arithT y)
55  {
56  kern.resize(width, width);
57 
58  kern(0,0) = (1.-x)*(1.-y);
59  kern(0,1) = (1.-x)*y;
60  kern(1,0) = x*(1.-y);
61  kern(1,1) = x*y;
62  }
63 };
64 
65 ///Typedef for bilinearTransform with single precision
66 /** \ingroup image_transforms
67  */
69 
70 ///Typedef for bilinearTransform with double precision
71 /** \ingroup image_transforms
72  */
74 
75 
76 ///Transformation by cubic convolution interpolation
77 /** Uses the cubic convolution interpolation kernel. See <a href="https://en.wikipedia.org/wiki/Bicubic_interpolation">https://en.wikipedia.org/wiki/Bicubic_interpolation </a>.
78  *
79  * The parameter \ref cubic should be left as the default -0.5 in most cases, which gives the bicubic spline interpolator. See
80  * <a href="https://en.wikipedia.org/wiki/Cubic_Hermite_spline">https://en.wikipedia.org/wiki/Cubic_Hermite_spline</a>.
81  *
82  * \tparam _arithT is the type in which to do all calculations. Should be a floating point type.
83  *
84  * \test Scenario: Verify direction and accuracy of various image shifts \ref tests_improc_imageTransforms_imageShift "[test doc]"
85  *
86  * \ingroup image_transforms
87  */
88 template<typename _arithT>
90 {
91  typedef _arithT arithT; ///< The type in which all calculations are performed.
92 
93  static const size_t width = 4;
94  static const size_t lbuff = 1;
95 
96  arithT cubic {-0.5}; ///< The kernel parameter. The default value -0.5 gives the bicubic spline interpolator.
97 
98  /// Default c'tor.
99  /**
100  * This will provide the bicubic spline interpolator
101  */
103 
104  /// Construct setting the kernel parameter.
105  explicit cubicConvolTransform( arithT c /**< [in] [optiona] The kernel parameter. The default value -0.5 gives the bicubic spline interpolator. */)
106  {
107  cubic = c;
108  }
109 
110  /// Copy c'tor
112  {
113  cubic = t.cubic;
114  }
115 
116  /// Calculate the kernel value for a given residual.
118  {
119  if(d <= 1) return (cubic+2.)*d*d*d - (cubic+3.)*d*d + 1.;
120 
121  if(d < 2) return cubic*d*d*d -5.*cubic*d*d + 8.*cubic*d - 4.*cubic;
122 
123  return 0;
124  }
125 
126  template<typename arrT, typename arithT>
127  void operator()( arrT & kern,
128  arithT x,
129  arithT y
130  )
131  {
132  arithT km2x,km1x,kp1x,kp2x;
133  arithT km2y,km1y,kp1y,kp2y;
134 
135  km2x = cubicConvolKernel((1.+x));
136  km1x = cubicConvolKernel(x);
137  kp1x = cubicConvolKernel(1.-x);
138  kp2x = cubicConvolKernel(2.-x);
139 
140  km2y = cubicConvolKernel((1.+y));
141  km1y = cubicConvolKernel(y);
142  kp1y = cubicConvolKernel(1.-y);
143  kp2y = cubicConvolKernel(2.-y);
144 
145  kern(0,0) = km2x*km2y;
146  kern(0,1) = km2x*km1y;
147  kern(0,2) = km2x*kp1y;
148  kern(0,3) = km2x*kp2y;
149 
150  kern(1,0) = km1x*km2y;
151  kern(1,1) = km1x*km1y;
152  kern(1,2) = km1x*kp1y;
153  kern(1,3) = km1x*kp2y;
154 
155  kern(2,0) = kp1x*km2y;
156  kern(2,1) = kp1x*km1y;
157  kern(2,2) = kp1x*kp1y;
158  kern(2,3) = kp1x*kp2y;
159 
160  kern(3,0) = kp2x*km2y;
161  kern(3,1) = kp2x*km1y;
162  kern(3,2) = kp2x*kp1y;
163  kern(3,3) = kp2x*kp2y;
164  }
165 };
166 
167 
168 
169 
170 ///Typedef for cubicConvolTransform with single precision
171 /** \ingroup image_transforms
172  */
174 
175 ///Typedef for cubicConvolTransform with double precision
176 /** \ingroup image_transforms
177  */
179 
180 /** \ingroup image_transforms
181  * @{
182  */
183 
184 
185 /// Rotate an image represented as an eigen array
186 /** Uses the given transformation type to rotate an image.
187  *
188  * \tparam transformT specifies the transformation to use [will be resolved by compiler]
189  * \tparam arrT is the eigen array type of the output [will be resolved by compiler]
190  * \tparam arrT2 is the eigen array type of the input [will be resolved by compiler]
191  * \tparam floatT is a floating point type [will be resolved by compiler in most cases]
192  *
193  */
194 template<typename transformT, typename arrT, typename arrT2, typename floatT>
195 void imageRotate( arrT & transim, ///< [out] The rotated image. Must be pre-allocated.
196  const arrT2 &im, ///< [in] The image to be rotated.
197  floatT dq, ///< [in] the angle, in radians, by which to rotate in the c.c.w. direction
198  transformT trans ///< [in] is the transformation to use
199  )
200 {
201  typedef typename transformT::arithT arithT;
202  arithT cosq, sinq;
203  arithT x0, y0, x,y;
204  arithT xcen, ycen;
205 
206  int Nrows, Ncols;
207 
208  int i0, j0;
209 
210  const int lbuff = transformT::lbuff;
211  const int width = transformT::width;
212 
213  cosq = cos(dq);
214  sinq = sin(dq);
215 
216  Nrows = im.rows();
217  Ncols = im.cols();
218 
219 
220  transim.resize(Nrows, Ncols);
221 
222  //The geometric image center
223  xcen = 0.5*(Nrows-1.);
224  ycen = 0.5*(Ncols-1.);
225 
226  int xulim = Nrows-width+lbuff;// - 1;
227  int yulim = Ncols-width+lbuff;// - 1;
228 
229  arithT xc_x_cosq = xcen*cosq;
230  arithT xc_x_sinq = xcen*sinq;
231  arithT yc_x_cosq = ycen*cosq;
232  arithT yc_x_sinq = ycen*sinq;
233 
234  xc_x_cosq += yc_x_sinq;
235  xc_x_sinq -= yc_x_cosq;
236 
237 
238  #ifdef MXLIB_USE_OMP
239  #pragma omp parallel private(x0,y0,i0,j0,x,y)
240  #endif
241  {
242  arithT i_x_cosq, i_x_sinq;
243  arrT kern;
244  kern.resize(width,width);
245 
246  #ifdef MXLIB_USE_OMP
247  #pragma omp for schedule(static, 1)
248  #endif
249  for(int i=0;i<Nrows; ++i)
250  {
251  i_x_cosq = i*cosq - xc_x_cosq;// + xcen;
252  i_x_sinq = -(i*sinq - xc_x_sinq);// + ycen;
253 
254  for(int j=0;j<Ncols; ++j)
255  {
256  //We are actually doing this rotation matrix:
257  //x0 = (i-xcen)*cosq + (j-ycen)*sinq;
258  //y0 = -(i-xcen)*sinq + (j-ycen)*cosq;
259  //This is the minimum-op representation of the above rotation matrix:
260  x0 = i_x_cosq + j*sinq;
261  y0 = i_x_sinq + j*cosq;
262 
263  //Get lower left index
264  i0 = x0 +xcen;
265  j0 = y0 +ycen;
266 
267  if(i0 <= lbuff || i0 >= xulim || j0 <= lbuff || j0 >= yulim)
268  {
269  transim(i,j) = 0;
270  continue;
271  }
272 
273  //Get the residual
274  x = x0+xcen-i0;
275  y = y0+ycen-j0;
276 
277  trans(kern, x, y);
278  transim(i,j) = (im.block(i0-lbuff,j0-lbuff, width, width) * kern).sum();
279  }//for j
280  }//for i
281  }//#pragma omp parallel
282 
283 }//void imageRotate(arrT & transim, const arrT2 &im, floatT dq, transformT trans)
284 
285 /// Shift an image by whole pixels with (optional) wrapping.
286 /** The output image can be smaller than the input image, in which case the wrapping (if enabled) still occurs for the input image, but only
287  * output images worth of pixels are actually shifted. This is useful, for instance, when propagating large turbulence phase screens
288  * where one only needs a small section at a time.
289  *
290  * \tparam outputArrT is the eigen array type of the output [will be resolved by compiler]
291  * \tparam inputArrT is the eigen array type of the input [will be resolved by compiler]
292  *
293  * \test Scenario: Verify direction and accuracy of various image shifts \ref tests_improc_imageTransforms_imageShift "[test doc]"
294  */
295 template<typename outputArrT, typename inputArrT>
296 void imageShiftWP( outputArrT & out, ///< [out] contains the shifted image. Must be pre-allocated, but can be smaller than the in array.
297  inputArrT & in, ///< [in] the image to be shifted.
298  int dx, ///< [in] the amount to shift in the x direction
299  int dy, ///< [in] the amount to shift in the y direction
300  bool wrap = true ///< [in] flag controlling whether or not to wrap around
301  )
302 {
303  dx %= in.rows();
304  dy %= in.cols();
305 
306  int outr = out.rows();
307  int outc = out.cols();
308  int inr = in.rows();
309  int inc = in.cols();
310 
311  #ifdef MXLIB_USE_OMP
312  #pragma omp parallel
313  #endif
314  {
315  int x, y;
316 
317  if(wrap)
318  {
319  #ifdef MXLIB_USE_OMP
320  #pragma omp for
321  #endif
322  for(int cc=0;cc<outc; ++cc)
323  {
324  y = cc - dy;
325 
326  if (y < 0) y += inc;
327  else if (y >= inc) y -= inc;
328 
329  for(int rr=0; rr<outr; ++rr)
330  {
331  x = rr - dx;
332 
333  if(x < 0) x += inr;
334  else if (x >= inr) x -= inr;
335 
336  out(rr, cc) = in(x,y);
337  }
338  }
339  }
340  else
341  {
342  #ifdef MXLIB_USE_OMP
343  #pragma omp for
344  #endif
345  for(int cc=0;cc<outc; ++cc)
346  {
347  y = cc - dy;
348 
349  if (y < 0 || y >= inc)
350  {
351  for(int rr=0; rr<outr; ++rr)
352  {
353  out(rr,cc) = 0;
354  }
355 
356  continue;
357  }
358 
359  for(int rr=0; rr<outr; ++rr)
360  {
361  x = rr - dx;
362 
363  if(x < 0 || x >= inr)
364  {
365  out(rr,cc) = 0;
366  continue;
367  }
368 
369  out(rr, cc) = in(x,y);
370  }
371  }
372  }// if(wrap)-else
373  }
374 }
375 
376 /// Shift an image by whole pixels, wrapping around, with a scaling image applied to the shifted image.
377 /** The output image can be smaller than the input image, in which case the wrapping still occurs for the input image, but only
378  * output images worth of pixels are actually shifted. This is useful, for instance, when propagating large turbulence phase screens
379  * where one only needs a small section at a time.
380  *
381  * The scaling is applied to the output image. The scale image must be the same size as the output image.
382  *
383  * \tparam outputArrT is the eigen array type of the output [will be resolved by compiler]
384  * \tparam inputArrT is the eigen array type of the input [will be resolved by compiler]
385  * \tparam scaleArrT is the eigen array type of the scale image [will be resolved by compiler]
386  *
387  */
388 template<typename outputArrT, typename inputArrT, typename scaleArrT>
389 void imageShiftWPScale( outputArrT & out, ///< [out] contains the shifted image. Must be pre-allocated, but can be smaller than the in array.
390  inputArrT & in, ///< [in] the image to be shifted.
391  scaleArrT & scale, ///< [in] image of scale values applied per-pixel to the output (shifted) image, same size as out
392  int dx, ///< [in] the amount to shift in the x direction
393  int dy ///< [in] the amount to shift in the y direction
394  )
395 {
396  dx %= in.rows();
397  dy %= in.cols();
398 
399  int outr = out.rows();
400  int outc = out.cols();
401  int inr = in.rows();
402  int inc = in.cols();
403  #ifdef MXLIB_USE_OMP
404  //#pragma omp parallel
405  #endif
406  {
407  int x, y;
408 
409  #ifdef MXLIB_USE_OMP
410  //#pragma omp for
411  #endif
412  for(int cc=0;cc<outc; ++cc)
413  {
414  y = cc - dy;
415 
416  if (y < 0) y += inc;
417  else if (y >= inc) y -= inc;
418 
419  for(int rr=0; rr<outr; ++rr)
420  {
421  x = rr - dx;
422 
423  if(x < 0) x += inr;
424  else if (x >= inr) x -= inr;
425 
426  out(rr, cc) = in(x,y)*scale(rr,cc);
427  }
428  }
429 
430  }
431 }
432 
433 /// Shift an image.
434 /** Uses the given transformation type to shift an image such that objects move by (\p dx,\p dy) pixels.
435  * The shift is such that an object located at the coordinate
436  * (\p -dx, \p -dy) from the center of the image will be moved to the center of the image. So to move an object
437  * located 2 pixels right (dx) and 2 pixels up (dy) from the center to be at the center, use \p dx = -2, \p dy = -2.
438  *
439  * Note that this does not treat the edges
440  * of the image, determined by the buffer width (lbuff) of the kernel and the size of shift. If you wish to
441  * treat the edges, you must pad the image by at least lbuff+abs(shift) pixels in each direction, and
442  * implement a strategy (zeros, mirror, wrap) prior to calling this function.
443  *
444  * \tparam arrOutT is the Eigen-like array type of the output [will be resolved by compiler]
445  * \tparam arrInT is the Eigen-like array type of the input [will be resolved by compiler]
446  * \tparam floatT1 is a floating point type [will be resolved by compiler]
447  * \tparam floatT2 is a floating point type [will be resolved by compiler]
448  * \tparam transformT specifies the transformation to use [will be resolved by compiler]
449  *
450  * \test Scenario: Verify direction and accuracy of various image shifts \ref tests_improc_imageTransforms_imageShift "[test doc]"
451  */
452 template<typename arrOutT, typename arrInT, typename floatT1, typename floatT2, typename transformT>
453 void imageShift( arrOutT & transim, ///< [out] Will contain the shifted image. Will be allocated.
454  const arrInT &im, ///< [in] the image to be shifted.
455  floatT1 dx, ///< [in] the amount to shift in the x direction
456  floatT2 dy, ///< [in] the amount to shift in the y direction
457  transformT trans ///< [in] trans is the transformation to use
458  )
459 {
460  typedef typename transformT::arithT arithT;
461 
462  int Nrows, Ncols;
463 
464  const int lbuff = transformT::lbuff;
465  const int width = transformT::width;
466 
467  //If this is a whole pixel, just do that.
468  if(dx == floor(dx) && dy == floor(dy)) return imageShiftWP(transim, im, dx, dy, false);
469 
470  Nrows = im.rows();
471  Ncols = im.cols();
472 
473  int xulim = Nrows-width+lbuff;
474  int yulim = Ncols-width+lbuff;
475 
476  transim.resize(Nrows, Ncols);
477 
478  #ifdef MXLIB_USE_OMP
479  #pragma omp parallel
480  #endif
481  {
482  int i0, j0;
483  // (rx, ry) is fractional residual of shift
484  arithT rx = 1-(dx-floor(dx));
485  arithT ry = 1-(dy-floor(dy));
486 
487  arrOutT kern;
488  kern.resize(width,width);
489  trans(kern, rx, ry);
490 
491  #ifdef MXLIB_USE_OMP
492  #pragma omp for
493  #endif
494  for(int i=0;i<Nrows; ++i)
495  {
496  // (i,j) is position in new image
497  // (i0,j0) is integer position in old image
498 
499  i0 = i-dx;
500 
501  if(i0 <= lbuff || i0 >= xulim)
502  {
503  for(int j=0;j<Ncols; ++j)
504  {
505  transim(i,j) = 0;
506  }
507  continue;
508  }
509 
510  for(int j=0;j<Ncols; ++j)
511  {
512  j0 = j-dy;
513 
514  if(j0 <= lbuff || j0 >= yulim)
515  {
516  transim(i,j) = 0;
517  continue;
518  }
519 
520  transim(i,j) = (im.block(i0-lbuff,j0-lbuff, width, width) * kern).sum();
521  }//for j
522  }//for i
523  }//#pragam omp
524 
525 } //imageShift
526 
527 
528 /// Magnify an image.
529 /** Uses the given transformation type to magnify the input image to the size of the output image.
530  *
531  * Here we assume that the image center is the mxlib standard:
532  * \code
533  x_center = 0.5*(im.rows()-1);
534  y_center = 0.5*(im.cols()-1);
535  * \endcode
536  * Some care is necessary to prevent magnification from shifting the image with respect to this center. The main result is that the
537  * magnification factors (which can be different in x and y) are defined thus:
538  * \code
539  x_mag = (transim.rows()-1.0) / (im.rows()-1.0);
540  y_mag = (transim.cols()-1.0) / (im.cols()-1.0);
541  * \endcode
542  *
543  * Example:
544  * \code
545  im1.resize(512,512);
546  //add image to im1
547  im2.resize(1024,1024);
548  imageMagnify(im2,im1, cubicConvolTransform<double>());
549  \endcode
550  * In this exmple, the image in im1 will be magnified by `1023.0/511.0 = 2.002x` and placed in im2.
551  *
552  * This transform function does not handle edges. If treatment of edges is desired, you must pad the input
553  * image using the desired strategy before calling this function. Note that the padded-size of the input image
554  * will affect the magnification factor.
555  *
556  * \tparam arrOutT is the eigen array type of the output.
557  * \tparam arrInT is the eigen array type of the input.
558  * \tparam transformT specifies the transformation to use.
559  */
560 template<typename arrOutT, typename arrInT, typename transformT>
561 void imageMagnify( arrOutT & transim, ///< [out] contains the magnified image. Must be pre-allocated.
562  const arrInT &im, ///< [in] is the image to be magnified.
563  transformT trans ///< [in] is the transformation to use
564  )
565 {
566  typedef typename transformT::arithT arithT;
567 
568  arithT x0, y0, x,y;
569 
570  int Nrows, Ncols;
571 
572  int i0, j0;
573 
574  const int lbuff = transformT::lbuff;
575  const int width = transformT::width;
576 
577  Nrows = transim.rows();
578  Ncols = transim.cols();
579 
580  int xulim = im.rows()-lbuff - 1;
581  int yulim = im.cols()-lbuff - 1;
582 
583  arithT x_scale = ( (arithT) im.rows()-1.0)/ (transim.rows()-1.0); //this is 1/x_mag
584  arithT y_scale = ( (arithT) im.cols()-1.0)/ (transim.cols()-1.0); //this is 1/y_mag
585 
586  arithT xcen = 0.5*( (arithT) transim.rows() - 1.0);
587  arithT ycen = 0.5*( (arithT) transim.cols() - 1.0);
588 
589  arithT xcen0 = 0.5*( (arithT) im.rows() - 1.0);
590  arithT ycen0 = 0.5*( (arithT) im.cols() - 1.0);
591 
592 // #pragma omp parallel private(x0,y0,i0,j0,x,y) num_threads(4)
593  {
594  arrOutT kern;
595  kern.resize(width,width);
596 
597  for(int j=0;j<Ncols; ++j)
598  {
599  // (i,j) is position in new image
600  // (x0,y0) is true position in old image
601  // (i0,j0) is integer position in old image
602  // (x, y) is fractional residual of (x0-i0, y0-j0)
603 
604  y0 = ycen0 + (j-ycen)*y_scale;
605  j0 = y0;
606 
607  if(j0 < lbuff || j0 >= yulim)
608  {
609  for(int i=0;i<Nrows; ++i)
610  {
611  transim(i,j) = 0;
612  }
613  continue;
614  }
615 
616 // #pragma omp for
617  for(int i=0;i<Nrows; ++i)
618  {
619  x0 = xcen0 + (i-xcen)*x_scale;
620  i0 = x0; //just converting to int
621 
622  if(i0 < lbuff || i0 >= xulim)
623  {
624  transim(i,j) = 0;
625  continue;
626  }
627 
628 
629  //Get the residual
630  x = x0-i0;
631  y = y0-j0;
632 
633  trans(kern, x, y);
634  transim(i,j) = (im.block(i0-lbuff,j0-lbuff, width, width) * kern).sum();
635  }//for j
636  }//for i
637  }//#pragma omp
638 
639 }
640 
641 /// Magnify an image with the cubic convolution interpolator.
642 /** Uses the cubic convolution interpolator to magnify the input image to the size of the output image.
643  *
644  * This is a wrapper for imageMagnify with the transform type specified.
645  *
646  * \tparam arrOutT is the eigen array type of the output.
647  * \tparam arrInT is the eigen array type of the input.
648  *
649  * \overload
650  */
651 template<typename arrOutT, typename arrInT>
652 void imageMagnify( arrOutT & transim, ///< [out] contains the magnified image. Must be pre-allocated.
653  const arrInT &im ///< [in] is the image to be magnified.
654  )
655 {
657 }
658 
659 /// Re-bin an image using the sum, reducing its size while conserving the total flux.
660 /** Optionally this can be the mean instead of the sum filter, in which case total flux is not conserved.
661  */
662 template<typename imageOutT, typename imageInT>
663 int imageRebinSum( imageOutT & imout, ///< [out] the re-binned image. Must be allocated to size which is an integer factor smaller than imin.
664  const imageInT & imin, ///< [in] the image to rebin
665  bool mean = false ///< [in] if true the output is the mean rather than the sum.
666  )
667 {
668  int rebin = imin.rows()/imout.rows();
669  if(imin.cols()/imout.cols() != rebin) return -1;
670 
671  int N = 1;
672  if(mean) N = rebin*rebin;
673  for(int i=0;i<imout.rows(); ++i)
674  {
675  for(int j=0; j<imout.cols(); ++j)
676  {
677  imout(i,j) = imin.block( i*rebin, j*rebin, rebin, rebin).sum()/N;
678  }
679  }
680 
681  return 0;
682 }
683 
684 /// Re-bin an image using the sum, reducing its size while conserving the total flux. Records the value and position of the re-binned max pixel.
685 /** Optionally this can be the mean instead of the sum filter, in which case total flux is not conserved.
686  *
687  * \overload
688  */
689 template<typename imageOutT, typename imageInT>
690 int imageRebinSum( imageOutT & imout, ///< [out] the re-binned image. Must be allocated to size which is an integer factor smaller than imin.
691  int & xMax, ///< [out] the x-locatioin of the max pixel
692  int & yMax, ///< [out] the y-locatioin of the max pixel
693  typename imageOutT::Scalar & pMax, ///< [out] the value of the max pixel
694  const imageInT & imin, ///< [in] the image to rebin
695  bool mean = false ///< [in] if true the output is the mean rather than the sum.
696  )
697 {
698  int rebin = imin.rows()/imout.rows();
699  if(imin.cols()/imout.cols() != rebin) return -1;
700 
701  int N = 1;
702  if(mean) N = rebin*rebin;
703 
704  xMax = 0;
705  yMax = 0;
706  pMax = std::numeric_limits<typename imageOutT::Scalar>::lowest();
707 
708  for(int i=0;i<imout.rows(); ++i)
709  {
710  for(int j=0; j<imout.cols(); ++j)
711  {
712  imout(i,j) = imin.block( i*rebin, j*rebin, rebin, rebin).sum()/N;
713  if(imout(i,j) > pMax)
714  {
715  pMax = imout(i,j);
716  xMax = i;
717  yMax = j;
718  }
719  }
720 
721  }
722 
723  return 0;
724 }
725 
726 /// Re-bin an image using the mean.
727 /** This is a wrapper for imageRebinSum with `mean=true`.
728  */
729 template<typename imageOutT, typename imageInT>
730 int imageRebinMean( imageOutT & imout, ///< [out] the re-binned image. Must be allocated to size which is an integer factor smaller than imin.
731  const imageInT & imin ///< [in] the image to rebin
732  )
733 {
734  return imageRebinSum(imout, imin, true);
735 }
736 
737 /// Re-bin an image using the mean. Records the value and position of the re-binned max pixel.
738 /** This is a wrapper for imageRebinSum with `mean=true`.
739  *
740  * \overload
741  */
742 template<typename imageOutT, typename imageInT>
743 int imageRebinMean( imageOutT & imout, ///< [out] the re-binned image. Must be allocated to size which is an integer factor smaller than imin.
744  int & xMax, ///< [out] the x-locatioin of the max pixel
745  int & yMax, ///< [out] the y-locatioin of the max pixel
746  typename imageOutT::Scalar & pMax, ///< [out] the value of the max pixel
747  const imageInT & imin, ///< [in] the image to rebin
748  bool mean = false ///< [in] if true the output is the mean rather than the sum.
749  )
750 {
751  return imageRebinSum(imout, xMax, yMax, pMax, imin, true);
752 }
753 
754 /// Re-bin an image, takes the mean with a min/max rejection.
755 /** The mean is calculated after rejecting the minimuma and maximum value.
756  */
757 template<typename imageOutT, typename imageInT>
758 int imageRebinMeanReject( imageOutT & imout, ///< [out] the re-binned image. Must be allocated to size which is an integer factor smaller than imin.
759  const imageInT & imin ///< [in] the image to rebin
760  )
761 {
762  int rebin = imin.rows()/imout.rows();
763  if(imin.cols()/imout.cols() != rebin) return -1;
764 
765  int N = rebin*rebin - 2;
766 
767  for(int i=0;i<imout.rows(); ++i)
768  {
769  for(int j=0; j<imout.cols(); ++j)
770  {
771  typename imageOutT::Scalar sum = 0;
772  typename imageOutT::Scalar max = imin(i*rebin, j*rebin);
773  typename imageOutT::Scalar min = imin(i*rebin, j*rebin);
774  for(int k=0;k<rebin;++k)
775  {
776  for(int l=0;l<rebin;++l)
777  {
778  sum += imin(i*rebin+k, j*rebin+l);
779  if(imin(i*rebin+k, j*rebin+l) > max) max = imin(i*rebin+k, j*rebin+l);
780  if(imin(i*rebin+k, j*rebin+l) < min) min = imin(i*rebin+k, j*rebin+l);
781 
782  }
783  }
784  imout(i,j) = (sum-max-min)/N;/**/
785  }
786  }
787 
788  return 0;
789 }
790 
791 /// Re-bin an image, takes the mean with a min/max rejection. Records the value and position of the re-binned max pixel.
792 /** The mean is calculated after rejecting the minimuma and maximum value.
793  *
794  * \overload
795  */
796 template<typename imageOutT, typename imageInT>
797 int imageRebinMeanReject( imageOutT & imout, ///< [out] the re-binned image. Must be allocated to size which is an integer factor smaller than imin.
798  int & xMax, ///< [out] the x-locatioin of the max pixel
799  int & yMax, ///< [out] the y-locatioin of the max pixel
800  typename imageOutT::Scalar & pMax, ///< [out] the value of the max pixel
801  const imageInT & imin ///< [in] the image to rebin
802  )
803 {
804  int rebin = imin.rows()/imout.rows();
805  if(imin.cols()/imout.cols() != rebin) return -1;
806 
807  int N = rebin*rebin - 2;
808 
809  xMax = 0;
810  yMax = 0;
811  pMax = std::numeric_limits<typename imageOutT::Scalar>::lowest();
812 
813  for(int i=0;i<imout.rows(); ++i)
814  {
815  for(int j=0; j<imout.cols(); ++j)
816  {
817  typename imageOutT::Scalar sum = 0;
818  typename imageOutT::Scalar max = imin(i*rebin, j*rebin);
819  typename imageOutT::Scalar min = imin(i*rebin, j*rebin);
820  for(int k=0;k<rebin;++k)
821  {
822  for(int l=0;l<rebin;++l)
823  {
824  sum += imin(i*rebin+k, j*rebin+l);
825  if(imin(i*rebin+k, j*rebin+l) > max) max = imin(i*rebin+k, j*rebin+l);
826  if(imin(i*rebin+k, j*rebin+l) < min) min = imin(i*rebin+k, j*rebin+l);
827 
828  }
829  }
830  imout(i,j) = (sum-max-min)/N;
831 
832  if(imout(i,j) > pMax)
833  {
834  pMax = imout(i,j);
835  xMax = i;
836  yMax = j;
837  }
838  }
839  }
840 
841  return 0;
842 }
843 
844 /// Down-sample an image, reducing its size while conserving the total flux.
845 /** If the old size is an integer multiple of the new size, this is just a re-bin. If not an integer multiple,
846  * the image is interpolated after performing the closest re-bin, and then re-normalized to conserve flux.
847  *
848  * \todo Allow selection of interpolator, providing a default version.
849  */
850 template<typename imageOutT, typename imageInT>
851 void imageDownSample(imageOutT & imout, const imageInT & imin)
852 {
853  typedef typename imageOutT::Scalar Scalar;
854 
855 
856  //Record this for normalization later
857  Scalar inputTotal = fabs(imin.sum());
858 
859 
860  //As a first step, rebin to nearest whole pixel factor which is larger than the desired output size
861  int closestRebin = imin.rows()/imout.rows();//, imin.cols()/imout.cols() );
862 
863  float sample = ( (float) imin.rows())/ closestRebin;
864 
865  while( sample != floor(sample))
866  {
867  --closestRebin;
868  if(closestRebin == 1) break;
869  sample = ( (float) imin.rows())/ closestRebin;
870  }
871 
872  //Eigen::Array<Scalar, Eigen::Dynamic, Eigen::Dynamic> temp;
873  imageOutT temp;
874  temp.resize( imin.rows()/closestRebin, imin.cols()/closestRebin);
875 
876  for(int i=0;i<temp.rows(); ++i)
877  {
878  for(int j=0; j<temp.cols(); ++j)
879  {
880  temp(i,j) = imin.block( i*closestRebin, j*closestRebin, closestRebin, closestRebin).sum();
881  }
882  }
883 
884  //If the output image is now the requested size return.
885  if(temp.rows() == imout.rows() && temp.cols() == imout.cols())
886  {
887  imout = temp;
888  return;
889  }
890  //Otherwise, re-sample using bilinear interpolation.
891  typedef bilinearTransform<Scalar> transformT;
892 
893  transformT trans;
894  //Eigen::Array<Scalar, -1,-1> kern;
895  imageOutT kern;
896 
897  const int lbuff = transformT::lbuff;
898  const int width = transformT::width;
899 
900  for(int i=0;i<imout.rows(); ++i)
901  {
902  for(int j=0;j<imout.cols(); ++j)
903  {
904  double x = ( (double) i/ imout.rows())*temp.rows();
905  double y = ( (double) j/ imout.cols())*temp.cols();
906 
907  trans(kern, x-floor(x), y-floor(y));
908 
909  imout(i,j) = (temp.block( floor(x)-lbuff, floor(y)-lbuff, width, width)*kern).sum();
910 
911  }
912  }
913 
914  //Normalize
915  Scalar outputTotal = fabs(imout.sum());
916  imout *= inputTotal/outputTotal;
917 }
918 
919 ///@}
920 
921 } //namespace improc
922 } //namespace mx
923 
924 
925 
926 
927 #endif //improc_imageTransforms_hpp
constexpr units::realT c()
The speed of light.
Definition: constants.hpp:60
constexpr units::realT k()
Boltzmann Constant.
Definition: constants.hpp:71
int imageRebinSum(imageOutT &imout, int &xMax, int &yMax, typename imageOutT::Scalar &pMax, const imageInT &imin, bool mean=false)
Re-bin an image using the sum, reducing its size while conserving the total flux. Records the value a...
bilinearTransform< double > bilinearTransd
Typedef for bilinearTransform with double precision.
int imageRebinMeanReject(imageOutT &imout, int &xMax, int &yMax, typename imageOutT::Scalar &pMax, const imageInT &imin)
Re-bin an image, takes the mean with a min/max rejection. Records the value and position of the re-bi...
void imageRotate(arrT &transim, const arrT2 &im, floatT dq, transformT trans)
Rotate an image represented as an eigen array.
cubicConvolTransform< double > cubicConvolTransd
Typedef for cubicConvolTransform with double precision.
void imageMagnify(arrOutT &transim, const arrInT &im)
Magnify an image with the cubic convolution interpolator.
int imageRebinMean(imageOutT &imout, int &xMax, int &yMax, typename imageOutT::Scalar &pMax, const imageInT &imin, bool mean=false)
Re-bin an image using the mean. Records the value and position of the re-binned max pixel.
void imageShift(arrOutT &transim, const arrInT &im, floatT1 dx, floatT2 dy, transformT trans)
Shift an image.
bilinearTransform< float > bilinearTransf
Typedef for bilinearTransform with single precision.
cubicConvolTransform< float > cubicConvolTransf
Typedef for cubicConvolTransform with single precision.
void imageDownSample(imageOutT &imout, const imageInT &imin)
Down-sample an image, reducing its size while conserving the total flux.
void imageShiftWPScale(outputArrT &out, inputArrT &in, scaleArrT &scale, int dx, int dy)
Shift an image by whole pixels, wrapping around, with a scaling image applied to the shifted image.
void imageShiftWP(outputArrT &out, inputArrT &in, int dx, int dy, bool wrap=true)
Shift an image by whole pixels with (optional) wrapping.
The mxlib c++ namespace.
Definition: mxError.hpp:107
Transformation by bi-linear interpolation.
Transformation by cubic convolution interpolation.
arithT cubic
The kernel parameter. The default value -0.5 gives the bicubic spline interpolator.
cubicConvolTransform(const cubicConvolTransform &t)
Copy c'tor.
arithT cubicConvolKernel(arithT d)
Calculate the kernel value for a given residual.
cubicConvolTransform(arithT c)
Construct setting the kernel parameter.
_arithT arithT
The type in which all calculations are performed.