// ____ ______ __ // / __ \ / ____// / // / /_/ // / / / // / ____// /___ / /___ PixInsight Class Library // /_/ \____//_____/ PCL 2.4.23 // ---------------------------------------------------------------------------- // pcl/StructuringElement.h - Released 2022-03-12T18:59:29Z // ---------------------------------------------------------------------------- // This file is part of the PixInsight Class Library (PCL). // PCL is a multiplatform C++ framework for development of PixInsight modules. // // Copyright (c) 2003-2022 Pleiades Astrophoto S.L. All Rights Reserved. // // Redistribution and use in both source and binary forms, with or without // modification, is permitted provided that the following conditions are met: // // 1. All redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // // 2. All redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // // 3. Neither the names "PixInsight" and "Pleiades Astrophoto", nor the names // of their contributors, may be used to endorse or promote products derived // from this software without specific prior written permission. For written // permission, please contact info@pixinsight.com. // // 4. All products derived from this software, in any form whatsoever, must // reproduce the following acknowledgment in the end-user documentation // and/or other materials provided with the product: // // "This product is based on software from the PixInsight project, developed // by Pleiades Astrophoto and its contributors (https://pixinsight.com/)." // // Alternatively, if that is where third-party acknowledgments normally // appear, this acknowledgment must be reproduced in the product itself. // // THIS SOFTWARE IS PROVIDED BY PLEIADES ASTROPHOTO AND ITS CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED // TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL PLEIADES ASTROPHOTO OR ITS // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, // EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, BUSINESS // INTERRUPTION; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; AND LOSS OF USE, // DATA OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. // ---------------------------------------------------------------------------- #ifndef __PCL_StructuringElement_h #define __PCL_StructuringElement_h /// \file pcl/StructuringElement.h #include #include #include #include #include #include // Reverse() #include // BitmapStructure #include #include #include namespace pcl { // ---------------------------------------------------------------------------- /*! * \class StructuringElement * \brief Abstract base class of all PCL structuring elements. * * %StructuringElement defines a square multiway structure that can be used, * along with a MorphologicalOperator, to build a MorphologicalTransformation. * * A structure is a square matrix of Boolean elements. When an element is true * we say that that element \e exists. Nonexistent (false) structure elements * are ignored when a morphological operator is applied to an image. * * A structuring element works for morphological transformations in a * similar way to a kernel filter for spatial convolutions: the * structure is placed centered on a pixel of the image being processed, and * only those neighbor pixels falling behind existing structure elements are * taken into account to compute the result of a morphological operator. When * this operation is repeated for each pixel in an image, it implements a * morphological transformation. * * %StructuringElement holds a multiway structure. Each way is * actually a separate structure, so a multiway structure actually groups a * set of structures with the same dimensions. When a multiway structure is * used to apply a morphological operator, the operator is first applied * through each way in the structure, and the result is the same operator * applied to the partial results. * * This class is thread-safe. This means that multiple execution threads can * access the same %StructuringElement instance concurrently to perform * morphological transformations. * * \sa MorphologicalOperator, MorphologicalTransformation */ class PCL_CLASS StructuringElement { public: /*! * Represents an element of a structure existence mask. */ typedef uint32 existence_mask_element; /*! * Represents a structure existence mask. */ typedef GenericVector existence_mask; /*! * Represents a set of structure existence masks. */ typedef GenericVector existence_mask_set; /*! * A vector type used to store the number of existing structure elements. */ typedef GenericVector existence_mask_count; /*! * Constructs a default \e box %StructuringElement object of the specified * \a size in pixels and number of ways \a n. */ StructuringElement( int size = 3, int n = 1 ) : m_size( Max( 3, size|1 ) ) , m_mask( Max( 1, n ) ) , m_count( 0, Max( 1, n ) ) { PCL_PRECONDITION( size >= 3 ) PCL_PRECONDITION( (size & 1) != 0 ) PCL_PRECONDITION( n >= 1 ) for ( int k = 0; k < NumberOfWays(); ++k ) m_mask[k] = existence_mask( existence_mask_element( 0 ), NumberOfElements() ); } /*! * Copy constructor. */ StructuringElement( const StructuringElement& x ) : m_size( x.m_size ) , m_mask( x.NumberOfWays() ) , m_count( 0, x.NumberOfWays() ) { for ( int k = 0; k < NumberOfWays(); ++k ) m_mask[k] = existence_mask( existence_mask_element( 0 ), NumberOfElements() ); } /*! * Destroys a %StructuringElement object. */ virtual ~StructuringElement() { } /*! * Returns a pointer to a dynamically allocated duplicate of this * structuring element. */ virtual StructuringElement* Clone() const = 0; /*! * Copy assignment operator. Returns a reference to this object. */ StructuringElement& operator =( const StructuringElement& x ) { if ( &x != this ) { volatile AutoLock lock( m_mutex ); m_size = x.m_size; m_mask = existence_mask_set( x.NumberOfWays() ); m_count = existence_mask_count( 0, x.NumberOfWays() ); m_reflected = false; m_initialized = 0; for ( int k = 0; k < NumberOfWays(); ++k ) m_mask[k] = existence_mask( existence_mask_element( 0 ), NumberOfElements() ); } return *this; } /*! * Returns the number of ways in this structure. */ int NumberOfWays() const { return int( m_mask.Length() ); } /*! * Returns the size of this structure in pixels. */ int Size() const { return m_size; } /*! * Returns the total number of structure elements, or Size()*Size(). Note * that this includes both existing and nonexistent structure elements. */ int NumberOfElements() const { return m_size*m_size; } /*! * Returns true iff this is a box structure. All elements in a box * structure are existing elements. * * The default implementation of this member function returns true if all * elements in the k-th way are reported as existing elements by the * ElementExists() member function. * * \note This is a performance-critical routine. When possible, derived * classes should reimplement it in more efficient ways (for example, a * circular structure is never a box structure). */ virtual bool IsBox( int k ) const { PCL_PRECONDITION( k >= 0 && k < NumberOfWays() ) Initialize(); return m_count[k] == NumberOfElements(); } /*! * Returns true iff a given element exists in this structure. * * \param i column position (X-coordinate) of the requested structure * element. Must be 0 <= i < n, where n is the structure size. * * \param j row position (Y-coordinate) of the requested structure * element. Must be 0 <= j < n, where n is the structure size. * * \param k Way index. Must be 0 <= k < m, where m is the number of ways * defined in this structure. */ virtual bool ElementExists( int i, int j, int k ) const { PCL_PRECONDITION( i >= 0 && i < m_size ) PCL_PRECONDITION( j >= 0 && j < m_size ) PCL_PRECONDITION( k >= 0 && k < NumberOfWays() ) return true; } /*! * Gets the subset of pixel sample values that correspond to existing * elements in a way of this structure. * * \param[out] h1 %Vector of samples from the input \a h array that * correspond to existing structure elements. * * \param[out] nh1 The number of existing elements in the k-th way of * this structure. This is also the number of samples * stored in the \a h1 vector. * * \param h Input array of samples. Must be the starting address of a * square matrix of \a n samples stored in row order, where \a n * is the number of elements in this structure (equal to * NumberOfElements()). * * \param k Way index. Must be 0 <= k < m, where m is the number of ways * defined in this structure. * * \note This function is thread-safe. */ template void PeekElements( T* h1, int& nh1, const T* h, int k ) const { PCL_PRECONDITION( h1 != 0 ) PCL_PRECONDITION( h != 0 ) PCL_PRECONDITION( k >= 0 && k < NumberOfWays() ) Initialize(); nh1 = 0; for ( existence_mask::const_iterator m = m_mask[k].Begin(), m1 = m_mask[k].End(); m < m1; ++m, ++h ) if ( *m ) { *h1++ = *h; ++nh1; } } /*! * Reflects this structure. * * Structure reflection is equivalent to a 180 degrees rotation of the * underlying structure mask for each way. This member function is called * internally by MorphologicalTransformation to apply dilation operators. */ void Reflect() { Initialize(); for ( existence_mask_set::iterator i = m_mask.Begin(); i != m_mask.End(); ++i ) pcl::Reverse( i->Begin(), i->End() ); m_reflected = !m_reflected; } /*! * Returns true iff this structure has been reflected. Note that after an * even number of successive reflections (which is a no-op) this member * function will return false. */ bool IsReflected() const { return m_reflected; } /*! * Initializes the internal existence tables. * * A structure is a square matrix of Boolean elements. When an element is * true we say that that element \e exists. Nonexistent (false) structure * elements are ignored when a morphological operator is applied to * transform an image. * * StructuringElement (and derived classes) uses a set of precomputed * existence tables that greatly improves performance of * morphological transformations, especially for complex structures. Calling * this member function forces the immediate calculation of existence * tables, if they haven't already been calculated previously. If the * existence tables already exist, this function does nothing. * * You normally should not need to call this member function directly, as * existence tables are automatically calculated when required. * * \note This is a thread-safe routine. It can be safely called from * multiple running threads. */ void Initialize() const { if ( !m_initialized ) { volatile AutoLock lock( m_mutex ); if ( m_initialized.Load() == 0 ) { int N = NumberOfWays(); for ( int k = 0; k < N; ++k ) { m_count[k] = 0; // redundant, but doesn't hurt existence_mask::iterator m = m_mask[k].Begin(); for ( int i = 0; i < m_size; ++i ) for ( int j = 0; j < m_size; ++j, ++m ) if ( ElementExists( i, j, k ) ) { *m = 1; ++m_count[k]; } else *m = 0; } m_initialized.Store( 1 ); } } } private: /* * Structure size. */ int m_size; /* * Element existence masks (one mask per way). * An element exists iff its corresponding mask element is nonzero. */ mutable existence_mask_set m_mask; /* * Number of existing elements for each way. */ mutable existence_mask_count m_count; /* * Flag true when the structure has been reflected. */ bool m_reflected = false; /* * Flag true when existence masks have been calculated. */ mutable AtomicInt m_initialized; /* * Thread synchronization. */ mutable Mutex m_mutex; }; // ---------------------------------------------------------------------------- /*! * \class BoxStructure * \brief Standard box (square) structure. * * A block structure defines all of its elements. For example, a box * structure of size 5 is: * *
 * x x x x x
 * x x x x x
 * x x x x x
 * x x x x x
 * x x x x x
 * 
