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