LCOV - code coverage report
Current view: top level - include/wfp - imagingUtils.hpp (source / functions) Coverage Total Hit
Test: mxlib Lines: 32.7 % 55 18
Test Date: 2026-08-30 22:23:37 Functions: 40.0 % 5 2

            Line data    Source code
       1              : /** \file imagingUtils.hpp
       2              :  * \brief Utilities for modeling image formation
       3              :  * \ingroup imaging
       4              :  * \author Jared R. Males (jaredmales@gmail.com)
       5              :  *
       6              :  */
       7              : 
       8              : //***********************************************************************//
       9              : // Copyright 2015, 2016, 2017 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 imagingUtils_hpp
      28              : #define imagingUtils_hpp
      29              : 
      30              : #include <cmath>
      31              : 
      32              : #include "../mxlib.hpp"
      33              : #include "../improc/eigenImage.hpp"
      34              : #include "../math/constants.hpp"
      35              : 
      36              : #include "imagingArray.hpp"
      37              : 
      38              : namespace mx
      39              : {
      40              : 
      41              : namespace wfp
      42              : {
      43              : 
      44              : /// Calculate the angular plate scale (radians per pixel) of an image after propagation by FFT.
      45              : /**
      46              :  *
      47              :  * \returns the platescale of the wavefront after propagation by FFT, in radians per pixel.
      48              :  *
      49              :  * \tparam realT a real floating point type
      50              :  *
      51              :  * \ingroup imaging
      52              :  */
      53              : template <typename realT>
      54              : realT fftPlateScale( size_t pixels,        ///< [in] the linear dimension of the FFT (including 0 pad, etc.)
      55              :                      realT metersPerPixel, ///< [in] the scale of the input wavefront [m/pix]
      56              :                      realT lambda          ///< [in] the wavelength of the wavefront [m]
      57              : )
      58              : {
      59              :     return ( lambda / metersPerPixel ) * ( 1. / pixels );
      60              : }
      61              : 
      62              : /// Fill in an Eigen-like array with a circular pupil mask.
      63              : /** Sets any pixel which is at rad \<= r \< rad+(1.0/overscan) pixels to rho = 1,
      64              :  *
      65              :  *
      66              :  * \retval 0 on success
      67              :  * \retval -1 on error
      68              :  *
      69              :  * \ingroup imaging
      70              :  */
      71              : template <class arrayT>
      72            0 : int circularPupil( arrayT &m, ///< [in.out] is the allocated Array.  Dimensions are used to create the pupil.
      73              :                    typename arrayT::Scalar eps = 0, ///< [in] [optional] is the central obscuration.  0-1, default is 0.
      74              :                    typename arrayT::Scalar rad = 0, ///< [in] [optional] is the desired radius. If rad \<= 0, then the
      75              :                                                     ///< maximum radius based on dimensions of m is used. Default is 0.
      76              :                    typename arrayT::Scalar overscan = 0 ///< [in] [optional] overscan in fractional pixels, to include
      77              :                                                         ///< partial pixels on the edge. Default is 0.
      78              : )
      79              : {
      80              : 
      81            0 :     if( eps < 0 )
      82              :     {
      83            0 :         internal::mxlib_error_report( error_t::invalidarg, "Central obscuration can not be < 0." );
      84            0 :         return -1;
      85              :     }
      86              : 
      87            0 :     if( eps > 1 )
      88              :     {
      89            0 :         internal::mxlib_error_report( error_t::invalidarg, "Central obscuration can not be > 1." );
      90            0 :         return -1;
      91              :     }
      92              : 
      93            0 :     size_t l0 = m.rows();
      94            0 :     size_t l1 = m.cols();
      95              : 
      96              :     typename arrayT::Scalar r;
      97            0 :     typename arrayT::Scalar xc = 0.5 * ( l0 - 1 );
      98            0 :     typename arrayT::Scalar yc = 0.5 * ( l1 - 1 );
      99              : 
     100            0 :     if( rad <= 0 )
     101            0 :         rad = 0.5 * std::min( l0 - 1, l1 - 1 );
     102              : 
     103            0 :     for( size_t i = 0; i < l0; i++ )
     104              :     {
     105            0 :         for( size_t j = 0; j < l1; j++ )
     106              :         {
     107            0 :             r = std::sqrt( std::pow( i - xc, 2 ) + std::pow( j - yc, 2 ) );
     108              : 
     109            0 :             if( r <= rad + ( overscan ) && r >= eps * rad )
     110            0 :                 m( i, j ) = 1;
     111              :             else
     112            0 :                 m( i, j ) = 0;
     113              :         }
     114              :     }
     115              : 
     116            0 :     return 0;
     117              : }
     118              : 
     119              : /// Draw a line in an image
     120              : /** \todo should handle width much more intelligently, this only works for ~45 degree lines.
     121              :  *
     122              :  * \tparam arrayT is an Eigen-like array with public typedef Scalar
     123              :  */
     124              : template <class arrayT>
     125              : void drawLine( arrayT &im,                       ///< [in.out] The input image, modified.
     126              :                typename arrayT::Scalar x0,       ///< [in] the x value, relative to image center, of the starting point
     127              :                typename arrayT::Scalar y0,       ///< [in] the y value, relative to image center, of the starting point
     128              :                typename arrayT::Scalar x1,       ///< [in] the x value, relative to image center, of the end point
     129              :                typename arrayT::Scalar y1,       ///< [in] the y value, relative to image center, of the end point
     130              :                typename arrayT::Scalar halfWidth ///< [in] the half-width of the line.
     131              : )
     132              : {
     133              : 
     134              :     int d1 = im.rows();
     135              :     int d2 = im.cols();
     136              : 
     137              :     typename arrayT::Scalar xc, yc;
     138              : 
     139              :     xc = 0.5 * ( im.rows() - 1 );
     140              :     yc = 0.5 * ( im.cols() - 1 );
     141              : 
     142              :     typename arrayT::Scalar m = ( y1 - y0 ) / ( x1 - x0 );
     143              :     int y;
     144              : 
     145              :     if( x1 > x0 )
     146              :     {
     147              :         for( int x = x0; x <= x1; ++x )
     148              :         {
     149              :             y = y0 + ( x - x0 ) * m;
     150              : 
     151              :             for( int i = 0; i <= halfWidth; ++i )
     152              :             {
     153              :                 if( x + xc >= 0 && x + xc < d1 && y + yc + i >= 0 && y + yc + i < d2 )
     154              :                     im( x + xc, y + yc + i ) = 0;
     155              :                 if( x + xc >= 0 && x + xc < d1 && y + yc - i >= 0 && y + yc - i < d2 )
     156              :                     im( x + xc, y + yc - i ) = 0;
     157              :             }
     158              :         }
     159              :     }
     160              :     else
     161              :     {
     162              :         for( int x = x1; x <= x0; ++x )
     163              :         {
     164              :             y = y0 + ( x - x0 ) * m;
     165              : 
     166              :             for( int i = 0; i <= halfWidth; ++i )
     167              :             {
     168              :                 if( x + xc >= 0 && x + xc < d1 && y + yc + i >= 0 && y + yc + i < d2 )
     169              :                     im( x + xc, y + yc + i ) = 0;
     170              :                 if( x + xc >= 0 && x + xc < d1 && y + yc - i >= 0 && y + yc - i < d2 )
     171              :                     im( x + xc, y + yc - i ) = 0;
     172              :             }
     173              :         }
     174              :     }
     175              : }
     176              : 
     177              : /// Create a complex pupil plane wavefront from a real amplitude mask.
     178              : /** The input real amplitude mask is placed in the center of a 0-padded complex array.
     179              :  *
     180              :  * \param [out] complexPupil the complex pupil plane wavefront
     181              :  * \param [in] realPupil a real amplitude mask.
     182              :  * \param [in] wavefrontSizePixels the desired size of the ouput wavefront, should be at least as big as realPupil
     183              :  *
     184              :  * \ingroup imaging
     185              :  */
     186              : template <typename arrayOutT, typename arrayInT>
     187            1 : void makeComplexPupil( arrayOutT &complexPupil, const arrayInT &realPupil, int wavefrontSizePixels )
     188              : {
     189              : 
     190            1 :     complexPupil.resize( wavefrontSizePixels, wavefrontSizePixels );
     191            1 :     complexPupil.setZero();
     192              : 
     193              :     // Lower-left corner of insertion region
     194            1 :     int bl = 0.5 * ( complexPupil.rows() - 1 ) - 0.5 * ( realPupil.rows() - 1. );
     195              : 
     196          129 :     for( int i = 0; i < realPupil.cols(); ++i )
     197              :     {
     198        16512 :         for( int j = 0; j < realPupil.rows(); ++j )
     199              :         {
     200        16384 :             complexPupil( bl + i, bl + j ) =
     201        32768 :                 typename arrayOutT::Scalar( realPupil( i, j ), 0 ); //*exp( typename arrayOutT::Scalar(0,1));
     202              :         }
     203              :     }
     204              :     // complexPupil.block(bl, bl, realPupil.rows(), realPupil.rows()) = realPupil*std::complex<realT>(1,0);
     205            1 : }
     206              : 
     207              : /// Create a complex wavefront from a real amplitude and a real phase.
     208              : /** The wavefront is placed in the center of a 0-padded complex array.
     209              :  *
     210              :  * \param [out] complexWavefront the complex pupil plane wavefront
     211              :  * \param [in] realAmplitude is the real-valued amplitude.
     212              :  * \param [in] realPhase is the real-valued phase in radians, same size as realAmplitude
     213              :  * \param [in] wavefrontSizePixels the desired size of the ouput wavefront, should be at least as big as the real arrays
     214              :  *
     215              :  * \ingroup imaging
     216              :  */
     217              : template <typename arrayOutT, typename arrayInT>
     218            0 : void makeComplexPupil( arrayOutT &complexWavefront,
     219              :                        const arrayInT &realAmplitude,
     220              :                        const arrayInT &realPhase,
     221              :                        int wavefrontSizePixels )
     222              : {
     223              : 
     224            0 :     complexWavefront.resize( wavefrontSizePixels, wavefrontSizePixels );
     225            0 :     complexWavefront.setConstant( typename arrayOutT::Scalar( 0, 0 ) );
     226              : 
     227              :     // Lower-left corner of insertion region
     228            0 :     int bl = 0.5 * ( complexWavefront.rows() - 1 ) - 0.5 * ( realAmplitude.rows() - 1. );
     229              : 
     230            0 :     for( int i = 0; i < realAmplitude.cols(); ++i )
     231              :     {
     232            0 :         for( int j = 0; j < realAmplitude.rows(); ++j )
     233              :         {
     234            0 :             complexWavefront( bl + j, bl + i ) =
     235            0 :                 realAmplitude( j, i ) * exp( ( typename arrayOutT::Scalar( 0, 1 ) ) * realPhase( j, i ) );
     236              :         }
     237              :     }
     238            0 : }
     239              : 
     240              : /// Apply a tilt to a wavefront
     241              : /**
     242              :  * \param complexWavefront [in.out] the complex wavefront to tilt, will be modified on output
     243              :  * \param xTilt [input] the amount of tilt in the x direction, in pixels
     244              :  * \param yTilt [input] the amount of tilt in the y direction, in pixels
     245              :  *
     246              :  * \ingroup imaging
     247              :  */
     248              : template <typename wavefrontT>
     249              : void tiltWavefront( wavefrontT &complexWavefront,
     250              :                     typename wavefrontT::Scalar::value_type xTilt,
     251              :                     typename wavefrontT::Scalar::value_type yTilt )
     252              : {
     253              :     typedef typename wavefrontT::Scalar complexT;
     254              :     typedef typename wavefrontT::Scalar::value_type realT;
     255              : 
     256              :     realT pi = math::pi<realT>();
     257              : 
     258              :     int wfsSizeX = complexWavefront.cols();
     259              :     int wfsSizeY = complexWavefront.rows();
     260              : 
     261              :     realT xCen = 0.5 * ( wfsSizeX - 1 );
     262              :     realT yCen = 0.5 * ( wfsSizeY - 1 );
     263              : 
     264              :     realT argX = 2.0 * pi / ( wfsSizeX - 1.0 );
     265              :     realT argY = 2.0 * pi / ( wfsSizeY - 1.0 );
     266              : 
     267              :     for( int ii = 0; ii < wfsSizeX; ++ii )
     268              :     {
     269              :         for( int jj = 0; jj < wfsSizeY; ++jj )
     270              :         {
     271              :             complexWavefront( ii, jj ) =
     272              :                 complexWavefront( ii, jj ) *
     273              :                 exp( complexT( (realT)0., argX * xTilt * ( ii - xCen ) + argY * yTilt * ( jj - yCen ) ) );
     274              :         }
     275              :     }
     276              : }
     277              : 
     278              : /// Extract a block from one image and insert it into a second
     279              : template <typename imageT1,
     280              :           typename imageT2>
     281            0 : void extractBlock( imageT1 &dest, ///< [in/out] the image in which to place the extracted block.  Must be pre-allocated.
     282              :                    int imX0,      ///< [in] the x/row-coord of the lower left pixel of the destination region.
     283              :                    int imXsz,     ///< [in] the x size (number of rows) of the block
     284              :                    int imY0,      ///< [in] the y/col-coord of the lower left pixel of the desination region
     285              :                    int imYsz,     ///< [in] the y=size (number of cols) ov the block
     286              :                    imageT2 &src,  ///< [in] the source of the block.  Must be large enough.
     287              :                    int wfX0,      ///< [in] the x/row-coord of the lower left pixel of the source region
     288              :                    int wfY0 )     ///< [in] the y/col-coord of the lower left pixel of the source region
     289              : {
     290            0 :     int dest_cols = dest.cols();
     291              : 
     292            0 :     int src_cols = src.cols();
     293              : 
     294              :     typedef typename imageT1::Scalar dataT;
     295              : 
     296              :     dataT *dest_data;
     297              :     dataT *src_data;
     298              : 
     299            0 :     for( int j = 0; j < imYsz; ++j )
     300              :     {
     301            0 :         dest_data = &dest.data()[imX0 + ( imY0 + j ) * dest_cols];
     302            0 :         src_data = &src.data()[wfX0 + ( wfY0 + j ) * src_cols];
     303              : 
     304            0 :         memcpy( dest_data, src_data, sizeof( dataT ) * imXsz );
     305              :     }
     306            0 : }
     307              : 
     308              : /// Extract a pixels from one image and insert them into a second based on a mask
     309              : /** Only pixels with a non-zero value in mask are changed in dest to have the value in src.  Other pixels are not
     310              :  * modified.
     311              :  */
     312              : template <typename imageT1,
     313              :           typename imageT2,
     314              :           typename imageT3>
     315              : void extractMaskedPixels( imageT1 &dest, ///< [in/out] the image in which to place the extracted pixels.  Must be the
     316              :                                          ///< same size as src and mask.
     317              :                           const imageT2 &src, ///< [in] the source of the pixels.  Must be the same size as mask.
     318              :                           const imageT3 &mask ///< [in] the mask image, where any value other than 0 indicates a pixel
     319              :                                               ///< to extract.  Must be the same size as src.
     320              : )
     321              : {
     322              :     if( dest.rows() != src.rows() )
     323              :     {
     324              :         throw mx::exception( error_t::sizeerr, "dest and src do not have same size (rows)" );
     325              :     }
     326              : 
     327              :     if( dest.cols() != src.cols() )
     328              :     {
     329              :         throw mx::exception( error_t::sizeerr, "dest and src do not have same size (cols)" );
     330              :     }
     331              : 
     332              :     if( src.rows() != mask.rows() )
     333              :     {
     334              :         throw mx::exception( error_t::sizeerr, "src and mask do not have same size (rows)" );
     335              :     }
     336              : 
     337              :     if( src.cols() != mask.cols() )
     338              :     {
     339              :         throw mx::exception( error_t::sizeerr, "src and mask do not have same size (cols)" );
     340              :     }
     341              : 
     342              :     for( int cc = 0; cc < dest.cols(); ++cc )
     343              :     {
     344              :         for( int rr = 0; rr < dest.rows(); ++rr )
     345              :         {
     346              :             if( mask( rr, cc ) != 0 )
     347              :             {
     348              :                 dest( rr, cc ) = src( rr, cc );
     349              :             }
     350              :         }
     351              :     }
     352              : }
     353              : 
     354              : template <typename realImageT, typename complexImageT>
     355            2 : void extractIntensityImage(
     356              :     realImageT &im, int imX0, int imXsz, int imY0, int imYsz, complexImageT &wf, int wfX0, int wfY0 )
     357              : {
     358            2 :     int im_rows = im.cols();
     359              : 
     360            2 :     int wf_rows = wf.cols();
     361              : 
     362              :     typename realImageT::Scalar *im_data;
     363              :     typename complexImageT::Scalar *wf_data;
     364              : 
     365         1026 :     for( int j = 0; j < imXsz; ++j )
     366              :     {
     367         1024 :         im_data = &im.data()[imX0 + ( imY0 + j ) * im_rows];
     368         1024 :         wf_data = &wf.data()[wfX0 + ( wfY0 + j ) * wf_rows];
     369       525312 :         for( int i = 0; i < imYsz; ++i )
     370              :         {
     371       524288 :             im_data[i] = norm( wf_data[i] );
     372              :         }
     373              :     }
     374            2 : }
     375              : 
     376              : /// Extract the intensity image from a complex wavefront and accumulate the result
     377              : /** Can work on a subset of either or both of the image and the wavefront.  This way only
     378              :  * a small part of the image can be extracted for an oversampled wavefront.
     379              :  */
     380              : template <typename realImageT, typename complexImageT, int cudaGPU = 0>
     381              : void extractIntensityImageAccum( realImageT &im,    /**<[out] The real image to populate with intensity */
     382              :                                  int imX0,          /**<[in] The x-coord of the starting image pixel*/
     383              :                                  int imXsz,         /**<[in] The number of x image pixels*/
     384              :                                  int imY0,          /**<[in] The y-coord of the starting image pixel */
     385              :                                  int imYsz,         /**<[in] The number of y image pixels */
     386              :                                  complexImageT &wf, /**<[in] The complex wavefront*/
     387              :                                  int wfX0,          /**<[in] The x-coord of the starting wavefront pixel*/
     388              :                                  int wfY0           /**<[in] The y-coord of the starting wavefront pixel*/
     389              : );
     390              : 
     391              : 
     392              : 
     393              : // CPU versions
     394              : template <>
     395              : void extractIntensityImageAccum<improc::eigenImage<float>, improc::eigenImage<std::complex<float>>, 0>(
     396              :     improc::eigenImage<float> &im, int imX0, int imXsz, int imY0, int imYsz, improc::eigenImage<std::complex<float>> &wf, int wfX0, int wfY0 );
     397              : 
     398              : template <>
     399              : void extractIntensityImageAccum<improc::eigenImage<double>, improc::eigenImage<std::complex<double>>, 0>(
     400              :     improc::eigenImage<double> &im, int imX0, int imXsz, int imY0, int imYsz, improc::eigenImage<std::complex<double>> &wf, int wfX0, int wfY0 );
     401              : 
     402              : 
     403              : #ifdef MXLIB_CUDA
     404              : 
     405              : 
     406              : // GPU versions
     407              : template <>
     408              : void extractIntensityImageAccum<cuda::cudaPtr<float>, cuda::cudaPtr<std::complex<float>>, 1>(
     409              :     cuda::cudaPtr<float> &im, int imX0, int imXsz, int imY0, int imYsz, cuda::cudaPtr<std::complex<float>> &wf, int wfX0, int wfY0 );
     410              : 
     411              : template <>
     412              : void extractIntensityImageAccum<cuda::cudaPtr<double>, cuda::cudaPtr<std::complex<double>>, 1>(
     413              :     cuda::cudaPtr<double> &im, int imX0, int imXsz, int imY0, int imYsz, cuda::cudaPtr<std::complex<double>> &wf, int wfX0, int wfY0 );
     414              : 
     415              : #endif
     416              : 
     417              : } // namespace wfp
     418              : } // namespace mx
     419              : 
     420              : #endif //__imagingUtils_hpp__
        

Generated by: LCOV version 2.0-1