* * where existing elements are marked as 'x'. * * \sa CircularStructure, OrthogonalStructure, DiagonalStructure, StarStructure */ class PCL_CLASS BoxStructure : public StructuringElement { public: /*! * Constructs a standard block structure of the specified \a size. */ BoxStructure( int size ) : StructuringElement( size, 1 ) { } /*! * Copy constructor. */ BoxStructure( const BoxStructure& ) = default; /*! */ StructuringElement* Clone() const override { return new BoxStructure( *this ); } /*! * Returns true iff this is a box structure. All elements in a box * structure are existing elements. * * As reimplemented by %BoxStructure, this member function always * returns true. */ bool IsBox( int k ) const override { PCL_PRECONDITION( k == 0 ) return true; } }; // ---------------------------------------------------------------------------- /*! * \class CircularStructure * \brief Standard circular structure. * * The standard circular structure of size 7 is defined as follows: * *
 * · · x x x · ·
 * · x x x x x ·
 * x x x x x x x
 * x x x x x x x
 * x x x x x x x
 * · x x x x x ·
 * · · x x x · ·
 * 
* * where existing elements are marked as 'x' and nonexisting elements are * denoted as '·'. * * Circular structures can help in preserving small structures, especially * rounded ones, like stars. * * \sa ThreeWayStructure, OrthogonalStructure, DiagonalStructure, StarStructure */ class PCL_CLASS CircularStructure : public StructuringElement { public: /*! * Constructs a standard circular structure of the specified \a diameter. */ CircularStructure( int diameter ) : StructuringElement( diameter, 1 ) { } /*! * Copy constructor. */ CircularStructure( const CircularStructure& ) = default; /*! */ StructuringElement* Clone() const override { return new CircularStructure( *this ); } /*! * Returns true iff this is a box structure. All elements in a box * structure are existing elements. * * As reimplemented by %CircularStructure, this member function always * returns false. */ bool IsBox( int k ) const override { PCL_PRECONDITION( k == 0 ) return false; } /*! */ bool ElementExists( int i, int j, int k ) const override { PCL_PRECONDITION( i >= 0 && i < Size() ) PCL_PRECONDITION( j >= 0 && j < Size() ) PCL_PRECONDITION( k == 0 ) float n2 = 0.5F*Size(); float di = i+0.5F - n2; float dj = j+0.5F - n2; return di*di + dj*dj <= n2*n2; } }; // ---------------------------------------------------------------------------- /*! * \class OrthogonalStructure * \brief Standard orthogonal structure. * * An orthogonal structure of size 5 is defined as follows: * *
 * · · x · ·
 * · · x · ·
 * x x x x x
 * · · x · ·
 * · · x · ·
 * 
