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