LCOV - code coverage report
Current view: top level - include/improc - imageMasks.hpp (source / functions) Coverage Total Hit
Test: mxlib Lines: 100.0 % 106 106
Test Date: 2026-08-30 22:23:37 Functions: 100.0 % 13 13

            Line data    Source code
       1              : /** \file imageMasks.hpp
       2              :  * \brief Declares and defines functions to work with image masks
       3              :  * \ingroup image_processing_files
       4              :  * \author Jared R. Males (jaredmales@gmail.com)
       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_imageMasks_hpp
      28              : #define improc_imageMasks_hpp
      29              : 
      30              : #include "../math/constants.hpp"
      31              : #include "../math/geo.hpp"
      32              : 
      33              : #include "eigenImage.hpp"
      34              : #include "imageTransforms.hpp"
      35              : 
      36              : namespace mx
      37              : {
      38              : 
      39              : namespace improc
      40              : {
      41              : 
      42              : /// Fills in the cells of an Eigen 2D Array with their radius from the center
      43              : /** \ingroup image_masks
      44              :  *
      45              :  * \tparam eigenT is an Eigen-like 2D array type
      46              :  */
      47              : template <class eigenT>
      48            3 : void radiusImage( eigenT &m, ///< [out] the allocated radius array, will be filled in with radius values.
      49              :                   typename eigenT::Scalar xc,       ///< [in] the x center
      50              :                   typename eigenT::Scalar yc,       ///< [in] the y center
      51              :                   typename eigenT::Scalar scale = 1 ///< [in] [optional] a scaling to apply to each value (default = 1)
      52              : )
      53              : {
      54              :     typedef typename eigenT::Scalar arithT;
      55              : 
      56              :     arithT f_x, f_y;
      57              : 
      58            3 :     size_t dim1 = m.rows();
      59            3 :     size_t dim2 = m.cols();
      60              : 
      61           24 :     for( size_t i = 0; i < dim1; i++ )
      62              :     {
      63           21 :         f_x = ( i - xc ) * ( i - xc );
      64              : 
      65          192 :         for( size_t j = 0; j < dim2; j++ )
      66              :         {
      67          171 :             f_y = ( j - yc ) * ( j - yc );
      68              : 
      69          171 :             m( i, j ) = sqrt( f_x + f_y ) * scale;
      70              :         }
      71              :     }
      72            3 : }
      73              : 
      74              : /// Fills in the cells of Eigen-like 2D Array with their radius from the canonical center
      75              : /** \ingroup image_masks
      76              :  *
      77              :  * The center is @f$ (x_c, y_c) = (0.5*(dim_1-1), 0.5*(dim_2 -1)) @f$.
      78              :  *
      79              :  * \tparam eigenT is an Eigen-like 2D array type
      80              :  */
      81              : template <class eigenT>
      82            2 : void radiusImage( eigenT &m,                        /**< [out] the allocated radius array,
      83              :                                                                will be filled in with radius values.*/
      84              :                   typename eigenT::Scalar scale = 1 /**< [in] [optional] a scaling to apply to
      85              :                                                                          each value (default = 1) */ )
      86              : {
      87              :     typedef typename eigenT::Scalar arithT;
      88              : 
      89              :     arithT xc, yc;
      90              : 
      91            2 :     xc = 0.5 * ( m.rows() - 1 );
      92            2 :     yc = 0.5 * ( m.cols() - 1 );
      93              : 
      94            2 :     radiusImage( m, xc, yc, scale );
      95            2 : }
      96              : 
      97              : /// Fills in the cells of an Eigen-like 2D Array with their angle relative to the center
      98              : /** \ingroup image_masks
      99              :  *
     100              :  * \tparam angleT is the angle type, either radiansT<realT> or degreesT<realT>.  Note that realT sets the type for all
     101              :  * arithmetic. \tparam eigenT is an Eigen-like 2D array type
     102              :  */
     103              : template <class angleT, class eigenT>
     104              : void angleImage( eigenT &m,                 /**< [out] the allocated angle array.  Will be filled
     105              :                                                        in with angle values.*/
     106              :                  typename angleT::realT xc, /**< [in] the x center*/
     107              :                  typename angleT::realT yc /**< [in] the y center */ )
     108              : {
     109              :     typedef typename angleT::realT realT;
     110              : 
     111              :     for( size_t j = 0; j < m.cols(); j++ )
     112              :     {
     113              :         realT f_y = ( static_cast<realT>( j ) - yc );
     114              : 
     115              :         for( size_t i = 0; i < m.rows(); i++ )
     116              :         {
     117              :             realT f_x = ( static_cast<realT>( i ) - xc );
     118              : 
     119              :             m( i, j ) = fmod( atan2( f_y, f_x ) + math::two_pi<realT>(), math::two_pi<realT>() ) * angleT::scale;
     120              :         }
     121              :     }
     122              : }
     123              : 
     124              : /// Fills in the cells of Eigen 2D Array with their angle relative the canonical center
     125              : /** \ingroup image_masks
     126              :  *
     127              :  * The center is @f$ (x_c, y_c) = (0.5*(dim_1-1), 0.5*(dim_2 -1)) @f$.
     128              :  *
     129              :  * \tparam angleT is the angle type, either radiansT<realT> or degreesT<realT>.  Note that realT sets the type for all
     130              :  * arithmetic. \tparam eigenT is an Eigen-like 2D array type
     131              :  */
     132              : template <class angleT, class eigenT>
     133              : void angleImage( eigenT &m /** < [out] the allocated angle array.  Will be filled in with angle values. */ )
     134              : {
     135              :     typedef typename angleT::realT realT;
     136              : 
     137              :     realT xc = 0.5 * ( m.rows() - 1 );
     138              :     realT yc = 0.5 * ( m.cols() - 1 );
     139              : 
     140              :     angleImage<angleT>( m, xc, yc );
     141              : }
     142              : 
     143              : /// Fills in the cells of Eigen-like arrays with their radius amd angle relative to the center
     144              : /** \ingroup image_masks
     145              :  *
     146              :  * \tparam angleT is the angle type, either radiansT<realT> or degreesT<realT>.  Note that realT sets the type for all
     147              :  * arithmetic. \tparam eigenT1 is an Eigen-like 2D array type.  Should be resolved by compiler. \tparam eigenT2 is an
     148              :  * Eigen-like 2D array type.  Should be resolved by compiler.
     149              :  *
     150              :  */
     151              : template <class angleT, class eigenT1, class eigenT2>
     152            5 : void radAngImage( eigenT1 &rIm,                     /**< [out] the allocated radius array, will be
     153              :                                                                filled in with radius values.*/
     154              :                   eigenT2 &qIm,                     /**< [out] the angle array, will be re-sized to match rIm.
     155              :                                                                Will be filled in with angle values.*/
     156              :                   typename angleT::realT xc,        /**< [in] the x center*/
     157              :                   typename angleT::realT yc,        /**< [in] the y center*/
     158              :                   typename angleT::realT rscale = 1 /**< [in] [optional] a scaling to apply to each
     159              :                                                                          radius value. Default is 1.0.*/)
     160              : {
     161              :     typedef typename angleT::realT realT;
     162              : 
     163            5 :     qIm.resize( rIm.rows(), rIm.cols() );
     164              : 
     165           37 :     for( int cc = 0; cc < rIm.cols(); ++cc )
     166              :     {
     167           32 :         realT f_y = ( ( static_cast<realT>( cc ) ) - yc );
     168              : 
     169          236 :         for( int rr = 0; rr < rIm.rows(); ++rr )
     170              :         {
     171          204 :             realT f_x = ( ( static_cast<realT>( rr ) ) - xc );
     172              : 
     173          204 :             rIm( rr, cc ) = std::sqrt( f_x * f_x + f_y * f_y ) * rscale;
     174          204 :             qIm( rr, cc ) = fmod( atan2( f_y, f_x ) + math::two_pi<realT>(), math::two_pi<realT>() ) * angleT::scale;
     175              :         }
     176              :     }
     177            5 : }
     178              : 
     179              : template <typename vecT>
     180              : struct maskCoordFormat;
     181              : 
     182              : template <>
     183              : struct maskCoordFormat<std::vector<size_t>>
     184              : {
     185          134 :     static size_t coord( int i, int j, int rows )
     186              :     {
     187          134 :         return j * rows + i;
     188              :     }
     189              : };
     190              : 
     191              : template <>
     192              : struct maskCoordFormat<std::vector<std::vector<int>>>
     193              : {
     194           95 :     static std::vector<int> coord( int i, int j, int rows )
     195              :     {
     196           95 :         return std::vector<int>( { i, j } );
     197              :     }
     198              : };
     199              : 
     200              : /// Get the coordinates of an annular region in an image
     201              : /**
     202              :  * \ingroup image_masks
     203              :  *
     204              :  * \tparam angleT is the angle type, either radiansT<realT> or degreesT<realT>.  Note that realT sets the type for all
     205              :  * arithmetic.
     206              :  * \tparam eigenT1 is an Eigen-like 2D array type.  Should be resolved by compiler.
     207              :  * \tparam eigenT2 is an Eigen-like 2D array type.  Should be resolved by compiler.
     208              :  * \tparam eigenT3 is an Eigen-like 2D array type.  Should be resolved by compiler.
     209              :  *
     210              :  * \returns a vector containing the coordinates formatted bt maskCoordFormat of the region defined
     211              :  * by the input parameters
     212              :  */
     213              : template <typename vecT, typename angleT, typename eigenT1, typename eigenT2, typename eigenT3 = eigenT1>
     214           13 : vecT annulusCoordsWorker( const eigenT1 &rIm,               /**< [in] a radius image of the type produced by
     215              :                                                                                 \ref radiusImage */
     216              :                           const eigenT2 &qIm,               /**< [in] an angle image of the type produce by
     217              :                                                                       \ref angleImage */
     218              :                           typename angleT::realT xcen,      /**< [in] the x center of the image */
     219              :                           typename angleT::realT ycen,      /**< [in] the y center of the image */
     220              :                           typename angleT::realT min_r,     /**< [in] the minimum radius of the region */
     221              :                           typename angleT::realT max_r,     /**< [in] the maximum radius of the region */
     222              :                           typename angleT::realT min_q,     /**< [in] the minimum angle of the region. */
     223              :                           typename angleT::realT max_q,     /**< [in] the maximum angle of the region. */
     224              :                           eigenT3 *mask = 0,                /**< [in] [optional] pointer to a mask image, only
     225              :                                                                                  pixels of value 1 are included
     226              :                                                                                  in the indices. */
     227              :                           typename angleT::realT pixbuf = 0 /**< [in] [optional] amount subtracted from the inclusive
     228              :                                                                                  inner radius and added to the exclusive
     229              :                                                                                  outer radius */ )
     230              : {
     231              : 
     232           13 :     vecT idx;
     233              : 
     234           13 :     const typename angleT::realT maxBufferedRadius = max_r + pixbuf;
     235           13 :     if( maxBufferedRadius <= 0 )
     236              :     {
     237            1 :         return idx;
     238              :     }
     239              : 
     240           12 :     int x0 = static_cast<int>( std::floor( xcen - maxBufferedRadius ) );
     241           12 :     if( x0 < 0 )
     242            3 :         x0 = 0;
     243              : 
     244           12 :     int x1 = static_cast<int>( std::ceil( xcen + maxBufferedRadius ) );
     245           12 :     if( x1 > rIm.rows() )
     246            3 :         x1 = rIm.rows();
     247              : 
     248           12 :     int y0 = static_cast<int>( std::floor( ycen - maxBufferedRadius ) );
     249           12 :     if( y0 < 0 )
     250            3 :         y0 = 0;
     251              : 
     252           12 :     int y1 = static_cast<int>( std::ceil( ycen + maxBufferedRadius ) );
     253           12 :     if( y1 > rIm.cols() )
     254            3 :         y1 = rIm.cols();
     255              : 
     256              :     // Normalize the angles by mod
     257           12 :     min_q = math::angleMod<angleT>( min_q );
     258              :     // If max_q is exactly 360/2pi (to within a tol of 100*eps) we don't mod, other mod.
     259           12 :     if( fabs( max_q - angleT::full ) > 100 * std::numeric_limits<typename angleT::realT>::epsilon() )
     260              :     {
     261            2 :         max_q = math::angleMod<angleT>( max_q );
     262              :     }
     263              : 
     264              :     // Find the mid-point to enable sectors wider than 180.
     265              :     typename angleT::realT mid_q;
     266           12 :     if( min_q <= max_q )
     267              :     {
     268           11 :         mid_q = 0.5 * ( min_q + max_q );
     269              :     }
     270              :     else
     271              :     {
     272            1 :         mid_q = 0.5 * ( ( min_q - angleT::full ) + max_q );
     273              :     }
     274              : 
     275           83 :     for( int j = y0; j < y1; ++j )
     276              :     {
     277          520 :         for( int i = x0; i < x1; ++i )
     278              :         {
     279          449 :             if( rIm( i, j ) < min_r - pixbuf )
     280              :             {
     281           55 :                 continue;
     282              :             }
     283          394 :             if( rIm( i, j ) >= max_r + pixbuf )
     284              :             {
     285          135 :                 continue;
     286              :             }
     287          259 :             if( !( ( math::angleDiff<angleT>( min_q, qIm( i, j ) ) >= 0 &&
     288          403 :                      math::angleDiff<angleT>( qIm( i, j ), mid_q ) > 0 ) ||
     289          144 :                    ( math::angleDiff<angleT>( mid_q, qIm( i, j ) ) >= 0 &&
     290          133 :                      math::angleDiff<angleT>( qIm( i, j ), max_q ) > 0 ) ) )
     291              :             {
     292           28 :                 continue;
     293              :             }
     294              : 
     295          231 :             if( mask )
     296              :             {
     297           32 :                 if( ( *mask )( i, j ) == 0 )
     298              :                 {
     299            2 :                     continue;
     300              :                 }
     301              :             }
     302              : 
     303          229 :             idx.push_back( maskCoordFormat<vecT>::coord( i, j, rIm.rows() ) );
     304              :         }
     305              :     }
     306              : 
     307           12 :     return idx;
     308              : } // LCOV_EXCL_LINE
     309              : 
     310              : /// Get the array coordinates of an annular region in an image
     311              : /**
     312              :  * \ingroup image_masks
     313              :  *
     314              :  * \tparam angleT is the angle type, either radiansT<realT> or degreesT<realT>.  Note that realT sets the type for all
     315              :  * arithmetic.
     316              :  * \tparam eigenT1 is an Eigen-like 2D array type.  Should be resolved by compiler.
     317              :  * \tparam eigenT2 is an Eigen-like 2D array type.  Should be resolved by compiler.
     318              :  * \tparam eigenT3 is an Eigen-like 2D array type.  Should be resolved by compiler.
     319              :  *
     320              :  * \returns a vector containing vectors of the 2D indices of the region defined by the input parameters.
     321              :  */
     322              : template <typename angleT, typename eigenT1, typename eigenT2, typename eigenT3 = eigenT1>
     323            5 : std::vector<std::vector<int>> annulusCoords( const eigenT1 &rIm, /**< [in] a radius image of the type produced by
     324              :                                                                             \ref radiusImage */
     325              :                                              const eigenT2 &qIm, /**< [in] an angle image of the type produce by
     326              :                                                                            \ref angleImage */
     327              :                                              typename angleT::realT xcen,  /**< [in] the x center of the image */
     328              :                                              typename angleT::realT ycen,  /**< [in] the y center of the image */
     329              :                                              typename angleT::realT min_r, /**< [in] the minimum radius of the region */
     330              :                                              typename angleT::realT max_r, /**< [in] the maximum radius of the region */
     331              :                                              typename angleT::realT min_q, /**< [in] the minimum angle of the region. */
     332              :                                              typename angleT::realT max_q, /**< [in] the maximum angle of the region. */
     333              :                                              eigenT3 *mask = 0, /**< [in] [optional] pointer to a mask image, only
     334              :                                                                                      pixels of value 1 are included
     335              :                                                                                      in the indices. */
     336              :                                              typename angleT::realT pixbuf = 0 /**< [in] [optional] amount subtracted
     337              :                                                                                                     from the inclusive
     338              :                                                                                                     inner radius and
     339              :                                                                                   added to the exclusive outer radius */
     340              : 
     341              : )
     342              : {
     343              :     return annulusCoordsWorker<std::vector<std::vector<int>>, angleT, eigenT1, eigenT2, eigenT3>( rIm,
     344              :                                                                                                   qIm,
     345              :                                                                                                   xcen,
     346              :                                                                                                   ycen,
     347              :                                                                                                   min_r,
     348              :                                                                                                   max_r,
     349              :                                                                                                   min_q,
     350              :                                                                                                   max_q,
     351              :                                                                                                   mask,
     352            5 :                                                                                                   pixbuf );
     353              : }
     354              : 
     355              : /// Get the vector indices of an annular region in an image
     356              : /**
     357              :  * \ingroup image_masks
     358              :  *
     359              :  * \tparam angleT is the angle type, either radiansT<realT> or degreesT<realT>.  Note that realT sets the type for all
     360              :  * arithmetic.
     361              :  * \tparam eigenT1 is an Eigen-like 2D array type.  Should be resolved by compiler.
     362              :  * \tparam eigenT2 is an Eigen-like 2D array type.  Should be resolved by compiler.
     363              :  * \tparam eigenT3 is an Eigen-like 2D array type.  Should be resolved by compiler.
     364              :  *
     365              :  * \returns a vector containing the 1D indices of the region defined by the input parameters
     366              :  */
     367              : template <typename angleT, typename eigenT1, typename eigenT2, typename eigenT3 = eigenT1>
     368            8 : std::vector<size_t> annulusIndices( const eigenT1 &rIm,               /**< [in] a radius image of the type produced by
     369              :                                                                                 \ref radiusImage */
     370              :                                     const eigenT2 &qIm,               /**< [in] an angle image of the type produce by
     371              :                                                                                 \ref angleImage */
     372              :                                     typename angleT::realT xcen,      /**< [in] the x center of the image */
     373              :                                     typename angleT::realT ycen,      /**< [in] the y center of the image */
     374              :                                     typename angleT::realT min_r,     /**< [in] the minimum radius of the region */
     375              :                                     typename angleT::realT max_r,     /**< [in] the maximum radius of the region */
     376              :                                     typename angleT::realT min_q,     /**< [in] the minimum angle of the region. */
     377              :                                     typename angleT::realT max_q,     /**< [in] the maximum angle of the region. */
     378              :                                     eigenT3 *mask = 0,                /**< [in] [optional] pointer to a mask image, only
     379              :                                                                                            pixels of value 1 are included
     380              :                                                                                            in the indices. */
     381              :                                     typename angleT::realT pixbuf = 0 /**< [in] [optional] amount subtracted from the
     382              :                                                                                            inclusive inner radius and
     383              :                                                                                            added to the exclusive outer
     384              :                                                                                            radius */
     385              : 
     386              : )
     387              : {
     388              :     return annulusCoordsWorker<std::vector<size_t>, angleT, eigenT1, eigenT2, eigenT3>( rIm,
     389              :                                                                                         qIm,
     390              :                                                                                         xcen,
     391              :                                                                                         ycen,
     392              :                                                                                         min_r,
     393              :                                                                                         max_r,
     394              :                                                                                         min_q,
     395              :                                                                                         max_q,
     396              :                                                                                         mask,
     397            8 :                                                                                         pixbuf );
     398              : }
     399              : 
     400              : /// Get the coordinates of the bounding rectangle of an annulus.
     401              : /**
     402              :  * \ingroup image_masks
     403              :  *
     404              :  * \tparam angleT is the angle type, either radiansT<realT> or degreesT<realT>.  Note that realT sets the type for all
     405              :  * arithmetic.
     406              :  *
     407              :  */
     408              : template <typename angleT>
     409              : void annulusBoundingRect( int &x0,                      ///< [out] The lower left x-coordinate of the bounding rect.
     410              :                           int &y0,                      ///< [out] The lower left y-coordinate of the bounding rect.
     411              :                           int &x1,                      ///< [out] The upper right x-coordinate of the bounding rect.
     412              :                           int &y1,                      ///< [out] The upper right y-coordinate of the bounding rect.
     413              :                           typename angleT::realT xcen,  ///< [in] the x center of the image
     414              :                           typename angleT::realT ycen,  ///< [in] the y center of the image
     415              :                           typename angleT::realT min_r, ///< [in] the minimum radius of the region
     416              :                           typename angleT::realT max_r, ///< [in] the maximum radius of the region
     417              :                           typename angleT::realT min_q, ///< [in] the minimum angle of the region.
     418              :                           typename angleT::realT max_q, ///< [in] the maximum angle of the region.
     419              :                           typename angleT::realT pixbuf = 0 /**< [in] [optional] the pixel buffer for radius
     420              :                                                                                  comparisons */
     421              : )
     422              : {
     423              :     typedef typename angleT::realT realT;
     424              : 
     425              :     // Get the corners
     426              :     realT x00 = xcen + ( min_r - pixbuf ) * cos( min_q * angleT::radians );
     427              :     realT y00 = ycen + ( min_r - pixbuf ) * sin( min_q * angleT::radians );
     428              :     realT x01 = xcen + ( max_r + pixbuf ) * cos( min_q * angleT::radians );
     429              :     realT y01 = ycen + ( max_r + pixbuf ) * sin( min_q * angleT::radians );
     430              : 
     431              :     realT x10 = xcen + ( min_r - pixbuf ) * cos( max_q * angleT::radians );
     432              :     realT y10 = ycen + ( min_r - pixbuf ) * sin( max_q * angleT::radians );
     433              :     realT x11 = xcen + ( max_r + pixbuf ) * cos( max_q * angleT::radians );
     434              :     realT y11 = ycen + ( max_r + pixbuf ) * sin( max_q * angleT::radians );
     435              : 
     436              :     // vertex of min_r is probably not necessary, but might as well
     437              :     realT x20 = xcen + ( min_r - pixbuf ) * cos( math::angleMean<angleT>( { min_q, max_q } ) * angleT::radians );
     438              :     realT y20 = ycen + ( min_r - pixbuf ) * sin( math::angleMean<angleT>( { min_q, max_q } ) * angleT::radians );
     439              : 
     440              :     // vertex of max_r
     441              :     realT x21 = xcen + ( max_r + pixbuf ) * cos( math::angleMean<angleT>( { min_q, max_q } ) * angleT::radians );
     442              :     realT y21 = ycen + ( max_r + pixbuf ) * sin( math::angleMean<angleT>( { min_q, max_q } ) * angleT::radians );
     443              : 
     444              :     x0 = std::ceil( std::min( { x00, x01, x10, x11, x20, x21 } ) );
     445              :     y0 = std::ceil( std::min( { y00, y01, y10, y11, y20, y21 } ) );
     446              :     x1 = std::ceil( std::max( { x00, x01, x10, x11, x20, x21 } ) );
     447              :     y1 = std::ceil( std::max( { y00, y01, y10, y11, y20, y21 } ) );
     448              : }
     449              : 
     450              : /// Reflect vector indices across the given center pixel.
     451              : /** This assumes column major order.
     452              :  * \returns the vector of reflected indices
     453              :  */
     454              : template <typename realT>
     455              : std::vector<size_t> reflectImageIndices( const std::vector<size_t> &idxi, ///< [in] the vector indices to reflect
     456              :                                          int w,                           ///< [in] the image width
     457              :                                          int h,                           ///< [in] the image height
     458              :                                          realT xc,                        ///< [in] the image center x coordinate
     459              :                                          realT yc                         ///< [in] the image center y coordinate
     460              : )
     461              : {
     462              :     std::vector<size_t> idxr;
     463              :     idxr.reserve( idxi.size() );
     464              : 
     465              :     int x0, y0, x1, y1;
     466              :     for( size_t n = 0; n < idxi.size(); ++n )
     467              :     {
     468              :         int y0 = idxi[n] / w;
     469              :         int x0 = idxi[n] - y0 * w;
     470              : 
     471              :         reflectImageCoords( x1, y1, x0, y0, xc, yc );
     472              : 
     473              :         if( x1 < w && y1 < h )
     474              :             idxr.push_back( y1 * w + x1 );
     475              :     }
     476              : 
     477              :     return idxr;
     478              : }
     479              : 
     480              : template <typename sizeT>
     481              : void rectangleIndices( std::vector<sizeT> &idx, sizeT rows, sizeT cols, sizeT xmin, sizeT xmax, sizeT ymin, sizeT ymax )
     482              : {
     483              : 
     484              :     // if(xmin < 0) xmin = 0;
     485              :     if( xmax > rows - 1 )
     486              :         xmax = rows - 1;
     487              : 
     488              :     // if(ymin < 0) ymin = 0;
     489              :     if( ymax > cols - 1 )
     490              :         ymax = cols - 1;
     491              : 
     492              :     idx.reserve( ( xmax - xmin + 1 ) * ( ymax - ymin + 1 ) );
     493              : 
     494              :     for( sizeT i = xmin; i <= xmax; ++i )
     495              :     {
     496              :         for( sizeT j = ymin; j <= ymax; ++j )
     497              :         {
     498              :             idx.push_back( j * rows + i );
     499              :         }
     500              :     }
     501              : }
     502              : 
     503              : template <class eigenT>
     504              : void rectangleIndices( std::vector<size_t> &idx, eigenT &mask, size_t xmin, size_t xmax, size_t ymin, size_t ymax )
     505              : {
     506              :     rectangleIndices<size_t>( idx, (size_t)mask.rows(), (size_t)mask.cols(), xmin, xmax, ymin, ymax );
     507              : }
     508              : 
     509              : /// Apply a mask to an image
     510              : /** The pixels indicated by the indices are set to a value.
     511              :  *
     512              :  * \ingroup image_masks
     513              :  *
     514              :  * \tparam eigenT is an Eigen-like 2D array type
     515              :  *
     516              :  */
     517              : template <class eigenT>
     518              : void applyMask( eigenT &maskedIm,               ///< [out] the image to mask (will be modified)
     519              :                 const std::vector<size_t> &idx, ///< [in] the indices of the pixels to mask
     520              :                 typename eigenT::Scalar maskval ///< [in] the mask value.
     521              : )
     522              : {
     523              :     for( size_t i = 0; i < idx.size(); ++i )
     524              :     {
     525              :         maskedIm( idx[i] ) = maskval;
     526              :     }
     527              : }
     528              : 
     529              : /// Apply a mask to an image
     530              : /** The pixels indicated by the coordinates in a vector are set to a value.
     531              :  *
     532              :  * \ingroup image_masks
     533              :  *
     534              :  * \tparam eigenT is an Eigen-like 2D array type
     535              :  *
     536              :  */
     537              : template <class eigenT>
     538              : void applyMask( eigenT &maskedIm,                           ///< [out] the image to mask (will be modified)
     539              :                 const std::vector<std::vector<int>> &coord, ///< [in] the coordinates of the pixels to mask
     540              :                 typename eigenT::Scalar maskval             ///< [in] the mask value.
     541              : )
     542              : {
     543              :     for( size_t i = 0; i < coord.size(); ++i )
     544              :     {
     545              :         maskedIm( coord[i][0], coord[i][1] ) = maskval;
     546              :     }
     547              : }
     548              : 
     549              : /// Mask a circle in an image.
     550              : /** The circle is describe by its center coordinates and radius. Any value can be set for the mask.
     551              :  * Pixels outside the masked circle are not altered.
     552              :  *
     553              :  * \tparam arrayT is an Eigen-like type.
     554              :  *
     555              :  * \ingroup image_masks
     556              :  */
     557              : template <class arrayT>
     558            1 : void maskCircle( arrayT &m,                           /**< [in.out] the image to be masked, is modified. */
     559              :                  typename arrayT::Scalar xcen,        /**< [in] the x coordinate of the center of the circle */
     560              :                  typename arrayT::Scalar ycen,        /**< [in] the y coordinate of the center of the circle */
     561              :                  typename arrayT::Scalar rad,         /**< [in] the radius of the circle */
     562              :                  typename arrayT::Scalar val,         /**< [in] the mask value. */
     563              :                  typename arrayT::Scalar pixbuf = 0.5 /**< [in] [optional] buffer for radius comparison.
     564              :                                                                 Default is 0.5 pixels.*/
     565              : )
     566              : {
     567            1 :     size_t l0 = m.rows();
     568            1 :     size_t l1 = m.cols();
     569              : 
     570              :     typename arrayT::Scalar rr;
     571              : 
     572          129 :     for( size_t c = 0; c < m.cols(); c++ )
     573              :     {
     574        16512 :         for( size_t r = 0; r < m.rows(); r++ )
     575              :         {
     576        16384 :             rr = sqrt( pow( r - xcen, 2 ) + pow( c - ycen, 2 ) );
     577              : 
     578        16384 :             if( rr <= rad + pixbuf )
     579        12892 :                 m( r, c ) = val;
     580              :         }
     581              :     }
     582            1 : }
     583              : 
     584              : /// Mask a circle in an image at the standard center.
     585              : /** The circle is centered at 0.5*(rows-1) and 0.5*(cols-1), and described by its radius. Any value can be set for the
     586              :  * mask. Pixels outside the masked circle are not altered.
     587              :  *
     588              :  * \tparam arrayT is an Eigen-like type.
     589              :  *
     590              :  * \ingroup image_masks
     591              :  */
     592              : template <class arrayT>
     593              : void maskCircle(
     594              :     arrayT &m,                           ///< [in.out] the image to be masked, is modified.
     595              :     typename arrayT::Scalar rad,         ///< [in] the radius of the circle
     596              :     typename arrayT::Scalar val,         ///< [in] the mask value.
     597              :     typename arrayT::Scalar pixbuf = 0.5 ///< [in] [optional] buffer for radius comparison.  Default is 0.5 pixels.
     598              : )
     599              : {
     600              :     return maskCircle( m, 0.5 * ( m.rows() - 1.0 ), 0.5 * ( m.cols() - 1.0 ), rad, val, pixbuf );
     601              : }
     602              : 
     603              : /// Mask an ellipse in an image.
     604              : /** The ellipse is describe by its center coordinates and x and y direction radii (the semi-major and -minor axes,
     605              :  * in either order). Any value can be set for the mask,cwith 0 being the default.
     606              :  *
     607              :  * \tparam arrayT is an Eigen-like type.
     608              :  *
     609              :  * \ingroup image_masks
     610              :  */
     611              : template <class arrayT>
     612              : void maskEllipse( arrayT &m,                           ///< [in.out] the image to be masked, is modified.
     613              :                   typename arrayT::Scalar xcen,        ///< [in] the x coordinate of the center of the ellipse
     614              :                   typename arrayT::Scalar ycen,        ///< [in] the y coordinate of the center of the ellipse
     615              :                   typename arrayT::Scalar xrad,        ///< [in] the x radius of the ellipse
     616              :                   typename arrayT::Scalar yrad,        ///< [in] the y radius of the ellipse
     617              :                   typename arrayT::Scalar ang,         ///< [in] the c.c.w. angle to rotate the ellipse by
     618              :                   typename arrayT::Scalar val = 0,     ///< [in] [optional] the mask value.  Default is 0.
     619              :                   typename arrayT::Scalar pixbuf = 0.5 /**< [in] [optional] buffer for radius comparison.
     620              :                                                                             Default is 0.5 pixels.*/
     621              : )
     622              : {
     623              :     typedef typename arrayT::Scalar realT;
     624              : 
     625              :     size_t l0 = m.rows();
     626              :     size_t l1 = m.cols();
     627              : 
     628              :     realT r;
     629              :     realT x;
     630              :     realT y;
     631              :     realT xe, ye;
     632              :     realT rad;
     633              : 
     634              :     realT cq = cos( ang );
     635              :     realT sq = sin( ang );
     636              : 
     637              :     realT xr, yr;
     638              : 
     639              :     for( size_t i = 0; i < l0; i++ )
     640              :     {
     641              :         x = i - xcen;
     642              :         for( size_t j = 0; j < l1; j++ )
     643              :         {
     644              :             y = j - ycen;
     645              : 
     646              :             if( x == 0 && y == 0 )
     647              :             {
     648              :                 m( i, j ) = val;
     649              :             }
     650              :             else
     651              :             {
     652              :                 xr = x * cq - y * sq;
     653              :                 yr = x * sq + y * cq;
     654              : 
     655              :                 // Coordinate on the ellipse where the line to the point intersects
     656              :                 xe = ( pow( xrad * yrad, 2 ) / ( pow( yrad, 2 ) + pow( xrad * yr / xr, 2 ) ) );
     657              :                 ye = ( pow( yrad, 2 ) - xe * pow( yrad / xrad, 2 ) );
     658              : 
     659              :                 rad = sqrt( xe + ye );
     660              : 
     661              :                 r = sqrt( pow( xr, 2 ) + pow( yr, 2 ) );
     662              : 
     663              :                 if( r <= rad + pixbuf )
     664              :                 {
     665              :                     m( i, j ) = val;
     666              :                 }
     667              :             }
     668              :         }
     669              :     }
     670              : }
     671              : 
     672              : /// Mask a wedge in an image.
     673              : /** The wedge is describe by its center coordinate, central angle, and angular half-width. Any value can be set for the
     674              :  * mask. Pixels outside the masked circle are not altered.
     675              :  *
     676              :  * \tparam arrayT is an Eigen-like type.
     677              :  *
     678              :  * \ingroup image_masks
     679              :  */
     680              : template <class arrayT>
     681           11 : void maskWedge( arrayT &m,                      ///< [in.out] the image to be masked, is modified.
     682              :                 typename arrayT::Scalar xcen,   ///< [in] the x coordinate of the center of the circle
     683              :                 typename arrayT::Scalar ycen,   ///< [in] the y coordinate of the center of the circle
     684              :                 typename arrayT::Scalar angCen, ///< [in] the central angle of the wedge, in degrees
     685              :                 typename arrayT::Scalar angHW,  ///< [in] the angular half-wdith of the wedge, in degrees
     686              :                 typename arrayT::Scalar val = 0 ///< [in] [optional] the mask value.  Default is 0.
     687              : )
     688              : {
     689           11 :     size_t l0 = m.rows();
     690           11 :     size_t l1 = m.cols();
     691              : 
     692              :     typedef typename arrayT::Scalar angleT;
     693              : 
     694              :     angleT dang;
     695              : 
     696        11275 :     for( size_t c = 0; c < m.cols(); c++ )
     697              :     {
     698     11545600 :         for( size_t r = 0; r < m.rows(); r++ )
     699              :         {
     700              : 
     701     11534336 :             dang = math::angleDiff<math::degreesT<angleT>>( math::rtod( atan2( c - ycen, r - xcen ) ), angCen );
     702              : 
     703     11534336 :             if( dang > -angHW && dang <= angHW )
     704              :             {
     705      3145728 :                 m( r, c ) = val;
     706              :             }
     707              :         }
     708              :     }
     709           11 : }
     710              : 
     711              : /// Draw a thin (1-pixel) line from one point to another.
     712              : /** Calculates the line connecting the two points and sets the pixels along that line to the
     713              :  * supplied value.
     714              :  *
     715              :  * \tparam realT a real floating point type
     716              :  *
     717              :  * \ingroup image_masks
     718              :  */
     719              : template <typename realT>
     720              : int drawLine( eigenImage<realT> &mask, ///< [in/out] [pre-allocated] The array in which to draw the line.
     721              :               realT x0,                ///< [in] The x coordinate of the first point
     722              :               realT y0,                ///< [in] The y coordinate of the first point
     723              :               realT x1,                ///< [in] The x coordinate of the second point
     724              :               realT y1,                ///< [in] The y coordinate of the second point
     725              :               realT val                ///< [in] The value to set on the pixels in the line
     726              : )
     727              : {
     728              :     if( fabs( x1 - x0 ) <= 1 && fabs( y1 - y0 ) <= 1 )
     729              :     {
     730              :         // If it is a single pixel, plot at the rounded average point.
     731              :         realT xa = 0.5 * ( x0 + x1 ) + 0.5;
     732              :         realT ya = 0.5 * ( y0 + y1 ) + 0.5;
     733              :         if( xa >= 0 && xa < mask.rows() && ya >= 0 && ya < mask.cols() )
     734              :             mask( (int)xa, (int)ya ) = val;
     735              :         return 0;
     736              :     }
     737              : 
     738              :     realT _x0, _x1, _y0, _y1;
     739              : 
     740              :     realT length = sqrt( pow( x1 - x0, 2 ) + pow( y1 - y0, 2 ) );
     741              : 
     742              :     // If X doesn't change enough, we try to do it as a function of y.
     743              :     if( fabs( x1 - x0 ) <= 1 )
     744              :     {
     745              :         if( y0 < y1 )
     746              :         {
     747              :             _x0 = x0;
     748              :             _x1 = x1;
     749              :             _y0 = y0;
     750              :             _y1 = y1;
     751              :         }
     752              :         else
     753              :         {
     754              :             _x0 = x1;
     755              :             _x1 = x0;
     756              :             _y0 = y1;
     757              :             _y1 = y0;
     758              :         }
     759              : 
     760              :         realT m = ( _x1 - _x0 ) / ( _y1 - _y0 );
     761              :         realT b = _x0 - m * _y0;
     762              : 
     763              :         realT dy = ( _y1 - _y0 ) / ( 2 * length + 1 );
     764              :         for( realT y = _y0; y <= _y1; y += dy )
     765              :         {
     766              :             realT x = m * y + b;
     767              : 
     768              :             if( x + 0.5 >= 0 && x + 0.5 < mask.rows() && y + 0.5 >= 0 && y + 0.5 < mask.cols() )
     769              :                 mask( (int)( x + 0.5 ), (int)( y + 0.5 ) ) = val;
     770              :         }
     771              : 
     772              :         return 0;
     773              :     }
     774              : 
     775              :     // Ordinarily draw in x.
     776              :     if( x0 < x1 )
     777              :     {
     778              :         _x0 = x0;
     779              :         _x1 = x1;
     780              :         _y0 = y0;
     781              :         _y1 = y1;
     782              :     }
     783              :     else
     784              :     {
     785              :         _x0 = x1;
     786              :         _x1 = x0;
     787              :         _y0 = y1;
     788              :         _y1 = y0;
     789              :     }
     790              : 
     791              :     realT m = ( _y1 - _y0 ) / ( _x1 - _x0 );
     792              :     realT b = _y0 - m * _x0;
     793              : 
     794              :     realT dx = ( _x1 - _x0 ) / ( 2 * length + 1 );
     795              :     for( realT x = _x0; x <= _x1; x += dx )
     796              :     {
     797              :         realT y = m * x + b;
     798              : 
     799              :         if( x + 0.5 >= 0 && x + 0.5 < mask.rows() && y + 0.5 >= 0 && y + 0.5 < mask.cols() )
     800              :             mask( (int)( x + 0.5 ), (int)( y + 0.5 ) ) = val;
     801              :     }
     802              : }
     803              : 
     804              : /// Draw a thick line from one point to another.
     805              : /** Calculates the line connecting the two points and sets the pixels along that line to the
     806              :  * supplied value.  Makes the line thick by drawing lines perpindicular to each point with
     807              :  * length equal to the specified width.
     808              :  *
     809              :  * \tparam realT a real floating point type
     810              :  *
     811              :  * \ingroup image_masks
     812              :  */
     813              : template <typename realT>
     814              : int drawLine( eigenImage<realT> &mask, ///< [in.out] [pre-allocated] The array in which to draw the line.
     815              :               realT x0,                ///< [in] The x coordinate of the first point
     816              :               realT y0,                ///< [in] The y coordinate of the first point
     817              :               realT x1,                ///< [in] The x coordinate of the second point
     818              :               realT y1,                ///< [in] The y coordinate of the second point
     819              :               realT width,             ///< [in] The desired full-width of the line
     820              :               realT val                ///< [in] The value to set on the pixels in the line
     821              : )
     822              : {
     823              :     // You can't do this.
     824              :     if( width <= 0 )
     825              :         return -1;
     826              : 
     827              :     // If no reason to try this hard, don't.
     828              :     if( width < 1 )
     829              :         return drawLine( mask, x0, y0, x1, y1, val );
     830              : 
     831              :     realT _x0, _x1, _y0, _y1;
     832              : 
     833              :     // If X doesn't change enough, we try to do it as a function of y.
     834              :     if( fabs( x1 - x0 ) <= 1 )
     835              :     {
     836              :         if( y0 < y1 )
     837              :         {
     838              :             _x0 = x0;
     839              :             _x1 = x1;
     840              :             _y0 = y0;
     841              :             _y1 = y1;
     842              :         }
     843              :         else
     844              :         {
     845              :             _x0 = x1;
     846              :             _x1 = x0;
     847              :             _y0 = y1;
     848              :             _y1 = y0;
     849              :         }
     850              : 
     851              :         realT m = ( _x1 - _x0 ) / ( _y1 - _y0 );
     852              :         realT b = _x0 - m * _y0;
     853              : 
     854              :         realT dy = ( _y1 - _y0 ) / ( mask.cols() + 1 );
     855              :         for( realT y = _y0; y <= _y1; y += dy )
     856              :         {
     857              :             realT x = m * y + b;
     858              : 
     859              :             realT xs, ys, xe, ye; // The start and end point of the perpindicular line.
     860              : 
     861              :             realT q1 = atan( m ) + 0.5 * math::pi<realT>();
     862              :             realT cq = cos( q1 );
     863              :             realT sq = sin( q1 );
     864              : 
     865              :             xs = x + 0.5 * width * cq;
     866              :             ys = y + 0.5 * width * sq;
     867              : 
     868              :             xe = x - 0.5 * width * cq;
     869              :             ye = y - 0.5 * width * sq;
     870              : 
     871              :             drawLine( mask, xs, ys, xe, ye, val );
     872              :         }
     873              : 
     874              :         return 0;
     875              :     }
     876              : 
     877              :     // Ordinarily draw in x.
     878              :     if( x0 < x1 )
     879              :     {
     880              :         _x0 = x0;
     881              :         _x1 = x1;
     882              :         _y0 = y0;
     883              :         _y1 = y1;
     884              :     }
     885              :     else
     886              :     {
     887              :         _x0 = x1;
     888              :         _x1 = x0;
     889              :         _y0 = y1;
     890              :         _y1 = y0;
     891              :     }
     892              : 
     893              :     realT m = ( _y1 - _y0 ) / ( _x1 - _x0 );
     894              :     realT b = _y0 - m * _x0;
     895              : 
     896              :     realT dx = ( _x1 - _x0 ) / ( mask.rows() + 1 );
     897              :     for( realT x = _x0; x <= _x1; x += dx )
     898              :     {
     899              :         realT y = m * x + b;
     900              : 
     901              :         realT xs, ys, xe, ye; // The start and end point of the perpindicular line.
     902              : 
     903              :         realT q1 = atan( m ) + 0.5 * math::pi<realT>();
     904              :         realT cq = cos( q1 );
     905              :         realT sq = sin( q1 );
     906              : 
     907              :         xs = x + 0.5 * width * cq;
     908              :         ys = y + 0.5 * width * sq;
     909              : 
     910              :         xe = x - 0.5 * width * cq;
     911              :         ye = y - 0.5 * width * sq;
     912              : 
     913              :         drawLine( mask, xs, ys, xe, ye, val );
     914              :     }
     915              : }
     916              : 
     917              : /// Populate a mask based on a typical CCD bleeding pattern.
     918              : /** Masks a circle for saturated pixels, as well as a horizontal bar for bleeding (in both directions if needed).
     919              :  *
     920              :  * \returns 0 on success, -1 otherwise.
     921              :  *
     922              :  * \tparam imT is an Eigen-like 2D array type.
     923              :  *
     924              :  * \ingroup image_masks
     925              :  */
     926              : template <typename imT>
     927              : int ccdBleedMask( imT &im,                 ///< [out] the mask, on output will be 1/0.  Must be allocated prior to call.
     928              :                   typename imT::Scalar x0, ///< [in] the x-coordinate of the center of the mask
     929              :                   typename imT::Scalar y0, ///< [in] the y-coordinate of the center of the mask
     930              :                   typename imT::Scalar rad,    ///< [in] the radius of the central circle.
     931              :                   typename imT::Scalar height, ///< [in] the half-height of the saturation bar.
     932              :                   typename imT::Scalar lwidth, ///< [in] the length of the saturation bar to the left.
     933              :                   typename imT::Scalar rwidth  ///< [in] the length of the saturation bar to the right.
     934              : )
     935              : {
     936              :     typename imT::Scalar x, y;
     937              : 
     938              :     for( int i = 0; i < im.rows(); ++i )
     939              :     {
     940              :         x = i;
     941              :         for( int j = 0; j < im.cols(); ++j )
     942              :         {
     943              :             y = j;
     944              : 
     945              :             if( sqrt( pow( x - x0, 2 ) + pow( y - y0, 2 ) ) <= rad )
     946              :             {
     947              :                 im( i, j ) = 0;
     948              :             }
     949              :             else if( fabs( y - y0 ) <= height && x >= x0 - lwidth && x <= x0 + rwidth )
     950              :             {
     951              :                 im( i, j ) = 0;
     952              :             }
     953              :             else
     954              :             {
     955              :                 im( i, j ) = 1;
     956              :             }
     957              :         }
     958              :     }
     959              : 
     960              :     return 0;
     961              : }
     962              : 
     963              : /// Cut out a region of an image specified by an index-mask.
     964              : /** The output will be a row-image containing the pixel values.
     965              :  *
     966              :  * \tparam imageTout is the output image Eigen-like type.
     967              :  * \tparam imageTin is the input image Eigen-like type.
     968              :  *
     969              :  * \ingroup image_masks
     970              :  */
     971              : template <typename imageTout, typename imageTin>
     972            3 : void cutImageRegion( imageTout &imout,     ///< [out] a row-image containing the pixel values specified by the indices.
     973              :                      const imageTin &imin, ///< [in] a 2-D image
     974              :                      const std::vector<size_t> &idx, ///< [in] the linear indices of the pixel values
     975              :                      bool resize = true ///< [in] [optional] flag controls whether imout is resized.  It is if true.
     976              : )
     977              : {
     978            3 :     if( resize )
     979              :     {
     980            2 :         imout.resize( idx.size(), 1 );
     981              :     }
     982              : 
     983              :     // #pragma omp parallel for schedule(static, 1)
     984            9 :     for( size_t i = 0; i < idx.size(); ++i )
     985              :     {
     986            6 :         imout( i ) = imin( idx[i] );
     987              :     }
     988            3 : }
     989              : 
     990              : /// Insert a region of an image specified by an index-mask.
     991              : /** Both the input and the output are row-images containing the pixel values.
     992              :  *
     993              :  * \tparam imageTout is the output image Eigen-like type.
     994              :  * \tparam imageTin is the input image Eigen-like type.
     995              :  *
     996              :  * \ingroup image_masks
     997              :  */
     998              : template <typename imageTout, typename imageTin>
     999            2 : void insertImageRegion( imageTout imout,               /**< [out] a row-image into which the pixel values
    1000              :                                                                   specified by the indices are inserted. */
    1001              :                         const imageTin &imin,          /**< [in] a row-image containing the pixel values,
    1002              :                                                                  same size as idx*/
    1003              :                         const std::vector<size_t> &idx /**< [in] the linear indices of the pixel values */
    1004              : )
    1005              : {
    1006              :     // #pragma omp parallel for schedule(static, 1)
    1007            5 :     for( size_t i = 0; i < idx.size(); ++i )
    1008              :     {
    1009            3 :         imout( idx[i], 0 ) = imin( i, 0 );
    1010              :     }
    1011            2 : }
    1012              : 
    1013              : /// Rotate a binary mask
    1014              : /** Sets edge pixels to 0 or 1 depending on the interpolation, being below/above 0.5.
    1015              :  */
    1016              : template <typename imageT, typename transformT = cubicConvolTransform<typename imageT::Scalar>>
    1017            2 : void rotateMask( imageT &rotMask, imageT &mask, typename imageT::Scalar angle )
    1018              : {
    1019            2 :     imageRotate( rotMask, mask, angle, transformT() );
    1020              : 
    1021           20 :     for( int jj = 0; jj < rotMask.cols(); ++jj )
    1022              :     {
    1023          180 :         for( int ii = 0; ii < rotMask.rows(); ++ii )
    1024              :         {
    1025          162 :             if( rotMask( ii, jj ) < 0.5 )
    1026          160 :                 rotMask( ii, jj ) = 0;
    1027              :             else
    1028            2 :                 rotMask( ii, jj ) = 1;
    1029              :         }
    1030              :     }
    1031            2 : }
    1032              : 
    1033              : } // namespace improc
    1034              : } // namespace mx
    1035              : 
    1036              : #endif // improc_imageMasks_hpp
        

Generated by: LCOV version 2.0-1