* * where existing elements are marked as 'x' and nonexisting elements are * denoted as '·'. * * \sa ThreeWayStructure, CircularStructure, DiagonalStructure, StarStructure */ class PCL_CLASS OrthogonalStructure : public StructuringElement { public: /*! * Constructs an orthogonal structure of the specified \a size. */ OrthogonalStructure( int size ) : StructuringElement( size, 1 ) { } /*! * Copy constructor. */ OrthogonalStructure( const OrthogonalStructure& ) = default; /*! */ StructuringElement* Clone() const override { return new OrthogonalStructure( *this ); } /*! * Returns true iff this is a box structure. All elements in a box * structure are existing elements. * * As reimplemented by %OrthogonalStructure, this member function always * returns false. */ bool IsBox( int k ) const override { PCL_PRECONDITION( k == 0 ) return false; } /*! */ bool ElementExists( int i, int j, int k ) const override { PCL_PRECONDITION( i >= 0 && i < Size() ) PCL_PRECONDITION( j >= 0 && j < Size() ) PCL_PRECONDITION( k == 0 ) int n2 = Size() >> 1; return i == n2 || j == n2; } }; // ---------------------------------------------------------------------------- /*! * \class DiagonalStructure * \brief Standard diagonal structure. * * A diagonal structure of size 5 is defined as follows: * *
 * x · · · x
 * · x · x ·
 * · · x · ·
 * · x · x ·
 * x · · · x
 * 
* * where existing elements are marked as 'x' and nonexisting elements are * denoted as '·'. * * \sa ThreeWayStructure, CircularStructure, OrthogonalStructure, StarStructure */ class PCL_CLASS DiagonalStructure : public StructuringElement { public: /*! * Constructs a diagonal structure of the specified \a size. */ DiagonalStructure( int size ) : StructuringElement( size, 1 ) { } /*! * Copy constructor. */ DiagonalStructure( const DiagonalStructure& ) = default; /*! */ StructuringElement* Clone() const override { return new DiagonalStructure( *this ); } /*! * Returns true iff this is a box structure. All elements in a box * structure are existing elements. * * As reimplemented by %DiagonalStructure, this member function always * returns false. */ bool IsBox( int k ) const override { PCL_PRECONDITION( k == 0 ) return false; } /*! */ bool ElementExists( int i, int j, int k ) const override { PCL_PRECONDITION( i >= 0 && i < Size() ) PCL_PRECONDITION( j >= 0 && j < Size() ) PCL_PRECONDITION( k == 0 ) return j == i || j == Size()-i-1; } }; // ---------------------------------------------------------------------------- /*! * \class StarStructure * \brief Standard star structure. * * A star structure of size 7 is defined as follows: * *
 * · · · x · · ·
 * · x · x · x ·
 * · · x x x · ·
 * x x x x x x x
 * · · x x x · ·
 * · x · x · x ·
 * · · · x · · ·
 * 
* * where existing elements are marked as 'x' and nonexisting elements are * denoted as '·'. * * \sa ThreeWayStructure, CircularStructure, OrthogonalStructure, * DiagonalStructure */ class PCL_CLASS StarStructure : public StructuringElement { public: /*! * Constructs a star structure of the specified \a size. */ StarStructure( int size ) : StructuringElement( size, 1 ) { } /*! * Copy constructor. */ StarStructure( const StarStructure& ) = default; /*! */ StructuringElement* Clone() const override { return new StarStructure( *this ); } /*! * Returns true iff this is a box structure. All elements in a box * structure are existing elements. * * As reimplemented by %StarStructure, this member function always * returns false. */ bool IsBox( int k ) const override { PCL_PRECONDITION( k == 0 ) return false; } /*! */ bool ElementExists( int i, int j, int k ) const override { PCL_PRECONDITION( i >= 0 && i < Size() ) PCL_PRECONDITION( j >= 0 && j < Size() ) PCL_PRECONDITION( k == 0 ) int n2 = Size() >> 1; if ( i == n2 || j == n2 ) return true; if ( j == i || j == Size()-i-1 ) { float n2 = 0.5F*Size(); float di = i+0.5F - n2; float dj = j+0.5F - n2; return di*di + dj*dj <= n2*n2; } return false; } }; // ---------------------------------------------------------------------------- /*! * \class ThreeWayStructure * \brief Standard three-way structure. * * The standard three-way structure of size 5 is defined as follows: * *
 *   Way #0       Way #1       Way #2
 *
 * · · x · ·    x · · · x    · · · · ·
 * · · x · ·    · x · x ·    · · · · ·
 * x x · x x    · · · · ·    · · x · ·
 * · · x · ·    · x · x ·    · · · · ·
 * · · x · ·    x · · · x    · · · · ·
 * 
* * where existing elements are marked as 'x' and nonexisting elements are * denoted as '·'. * * The standard three-way structure leads to ranking operations where data * from different spatial directions are ranked separately. When used to apply * morphological operators, standard three-way structures can preserve edges * better than box structures, especially when applied recursively. * * \sa CircularStructure, OrthogonalStructure, DiagonalStructure, StarStructure */ class PCL_CLASS ThreeWayStructure : public StructuringElement { public: /*! * Constructs a standard three-way structure of the specified \a size. */ ThreeWayStructure( int size ) : StructuringElement( size, 3 ) { } /*! * Copy constructor. */ ThreeWayStructure( const ThreeWayStructure& ) = default; /*! */ StructuringElement* Clone() const override { return new ThreeWayStructure( *this ); } /*! * Returns true iff this is a box structure. All elements in a box * structure are existing elements. * * As reimplemented by %ThreeWayStructure, this member function always * returns false. */ bool IsBox( int k ) const override { PCL_PRECONDITION( k >= 0 && k < 3 ) return false; } /*! */ bool ElementExists( int i, int j, int k ) const override { PCL_PRECONDITION( i >= 0 && i < Size() ) PCL_PRECONDITION( j >= 0 && j < Size() ) PCL_PRECONDITION( k >= 0 && k < 3 ) int n2 = Size() >> 1; switch ( k ) { default: case 0: // central element return i == n2 && j == n2; case 1: // horizontal and vertical elements return i == n2 || j == n2; case 2: // diagonal elements return (j == i || j == Size()-i-1) && i != n2; } } }; // ---------------------------------------------------------------------------- /*! * \class BitmapStructure * \brief A structuring element where static strings are used to define custom * existence matrices. * * %BitmapStructure allows you to build a structuring element using character * strings to define the existence of structure elements. Each string defines a * structure way, where 'x' characters correspond to existing structure * elements. For example, the following array of static strings: * *
 * const char* B[] = { "--x--"
 *                     "-xxx-"
 *                     "xxxxx"
 *                     "-xxx-"
 *                     "--x--",
 *                     //
 *                     "-xxx-"
 *                     "xxxxx"
 *                     "xxxxx"
 *                     "xxxxx"
 *                     "-xxx-" };
 * 
* * would define a 5x5 structure with two ways. 'x' characters define existing * elements; other character values ('-' in this case) are interpreted as * nonexistent elements. The corresponding %BitmapStructure object would be * constructed as follows: * * BitmapStructure S( B, 5, 2 ); * * This class is a convenient, straightforward approach to defining multi-way * custom structuring elements that can be used directly with the * MorphologicalTransformation class. */ class PCL_CLASS BitmapStructure : public StructuringElement { public: /*! * Represents a structure bitmap. Each bitmap defines the existence matrix * for a \e way of the structuring element. */ typedef IsoString bitmap; /*! * Represents a set of bitmaps used to define the structure ways in a * %BitmapStructure object. */ typedef IsoStringList bitmap_set; /*! * Constructs a %BitmapStructure object of the specified \a size and \a n * number of ways. * * \param bitmaps An array of null-terminated strings representing the * set of ways in this structuring element. There must be * \a n contiguous null-terminated strings of length * \a size*size characters at the address specified by * this argument. In these strings, 'x' characters * correspond to existing structure elements; other * character values will be interpreted as nonexistent * structure elements. * * \param size Size of this structure. Must be an odd integer >= 3. * * \param n Number of ways in this structure. Must be >= 1. */ BitmapStructure( const char** bitmaps, int size, int n = 1 ) : StructuringElement( size, n ) { PCL_PRECONDITION( bitmaps != nullptr ) PCL_PRECONDITION( *bitmaps != '\0' ) for ( int i = 0; i < NumberOfWays(); ++i ) m_bitmaps.Add( bitmap( bitmaps[i] ) ); } /*! * Copy constructor. */ BitmapStructure( const BitmapStructure& ) = default; /*! */ StructuringElement* Clone() const override { return new BitmapStructure( *this ); } /*! */ bool ElementExists( int i, int j, int k ) const override { PCL_PRECONDITION( i >= 0 && i < Size() ) PCL_PRECONDITION( j >= 0 && j < Size() ) PCL_PRECONDITION( k >= 0 && k < NumberOfWays() ) return m_bitmaps[k][i + j*Size()] == 'x'; } protected: bitmap_set m_bitmaps; }; // ---------------------------------------------------------------------------- } // pcl #endif // __PCL_StructuringElement_h // ---------------------------------------------------------------------------- // EOF pcl/StructuringElement.h - Released 2022-03-12T18:59:29Z