// ____ ______ __ // / __ \ / ____// / // / /_/ // / / / // / ____// /___ / /___ PixInsight Class Library // /_/ \____//_____/ PCL 2.4.23 // ---------------------------------------------------------------------------- // pcl/Image.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_Image_h #define __PCL_Image_h /// \file pcl/Image.h #include #include #include #include #include #include #include #include #include #include #include #include #ifndef __PCL_IMAGE_NO_BITMAP # ifdef __PCL_BUILDING_PIXINSIGHT_APPLICATION # ifndef __PI_Bitmap_h # include // using server-side bitmaps # endif using namespace pi; # else # ifndef __PCL_Bitmap_h # include // using client-side bitmaps # endif # endif # ifndef __PCL_Color_h # include // for RGBA # endif #endif #ifdef __PCL_BUILDING_PIXINSIGHT_APPLICATION namespace pi { class SharedImage; } #endif namespace pcl { // ---------------------------------------------------------------------------- /*! * \namespace pcl::ImageOp * \brief Arithmetic, bitwise logical and pixel composition operations. * * Supported operations: * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
ImageOp::Nop No operation
ImageOp::Mov Copy pixels
ImageOp::Add Add
ImageOp::Sub Subtract
ImageOp::Mul Multiply
ImageOp::Div Divide
ImageOp::Pow Raise
ImageOp::Dif Absolute difference
ImageOp::Min Minimum value
ImageOp::Max Maximum value
ImageOp::Or Bitwise OR (inclusive OR)
ImageOp::And Bitwise AND
ImageOp::Xor Bitwise XOR (exclusive OR)
ImageOp::Not Bitwise NOT
ImageOp::Nor Bitwise NOR (inclusive NOR)
ImageOp::Nand Bitwise NAND
ImageOp::Xnor Bitwise XNOR (exclusive NOR)
ImageOp::ColorBurn Color burn pixel composition operator
ImageOp::LinearBurn Linear burn pixel composition operator
ImageOp::Screen Screen pixel composition operator
ImageOp::ColorDodge Color dodge pixel composition operator
ImageOp::Overlay Overlay pixel composition operator
ImageOp::SoftLight Soft light pixel composition operator
ImageOp::HardLight Hard light pixel composition operator
ImageOp::VividLight Vivid light pixel composition operator
ImageOp::LinearLight Linear light pixel composition operator
ImageOp::PinLight Pin light pixel composition operator
ImageOp::Exclusion Exclusion pixel composition operator
*/ namespace ImageOp { enum value_type { Nop, // No operation Mov, // a = b Add, // a + b Sub, // a - b Mul, // a * b Div, // a / b Pow, // Pow( a, b ) Dif, // |a - b| Min, // Min( a, b ) Max, // Max( a, b ) Or, // a | b And, // a & b Xor, // a ^ b Not, // ~a Nor, // ~(a | b) Nand, // ~(a & b) Xnor, // ~(a ^ b) ColorBurn, // 1 - Min( (1 - a)/b, 1 ) LinearBurn, // a + b - 1 Screen, // 1 - (1 - a)*(1 - b) ColorDodge, // Min( a/(1 - b), 1 ) Overlay, // (a > 0.5) ? 1 - ((1 - 2*(a - 0.5)) * (1 - b)) : 2*a*b SoftLight, // (b > 0.5) ? 1 - (1 - a)*(1 - b - 0.5) : a*(b + 0.5) HardLight, // (b > 0.5) ? 1 - (1 - a)*(1 - 2*(b - 0.5)) : 2*a*b VividLight, // (b > 0.5) ? 1 - Max( (1 - a)/(b - 0.5)/2, 1.0 ) : Min( a/(1 - 2*b ), 1.0 ) LinearLight, // (b > 0.5) ? Max( a + 2*(b - 0.5), 1.0 ) : Max( a + 2*b - 1, 1.0 ) PinLight, // (b > 0.5) ? Max( a, 2*(b - 0.5) ) : Min( a, 2*b ) Exclusion, // 0.5 - 2*(a - 0.5)*(b - 0.5) NumberOfOperators }; /*! * Returns true iff the specified operator \a op is an arithmetic operator. */ inline bool IsArithmeticOperator( int op ) noexcept { return op >= Add && op <= Dif; } /*! * Returns true iff the specified operator \a op is a bitwise logical * operator. */ inline bool IsBitwiseLogicalOperator( int op ) noexcept { return op >= Or && op <= Xnor; } /*! * Returns true iff the specified operator \a op is a move or replace * operator. */ inline bool IsMoveOperator( int op ) noexcept { return op == Mov || op == Min || op == Max; } /*! * Returns true iff the specified operator \a op is a pixel composition * operator. */ inline bool IsPixelCompositionOperator( int op ) noexcept { return op >= ColorBurn && op <= Exclusion; } /*! * Returns a string with the name of a pixel \a operation. */ String Id( value_type operation ); } // ---------------------------------------------------------------------------- #define m_pixelData m_data->data #define m_channelData( c ) reinterpret_cast( PCL_ASSUME_ALIGNED_32( m_pixelData[c] ) ) #define m_allocator m_data->allocator #define m_width m_geometry->width #define m_height m_geometry->height #define m_numberOfChannels m_geometry->numberOfChannels #define m_colorSpace m_color->colorSpace #define m_RGBWS m_color->RGBWS #define m_channel m_selected.channel #define m_lastChannel m_selected.lastChannel #define m_point m_selected.point #define m_rectangle m_selected.rectangle #define m_clipLow m_selected.clipLow #define m_clipHigh m_selected.clipHigh #define m_clippedLow m_selected.clippedLow #define m_clippedHigh m_selected.clippedHigh // ---------------------------------------------------------------------------- class PCL_CLASS ImageVariant; class PCL_CLASS ImageTransformation; class PCL_CLASS BidirectionalImageTransformation; // ---------------------------------------------------------------------------- /*! * \class GenericImage * \brief Implements a generic, two-dimensional, shared or local image. * * Template class %GenericImage implements a two-dimensional image in PCL. It * inherits all public properties and functionality from its base classes: * ImageGeometry, ImageColor, and AbstractImage, and adds a rich set of * processing primitives through a versatile and easily adaptable interface. * * Template instantiations of %GenericImage and GenericPixelTraits provide * instantiable image classes adapted to particular pixel sample types. Seven * image types are already predefined in this way by PCL, corresponding to all * supported real, integer and complex two-dimensional images in the current * versions of the PixInsight platform: * * * * * * * * * *
pcl::Image 32-bit floating point real image
pcl::DImage 64-bit floating point real image
pcl::ComplexImage 32-bit floating point complex image
pcl::DComplexImage 64-bit floating point complex image
pcl::UInt8Image 8-bit unsigned integer image
pcl::UInt16Image 16-bit unsigned integer image
pcl::UInt32Image 32-bit unsigned integer image
* * Developers can define new image types easily by writing suitable * instantiations of GenericPixelTraits and the corresponding instantiations of * %GenericImage. This system allows for the implementation of new image types * tightly adapted to particular requirements. * * \sa \ref image_types_2d "2-D Image Types", * \ref image_iterators "Image Iterators", ImageGeometry, ImageColor, * AbstractImage, ImageVariant, GenericPixelTraits */ template class PCL_CLASS GenericImage : public AbstractImage { public: /*! * Represents the GenericPixelTraits instantiation used in this template * instantiation of %GenericImage. * * The \c pixel_traits type identifies a class implementing basic storage * and functional primitives adapted to a particular sample data type. */ typedef P pixel_traits; /*! * Represents a class responsible for allocation and deallocation of pixel * data in this template instantiation of %GenericImage. \c pixel_allocator * must be able to handle shared and local pixel data allocation and * deallocation in a transparent way. * * By default, %GenericImage uses an instantiation of the PixelAllocator * template class for \c pixel_traits. */ typedef PixelAllocator

pixel_allocator; /*! * Represents the data type used to store pixel sample values in this * template instantiation of %GenericImage. */ typedef typename pixel_traits::sample sample; /*! * An enumerated type that represents the set of supported color spaces. * Valid constants for this enumeration are defined in the ColorSpace * namespace. */ typedef AbstractImage::color_space color_space; /*! * The type of a container class used to store rectangular and channel range * selections. */ typedef AbstractImage::selection_stack selection_stack; /*! * An enumerated type that represents the set of supported arithmetic and * bitwise pixel operations. Valid constants for this enumeration are * defined in the ImageOp namespace. */ typedef ImageOp::value_type image_op; /*! * A vector of pixel sample values. */ typedef GenericVector sample_vector; /*! * A dynamic array of pixel sample values. */ typedef Array sample_array; // ------------------------------------------------------------------------- /*! * \defgroup image_iterators Image Iterator Classes */ // ------------------------------------------------------------------------- /*! * \class pcl::GenericImage::sample_iterator * \brief Mutable pixel sample iterator. * * A mutable pixel sample iterator provides read/write, random access to * pixel samples in a single channel of an image. * * \ingroup image_iterators */ class sample_iterator { public: /*! * Represents the type of the iterated image. */ typedef GenericImage

image_type; /*! * Represents the pixel traits class used by the iterated image. */ typedef typename image_type::pixel_traits pixel_traits; /*! * Represents a pixel sample of the iterated image. */ typedef typename image_type::sample sample; /*! * Default constructor. Initializes an invalid iterator. */ sample_iterator() = default; /*! * Constructs a mutable pixel sample iterator for one channel of an * image. * * \param image The image to iterate. * * \param channel Channel index. If this parameter is a negative integer, * then this object will be initialized to iterate on the * currently selected channel of the \a image. If an * invalid (i.e., nonexistent) channel index is specified, * then the resulting iterator will also be invalid, with * an empty iteration range. The default value is -1. */ sample_iterator( image_type& image, int channel = -1 ) : m_image( &image ) { m_image->EnsureUnique(); if ( m_image->ParseChannel( channel ) ) { m_iterator = (*m_image)[channel]; m_end = m_iterator + m_image->NumberOfPixels(); } } /*! * Constructs a mutable pixel sample iterator for a range of contiguous * pixel samples of an image. * * \param image The image to iterate. * * \param i Pointer to the first pixel sample in the iteration * range. The iterator will be initialized to access this * pixel sample just after construction. * * \param j End of the iteration range. The iterator will be * invalid when it reaches (or surpasses) this point. * * \note Both iteration limits \a i and \a j must be pointers to pixel * samples in the \e same channel of the specified \a image. */ sample_iterator( image_type& image, sample* i, sample* j ) : m_image( &image ) , m_iterator( i ) , m_end( j ) { } /*! * Copy constructor. */ sample_iterator( const sample_iterator& ) = default; /*! * Copy assignment operator. Returns a reference to this object. */ sample_iterator& operator =( const sample_iterator& ) = default; /*! * Returns true only if this iterator is valid. A valid iterator defines * a valid iterated image and a non-null position. */ bool IsValid() const noexcept { return m_image != nullptr && m_iterator != nullptr; } /*! * Returns a reference to the image being iterated by this object. */ image_type& Image() const noexcept { return *m_image; } /*! * Returns a pointer to the pixel sample pointed to by this iterator. */ sample* Position() const noexcept { return m_iterator; } /*! * Boolean type conversion operator. Returns true if this iterator is * active. A pixel sample iterator is active if it has not reached (or * surpassed) its iteration limit: either the end of the iterated image * channel, or the end of the iteration range, depending on how the * iterator has been constructed. */ operator bool() const noexcept { return m_iterator < m_end; } /*! * Indirection operator. Returns a reference to the pixel sample pointed * to by this iterator. */ sample& operator *() const noexcept { return *m_iterator; } /*! * Pre-increment operator. Causes this iterator to point to the next * pixel sample in the iterated image channel. Returns a reference to * this iterator. */ sample_iterator& operator ++() noexcept { ++m_iterator; return *this; } /*! * Post-increment operator. Causes this iterator to point to the next * pixel sample in the iterated image channel. Returns a copy of the * iterator as it was before incrementing it. */ sample_iterator operator ++( int ) noexcept { return sample_iterator( *m_image, m_iterator++, m_end ); } /*! * Pre-decrement operator. Causes this iterator to point to the previous * pixel sample in the iterated image channel, then returns a reference * to this iterator. */ sample_iterator& operator --() noexcept { --m_iterator; return *this; } /*! * Post-decrement operator. Causes this iterator to point to the previous * pixel sample in the iterated image channel. Returns a copy of the * iterator as it was before decrementing it. */ sample_iterator operator --( int ) noexcept { return sample_iterator( *m_image, m_iterator--, m_end ); } /*! * Scalar assignment/addition operator. Increments this iterator by a * distance \a delta from its current position. Positive increments cause * this iterator to move forward by \a delta pixel samples. Negative * increments move this iterator backward by \a delta pixel samples. * Returns a reference to this iterator. */ sample_iterator& operator +=( distance_type delta ) noexcept { m_iterator += delta; return *this; } /*! * Scalar assignment/subtraction operator. Decrements this iterator by a * distance \a delta from its current position. Positive increments cause * this iterator to move backward by \a delta pixel samples. Negative * increments move this iterator forward by \a delta pixel samples. * Returns a reference to this iterator. */ sample_iterator& operator -=( distance_type delta ) noexcept { m_iterator -= delta; return *this; } /*! * Moves this iterator on the iterated image channel by the specified * horizontal and vertical increments in pixels, \a dx and \a dy * respectively, relative to its current position. Positive (negative) * \a dx increments move the iterator rightwards (leftwards). Positive * (negative) \a dy increments move the iterator downwards (upwards). */ sample_iterator& MoveBy( int dx, int dy ) noexcept { m_iterator += distance_type( dy )*m_image->Width() + distance_type( dx ); return *this; } /*! * Scalar-to-iterator addition operator. Returns an iterator equivalent * to the specified iterator \a i incremented by a distance \a delta. */ friend sample_iterator operator +( const sample_iterator& i, distance_type delta ) noexcept { return sample_iterator( *i.m_image, i.m_iterator + delta, i.m_end ); } /*! * Iterator-to-scalar addition operator. This operator implements the * commutative property of scalar-to-iterator addition. */ friend sample_iterator operator +( distance_type delta, const sample_iterator& i ) noexcept { return sample_iterator( *i.m_image, i.m_iterator + delta, i.m_end ); } /*! * Scalar-to-iterator subtraction operator. Returns an iterator equal to * the specified iterator \a i decremented by a distance \a delta. */ friend sample_iterator operator -( const sample_iterator& i, distance_type delta ) noexcept { return sample_iterator( *i.m_image, i.m_iterator - delta, i.m_end ); } /*! * Iterator subtraction operator. Returns the distance in pixels between * the specified iterators \a i and \a j. */ friend distance_type operator -( const sample_iterator& i, const sample_iterator& j ) noexcept { return i.m_iterator - j.m_iterator; } /*! * Returns the distance in pixels between an iterator \a i and a sample * pointer \a j. */ friend distance_type operator -( const sample_iterator& i, const sample* j ) noexcept { return i.m_iterator - j; } /*! * Returns the distance in pixels between a sample pointer \a i and an * iterator \a j. */ friend distance_type operator -( const sample* i, const sample_iterator& j ) noexcept { return i - j.m_iterator; } /*! * Equality operator. Returns true if two iterators \a i and \a j point * to the same pixel sample. */ friend bool operator ==( const sample_iterator& i, const sample_iterator& j ) noexcept { return i.m_iterator == j.m_iterator; } /*! * Returns true iff an iterator \a i and a sample pointer \a j point to * the same pixel sample. */ friend bool operator ==( const sample_iterator& i, const sample* j ) noexcept { return i.m_iterator == j; } /*! * Returns true iff a sample pointer \a i and an iterator \a j point to * the same pixel sample. */ friend bool operator ==( const sample* i, const sample_iterator& j ) noexcept { return i == j.m_iterator; } /*! * Less than operator. Returns true if the specified iterator \a i * precedes another iterator \a j. */ friend bool operator <( const sample_iterator& i, const sample_iterator& j ) noexcept { return i.m_iterator < j.m_iterator; } /*! * Returns true iff an iterator \a i precedes a sample pointer \a j. */ friend bool operator <( const sample_iterator& i, const sample* j ) noexcept { return i.m_iterator < j; } /*! * Returns true iff a sample pointer \a i precedes an iterator \a j. */ friend bool operator <( const sample* i, const sample_iterator& j ) noexcept { return i < j.m_iterator; } protected: image_type* m_image = nullptr; sample* __restrict__ m_iterator = nullptr; const sample* __restrict__ m_end = nullptr; friend class const_sample_iterator; }; // ------------------------------------------------------------------------- /*! * \class pcl::GenericImage::const_sample_iterator * \brief Immutable pixel sample iterator. * * An immutable pixel sample iterator provides read-only, random access to * pixel samples in a single channel of an image. * * \ingroup image_iterators */ class const_sample_iterator { public: /*! * Represents the type of the iterated image. */ typedef GenericImage

image_type; /*! * Represents the pixel traits class used by the iterated image. */ typedef typename image_type::pixel_traits pixel_traits; /*! * Represents a pixel sample of the iterated image. */ typedef typename image_type::sample sample; /*! * Default constructor. Initializes an invalid iterator. */ const_sample_iterator() = default; /*! * Constructs an immutable pixel sample iterator for one channel of an * image. * * \param image The constant image to iterate. * * \param channel Channel index. If this parameter is a negative integer, * then this object will be initialized to iterate on the * currently selected channel of the \a image. If an * invalid (i.e., nonexistent) channel index is specified, * then the resulting iterator will also be invalid, with * an empty iteration range. The default value is -1. */ const_sample_iterator( const image_type& image, int channel = -1 ) : m_image( &image ) { if ( m_image->ParseChannel( channel ) ) { m_iterator = (*m_image)[channel]; m_end = m_iterator + m_image->NumberOfPixels(); } } /*! * Constructs an immutable pixel sample iterator for a range of * contiguous pixel samples of an image. * * \param image The constant image to iterate. * * \param i Pointer to the first constant pixel sample in the * iteration range. The iterator will be initialized to * access this pixel sample just after construction. * * \param j End of the iteration range. The iterator will be * invalid when it reaches (or surpasses) this point. * * \note Both iteration limits \a i and \a j must be pointers to constant * pixel samples in the \e same channel of the specified \a image. */ const_sample_iterator( const image_type& image, const sample* i, const sample* j ) : m_image( &image ) , m_iterator( i ) , m_end( j ) { } /*! * Constructs an immutable iterator from a mutable iterator. * * \note Don't confuse this constructor with the copy constructor for the * const_sample_iterator class. Note that the argument to this function * is a reference to an object of a different class. */ const_sample_iterator( const sample_iterator& i ) : m_image( i.m_image ) , m_iterator( i.m_iterator ) , m_end( i.m_end ) { } /*! * Copy constructor. */ const_sample_iterator( const const_sample_iterator& ) = default; /*! * Assigns a mutable iterator to this object. Returns a reference to this * iterator. */ const_sample_iterator& operator =( const sample_iterator& i ) noexcept { m_image = i.m_image; m_iterator = i.m_iterator; m_end = i.m_end; return *this; } /*! * Copy assignment operator. Returns a reference to this object. */ const_sample_iterator& operator =( const const_sample_iterator& ) = default; /*! * Returns true only if this iterator is valid. A valid iterator defines * a valid iterated image and a non-null position. */ bool IsValid() const noexcept { return m_image != nullptr && m_iterator != nullptr; } /*! * Returns a reference to the constant image being iterated by this * object. */ const image_type& Image() const noexcept { return *m_image; } /*! * Returns a pointer to the constant pixel sample pointed to by this * iterator. */ const sample* Position() const noexcept { return m_iterator; } /*! * Boolean type conversion operator. Returns true if this iterator is * active. A pixel sample iterator is active if it has not reached (or * surpassed) its iteration limit: either the end of the iterated image * channel, or the end of the iteration range, depending on how the * iterator has been constructed. */ operator bool() const noexcept { return m_iterator < m_end; } /*! * Indirection operator. Returns a reference to the constant pixel sample * pointed to by this iterator. */ const sample& operator *() const noexcept { return *m_iterator; } /*! * Pre-increment operator. Causes this iterator to point to the next * pixel sample in the iterated image channel, then returns a reference * to this iterator. */ const_sample_iterator& operator ++() noexcept { ++m_iterator; return *this; } /*! * Post-increment operator. Causes this iterator to point to the next * pixel sample in the iterated image channel. Returns a copy of the * iterator as it was before incrementing it. */ const_sample_iterator operator ++( int ) noexcept { return const_sample_iterator( *m_image, m_iterator++, m_end ); } /*! * Pre-decrement operator. Causes this iterator to point to the previous * pixel sample in the iterated image channel, then returns a reference * to this iterator. */ const_sample_iterator& operator --() noexcept { --m_iterator; return *this; } /*! * Post-decrement operator. Causes this iterator to point to the previous * pixel sample in the iterated image channel. Returns a copy of the * iterator as it was before decrementing it. */ const_sample_iterator operator --( int ) noexcept { return const_sample_iterator( *m_image, m_iterator--, m_end ); } /*! * Scalar assignment/addition operator. Increments this iterator by a * distance \a delta from its current position. Positive increments cause * this iterator to move forward by \a delta pixel samples. Negative * increments move this iterator backward by \a delta pixel samples. * Returns a reference to this iterator. */ const_sample_iterator& operator +=( distance_type delta ) noexcept { m_iterator += delta; return *this; } /*! * Scalar assignment/subtraction operator. Decrements this iterator by a * distance \a delta from its current position. Positive increments cause * this iterator to move backward by \a delta pixel samples. Negative * increments move this iterator forward by \a delta pixel samples. * Returns a reference to this iterator. */ const_sample_iterator& operator -=( distance_type delta ) noexcept { m_iterator -= delta; return *this; } /*! * Moves this iterator on the iterated image channel by the specified * horizontal and vertical increments in pixels, \a dx and \a dy * respectively, relative to its current position. Positive (negative) * \a dx increments move the iterator rightwards (leftwards). Positive * (negative) \a dy increments move the iterator downwards (upwards). */ const_sample_iterator& MoveBy( int dx, int dy ) noexcept { m_iterator += distance_type( dy )*m_image->Width() + distance_type( dx ); return *this; } /*! * Scalar-to-iterator addition operator. Returns an iterator equivalent * to the specified iterator \a i incremented by a distance \a delta. */ friend const_sample_iterator operator +( const const_sample_iterator& i, distance_type delta ) noexcept { return const_sample_iterator( *i.m_image, i.m_iterator + delta, i.m_end ); } /*! * Iterator-to-scalar addition operator. This operator implements the * commutative property of scalar-to-iterator addition. */ friend const_sample_iterator operator +( distance_type delta, const const_sample_iterator& i ) noexcept { return const_sample_iterator( *i.m_image, i.m_iterator + delta, i.m_end ); } /*! * Scalar-to-iterator subtraction operator. Returns an iterator equal to * the specified iterator \a i decremented by a distance \a delta. */ friend const_sample_iterator operator -( const const_sample_iterator& i, distance_type delta ) noexcept { return const_sample_iterator( *i.m_image, i.m_iterator - delta, i.m_end ); } /*! * Iterator subtraction operator. Returns the distance in pixels between * the specified iterators \a i and \a j. */ friend distance_type operator -( const const_sample_iterator& i, const const_sample_iterator& j ) noexcept { return i.m_iterator - j.m_iterator; } /*! * Returns the distance in pixels between an iterator \a i and a sample * pointer \a j. */ friend distance_type operator -( const const_sample_iterator& i, const sample* j ) noexcept { return i.m_iterator - j; } /*! * Returns the distance in pixels between a sample pointer \a i and an * iterator \a j. */ friend distance_type operator -( const sample* i, const const_sample_iterator& j ) noexcept { return i - j.m_iterator; } /*! * Equality operator. Returns true if two iterators \a i and \a j point * to the same pixel sample. */ friend bool operator ==( const const_sample_iterator& i, const const_sample_iterator& j ) noexcept { return i.m_iterator == j.m_iterator; } /*! * Returns true iff an iterator \a i and a sample pointer \a j point to * the same pixel sample. */ friend bool operator ==( const const_sample_iterator& i, const sample* j ) noexcept { return i.m_iterator == j; } /*! * Returns true iff a sample pointer \a i and an iterator \a j point to * the same pixel sample. */ friend bool operator ==( const sample* i, const const_sample_iterator& j ) noexcept { return i == j.m_iterator; } /*! * Less than operator. Returns true if the specified iterator \a i * precedes another iterator \a j. */ friend bool operator <( const const_sample_iterator& i, const const_sample_iterator& j ) noexcept { return i.m_iterator < j.m_iterator; } /*! * Returns true iff an iterator \a i precedes a sample pointer \a j. */ friend bool operator <( const const_sample_iterator& i, const sample* j ) noexcept { return i.m_iterator < j; } /*! * Returns true iff a sample pointer \a i precedes an iterator \a j. */ friend bool operator <( const sample* i, const const_sample_iterator& j ) noexcept { return i < j.m_iterator; } protected: const image_type* m_image = nullptr; const sample* __restrict__ m_iterator = nullptr; const sample* __restrict__ m_end = nullptr; }; // ------------------------------------------------------------------------- template class roi_sample_iterator_base { protected: image_type* m_image = nullptr; sample_pointer m_iterator = nullptr; sample_pointer m_rowBegin = nullptr; sample_pointer m_rowEnd = nullptr; sample_pointer m_end = nullptr; roi_sample_iterator_base() = default; roi_sample_iterator_base( image_type& image, const Rect& rect, int channel ) : m_image( &image ) { Rect r = rect; if ( m_image->ParseRect( r ) ) { if ( m_image->ParseChannel( channel ) ) { m_iterator = m_rowBegin = m_image->PixelAddress( r.x0, r.y0, channel ); m_rowEnd = m_rowBegin + r.Width(); m_end = m_rowEnd + ((r.Height() - 1)*m_image->Width()); } } } roi_sample_iterator_base( image_type& image, sample_pointer i, sample_pointer j ) : m_image( &image ) { if ( j < i ) pcl::Swap( i, j ); m_iterator = m_rowBegin = i; m_rowEnd = m_rowBegin + (j - i)%m_image->Width(); m_end = j; } roi_sample_iterator_base( const roi_sample_iterator_base& i ) = default; roi_sample_iterator_base& operator =( const roi_sample_iterator_base& ) = default; void Increment() noexcept { if ( ++m_iterator == m_rowEnd ) { m_rowBegin += m_image->Width(); m_rowEnd += m_image->Width(); m_iterator = m_rowBegin; } } void Decrement() noexcept { if ( m_iterator == m_rowBegin ) { m_rowBegin -= m_image->Width(); m_rowEnd -= m_image->Width(); m_iterator = m_rowEnd; } --m_iterator; } void MoveBy( int cols, int rows ) noexcept { cols += m_iterator - m_rowBegin; if ( cols != 0 ) { int w = m_rowEnd - m_rowBegin; if ( pcl::Abs( cols ) >= w ) { rows += cols/w; cols %= w; } } int dy = rows * m_image->Width(); m_rowBegin += dy; m_rowEnd += dy; m_iterator = m_rowBegin + cols; } }; // ------------------------------------------------------------------------- /*! * \class pcl::GenericImage::roi_sample_iterator * \brief Mutable region-of-interest pixel sample iterator. * * A mutable, region-of-interest (ROI) pixel sample iterator provides * read/write, random access to pixel samples within a rectangular subset of * a single channel of an image. * * \ingroup image_iterators */ class roi_sample_iterator : private roi_sample_iterator_base, sample*> { public: /*! * Represents the type of the iterated image. */ typedef GenericImage

image_type; /*! * Represents the pixel traits class used by the iterated image. */ typedef typename image_type::pixel_traits pixel_traits; /*! * Represents a pixel sample of the iterated image. */ typedef typename image_type::sample sample; typedef roi_sample_iterator_base, sample*> iterator_base; /*! * Default constructor. Initializes an invalid iterator. */ roi_sample_iterator() = default; /*! * Constructs a mutable, region-of-interest (ROI) pixel sample iterator * for one channel of an image. * * \param image The image to iterate. * * \param rect Region of interest. If an empty rectangle is specified, * the current rectangular selection in the \a image will * be used. If the specified rectangle is not empty and * extends beyond image boundaries, only the intersection * with the image will be used. If that intersection does * not exist, then the resulting iterator will be invalid, * with an empty iteration range. The default value is an * empty rectangle. * * \param channel Channel index. If this parameter is a negative integer, * then this object will be initialized to iterate on the * currently selected channel of the \a image. If an * invalid (i.e., nonexistent) channel index is specified, * then the resulting iterator will also be invalid, with * an empty iteration range. The default value is -1. */ roi_sample_iterator( image_type& image, const Rect& rect = Rect( 0 ), int channel = -1 ) : iterator_base( image.EnsureUnique(), rect, channel ) { } /*! * Constructs a mutable, region-of-interest (ROI) pixel sample iterator * for a range of pixel samples of an image. * * \param image The image to iterate. * * \param i Pointer to the first pixel sample in the iteration * range. The iterator will be initialized to access this * pixel sample just after construction. * * \param j End of the iteration range. The iterator will be * invalid when it reaches (or surpasses) this point. * * \note Both iteration limits \a i and \a j must be pointers to pixel * samples in the \e same channel of the specified \a image. */ roi_sample_iterator( image_type& image, sample* i, sample* j ) : iterator_base( image, i, j ) { } /*! * Copy constructor. */ roi_sample_iterator( const roi_sample_iterator& ) = default; /*! * Copy assignment operator. Returns a reference to this object. */ roi_sample_iterator& operator =( const roi_sample_iterator& ) = default; /*! * Returns true only if this iterator is valid. A valid iterator defines * a valid iterated image and a non-null position. */ bool IsValid() const noexcept { return this->m_image != nullptr && this->m_iterator != nullptr; } /*! * Returns a reference to the image being iterated by this object. */ image_type& Image() const noexcept { return *this->m_image; } /*! * Returns a pointer to the pixel sample pointed to by this iterator. */ sample* Position() const noexcept { return this->m_iterator; } /*! * Boolean type conversion operator. Returns true if this iterator is * active. A ROI pixel sample iterator is active if it has not reached * (or surpassed) its iteration limit, i.e. the bottom right corner of * its iterated region of interest. */ operator bool() const noexcept { return this->m_iterator < this->m_end; } /*! * Indirection operator. Returns a reference to the pixel sample pointed * to by this iterator. */ sample& operator *() const noexcept { return *this->m_iterator; } /*! * Pre-increment operator. Causes this iterator to point to the next * pixel sample in the iterated region of interest, then returns a * reference to this iterator. */ roi_sample_iterator& operator ++() noexcept { this->Increment(); return *this; } /*! * Post-increment operator. Causes this iterator to point to the next * pixel sample in the iterated region of interest. Returns a copy of the * iterator as it was before incrementing it. */ roi_sample_iterator operator ++( int ) noexcept { roi_sample_iterator i0( *this ); this->Increment(); return i0; } /*! * Pre-decrement operator. Causes this iterator to point to the previous * pixel sample in the iterated region of interest, then returns a * reference to this iterator. */ roi_sample_iterator& operator --() noexcept { this->Decrement(); return *this; } /*! * Post-decrement operator. Causes this iterator to point to the previous * pixel sample in the iterated region of interest. Returns a copy of the * iterator as it was before decrementing it. */ roi_sample_iterator operator --( int ) noexcept { roi_sample_iterator i0( *this ); this->Decrement(); return i0; } /*! * Scalar assignment/addition operator. Increments this iterator by a * distance \a delta from its current position, within the rectangular * region of interest being iterated. Positive increments cause this * iterator to move forward (rightwards and downwards) by \a delta pixel * samples. Negative increments move this iterator backward (leftwards * and upwards) by \a delta pixel samples. Returns a reference to this * iterator. */ roi_sample_iterator& operator +=( distance_type delta ) noexcept { int w = this->m_rowEnd - this->m_rowBegin; iterator_base::MoveBy( delta%w, delta/w ); return *this; } /*! * Scalar assignment/subtraction operator. Decrements this iterator by a * distance \a delta from its current position, within the rectangular * region of interest being iterated. Positive increments cause this * iterator to move backward (leftwards and upwards) by \a delta pixel * samples. Negative increments move this iterator forward (rightwards * and downwards) by \a delta pixel samples. Returns a reference to this * iterator. */ roi_sample_iterator& operator -=( distance_type delta ) noexcept { int w = this->m_rowEnd - this->m_rowBegin; iterator_base::MoveBy( -delta%w, -delta/w ); return *this; } /*! * Moves this iterator within its rectangular region of interest by the * specified horizontal and vertical increments in pixels, \a dx and * \a dy respectively, relative to its current position. Positive * (negative) \a dx increments move the iterator rightwards (leftwards). * Positive (negative) \a dy increments move the iterator downwards * (upwards). */ roi_sample_iterator& MoveBy( int dx, int dy ) noexcept { iterator_base::MoveBy( dx, dy ); return *this; } /*! * Scalar-to-iterator addition operator. Returns an iterator equivalent * to the specified iterator \a i incremented by a distance \a delta. */ friend roi_sample_iterator operator +( const roi_sample_iterator& i, distance_type delta ) noexcept { roi_sample_iterator j( i ); j += delta; return j; } /*! * Iterator-to-scalar addition operator. This operator implements the * commutative property of scalar-to-iterator addition. */ friend roi_sample_iterator operator +( distance_type delta, const roi_sample_iterator& i ) noexcept { roi_sample_iterator j( i ); j += delta; return j; } /*! * Scalar-to-iterator subtraction operator. Returns an iterator equal to * the specified iterator \a i decremented by a distance \a delta. */ friend roi_sample_iterator operator -( const roi_sample_iterator& i, distance_type delta ) noexcept { roi_sample_iterator j( i ); j -= delta; return j; } /*! * Equality operator. Returns true if two iterators \a i and \a j point * to the same pixel sample. */ friend bool operator ==( const roi_sample_iterator& i, const roi_sample_iterator& j ) noexcept { return i.m_iterator == j.m_iterator; } /*! * Returns true iff an iterator \a i and a sample pointer \a j point to * the same pixel sample. */ friend bool operator ==( const roi_sample_iterator& i, const sample* j ) noexcept { return i.m_iterator == j; } /*! * Returns true iff a sample pointer \a i and an iterator \a j point to * the same pixel sample. */ friend bool operator ==( const sample* i, const roi_sample_iterator& j ) noexcept { return i == j.m_iterator; } /*! * Less than operator. Returns true if the specified iterator \a i * precedes another iterator \a j. */ friend bool operator <( const roi_sample_iterator& i, const roi_sample_iterator& j ) noexcept { return i.m_iterator < j.m_iterator; } /*! * Returns true iff an iterator \a i precedes a sample pointer \a j. */ friend bool operator <( const roi_sample_iterator& i, const sample* j ) noexcept { return i.m_iterator < j; } /*! * Returns true iff a sample pointer \a i precedes an iterator \a j. */ friend bool operator <( const sample* i, const roi_sample_iterator& j ) noexcept { return i < j.m_iterator; } friend class const_roi_pixel_iterator; }; // ------------------------------------------------------------------------- /*! * \class pcl::GenericImage::const_roi_sample_iterator * \brief Immutable region-of-interest pixel sample iterator. * * An immutable, region-of-interest pixel sample iterator provides * read-only, random access to pixel samples within a rectangular subset of * a single channel of an image. * * \ingroup image_iterators */ class const_roi_sample_iterator : private roi_sample_iterator_base, const sample*> { public: /*! * Represents the type of the iterated image. */ typedef GenericImage

image_type; /*! * Represents the pixel traits class used by the iterated image. */ typedef typename image_type::pixel_traits pixel_traits; /*! * Represents a pixel sample of the iterated image. */ typedef typename image_type::sample sample; typedef roi_sample_iterator_base, const sample*> iterator_base; /*! * Default constructor. Initializes an invalid iterator. */ const_roi_sample_iterator() = default; /*! * Constructs an immutable, region-of-interest (ROI) pixel sample * iterator for one channel of an image. * * \param image The constant image to iterate. * * \param rect Region of interest. If an empty rectangle is specified, * the current rectangular selection in the \a image will * be used. If the specified rectangle is not empty and * extends beyond image boundaries, only the intersection * with the image will be used. If that intersection does * not exist, then the resulting iterator will be invalid, * with an empty iteration range. The default value is an * empty rectangle. * * \param channel Channel index. If this parameter is a negative integer, * then this object will be initialized to iterate on the * currently selected channel of the \a image. If an * invalid (i.e., nonexistent) channel index is specified, * then the resulting iterator will also be invalid, with * an empty iteration range. The default value is -1. */ const_roi_sample_iterator( const image_type& image, const Rect& rect = Rect( 0 ), int channel = -1 ) : iterator_base( image, rect, channel ) { } /*! * Constructs an immutable, region-of-interest (ROI) pixel sample * iterator for a range of pixel samples of an image. * * \param image The constant image to iterate. * * \param i Pointer to the first constant pixel sample in the * iteration range. The iterator will be initialized to * access this pixel sample just after construction. * * \param j End of the iteration range. The iterator will be * invalid when it reaches (or surpasses) this point. * * \note Both iteration limits \a i and \a j must be pointers to constant * pixel samples in the \e same channel of the specified \a image. */ const_roi_sample_iterator( const image_type& image, const sample* i, const sample* j ) : iterator_base( image, i, j ) { } /*! * Constructs an immutable ROI pixel sample iterator from a mutable ROI * pixel sample iterator. * * \note Don't confuse this constructor with the copy constructor for the * const_roi_sample_iterator class. Note that the argument to this * function is a reference to an object of a different class. */ const_roi_sample_iterator( const roi_sample_iterator& i ) : iterator_base( i.m_image, i.m_rowBegin, i.m_end ) { } /*! * Copy constructor. */ const_roi_sample_iterator( const const_roi_sample_iterator& ) = default; /*! * Assigns a mutable iterator to this object. Returns a reference to this * iterator. */ const_roi_sample_iterator& operator =( const roi_sample_iterator& i ) noexcept { this->m_image = i.m_image; this->m_iterator = i.m_iterator; this->m_rowBegin = i.m_rowBegin; this->m_rowEnd = i.m_rowEnd; this->m_end = i.m_end; return *this; } /*! * Copy assignment operator. Returns a reference to this object. */ const_roi_sample_iterator& operator =( const const_roi_sample_iterator& ) = default; /*! * Returns true only if this iterator is valid. A valid iterator defines * a valid iterated image and a non-null position. */ bool IsValid() const noexcept { return this->m_image != nullptr && this->m_iterator != nullptr; } /*! * Returns a reference to the constant image being iterated by this * object. */ const image_type& Image() const noexcept { return *this->m_image; } /*! * Returns a pointer to the constant pixel sample pointed to by this * iterator. */ const sample* Position() const noexcept { return this->m_iterator; } /*! * Boolean type conversion operator. Returns true if this iterator is * active. A ROI pixel sample iterator is active if it has not reached * (or surpassed) its iteration limit, i.e. the bottom right corner of * its iterated region of interest. */ operator bool() const noexcept { return this->m_iterator < this->m_end; } /*! * Indirection operator. Returns a reference to the constant pixel sample * pointed to by this iterator. */ const sample& operator *() const noexcept { return *this->m_iterator; } /*! * Pre-increment operator. Causes this iterator to point to the next * pixel sample in the iterated region of interest, then returns a * reference to this iterator. */ const_roi_sample_iterator& operator ++() noexcept { this->Increment(); return *this; } /*! * Post-increment operator. Causes this iterator to point to the next * pixel sample in the iterated region of interest. Returns a copy of the * iterator as it was before incrementing it. */ const_roi_sample_iterator operator ++( int ) noexcept { const_roi_sample_iterator i0( *this ); this->Increment(); return i0; } /*! * Pre-decrement operator. Causes this iterator to point to the previous * pixel sample in the iterated region of interest, then returns a * reference to this iterator. */ const_roi_sample_iterator& operator --() noexcept { this->Decrement(); return *this; } /*! * Post-decrement operator. Causes this iterator to point to the previous * pixel sample in the iterated region of interest. Returns a copy of the * iterator as it was before decrementing it. */ const_roi_sample_iterator operator --( int ) noexcept { const_roi_sample_iterator i0( *this ); this->Decrement(); return i0; } /*! * Scalar assignment/addition operator. Increments this iterator by a * distance \a delta from its current position, within the rectangular * region of interest being iterated. Positive increments cause this * iterator to move forward (rightwards and downwards) by \a delta pixel * samples. Negative increments move this iterator backward (leftwards * and upwards) by \a delta pixel samples. Returns a reference to this * iterator. */ const_roi_sample_iterator& operator +=( distance_type delta ) noexcept { int w = this->m_rowEnd - this->m_rowBegin; iterator_base::MoveBy( delta%w, delta/w ); return *this; } /*! * Scalar assignment/subtraction operator. Decrements this iterator by a * distance \a delta from its current position, within the rectangular * region of interest being iterated. Positive increments cause this * iterator to move backward (leftwards and upwards) by \a delta pixel * samples. Negative increments move this iterator forward (rightwards * and downwards) by \a delta pixel samples. Returns a reference to this * iterator. */ const_roi_sample_iterator& operator -=( distance_type delta ) noexcept { int w = this->m_rowEnd - this->m_rowBegin; iterator_base::MoveBy( -delta%w, -delta/w ); return *this; } /*! * Moves this iterator within its rectangular region of interest by the * specified horizontal and vertical increments in pixels, \a dx and * \a dy respectively, relative to its current position. Positive * (negative) \a dx increments move the iterator rightwards (leftwards). * Positive (negative) \a dy increments move the iterator downwards * (upwards). */ const_roi_sample_iterator& MoveBy( int dx, int dy ) noexcept { iterator_base::MoveBy( dx, dy ); return *this; } /*! * Scalar-to-iterator addition operator. Returns an iterator equivalent * to the specified iterator \a i incremented by a distance \a delta. */ friend const_roi_sample_iterator operator +( const const_roi_sample_iterator& i, distance_type delta ) noexcept { const_roi_sample_iterator j( i ); j += delta; return j; } /*! * Iterator-to-scalar addition operator. This operator implements the * commutative property of scalar-to-iterator addition. */ friend const_roi_sample_iterator operator +( distance_type delta, const const_roi_sample_iterator& i ) noexcept { const_roi_sample_iterator j( i ); j += delta; return j; } /*! * Scalar-to-iterator subtraction operator. Returns an iterator equal to * the specified iterator \a i decremented by a distance \a delta. */ friend const_roi_sample_iterator operator -( const const_roi_sample_iterator& i, distance_type delta ) noexcept { const_roi_sample_iterator j( i ); j -= delta; return j; } /*! * Equality operator. Returns true if two iterators \a i and \a j point * to the same pixel sample. */ friend bool operator ==( const const_roi_sample_iterator& i, const const_roi_sample_iterator& j ) noexcept { return i.m_iterator == j.m_iterator; } /*! * Returns true iff an iterator \a i and a sample pointer \a j point to * the same pixel sample. */ friend bool operator ==( const const_roi_sample_iterator& i, const sample* j ) noexcept { return i.m_iterator == j; } /*! * Returns true iff a sample pointer \a i and an iterator \a j point to * the same pixel sample. */ friend bool operator ==( const sample* i, const const_roi_sample_iterator& j ) noexcept { return i == j.m_iterator; } /*! * Less than operator. Returns true if the specified iterator \a i * precedes another iterator \a j. */ friend bool operator <( const const_roi_sample_iterator& i, const const_roi_sample_iterator& j ) noexcept { return i.m_iterator < j.m_iterator; } /*! * Returns true iff an iterator \a i precedes a sample pointer \a j. */ friend bool operator <( const const_roi_sample_iterator& i, const sample* j ) noexcept { return i.m_iterator < j; } /*! * Returns true iff a sample pointer \a i precedes an iterator \a j. */ friend bool operator <( const sample* i, const const_roi_sample_iterator& j ) noexcept { return i < j.m_iterator; } }; // ------------------------------------------------------------------------- template class filter_sample_iterator_base : public iterator_base { protected: filter_type m_filter; sample_pointer m_begin = nullptr; filter_sample_iterator_base() = default; filter_sample_iterator_base( image_type& image, const filter_type& filter, int channel ) : iterator_base( image, channel ) , m_filter( filter ) , m_begin( iterator_base::m_iterator ) { JumpToNextValidSample(); } filter_sample_iterator_base( image_type& image, const filter_type& filter, sample_pointer i, sample_pointer j ) : iterator_base( image, i, j ) , m_filter( filter ) , m_begin( iterator_base::m_iterator ) { JumpToNextValidSample(); } filter_sample_iterator_base( const iterator_base& i, const filter_type& filter ) : iterator_base( i ) , m_filter( filter ) , m_begin( iterator_base::m_iterator ) { JumpToNextValidSample(); } filter_sample_iterator_base( const filter_sample_iterator_base& ) = default; filter_sample_iterator_base& operator =( const filter_sample_iterator_base& ) = default; filter_sample_iterator_base& operator =( const iterator_base& i ) noexcept { (void)iterator_base::operator =( i ); JumpToNextValidSample(); return *this; } void JumpToNextValidSample() noexcept { while ( this->m_iterator < this->m_end && !this->m_filter( *this->m_iterator ) ) ++this->m_iterator; } void JumpToPrevValidSample() noexcept { while ( this->m_iterator > this->m_begin && !this->m_filter( *this->m_iterator ) ) --this->m_iterator; } }; // ------------------------------------------------------------------------- /*! * \class pcl::GenericImage::filter_sample_iterator * \brief Mutable filter pixel sample iterator. * * A mutable pixel sample iterator provides read/write, random access to * pixel samples in a single channel of an image. A filter iterator uses a * predicate object to filter pixel samples within its iteration range. * * Filter Predicates * * For a mutable filter sample iterator, the class template argument F must * provide a function call operator of the form: * * \code bool F::operator()( sample& v ) const \endcode * * which returns true if the passed pixel sample value \a v is valid in the * context of the filter iterator. The filter iterator stores a private * instance of F to validate pixel samples, so the F class must provide also * copy constructor semantics, either explicitly or implicitly. * * Filter Iterators * * Each time a filter iterator is constructed, incremented, decremented, or * moved forward or backward, the filter predicate is called to validate the * pixel or pixel sample pointed to by the iterator. If the pointed item is * not valid, the iterator is incremented or decremented (depending on the * operation performed initially) until it finds a valid one, or until it * reaches the end of the iteration range, whichever happens first. In this * way a filter iterator gives access only to valid items in a completely * automatic and transparent fashion. * * \ingroup image_iterators */ template class filter_sample_iterator : public filter_sample_iterator_base, sample_iterator, sample*, F> { public: /*! * Represents the type of the iterated image. */ typedef GenericImage

image_type; /*! * Represents the pixel traits class used by the iterated image. */ typedef typename image_type::pixel_traits pixel_traits; /*! * Represents a pixel sample of the iterated image. */ typedef typename image_type::sample sample; /*! * Represents the type of the unary predicate used by this filter * iterator. */ typedef F filter_type; typedef filter_sample_iterator_base, sample_iterator, sample*, F> iterator_base; /*! * Default constructor. Initializes an invalid iterator. */ filter_sample_iterator() = default; /*! * Constructs a mutable filter pixel sample iterator for one channel of * an image. * * \param image The image to iterate. * * \param filter Reference to a predicate object that will be used to * filter pixel sample values. * * \param channel Channel index. If this parameter is a negative integer, * then this object will be initialized to iterate on the * currently selected channel of the \a image. If an * invalid (i.e., nonexistent) channel index is specified, * then the resulting iterator will also be invalid, with * an empty iteration range. The default value is -1. */ filter_sample_iterator( image_type& image, const F& filter, int channel = -1 ) : iterator_base( image.EnsureUnique(), filter, channel ) { } /*! * Constructs a mutable filter pixel sample iterator for a range of * contiguous pixel samples of an image. * * \param image The image to iterate. * * \param filter Reference to a predicate object that will be used to * filter pixel sample values. * * \param i Pointer to the first pixel sample in the iteration * range. * * \param j End of the iteration range. The iterator will be * invalid when it reaches (or surpasses) this point. * * \note Both iteration limits \a i and \a j must be pointers to pixel * samples in the \e same channel of the specified \a image. */ filter_sample_iterator( image_type& image, const F& filter, sample* i, sample* j ) : iterator_base( image, filter, i, j ) { } /*! * Constructs a mutable filter pixel sample iterator from a sample * iterator and the specified \a filter. */ filter_sample_iterator( const sample_iterator& i, const F& filter ) : iterator_base( i, filter ) { } /*! * Copy constructor. */ filter_sample_iterator( const filter_sample_iterator& i ) = default; /*! * Copy assignment operator. Returns a reference to this iterator. */ filter_sample_iterator& operator =( const filter_sample_iterator& ) = default; /*! * Assigns a pixel sample iterator to this object. Returns a reference to * this iterator. */ filter_sample_iterator& operator =( const sample_iterator& i ) noexcept { (void)iterator_base::operator =( i ); return *this; } /*! * Returns true only if this iterator is valid. A valid iterator defines * a valid iterated image and a non-null position. */ bool IsValid() const noexcept { return this->m_image != nullptr && this->m_iterator != nullptr; } /*! * Returns a reference to the image being iterated by this object. */ image_type& Image() const noexcept { return *this->m_image; } /*! * Returns a reference to the immutable predicate object used by this * filter iterator. */ const filter_type& Filter() const noexcept { return this->m_filter; } /*! * Returns a reference to the mutable predicate object used by this * filter iterator. */ filter_type& Filter() noexcept { return this->m_filter; } /*! * Returns a pointer to the pixel sample pointed to by this iterator. */ sample* Position() const noexcept { return this->m_iterator; } /*! * Boolean type conversion operator. Returns true if this iterator is * active. A pixel sample iterator is active if it has not reached (or * surpassed) its iteration limit: either the end of the iterated image * channel, or the end of the iteration range, depending on how the * iterator has been constructed. */ operator bool() const noexcept { return this->m_iterator < this->m_end; } /*! * Indirection operator. Returns a reference to the pixel sample pointed * to by this iterator. */ sample& operator *() const noexcept { return *this->m_iterator; } /*! * Pre-increment operator. Causes this iterator to point to the next * valid pixel sample in the iterated image channel. Returns a reference * to this iterator. */ filter_sample_iterator& operator ++() noexcept { ++this->m_iterator; this->JumpToNextValidSample(); return *this; } /*! * Post-increment operator. Causes this iterator to point to the next * valid pixel sample in the iterated image channel. Returns a copy of * the iterator as it was before incrementing it. */ filter_sample_iterator operator ++( int ) noexcept { sample* __restrict__ i0 = this->m_iterator++; this->JumpToNextValidSample(); return filter_sample_iterator( *this->m_image, this->m_filter, i0, this->m_end ); } /*! * Pre-decrement operator. Causes this iterator to point to the previous * valid pixel sample in the iterated image channel, then returns a * reference to this iterator. */ filter_sample_iterator& operator --() noexcept { --this->m_iterator; this->JumpToPrevValidSample(); return *this; } /*! * Post-decrement operator. Causes this iterator to point to the previous * valid pixel sample in the iterated image channel. Returns a copy of * the iterator as it was before decrementing it. */ filter_sample_iterator operator --( int ) noexcept { sample* __restrict__ i0 = this->m_iterator--; this->JumpToPrevValidSample(); return filter_sample_iterator( *this->m_image, this->m_filter, i0, this->m_end ); } /*! * Scalar assignment/addition operator. Increments this iterator by a * distance \a delta from its current position. Positive increments cause * this iterator to move forward by \a delta pixel samples. Negative * increments move this iterator backward by \a delta pixel samples. * Returns a reference to this iterator. */ filter_sample_iterator& operator +=( distance_type delta ) noexcept { this->m_iterator += delta; this->JumpToNextValidSample(); return *this; } /*! * Scalar assignment/subtraction operator. Decrements this iterator by a * distance \a delta from its current position. Positive increments cause * this iterator to move backward by \a delta pixel samples. Negative * increments move this iterator forward by \a delta pixel samples. * Returns a reference to this iterator. */ filter_sample_iterator& operator -=( distance_type delta ) noexcept { this->m_iterator -= delta; this->JumpToPrevValidSample(); return *this; } /*! * Moves this iterator on the iterated image channel by the specified * horizontal and vertical increments in pixels, \a dx and \a dy * respectively, relative to its current position. Positive (negative) * \a dx increments move the iterator rightwards (leftwards). Positive * (negative) \a dy increments move the iterator downwards (upwards). */ filter_sample_iterator& MoveBy( int dx, int dy ) noexcept { distance_type d = distance_type( dy )*this->m_image->Width() + distance_type( dx ); this->m_iterator += d; if ( d >= 0 ) this->JumpToNextValidSample(); else this->JumpToPrevValidSample(); return *this; } /*! * Scalar-to-iterator addition operator. Returns an iterator equivalent * to the specified iterator \a i incremented by a distance \a delta. */ friend filter_sample_iterator operator +( const filter_sample_iterator& i, distance_type delta ) noexcept { return filter_sample_iterator( *i.m_image, i.m_iterator + delta, i.m_end ); } /*! * Iterator-to-scalar addition operator. This operator implements the * commutative property of scalar-to-iterator addition. */ friend filter_sample_iterator operator +( distance_type delta, const filter_sample_iterator& i ) noexcept { return filter_sample_iterator( *i.m_image, i.m_iterator + delta, i.m_end ); } /*! * Scalar-to-iterator subtraction operator. Returns an iterator equal to * the specified iterator \a i decremented by a distance \a delta. */ friend filter_sample_iterator operator -( const filter_sample_iterator& i, distance_type delta ) noexcept { return filter_sample_iterator( *i.m_image, i.m_iterator - delta, i.m_end ); } /*! * Iterator subtraction operator. Returns the distance in pixels between * the specified iterators \a i and \a j. */ friend distance_type operator -( const filter_sample_iterator& i, const filter_sample_iterator& j ) noexcept { return i.m_iterator - j.m_iterator; } /*! * Equality operator. Returns true if two iterators \a i and \a j point * to the same pixel sample. */ friend bool operator ==( const filter_sample_iterator& i, const filter_sample_iterator& j ) noexcept { return i.m_iterator == j.m_iterator; } /*! * Returns true iff an iterator \a i and a sample pointer \a j point to * the same pixel sample. */ friend bool operator ==( const filter_sample_iterator& i, const sample* j ) noexcept { return i.m_iterator == j; } /*! * Returns true iff a sample pointer \a i and an iterator \a j point to * the same pixel sample. */ friend bool operator ==( const sample* i, const filter_sample_iterator& j ) noexcept { return i == j.m_iterator; } /*! * Less than operator. Returns true if the specified iterator \a i * precedes another iterator \a j. */ friend bool operator <( const filter_sample_iterator& i, const filter_sample_iterator& j ) noexcept { return i.m_iterator < j.m_iterator; } /*! * Returns true iff an iterator \a i precedes a sample pointer \a j. */ friend bool operator <( const filter_sample_iterator& i, const sample* j ) noexcept { return i.m_iterator < j; } /*! * Returns true iff a sample pointer \a i precedes an iterator \a j. */ friend bool operator <( const sample* i, const filter_sample_iterator& j ) noexcept { return i < j.m_iterator; } }; // ------------------------------------------------------------------------- /*! * \class pcl::GenericImage::const_filter_sample_iterator * \brief Immutable filter pixel sample iterator. * * An immutable pixel sample iterator provides read-only, random access to * pixel samples in a single channel of an image. A filter iterator uses a * predicate object to filter pixel samples within its iteration range. * * Filter Predicates * * For an immutable filter sample iterator, the class template argument F * must provide a function call operator of the form: * * \code bool F::operator()( const sample& v ) const \endcode * * which returns true if the passed pixel sample value \a v is valid in the * context of the filter iterator. The filter iterator stores a private * instance of F to validate pixel samples, so the F class must provide also * copy constructor semantics, either explicitly or implicitly. * * Filter Iterators * * Each time a filter iterator is constructed, incremented, decremented, or * moved forward or backward, the filter predicate is called to validate the * pixel or pixel sample pointed to by the iterator. If the pointed item is * not valid, the iterator is incremented or decremented (depending on the * operation performed initially) until it finds a valid one, or until it * reaches the end of the iteration range, whichever happens first. In this * way a filter iterator gives access only to valid items in a completely * automatic and transparent fashion. * * \ingroup image_iterators */ template class const_filter_sample_iterator : public filter_sample_iterator_base, const_sample_iterator, const sample*, F> { public: /*! * Represents the type of the iterated image. */ typedef GenericImage

image_type; /*! * Represents the pixel traits class used by the iterated image. */ typedef typename image_type::pixel_traits pixel_traits; /*! * Represents a pixel sample of the iterated image. */ typedef typename image_type::sample sample; /*! * Represents the type of the unary predicate used by this filter * iterator. */ typedef F filter_type; typedef filter_sample_iterator_base, const_sample_iterator, const sample*, F> iterator_base; /*! * Default constructor. Initializes an invalid iterator. */ const_filter_sample_iterator() = default; /*! * Constructs an immutable filter pixel sample iterator for one channel * of an image. * * \param image The constant image to iterate. * * \param filter Reference to a predicate object that will be used to * filter pixel sample values. * * \param channel Channel index. If this parameter is a negative integer, * then this object will be initialized to iterate on the * currently selected channel of the \a image. If an * invalid (i.e., nonexistent) channel index is specified, * then the resulting iterator will also be invalid, with * an empty iteration range. The default value is -1. */ const_filter_sample_iterator( const image_type& image, const F& filter, int channel = -1 ) : iterator_base( image, filter, channel ) { } /*! * Constructs an immutable filter pixel sample iterator for a range of * contiguous pixel samples of an image. * * \param image The constant image to iterate. * * \param filter Reference to a predicate object that will be used to * filter pixel sample values. * * \param i Pointer to the first constant pixel sample in the * iteration range. * * \param j End of the iteration range. The iterator will be * invalid when it reaches (or surpasses) this point. * * \note Both iteration limits \a i and \a j must be pointers to constant * pixel samples in the \e same channel of the specified \a image. */ const_filter_sample_iterator( const image_type& image, const F& filter, const sample* i, const sample* j ) : iterator_base( image, filter, i, j ) { } /*! * Constructs an immutable filter pixel sample iterator from a mutable * sample iterator and the specified \a filter. */ const_filter_sample_iterator( const sample_iterator& i, const F& filter ) : iterator_base( i.m_image, filter, i.m_iterator, i.m_end ) { } /*! * Constructs an immutable filter pixel sample iterator from an immutable * sample iterator and the specified \a filter. */ const_filter_sample_iterator( const const_sample_iterator& i, const F& filter ) : iterator_base( i, filter ) { } /*! * Constructs an immutable filter pixel sample iterator from a mutable * filter pixel sample iterator. */ const_filter_sample_iterator( const filter_sample_iterator& i ) : iterator_base( i ) { } /*! * Copy constructor. */ const_filter_sample_iterator( const const_filter_sample_iterator& ) = default; /*! * Copy assignment operator. Returns a reference to this iterator. */ const_filter_sample_iterator& operator =( const const_filter_sample_iterator& ) = default; /*! * Assigns a mutable pixel sample iterator to this object. Returns a * reference to this iterator. */ const_filter_sample_iterator& operator =( const sample_iterator& i ) noexcept { (void)const_sample_iterator::operator =( i ); this->JumpToNextValidSample(); return *this; } /*! * Assigns an immutable pixel sample iterator to this object. Returns a * reference to this iterator. */ const_filter_sample_iterator& operator =( const const_sample_iterator& i ) noexcept { (void)iterator_base::operator =( i ); return *this; } /*! * Returns true only if this iterator is valid. A valid iterator defines * a valid iterated image and a non-null position. */ bool IsValid() const noexcept { return this->m_image != nullptr && this->m_iterator != nullptr; } /*! * Returns a reference to the constant image being iterated by this * object. */ const image_type& Image() const noexcept { return *this->m_image; } /*! * Returns a reference to the immutable predicate object used by this * filter iterator. */ const filter_type& Filter() const noexcept { return this->m_filter; } /*! * Returns a reference to the mutable predicate object used by this * filter iterator. */ filter_type& Filter() noexcept { return this->m_filter; } /*! * Returns a pointer to the constant pixel sample pointed to by this * iterator. */ const sample* Position() const noexcept { return this->m_iterator; } /*! * Boolean type conversion operator. Returns true if this iterator is * active. A pixel sample iterator is active if it has not reached (or * surpassed) its iteration limit: either the end of the iterated image * channel, or the end of the iteration range, depending on how the * iterator has been constructed. */ operator bool() const noexcept { return this->m_iterator < this->m_end; } /*! * Indirection operator. Returns a reference to the constant pixel sample * pointed to by this iterator. */ const sample& operator *() const noexcept { return *this->m_iterator; } /*! * Pre-increment operator. Causes this iterator to point to the next * valid pixel sample in the iterated image channel. Returns a reference * to this iterator. */ const_filter_sample_iterator& operator ++() noexcept { ++this->m_iterator; this->JumpToNextValidSample(); return *this; } /*! * Post-increment operator. Causes this iterator to point to the next * valid pixel sample in the iterated image channel. Returns a copy of * the iterator as it was before incrementing it. */ const_filter_sample_iterator operator ++( int ) noexcept { sample* __restrict__ i0 = this->m_iterator++; this->JumpToNextValidSample(); return const_filter_sample_iterator( *this->m_image, this->m_filter, i0, this->m_end ); } /*! * Pre-decrement operator. Causes this iterator to point to the previous * valid pixel sample in the iterated image channel, then returns a * reference to this iterator. */ const_filter_sample_iterator& operator --() noexcept { --this->m_iterator; this->JumpToPrevValidSample(); return *this; } /*! * Post-decrement operator. Causes this iterator to point to the previous * valid pixel sample in the iterated image channel. Returns a copy of * the iterator as it was before decrementing it. */ const_filter_sample_iterator operator --( int ) noexcept { sample* __restrict__ i0 = this->m_iterator--; this->JumpToPrevValidSample(); return const_filter_sample_iterator( *this->m_image, this->m_filter, i0, this->m_end ); } /*! * Scalar assignment/addition operator. Increments this iterator by a * distance \a delta from its current position. Positive increments cause * this iterator to move forward by \a delta pixel samples. Negative * increments move this iterator backward by \a delta pixel samples. * Returns a reference to this iterator. */ const_filter_sample_iterator& operator +=( distance_type delta ) noexcept { this->m_iterator += delta; this->JumpToNextValidSample(); return *this; } /*! * Scalar assignment/subtraction operator. Decrements this iterator by a * distance \a delta from its current position. Positive increments cause * this iterator to move backward by \a delta pixel samples. Negative * increments move this iterator forward by \a delta pixel samples. * Returns a reference to this iterator. */ const_filter_sample_iterator& operator -=( distance_type delta ) noexcept { this->m_iterator -= delta; this->JumpToPrevValidSample(); return *this; } /*! * Moves this iterator on the iterated image channel by the specified * horizontal and vertical increments in pixels, \a dx and \a dy * respectively, relative to its current position. Positive (negative) * \a dx increments move the iterator rightwards (leftwards). Positive * (negative) \a dy increments move the iterator downwards (upwards). */ const_filter_sample_iterator& MoveBy( int dx, int dy ) noexcept { distance_type d = distance_type( dy )*this->m_image->Width() + distance_type( dx ); this->m_iterator += d; if ( d >= 0 ) this->JumpToNextValidSample(); else this->JumpToPrevValidSample(); return *this; } /*! * Scalar-to-iterator addition operator. Returns an iterator equivalent * to the specified iterator \a i incremented by a distance \a delta. */ friend const_filter_sample_iterator operator +( const const_filter_sample_iterator& i, distance_type delta ) noexcept { return const_filter_sample_iterator( *i.m_image, i.m_iterator + delta, i.m_end ); } /*! * Iterator-to-scalar addition operator. This operator implements the * commutative property of scalar-to-iterator addition. */ friend const_filter_sample_iterator operator +( distance_type delta, const const_filter_sample_iterator& i ) noexcept { return const_filter_sample_iterator( *i.m_image, i.m_iterator + delta, i.m_end ); } /*! * Scalar-to-iterator subtraction operator. Returns an iterator equal to * the specified iterator \a i decremented by a distance \a delta. */ friend const_filter_sample_iterator operator -( const const_filter_sample_iterator& i, distance_type delta ) noexcept { return const_filter_sample_iterator( *i.m_image, i.m_iterator - delta, i.m_end ); } /*! * Iterator subtraction operator. Returns the distance in pixels between * the specified iterators \a i and \a j. */ friend distance_type operator -( const const_filter_sample_iterator& i, const const_filter_sample_iterator& j ) noexcept { return i.m_iterator - j.m_iterator; } /*! * Equality operator. Returns true if two iterators \a i and \a j point * to the same pixel sample. */ friend bool operator ==( const const_filter_sample_iterator& i, const const_filter_sample_iterator& j ) noexcept { return i.m_iterator == j.m_iterator; } /*! * Returns true iff an iterator \a i and a sample pointer \a j point to * the same pixel sample. */ friend bool operator ==( const const_filter_sample_iterator& i, const sample* j ) noexcept { return i.m_iterator == j; } /*! * Returns true iff a sample pointer \a i and an iterator \a j point to * the same pixel sample. */ friend bool operator ==( const sample* i, const const_filter_sample_iterator& j ) noexcept { return i == j.m_iterator; } /*! * Less than operator. Returns true if the specified iterator \a i * precedes another iterator \a j. */ friend bool operator <( const const_filter_sample_iterator& i, const const_filter_sample_iterator& j ) noexcept { return i.m_iterator < j.m_iterator; } /*! * Returns true iff an iterator \a i precedes a sample pointer \a j. */ friend bool operator <( const const_filter_sample_iterator& i, const sample* j ) noexcept { return i.m_iterator < j; } /*! * Returns true iff a sample pointer \a i precedes an iterator \a j. */ friend bool operator <( const sample* i, const const_filter_sample_iterator& j ) noexcept { return i < j.m_iterator; } }; // ------------------------------------------------------------------------- template class roi_filter_sample_iterator_base : public roi_sample_iterator_base { protected: typedef roi_sample_iterator_base roi_iterator_base; filter_type m_filter; sample_pointer m_begin = nullptr; roi_filter_sample_iterator_base() = default; roi_filter_sample_iterator_base( image_type& image, const filter_type& filter, const Rect& rect, int channel ) : roi_iterator_base( image, rect, channel ) , m_filter( filter ) , m_begin( roi_iterator_base::m_iterator ) { JumpToNextValidSample(); } roi_filter_sample_iterator_base( image_type& image, const filter_type& filter, sample_pointer i, sample_pointer j ) : roi_iterator_base( image, i, j ) , m_filter( filter ) , m_begin( roi_iterator_base::m_iterator ) { JumpToNextValidSample(); } roi_filter_sample_iterator_base( const roi_iterator_base& i, const filter_type& filter ) : roi_iterator_base( i ) , m_filter( filter ) , m_begin( roi_iterator_base::m_iterator ) { JumpToNextValidSample(); } roi_filter_sample_iterator_base( const roi_filter_sample_iterator_base& ) = default; roi_filter_sample_iterator_base& operator =( const roi_filter_sample_iterator_base& i ) = default; roi_filter_sample_iterator_base& operator =( const roi_iterator_base& i ) noexcept { (void)roi_iterator_base::operator =( i ); JumpToNextValidSample(); return *this; } void JumpToNextValidSample() noexcept { while ( this->m_iterator < this->m_end && !this->m_filter( *this->m_iterator ) ) roi_iterator_base::Increment(); } void JumpToPrevValidSample() noexcept { while ( this->m_iterator > this->m_begin && !this->m_filter( *this->m_iterator ) ) roi_iterator_base::Decrement(); } }; // ------------------------------------------------------------------------- /*! * \class pcl::GenericImage::roi_filter_sample_iterator * \brief Mutable region-of-interest, filter pixel sample iterator. * * A mutable, region-of-interest (ROI), filter pixel sample iterator * combines the capabilities of roi_sample_iterator and * filter_sample_iterator in a single iterator class. * * \ingroup image_iterators */ template class roi_filter_sample_iterator : public roi_filter_sample_iterator_base, sample*, F> { public: /*! * Represents the type of the iterated image. */ typedef GenericImage

image_type; /*! * Represents the pixel traits class used by the iterated image. */ typedef typename image_type::pixel_traits pixel_traits; /*! * Represents a pixel sample of the iterated image. */ typedef typename image_type::sample sample; /*! * Represents the type of the unary predicate used by this filter * iterator. */ typedef F filter_type; typedef roi_filter_sample_iterator_base, sample*, F> iterator_base; /*! * Default constructor. Initializes an invalid iterator. */ roi_filter_sample_iterator() = default; /*! * Constructs a mutable, region-of-interest (ROI), filter pixel sample * iterator for one channel of an image. * * \param image The image to iterate. * * \param filter Reference to a predicate object that will be used to * filter pixel sample values. * * \param rect Region of interest. If an empty rectangle is specified, * the current rectangular selection in the \a image will * be used. If the specified rectangle is not empty and * extends beyond image boundaries, only the intersection * with the image will be used. If that intersection does * not exist, then the resulting iterator will be invalid, * with an empty iteration range. The default value is an * empty rectangle. * * \param channel Channel index. If this parameter is a negative integer, * then this object will be initialized to iterate on the * currently selected channel of the \a image. If an * invalid (i.e., nonexistent) channel index is specified, * then the resulting iterator will also be invalid, with * an empty iteration range. The default value is -1. */ roi_filter_sample_iterator( image_type& image, const F& filter, const Rect& rect = Rect( 0 ), int channel = -1 ) : iterator_base( image.EnsureUnique(), filter, rect, channel ) { } /*! * Constructs a mutable, region-of-interest (ROI), filter pixel sample * iterator for a range of pixel samples of an image. * * \param image The image to iterate. * * \param filter Reference to a predicate object that will be used to * filter pixel sample values. * * \param i Pointer to the first pixel sample in the iteration * range. * * \param j End of the iteration range. The iterator will be * invalid when it reaches (or surpasses) this point. * * \note Both iteration limits \a i and \a j must be pointers to pixel * samples in the \e same channel of the specified \a image. */ roi_filter_sample_iterator( image_type& image, const F& filter, sample* i, sample* j ) : iterator_base( image, filter, i, j ) { } /*! * Constructs a mutable, region-of-interest (ROI), filter pixel sample * iterator from the specified ROI iterator \a i and \a filter. */ roi_filter_sample_iterator( const roi_sample_iterator& i, const F& filter ) : iterator_base( i, filter ) { } /*! * Copy constructor. */ roi_filter_sample_iterator( const roi_filter_sample_iterator& ) = default; /*! * Copy assignment operator. Returns a reference to this object. */ roi_filter_sample_iterator& operator =( const roi_filter_sample_iterator& ) = default; /*! * Assigns a ROI pixel sample iterator. Returns a reference to this * object. */ roi_filter_sample_iterator& operator =( const roi_sample_iterator& i ) noexcept { (void)iterator_base::operator =( i ); return *this; } /*! * Returns true only if this iterator is valid. A valid iterator defines * a valid iterated image and a non-null position. */ bool IsValid() const noexcept { return this->m_image != nullptr && this->m_iterator != nullptr; } /*! * Returns a reference to the image being iterated by this object. */ image_type& Image() const noexcept { return *this->m_image; } /*! * Returns a reference to the immutable predicate object used by this * filter iterator. */ const filter_type& Filter() const noexcept { return this->m_filter; } /*! * Returns a reference to the mutable predicate object used by this * filter iterator. */ filter_type& Filter() noexcept { return this->m_filter; } /*! * Returns a pointer to the pixel sample pointed to by this iterator. */ sample* Position() const noexcept { return this->m_iterator; } /*! * Boolean type conversion operator. Returns true if this iterator is * active. A ROI pixel sample iterator is active if it has not reached * (or surpassed) its iteration limit, i.e. the bottom right corner of * its iterated region of interest. */ operator bool() const noexcept { return this->m_iterator < this->m_end; } /*! * Indirection operator. Returns a reference to the pixel sample pointed * to by this iterator. */ sample& operator *() const noexcept { return *this->m_iterator; } /*! * Pre-increment operator. Causes this iterator to point to the next * valid pixel sample in the iterated region of interest. Returns a * reference to this iterator. */ roi_filter_sample_iterator& operator ++() noexcept { this->Increment(); this->JumpToNextValidSample(); return *this; } /*! * Post-increment operator. Causes this iterator to point to the next * valid pixel sample in the iterated region of interest. Returns a copy * of the iterator as it was before incrementing it. */ roi_filter_sample_iterator operator ++( int ) noexcept { roi_filter_sample_iterator i0( *this ); this->Increment(); this->JumpToNextValidSample(); return i0; } /*! * Pre-decrement operator. Causes this iterator to point to the previous * valid pixel sample in the iterated region of interest, then returns a * reference to this iterator. */ roi_filter_sample_iterator& operator --() noexcept { this->Decrement(); this->JumpToPrevValidSample(); return *this; } /*! * Post-decrement operator. Causes this iterator to point to the previous * valid pixel sample in the iterated region of interest. Returns a copy * of the iterator as it was before decrementing it. */ roi_filter_sample_iterator operator --( int ) noexcept { roi_filter_sample_iterator i0( *this ); this->Decrement(); this->JumpToPrevValidSample(); return i0; } /*! * Scalar assignment/addition operator. Increments this iterator by a * distance \a delta from its current position, within the rectangular * region of interest being iterated. Positive increments cause this * iterator to move forward (rightwards and downwards) by \a delta pixel * samples. Negative increments move this iterator backward (leftwards * and upwards) by \a delta pixel samples. Returns a reference to this * iterator. */ roi_filter_sample_iterator& operator +=( distance_type delta ) noexcept { int w = this->m_rowEnd - this->m_rowBegin; return MoveBy( delta%w, delta/w ); } /*! * Scalar assignment/subtraction operator. Decrements this iterator by a * distance \a delta from its current position, within the rectangular * region of interest being iterated. Positive increments cause this * iterator to move backward (leftwards and upwards) by \a delta pixel * samples. Negative increments move this iterator forward (rightwards * and downwards) by \a delta pixel samples. Returns a reference to this * iterator. */ roi_filter_sample_iterator& operator -=( distance_type delta ) noexcept { int w = this->m_rowEnd - this->m_rowBegin; return MoveBy( -delta%w, -delta/w ); } /*! * Moves this iterator within its rectangular region of interest by the * specified horizontal and vertical increments in pixels, \a dx and * \a dy respectively, relative to its current position. Positive * (negative) \a dx increments move the iterator rightwards (leftwards). * Positive (negative) \a dy increments move the iterator downwards * (upwards). */ roi_filter_sample_iterator& MoveBy( int dx, int dy ) noexcept { sample* __restrict__ i0 = this->m_iterator; iterator_base::MoveBy( dx, dy ); if ( this->m_iterator >= i0 ) this->JumpToNextValidSample(); else this->JumpToPrevValidSample(); return *this; } /*! * Scalar-to-iterator addition operator. Returns an iterator equivalent * to the specified iterator \a i incremented by a distance \a delta. */ friend roi_filter_sample_iterator operator +( const roi_filter_sample_iterator& i, distance_type delta ) noexcept { roi_filter_sample_iterator j( i ); j += delta; return j; } /*! * Iterator-to-scalar addition operator. This operator implements the * commutative property of scalar-to-iterator addition. */ friend roi_filter_sample_iterator operator +( distance_type delta, const roi_filter_sample_iterator& i ) noexcept { roi_filter_sample_iterator j( i ); j += delta; return j; } /*! * Scalar-to-iterator subtraction operator. Returns an iterator equal to * the specified iterator \a i decremented by a distance \a delta. */ friend roi_filter_sample_iterator operator -( const roi_filter_sample_iterator& i, distance_type delta ) noexcept { roi_filter_sample_iterator j( i ); j -= delta; return j; } /*! * Equality operator. Returns true if two iterators \a i and \a j point * to the same pixel sample. */ friend bool operator ==( const roi_filter_sample_iterator& i, const roi_filter_sample_iterator& j ) noexcept { return i.m_iterator == j.m_iterator; } /*! * Returns true iff an iterator \a i and a sample pointer \a j point to * the same pixel sample. */ friend bool operator ==( const roi_filter_sample_iterator& i, const sample* j ) noexcept { return i.m_iterator == j; } /*! * Returns true iff a sample pointer \a i and an iterator \a j point to * the same pixel sample. */ friend bool operator ==( const sample* i, const roi_filter_sample_iterator& j ) noexcept { return i == j.m_iterator; } /*! * Less than operator. Returns true if the specified iterator \a i * precedes another iterator \a j. */ friend bool operator <( const roi_filter_sample_iterator& i, const roi_filter_sample_iterator& j ) noexcept { return i.m_iterator < j.m_iterator; } /*! * Returns true iff an iterator \a i precedes a sample pointer \a j. */ friend bool operator <( const roi_filter_sample_iterator& i, const sample* j ) noexcept { return i.m_iterator < j; } /*! * Returns true iff a sample pointer \a i precedes an iterator \a j. */ friend bool operator <( const sample* i, const roi_filter_sample_iterator& j ) noexcept { return i < j.m_iterator; } }; // ------------------------------------------------------------------------- /*! * \class pcl::GenericImage::const_roi_filter_sample_iterator * \brief Immutable region-of-interest, filter pixel sample iterator. * * An immutable, region-of-interest (ROI), filter pixel sample iterator * combines the capabilities of const_roi_sample_iterator and * const_filter_sample_iterator in a single iterator class. * * \ingroup image_iterators */ template class const_roi_filter_sample_iterator : public roi_filter_sample_iterator_base, const sample*, F> { public: /*! * Represents the type of the iterated image. */ typedef GenericImage

image_type; /*! * Represents the pixel traits class used by the iterated image. */ typedef typename image_type::pixel_traits pixel_traits; /*! * Represents a pixel sample of the iterated image. */ typedef typename image_type::sample sample; /*! * Represents the type of the unary predicate used by this filter * iterator. */ typedef F filter_type; typedef roi_filter_sample_iterator_base, const sample*, F> iterator_base; /*! * Default constructor. Initializes an invalid iterator. */ const_roi_filter_sample_iterator() = default; /*! * Constructs an immutable, region-of-interest (ROI), filter pixel sample * iterator for one channel of an image. * * \param image The constant image to iterate. * * \param filter Reference to a predicate object that will be used to * filter pixel sample values. * * \param rect Region of interest. If an empty rectangle is specified, * the current rectangular selection in the \a image will * be used. If the specified rectangle is not empty and * extends beyond image boundaries, only the intersection * with the image will be used. If that intersection does * not exist, then the resulting iterator will be invalid, * with an empty iteration range. The default value is an * empty rectangle. * * \param channel Channel index. If this parameter is a negative integer, * then this object will be initialized to iterate on the * currently selected channel of the \a image. If an * invalid (i.e., nonexistent) channel index is specified, * then the resulting iterator will also be invalid, with * an empty iteration range. The default value is -1. */ const_roi_filter_sample_iterator( const image_type& image, const F& filter, const Rect& rect = Rect( 0 ), int channel = -1 ) : iterator_base( image, filter, rect, channel ) { } /*! * Constructs an immutable, region-of-interest (ROI), filter pixel sample * iterator for a range of pixel samples of an image. * * \param image The constant image to iterate. * * \param filter Reference to a predicate object that will be used to * filter pixel sample values. * * \param i Pointer to the first constant pixel sample in the * iteration range. * * \param j End of the iteration range. The iterator will be * invalid when it reaches (or surpasses) this point. * * \note Both iteration limits \a i and \a j must be pointers to pixel * samples in the \e same channel of the specified \a image. */ const_roi_filter_sample_iterator( const image_type& image, const F& filter, const sample* i, const sample* j ) : iterator_base( image, filter, i, j ) { } /*! * Constructs an immutable, region-of-interest (ROI), filter pixel sample * iterator from the specified immutable ROI iterator \a i and \a filter. */ const_roi_filter_sample_iterator( const const_roi_sample_iterator& i, const F& filter ) : iterator_base( i, filter ) { } /*! * Copy constructor. */ const_roi_filter_sample_iterator( const const_roi_filter_sample_iterator& ) = default; /*! * Copy assignment operator. Returns a reference to this object. */ const_roi_filter_sample_iterator& operator =( const const_roi_filter_sample_iterator& ) = default; /*! * Assigns an immutable ROI pixel sample iterator. Returns a reference to * this object. */ const_roi_filter_sample_iterator& operator =( const const_roi_sample_iterator& i ) noexcept { (void)iterator_base::operator =( i ); return *this; } /*! * Returns true only if this iterator is valid. A valid iterator defines * a valid iterated image and a non-null position. */ bool IsValid() const noexcept { return this->m_image != nullptr && this->m_iterator != nullptr; } /*! * Returns a reference to the constant image being iterated by this * object. */ const image_type& Image() const noexcept { return *this->m_image; } /*! * Returns a reference to the immutable predicate object used by this * filter iterator. */ const filter_type& Filter() const noexcept { return this->m_filter; } /*! * Returns a reference to the mutable predicate object used by this * filter iterator. */ filter_type& Filter() noexcept { return this->m_filter; } /*! * Returns a pointer to the constant pixel sample pointed to by this * iterator. */ const sample* Position() const noexcept { return this->m_iterator; } /*! * Boolean type conversion operator. Returns true if this iterator is * active. A ROI pixel sample iterator is active if it has not reached * (or surpassed) its iteration limit, i.e. the bottom right corner of * its iterated region of interest. */ operator bool() const noexcept { return this->m_iterator < this->m_end; } /*! * Indirection operator. Returns a reference to the constant pixel sample * pointed to by this iterator. */ const sample& operator *() const noexcept { return *this->m_iterator; } /*! * Pre-increment operator. Causes this iterator to point to the next * valid pixel sample in the iterated region of interest. Returns a * reference to this iterator. */ const_roi_filter_sample_iterator& operator ++() noexcept { this->Increment(); this->JumpToNextValidSample(); return *this; } /*! * Post-increment operator. Causes this iterator to point to the next * valid pixel sample in the iterated region of interest. Returns a copy * of the iterator as it was before incrementing it. */ const_roi_filter_sample_iterator operator ++( int ) noexcept { const_roi_filter_sample_iterator i0( *this ); this->Increment(); this->JumpToNextValidSample(); return i0; } /*! * Pre-decrement operator. Causes this iterator to point to the previous * valid pixel sample in the iterated region of interest, then returns a * reference to this iterator. */ const_roi_filter_sample_iterator& operator --() noexcept { this->Decrement(); this->JumpToPrevValidSample(); return *this; } /*! * Post-decrement operator. Causes this iterator to point to the previous * valid pixel sample in the iterated region of interest. Returns a copy * of the iterator as it was before decrementing it. */ const_roi_filter_sample_iterator operator --( int ) noexcept { const_roi_filter_sample_iterator i0( *this ); this->Decrement(); this->JumpToPrevValidSample(); return i0; } /*! * Scalar assignment/addition operator. Increments this iterator by a * distance \a delta from its current position, within the rectangular * region of interest being iterated. Positive increments cause this * iterator to move forward (rightwards and downwards) by \a delta pixel * samples. Negative increments move this iterator backward (leftwards * and upwards) by \a delta pixel samples. Returns a reference to this * iterator. */ const_roi_filter_sample_iterator& operator +=( distance_type delta ) noexcept { int w = this->m_rowEnd - this->m_rowBegin; return MoveBy( delta%w, delta/w ); } /*! * Scalar assignment/subtraction operator. Decrements this iterator by a * distance \a delta from its current position, within the rectangular * region of interest being iterated. Positive increments cause this * iterator to move backward (leftwards and upwards) by \a delta pixel * samples. Negative increments move this iterator forward (rightwards * and downwards) by \a delta pixel samples. Returns a reference to this * iterator. */ const_roi_filter_sample_iterator& operator -=( distance_type delta ) noexcept { int w = this->m_rowEnd - this->m_rowBegin; return MoveBy( -delta%w, -delta/w ); } /*! * Moves this iterator within its rectangular region of interest by the * specified horizontal and vertical increments in pixels, \a dx and * \a dy respectively, relative to its current position. Positive * (negative) \a dx increments move the iterator rightwards (leftwards). * Positive (negative) \a dy increments move the iterator downwards * (upwards). */ const_roi_filter_sample_iterator& MoveBy( int dx, int dy ) noexcept { const sample* __restrict__ i0 = this->m_iterator; iterator_base::MoveBy( dx, dy ); if ( this->m_iterator >= i0 ) this->JumpToNextValidSample(); else this->JumpToPrevValidSample(); return *this; } /*! * Scalar-to-iterator addition operator. Returns an iterator equivalent * to the specified iterator \a i incremented by a distance \a delta. */ friend const_roi_filter_sample_iterator operator +( const const_roi_filter_sample_iterator& i, distance_type delta ) noexcept { const_roi_filter_sample_iterator j( i ); j += delta; return j; } /*! * Iterator-to-scalar addition operator. This operator implements the * commutative property of scalar-to-iterator addition. */ friend const_roi_filter_sample_iterator operator +( distance_type delta, const const_roi_filter_sample_iterator& i ) noexcept { const_roi_filter_sample_iterator j( i ); j += delta; return j; } /*! * Scalar-to-iterator subtraction operator. Returns an iterator equal to * the specified iterator \a i decremented by a distance \a delta. */ friend const_roi_filter_sample_iterator operator -( const const_roi_filter_sample_iterator& i, distance_type delta ) noexcept { const_roi_filter_sample_iterator j( i ); j -= delta; return j; } /*! * Equality operator. Returns true if two iterators \a i and \a j point * to the same pixel sample. */ friend bool operator ==( const const_roi_filter_sample_iterator& i, const const_roi_filter_sample_iterator& j ) noexcept { return i.m_iterator == j.m_iterator; } /*! * Returns true iff an iterator \a i and a sample pointer \a j point to * the same pixel sample. */ friend bool operator ==( const const_roi_filter_sample_iterator& i, const sample* j ) noexcept { return i.m_iterator == j; } /*! * Returns true iff a sample pointer \a i and an iterator \a j point to * the same pixel sample. */ friend bool operator ==( const sample* i, const const_roi_filter_sample_iterator& j ) noexcept { return i == j.m_iterator; } /*! * Less than operator. Returns true if the specified iterator \a i * precedes another iterator \a j. */ friend bool operator <( const const_roi_filter_sample_iterator& i, const const_roi_filter_sample_iterator& j ) noexcept { return i.m_iterator < j.m_iterator; } /*! * Returns true iff an iterator \a i precedes a sample pointer \a j. */ friend bool operator <( const const_roi_filter_sample_iterator& i, const sample* j ) noexcept { return i.m_iterator < j; } /*! * Returns true iff a sample pointer \a i precedes an iterator \a j. */ friend bool operator <( const sample* i, const const_roi_filter_sample_iterator& j ) noexcept { return i < j.m_iterator; } }; // ------------------------------------------------------------------------- /*! * \class pcl::GenericImage::pixel_iterator * \brief Mutable pixel iterator. * * A mutable pixel iterator provides read/write, random access to all pixels * of an image. * * \ingroup image_iterators */ class pixel_iterator { public: /*! * Represents the type of the iterated image. */ typedef GenericImage

image_type; /*! * Represents the pixel traits class used by the iterated image. */ typedef typename image_type::pixel_traits pixel_traits; /*! * Represents a pixel sample of the iterated image. */ typedef typename image_type::sample sample; typedef GenericVector iterator_type; /*! * Default constructor. Initializes an invalid iterator. */ pixel_iterator() = default; /*! * Constructs a mutable pixel iterator for the specified \a image. */ pixel_iterator( image_type& image ) : m_image( &image ) { m_image->EnsureUnique(); if ( !m_image->IsEmpty() ) { m_iterator = iterator_type( m_image->NumberOfChannels() ); for ( int i = 0; i < m_iterator.Length(); ++i ) m_iterator[i] = (*m_image)[i]; m_end = m_iterator[0] + m_image->NumberOfPixels(); } } /*! * Copy constructor. */ pixel_iterator( const pixel_iterator& ) = default; /*! * Copy assignment operator. Returns a reference to this iterator. */ pixel_iterator& operator =( const pixel_iterator& ) = default; /*! * Returns true only if this iterator is valid. A valid iterator defines * a valid iterated image and a non-null position. */ bool IsValid() const noexcept { return m_image != nullptr && !m_iterator.IsEmpty(); } /*! * Returns a reference to the image being iterated by this object. */ image_type& Image() const noexcept { return *m_image; } /*! * Returns a pointer to the pixel sample pointed to by this iterator in * the specified \a channel. */ sample* Position( int channel ) const noexcept { return m_iterator[channel]; } /*! * Boolean type conversion operator. Returns true if this iterator is * active. A pixel iterator is active if it has not reached (or * surpassed) its iteration limit. */ operator bool() const noexcept { return m_iterator[0] < m_end; } /*! * Array subscript operator. Returns a reference to the pixel sample * pointed to by this iterator in the specified \a channel. */ sample& operator []( int channel ) const noexcept { return *m_iterator[channel]; } /*! * Pre-increment operator. Causes this iterator to point to the next * pixel in the iterated image. Returns a reference to this iterator. */ pixel_iterator& operator ++() noexcept { for ( int i = 0; i < m_iterator.Length(); ++i ) ++m_iterator[i]; return *this; } /*! * Post-increment operator. Causes this iterator to point to the next * pixel in the iterated image. Returns a copy of the iterator as it was * before incrementing it. */ pixel_iterator operator ++( int ) noexcept { pixel_iterator i0( *this ); for ( int i = 0; i < m_iterator.Length(); ++i ) ++m_iterator[i]; return i0; } /*! * Pre-decrement operator. Causes this iterator to point to the previous * pixel in the iterated image. Returns a reference to this iterator. */ pixel_iterator& operator --() noexcept { for ( int i = 0; i < m_iterator.Length(); ++i ) --m_iterator[i]; return *this; } /*! * Post-decrement operator. Causes this iterator to point to the previous * pixel in the iterated image. Returns a copy of the iterator as it was * before decrementing it. */ pixel_iterator operator --( int ) noexcept { pixel_iterator i0( *this ); for ( int i = 0; i < m_iterator.Length(); ++i ) --m_iterator[i]; return i0; } /*! * Scalar assignment/addition operator. Increments this iterator by a * distance \a delta from its current position. Positive increments cause * this iterator to move forward by \a delta pixels. Negative increments * move this iterator backward by \a delta pixels. Returns a reference to * this iterator. */ pixel_iterator& operator +=( distance_type delta ) noexcept { for ( int i = 0; i < m_iterator.Length(); ++i ) m_iterator[i] += delta; return *this; } /*! * Scalar assignment/subtraction operator. Decrements this iterator by a * distance \a delta from its current position. Positive increments cause * this iterator to move backward by \a delta pixels. Negative increments * move this iterator forward by \a delta pixels. Returns a reference to * this iterator. */ pixel_iterator& operator -=( distance_type delta ) noexcept { for ( int i = 0; i < m_iterator.Length(); ++i ) m_iterator[i] -= delta; return *this; } /*! * Moves this iterator on the iterated image by the specified horizontal * and vertical increments in pixels, \a dx and \a dy respectively, * relative to its current position. Positive (negative) \a dx increments * move the iterator rightwards (leftwards). Positive (negative) \a dy * increments move the iterator downwards (upwards). */ pixel_iterator& MoveBy( int dx, int dy ) noexcept { return operator +=( distance_type( dy )*m_image->Width() + distance_type( dx ) ); } /*! * Scalar-to-iterator addition operator. Returns an iterator equivalent * to the specified iterator \a i incremented by a distance \a delta. */ friend pixel_iterator operator +( const pixel_iterator& i, distance_type delta ) noexcept { pixel_iterator j( i ); j += delta; return j; } /*! * Iterator-to-scalar addition operator. This operator implements the * commutative property of scalar-to-iterator addition. */ friend pixel_iterator operator +( distance_type delta, const pixel_iterator& i ) noexcept { pixel_iterator j( i ); j += delta; return j; } /*! * Scalar-to-iterator subtraction operator. Returns an iterator equal to * the specified iterator \a i decremented by a distance \a delta. */ friend pixel_iterator operator -( const pixel_iterator& i, distance_type delta ) noexcept { pixel_iterator j( i ); j -= delta; return j; } /*! * Iterator subtraction operator. Returns the distance in pixels between * the specified iterators \a i and \a j. */ friend distance_type operator -( const pixel_iterator& i, const pixel_iterator& j ) noexcept { return i.m_iterator[0] - j.m_iterator[0]; } /*! * Equality operator. Returns true if two iterators \a i and \a j point * to the same pixel. */ friend bool operator ==( const pixel_iterator& i, const pixel_iterator& j ) noexcept { return i.m_iterator[0] == j.m_iterator[0]; } /*! * Less than operator. Returns true if the specified iterator \a i * precedes another iterator \a j. */ friend bool operator <( const pixel_iterator& i, const pixel_iterator& j ) noexcept { return i.m_iterator[0] < j.m_iterator[0]; } protected: image_type* m_image = nullptr; iterator_type m_iterator; const sample* __restrict__ m_end = nullptr; }; // ------------------------------------------------------------------------- /*! * \class pcl::GenericImage::const_pixel_iterator * \brief Immutable pixel iterator. * * An immutable pixel iterator provides read-only, random access to all * pixels of an image. * * \ingroup image_iterators */ class const_pixel_iterator { public: /*! * Represents the type of the iterated image. */ typedef GenericImage

image_type; /*! * Represents the pixel traits class used by the iterated image. */ typedef typename image_type::pixel_traits pixel_traits; /*! * Represents a pixel sample of the iterated image. */ typedef typename image_type::sample sample; typedef GenericVector iterator_type; /*! * Default constructor. Initializes an invalid iterator. */ const_pixel_iterator() = default; /*! * Constructs an immutable pixel iterator for the specified \a image. */ const_pixel_iterator( const image_type& image ) : m_image( &image ) { if ( !m_image->IsEmpty() ) { m_iterator = iterator_type( m_image->NumberOfChannels() ); for ( int i = 0; i < m_iterator.Length(); ++i ) m_iterator[i] = (*m_image)[i]; m_end = m_iterator[0] + m_image->NumberOfPixels(); } } /*! * Copy constructor. */ const_pixel_iterator( const const_pixel_iterator& ) = default; /*! * Copy assignment operator. Returns a reference to this iterator. */ const_pixel_iterator& operator =( const const_pixel_iterator& ) = default; /*! * Returns true only if this iterator is valid. A valid iterator defines * a valid iterated image and a non-null position. */ bool IsValid() const noexcept { return m_image != nullptr && !m_iterator.IsEmpty(); } /*! * Returns a reference to the constant image being iterated by this * object. */ const image_type& Image() const noexcept { return *m_image; } /*! * Returns a pointer to the constant pixel sample pointed to by this * iterator in the specified \a channel. */ const sample* Position( int channel ) const noexcept { return m_iterator[channel]; } /*! * Boolean type conversion operator. Returns true if this iterator is * active. A pixel iterator is active if it has not reached (or * surpassed) its iteration limit. */ operator bool() const noexcept { return m_iterator[0] < m_end; } /*! * Array subscript operator. Returns a reference to the constant pixel * sample pointed to by this iterator in the specified \a channel. */ const sample& operator []( int channel ) const noexcept { return *m_iterator[channel]; } /*! * Pre-increment operator. Causes this iterator to point to the next * pixel in the iterated image. Returns a reference to this iterator. */ const_pixel_iterator& operator ++() noexcept { for ( int i = 0; i < m_iterator.Length(); ++i ) ++m_iterator[i]; return *this; } /*! * Post-increment operator. Causes this iterator to point to the next * pixel in the iterated image. Returns a copy of the iterator as it was * before incrementing it. */ const_pixel_iterator operator ++( int ) noexcept { const_pixel_iterator i0( *this ); for ( int i = 0; i < m_iterator.Length(); ++i ) ++m_iterator[i]; return i0; } /*! * Pre-decrement operator. Causes this iterator to point to the previous * pixel in the iterated image. Returns a reference to this iterator. */ const_pixel_iterator& operator --() noexcept { for ( int i = 0; i < m_iterator.Length(); ++i ) --m_iterator[i]; return *this; } /*! * Post-decrement operator. Causes this iterator to point to the previous * pixel in the iterated image. Returns a copy of the iterator as it was * before decrementing it. */ const_pixel_iterator operator --( int ) noexcept { const_pixel_iterator i0( *this ); for ( int i = 0; i < m_iterator.Length(); ++i ) --m_iterator[i]; return i0; } /*! * Scalar assignment/addition operator. Increments this iterator by a * distance \a delta from its current position. Positive increments cause * this iterator to move forward by \a delta pixels. Negative increments * move this iterator backward by \a delta pixels. Returns a reference to * this iterator. */ const_pixel_iterator& operator +=( distance_type delta ) noexcept { for ( int i = 0; i < m_iterator.Length(); ++i ) m_iterator[i] += delta; return *this; } /*! * Scalar assignment/subtraction operator. Decrements this iterator by a * distance \a delta from its current position. Positive increments cause * this iterator to move backward by \a delta pixels. Negative increments * move this iterator forward by \a delta pixels. Returns a reference to * this iterator. */ const_pixel_iterator& operator -=( distance_type delta ) noexcept { for ( int i = 0; i < m_iterator.Length(); ++i ) m_iterator[i] -= delta; return *this; } /*! * Moves this iterator on the iterated image by the specified horizontal * and vertical increments in pixels, \a dx and \a dy respectively, * relative to its current position. Positive (negative) \a dx increments * move the iterator rightwards (leftwards). Positive (negative) \a dy * increments move the iterator downwards (upwards). */ const_pixel_iterator& MoveBy( int dx, int dy ) noexcept { return operator +=( distance_type( dy )*m_image->Width() + distance_type( dx ) ); } /*! * Scalar-to-iterator addition operator. Returns an iterator equivalent * to the specified iterator \a i incremented by a distance \a delta. */ friend const_pixel_iterator operator +( const const_pixel_iterator& i, distance_type delta ) noexcept { const_pixel_iterator j( i ); j += delta; return j; } /*! * Iterator-to-scalar addition operator. This operator implements the * commutative property of scalar-to-iterator addition. */ friend const_pixel_iterator operator +( distance_type delta, const const_pixel_iterator& i ) noexcept { const_pixel_iterator j( i ); j += delta; return j; } /*! * Scalar-to-iterator subtraction operator. Returns an iterator equal to * the specified iterator \a i decremented by a distance \a delta. */ friend const_pixel_iterator operator -( const const_pixel_iterator& i, distance_type delta ) noexcept { const_pixel_iterator j( i ); j -= delta; return j; } /*! * Iterator subtraction operator. Returns the distance in pixels between * the specified iterators \a i and \a j. */ friend distance_type operator -( const const_pixel_iterator& i, const const_pixel_iterator& j ) noexcept { return i.m_iterator[0] - j.m_iterator[0]; } /*! * Equality operator. Returns true if two iterators \a i and \a j point * to the same pixel. */ friend bool operator ==( const const_pixel_iterator& i, const const_pixel_iterator& j ) noexcept { return i.m_iterator[0] == j.m_iterator[0]; } /*! * Less than operator. Returns true if the specified iterator \a i * precedes another iterator \a j. */ friend bool operator <( const const_pixel_iterator& i, const const_pixel_iterator& j ) noexcept { return i.m_iterator[0] < j.m_iterator[0]; } protected: const image_type* m_image = nullptr; iterator_type m_iterator; const sample* __restrict__ m_end = nullptr; }; // ------------------------------------------------------------------------- template class roi_pixel_iterator_base { protected: typedef GenericVector iterator_type; image_type* m_image = nullptr; iterator_type m_iterator; sample_pointer m_rowBegin = nullptr; sample_pointer m_rowEnd = nullptr; sample_pointer m_end = nullptr; roi_pixel_iterator_base() = default; roi_pixel_iterator_base( image_type& image, const Rect& rect ) : m_image( &image ) { Rect r = rect; if ( m_image->ParseRect( r ) ) { m_iterator = iterator_type( m_image->NumberOfChannels() ); for ( int i = 0; i < m_iterator.Length(); ++i ) m_iterator[i] = m_image->PixelAddress( r.x0, r.y0, i ); m_rowBegin = m_iterator[0]; m_rowEnd = m_rowBegin + r.Width(); m_end = m_rowEnd + ((r.Height() - 1)*m_image->Width()); } } roi_pixel_iterator_base( const roi_pixel_iterator_base& ) = default; roi_pixel_iterator_base& operator =( const roi_pixel_iterator_base& ) = default; void Increment() noexcept { for ( int i = 0; i < m_iterator.Length(); ++i ) ++m_iterator[i]; if ( m_iterator[0] == m_rowEnd ) { int w = m_rowEnd - m_rowBegin; for ( int i = 0; i < m_iterator.Length(); ++i ) m_iterator[i] += m_image->Width() - w; m_rowBegin += m_image->Width(); m_rowEnd += m_image->Width(); } } void Decrement() noexcept { if ( m_iterator[0] == m_rowBegin ) { int w = m_rowEnd - m_rowBegin; for ( int i = 0; i < m_iterator.Length(); ++i ) m_iterator[i] -= m_image->Width() - w; m_rowBegin -= m_image->Width(); m_rowEnd -= m_image->Width(); } for ( int i = 0; i < m_iterator.Length(); ++i ) --m_iterator[i]; } void MoveBy( int cols, int rows ) noexcept { int dx = m_iterator[0] - m_rowBegin; for ( int i = 0; i < m_iterator.Length(); ++i ) m_iterator[i] -= dx; cols += dx; if ( cols != 0 ) { int w = m_rowEnd - m_rowBegin; if ( pcl::Abs( cols ) >= w ) { rows += cols/w; cols %= w; } } int dy = rows * m_image->Width(); for ( int i = 0; i < m_iterator.Length(); ++i ) m_iterator[i] += dy + cols; m_rowBegin += dy; m_rowEnd += dy; } }; // ------------------------------------------------------------------------- /*! * \class pcl::GenericImage::roi_pixel_iterator * \brief Mutable region-of-interest pixel iterator. * * A mutable, region-of-interest (ROI) pixel iterator provides read/write, * random access to pixels within a rectangular subset of an image. * * \ingroup image_iterators */ class roi_pixel_iterator : private roi_pixel_iterator_base, sample*> { public: /*! * Represents the type of the iterated image. */ typedef GenericImage

image_type; /*! * Represents the pixel traits class used by the iterated image. */ typedef typename image_type::pixel_traits pixel_traits; /*! * Represents a pixel sample of the iterated image. */ typedef typename image_type::sample sample; typedef roi_pixel_iterator_base, sample*> iterator_base; /*! * Default constructor. Initializes an invalid iterator. */ roi_pixel_iterator() = default; /*! * Constructs a mutable, region-of-interest (ROI) pixel iterator. * * \param image The image to iterate. * * \param rect Region of interest. If an empty rectangle is specified, * the current rectangular selection in the \a image will * be used. If the specified rectangle is not empty and * extends beyond image boundaries, only the intersection * with the image will be used. If that intersection does * not exist, then the resulting iterator will be invalid, * with an empty iteration range. The default value is an * empty rectangle. */ roi_pixel_iterator( image_type& image, const Rect& rect = Rect( 0 ) ) : iterator_base( image.EnsureUnique(), rect ) { } /*! * Copy constructor. */ roi_pixel_iterator( const roi_pixel_iterator& ) = default; /*! * Copy assignment operator. Returns a reference to this object. */ roi_pixel_iterator& operator =( const roi_pixel_iterator& ) = default; /*! * Returns true only if this iterator is valid. A valid iterator defines * a valid iterated image and a non-null position. */ bool IsValid() const noexcept { return this->m_image != nullptr && !this->m_iterator.IsEmpty(); } /*! * Returns a reference to the image being iterated by this object. */ image_type& Image() const noexcept { return *this->m_image; } /*! * Returns a pointer to the pixel sample pointed to by this iterator in * the specified \a channel. */ sample* Position( int channel ) const noexcept { return this->m_iterator[channel]; } /*! * Boolean type conversion operator. Returns true if this iterator is * active. A ROI pixel iterator is active if it has not reached (or * surpassed) its iteration limit, i.e. the bottom right corner of its * iterated region of interest. */ operator bool() const noexcept { return this->m_iterator[0] < this->m_end; } /*! * Array subscript operator. Returns a reference to the pixel sample * pointed to by this iterator in the specified \a channel. */ sample& operator []( int channel ) const noexcept { return *this->m_iterator[channel]; } /*! * Pre-increment operator. Causes this iterator to point to the next * pixel in the iterated region of interest, then returns a reference to * this iterator. */ roi_pixel_iterator& operator ++() noexcept { this->Increment(); return *this; } /*! * Post-increment operator. Causes this iterator to point to the next * pixel in the iterated region of interest. Returns a copy of the * iterator as it was before incrementing it. */ roi_pixel_iterator operator ++( int ) noexcept { roi_pixel_iterator i0( *this ); this->Increment(); return i0; } /*! * Pre-decrement operator. Causes this iterator to point to the previous * pixel in the iterated region of interest, then returns a reference to * this iterator. */ roi_pixel_iterator& operator --() noexcept { this->Decrement(); return *this; } /*! * Post-decrement operator. Causes this iterator to point to the previous * pixel in the iterated region of interest. Returns a copy of the * iterator as it was before decrementing it. */ roi_pixel_iterator operator --( int ) noexcept { roi_pixel_iterator i0( *this ); this->Decrement(); return i0; } /*! * Scalar assignment/addition operator. Increments this iterator by a * distance \a delta from its current position, within the rectangular * region of interest being iterated. Positive increments cause this * iterator to move forward (rightwards and downwards) by \a delta pixel * samples. Negative increments move this iterator backward (leftwards * and upwards) by \a delta pixel samples. Returns a reference to this * iterator. */ roi_pixel_iterator& operator +=( distance_type delta ) noexcept { int w = this->m_rowEnd - this->m_rowBegin; iterator_base::MoveBy( delta%w, delta/w ); return *this; } /*! * Scalar assignment/subtraction operator. Decrements this iterator by a * distance \a delta from its current position, within the rectangular * region of interest being iterated. Positive increments cause this * iterator to move backward (leftwards and upwards) by \a delta pixel * samples. Negative increments move this iterator forward (rightwards * and downwards) by \a delta pixel samples. Returns a reference to this * iterator. */ roi_pixel_iterator& operator -=( distance_type delta ) noexcept { int w = this->m_rowEnd - this->m_rowBegin; iterator_base::MoveBy( -delta%w, -delta/w ); return *this; } /*! * Moves this iterator within its rectangular region of interest by the * specified horizontal and vertical increments in pixels, \a dx and * \a dy respectively, relative to its current position. Positive * (negative) \a dx increments move the iterator rightwards (leftwards). * Positive (negative) \a dy increments move the iterator downwards * (upwards). */ roi_pixel_iterator& MoveBy( int dx, int dy ) noexcept { iterator_base::MoveBy( dx, dy ); return *this; } /*! * Scalar-to-iterator addition operator. Returns an iterator equivalent * to the specified iterator \a i incremented by a distance \a delta. */ friend roi_pixel_iterator operator +( const roi_pixel_iterator& i, distance_type delta ) noexcept { roi_pixel_iterator j( i ); j += delta; return j; } /*! * Iterator-to-scalar addition operator. This operator implements the * commutative property of scalar-to-iterator addition. */ friend roi_pixel_iterator operator +( distance_type delta, const roi_pixel_iterator& i ) noexcept { roi_pixel_iterator j( i ); j += delta; return j; } /*! * Scalar-to-iterator subtraction operator. Returns an iterator equal to * the specified iterator \a i decremented by a distance \a delta. */ friend roi_pixel_iterator operator -( const roi_pixel_iterator& i, distance_type delta ) noexcept { roi_pixel_iterator j( i ); j -= delta; return j; } /*! * Equality operator. Returns true if two iterators \a i and \a j point * to the same pixel. */ friend bool operator ==( const roi_pixel_iterator& i, const roi_pixel_iterator& j ) noexcept { return i.m_iterator[0] == j.m_iterator[0]; } /*! * Less than operator. Returns true if the specified iterator \a i * precedes another iterator \a j. */ friend bool operator <( const roi_pixel_iterator& i, const roi_pixel_iterator& j ) noexcept { return i.m_iterator[0] < j.m_iterator[0]; } }; // ------------------------------------------------------------------------- /*! * \class pcl::GenericImage::const_roi_pixel_iterator * \brief Immutable region-of-interest pixel iterator. * * An immutable, region-of-interest (ROI) pixel iterator provides read-only, * random access to pixels within a rectangular subset of an image. * * \ingroup image_iterators */ class const_roi_pixel_iterator : private roi_pixel_iterator_base, const sample*> { public: /*! * Represents the type of the iterated image. */ typedef GenericImage

image_type; /*! * Represents the pixel traits class used by the iterated image. */ typedef typename image_type::pixel_traits pixel_traits; /*! * Represents a pixel sample of the iterated image. */ typedef typename image_type::sample sample; typedef roi_pixel_iterator_base, const sample*> iterator_base; /*! * Default constructor. Initializes an invalid iterator. */ const_roi_pixel_iterator() = default; /*! * Constructs an immutable, region-of-interest (ROI) pixel iterator. * * \param image The constant image to iterate. * * \param rect Region of interest. If an empty rectangle is specified, * the current rectangular selection in the \a image will * be used. If the specified rectangle is not empty and * extends beyond image boundaries, only the intersection * with the image will be used. If that intersection does * not exist, then the resulting iterator will be invalid, * with an empty iteration range. The default value is an * empty rectangle. */ const_roi_pixel_iterator( const image_type& image, const Rect& rect = Rect( 0 ) ) : iterator_base( image, rect ) { } /*! * Copy constructor. */ const_roi_pixel_iterator( const const_roi_pixel_iterator& ) = default; /*! * Copy assignment operator. Returns a reference to this object. */ const_roi_pixel_iterator& operator =( const const_roi_pixel_iterator& ) = default; /*! * Returns true only if this iterator is valid. A valid iterator defines * a valid iterated image and a non-null position. */ bool IsValid() const noexcept { return this->m_image != nullptr && !this->m_iterator.IsEmpty(); } /*! * Returns a reference to the constant image being iterated by this * object. */ const image_type& Image() const noexcept { return *this->m_image; } /*! * Returns a pointer to the constant pixel sample pointed to by this * iterator in the specified \a channel. */ const sample* Position( int channel ) const noexcept { return this->m_iterator[channel]; } /*! * Boolean type conversion operator. Returns true if this iterator is * active. A ROI pixel iterator is active if it has not reached (or * surpassed) its iteration limit, i.e. the bottom right corner of its * iterated region of interest. */ operator bool() const noexcept { return this->m_iterator[0] < this->m_end; } /*! * Array subscript operator. Returns a reference to the constant pixel * sample pointed to by this iterator in the specified \a channel. */ const sample& operator []( int channel ) const noexcept { return *this->m_iterator[channel]; } /*! * Pre-increment operator. Causes this iterator to point to the next * pixel in the iterated region of interest, then returns a reference to * this iterator. */ const_roi_pixel_iterator& operator ++() noexcept { this->Increment(); return *this; } /*! * Post-increment operator. Causes this iterator to point to the next * pixel in the iterated region of interest. Returns a copy of the * iterator as it was before incrementing it. */ const_roi_pixel_iterator operator ++( int ) noexcept { const_roi_pixel_iterator i0( *this ); this->Increment(); return i0; } /*! * Pre-decrement operator. Causes this iterator to point to the previous * pixel in the iterated region of interest, then returns a reference to * this iterator. */ const_roi_pixel_iterator& operator --() noexcept { this->Decrement(); return *this; } /*! * Post-decrement operator. Causes this iterator to point to the previous * pixel in the iterated region of interest. Returns a copy of the * iterator as it was before decrementing it. */ const_roi_pixel_iterator operator --( int ) noexcept { const_roi_pixel_iterator i0( *this ); this->Decrement(); return i0; } /*! * Scalar assignment/addition operator. Increments this iterator by a * distance \a delta from its current position, within the rectangular * region of interest being iterated. Positive increments cause this * iterator to move forward (rightwards and downwards) by \a delta pixel * samples. Negative increments move this iterator backward (leftwards * and upwards) by \a delta pixel samples. Returns a reference to this * iterator. */ const_roi_pixel_iterator& operator +=( distance_type delta ) noexcept { int w = this->m_rowEnd - this->m_rowBegin; iterator_base::MoveBy( delta%w, delta/w ); return *this; } /*! * Scalar assignment/subtraction operator. Decrements this iterator by a * distance \a delta from its current position, within the rectangular * region of interest being iterated. Positive increments cause this * iterator to move backward (leftwards and upwards) by \a delta pixel * samples. Negative increments move this iterator forward (rightwards * and downwards) by \a delta pixel samples. Returns a reference to this * iterator. */ const_roi_pixel_iterator& operator -=( distance_type delta ) noexcept { int w = this->m_rowEnd - this->m_rowBegin; iterator_base::MoveBy( -delta%w, -delta/w ); return *this; } /*! * Moves this iterator within its rectangular region of interest by the * specified horizontal and vertical increments in pixels, \a dx and * \a dy respectively, relative to its current position. Positive * (negative) \a dx increments move the iterator rightwards (leftwards). * Positive (negative) \a dy increments move the iterator downwards * (upwards). */ const_roi_pixel_iterator& MoveBy( int dx, int dy ) noexcept { iterator_base::MoveBy( dx, dy ); return *this; } /*! * Scalar-to-iterator addition operator. Returns an iterator equivalent * to the specified iterator \a i incremented by a distance \a delta. */ friend const_roi_pixel_iterator operator +( const const_roi_pixel_iterator& i, distance_type delta ) noexcept { const_roi_pixel_iterator j( i ); j += delta; return j; } /*! * Iterator-to-scalar addition operator. This operator implements the * commutative property of scalar-to-iterator addition. */ friend const_roi_pixel_iterator operator +( distance_type delta, const const_roi_pixel_iterator& i ) noexcept { const_roi_pixel_iterator j( i ); j += delta; return j; } /*! * Scalar-to-iterator subtraction operator. Returns an iterator equal to * the specified iterator \a i decremented by a distance \a delta. */ friend const_roi_pixel_iterator operator -( const const_roi_pixel_iterator& i, distance_type delta ) noexcept { const_roi_pixel_iterator j( i ); j -= delta; return j; } /*! * Equality operator. Returns true if two iterators \a i and \a j point * to the same pixel. */ friend bool operator ==( const const_roi_pixel_iterator& i, const const_roi_pixel_iterator& j ) noexcept { return i.m_iterator[0] == j.m_iterator[0]; } /*! * Less than operator. Returns true if the specified iterator \a i * precedes another iterator \a j. */ friend bool operator <( const const_roi_pixel_iterator& i, const const_roi_pixel_iterator& j ) noexcept { return i.m_iterator[0] < j.m_iterator[0]; } }; // ------------------------------------------------------------------------- template class filter_pixel_iterator_base : public iterator_base { protected: filter_type m_filter; sample_pointer m_begin = nullptr; filter_pixel_iterator_base() = default; filter_pixel_iterator_base( image_type& image, const filter_type& filter ) : iterator_base( image ) , m_filter( filter ) , m_begin( iterator_base::m_iterator ) { JumpToNextValidSample(); } filter_pixel_iterator_base( const iterator_base& i, const filter_type& filter ) : iterator_base( i ) , m_filter( filter ) , m_begin( iterator_base::m_iterator ) { JumpToNextValidSample(); } filter_pixel_iterator_base( const filter_pixel_iterator_base& ) = default; filter_pixel_iterator_base& operator =( const filter_pixel_iterator_base& ) = default; filter_pixel_iterator_base& operator =( const iterator_base& i ) noexcept { (void)iterator_base::operator =( i ); JumpToNextValidSample(); } void JumpToNextValidSample() noexcept { while ( this->m_iterator[0] < this->m_end && !this->m_filter( this->m_iterator ) ) for ( int i = 0; i < this->m_iterator.Length(); ++i ) ++this->m_iterator[i]; } void JumpToPrevValidSample() noexcept { while ( this->m_iterator[0] > this->m_begin && !this->m_filter( this->m_iterator ) ) for ( int i = 0; i < this->m_iterator.Length(); ++i ) --this->m_iterator[i]; } }; // ------------------------------------------------------------------------- /*! * \class pcl::GenericImage::filter_pixel_iterator * \brief Mutable filter pixel iterator. * * A mutable pixel iterator provides read/write, random access to all pixels * in an image. A filter pixel iterator uses a predicate object to filter * pixels within its iteration range. * * Filter Predicates * * For a mutable filter pixel iterator, the class template argument F must * provide a function call operator of the form: * * \code bool F::operator()( const GenericVector\& f ) const \endcode * * Each component of the passed vector \a f is a pointer to a pixel sample * in the corresponding channel of the iterated image. The function call * operator must return true if the pointed pixel is valid in the context of * the filter iterator. The filter iterator stores a private instance of F * to validate pixels, so the F class must provide also copy constructor * semantics, either explicitly or implicitly. * * Filter Iterators * * Each time a filter iterator is constructed, incremented, decremented, or * moved forward or backward, the filter predicate is called to validate the * pixel or pixel sample pointed to by the iterator. If the pointed item is * not valid, the iterator is incremented or decremented (depending on the * operation performed initially) until it finds a valid one, or until it * reaches the end of the iteration range, whichever happens first. In this * way a filter iterator gives access only to valid items in a completely * automatic and transparent fashion. * * \ingroup image_iterators */ template class filter_pixel_iterator : public filter_pixel_iterator_base, pixel_iterator, sample*, F> { public: /*! * Represents the type of the iterated image. */ typedef GenericImage

image_type; /*! * Represents the pixel traits class used by the iterated image. */ typedef typename image_type::pixel_traits pixel_traits; /*! * Represents a pixel sample of the iterated image. */ typedef typename image_type::sample sample; /*! * Represents the type of the unary predicate used by this filter * iterator. */ typedef F filter_type; typedef filter_pixel_iterator_base, pixel_iterator, sample*, F> iterator_base; /*! * Default constructor. Initializes an invalid iterator. */ filter_pixel_iterator() = default; /*! * Constructs a mutable filter pixel iterator. * * \param image The image to iterate. * * \param filter Reference to a predicate object that will be used to * filter pixels. */ filter_pixel_iterator( image_type& image, const F& filter ) : iterator_base( image.EnsureUnique(), filter ) { } /*! * Constructs a mutable filter pixel iterator from a mutable pixel * iterator and the specified \a filter. */ filter_pixel_iterator( const pixel_iterator& i, const F& filter ) : iterator_base( i, filter ) { } /*! * Copy constructor. */ filter_pixel_iterator( const filter_pixel_iterator& ) = default; /*! * Copy assignment operator. Returns a reference to this iterator. */ filter_pixel_iterator& operator =( const filter_pixel_iterator& ) = default; /*! * Assigns a mutable pixel iterator to this object. Returns a reference * to this iterator. */ filter_pixel_iterator& operator =( const pixel_iterator& i ) noexcept { (void)iterator_base::operator =( i ); return *this; } /*! * Returns true only if this iterator is valid. A valid iterator defines * a valid iterated image and a non-null position. */ bool IsValid() const noexcept { return this->m_image != nullptr && !this->m_iterator.IsEmpty(); } /*! * Returns a reference to the image being iterated by this object. */ image_type& Image() const noexcept { return *this->m_image; } /*! * Returns a reference to the immutable predicate object used by this * filter iterator. */ const filter_type& Filter() const noexcept { return this->m_filter; } /*! * Returns a reference to the mutable predicate object used by this * filter iterator. */ filter_type& Filter() noexcept { return this->m_filter; } /*! * Returns a pointer to the pixel sample pointed to by this iterator in * the specified \a channel. */ sample* Position( int channel ) const noexcept { return this->m_iterator[channel]; } /*! * Boolean type conversion operator. Returns true if this iterator is * active. A pixel iterator is active if it has not reached (or * surpassed) its iteration limit. */ operator bool() const noexcept { return this->m_iterator[0] < this->m_end; } /*! * Array subscript operator. Returns a reference to the pixel sample * pointed to by this iterator in the specified \a channel. */ sample& operator []( int channel ) const noexcept { return *this->m_iterator[channel]; } /*! * Pre-increment operator. Causes this iterator to point to the next * valid pixel in the iterated image. Returns a reference to this * iterator. */ filter_pixel_iterator& operator ++() noexcept { for ( int i = 0; i < this->m_iterator.Length(); ++i ) ++this->m_iterator[i]; this->JumpToNextValidSample(); return *this; } /*! * Post-increment operator. Causes this iterator to point to the next * valid pixel in the iterated image channel. Returns a copy of the * iterator as it was before incrementing it. */ filter_pixel_iterator operator ++( int ) noexcept { filter_pixel_iterator i0( *this ); for ( int i = 0; i < this->m_iterator.Length(); ++i ) ++this->m_iterator[i]; this->JumpToNextValidSample(); return i0; } /*! * Pre-decrement operator. Causes this iterator to point to the previous * valid pixel in the iterated image channel, then returns a reference to * this iterator. */ filter_pixel_iterator& operator --() noexcept { for ( int i = 0; i < this->m_iterator.Length(); ++i ) --this->m_iterator[i]; this->JumpToPrevValidSample(); return *this; } /*! * Post-decrement operator. Causes this iterator to point to the previous * valid pixel in the iterated image channel. Returns a copy of the * iterator as it was before decrementing it. */ filter_pixel_iterator operator --( int ) noexcept { filter_pixel_iterator i0( *this ); for ( int i = 0; i < this->m_iterator.Length(); ++i ) --this->m_iterator[i]; this->JumpToPrevValidSample(); return i0; } /*! * Scalar assignment/addition operator. Increments this iterator by a * distance \a delta from its current position. Positive increments cause * this iterator to move forward by \a delta pixel samples. Negative * increments move this iterator backward by \a delta pixel samples. * Returns a reference to this iterator. */ filter_pixel_iterator& operator +=( distance_type delta ) noexcept { for ( int i = 0; i < this->m_iterator.Length(); ++i ) this->m_iterator[i] += delta; this->JumpToNextValidSample(); return *this; } /*! * Scalar assignment/subtraction operator. Decrements this iterator by a * distance \a delta from its current position. Positive increments cause * this iterator to move backward by \a delta pixel samples. Negative * increments move this iterator forward by \a delta pixel samples. * Returns a reference to this iterator. */ filter_pixel_iterator& operator -=( distance_type delta ) noexcept { for ( int i = 0; i < this->m_iterator.Length(); ++i ) this->m_iterator[i] -= delta; this->JumpToPrevValidSample(); return *this; } /*! * Moves this iterator on the iterated image by the specified horizontal * and vertical increments in pixels, \a dx and \a dy respectively, * relative to its current position. Positive (negative) \a dx increments * move the iterator rightwards (leftwards). Positive (negative) \a dy * increments move the iterator downwards (upwards). */ filter_pixel_iterator& MoveBy( int dx, int dy ) noexcept { distance_type d = distance_type( dy )*this->m_image->Width() + distance_type( dx ); for ( int i = 0; i < this->m_iterator.Length(); ++i ) this->m_iterator[i] += d; if ( d >= 0 ) this->JumpToNextValidSample(); else this->JumpToPrevValidSample(); return *this; } /*! * Scalar-to-iterator addition operator. Returns an iterator equivalent * to the specified iterator \a i incremented by a distance \a delta. */ friend filter_pixel_iterator operator +( const filter_pixel_iterator& i, distance_type delta ) noexcept { filter_pixel_iterator j( i ); j += delta; return j; } /*! * Iterator-to-scalar addition operator. This operator implements the * commutative property of scalar-to-iterator addition. */ friend filter_pixel_iterator operator +( distance_type delta, const filter_pixel_iterator& i ) noexcept { filter_pixel_iterator j( i ); j += delta; return j; } /*! * Scalar-to-iterator subtraction operator. Returns an iterator equal to * the specified iterator \a i decremented by a distance \a delta. */ friend filter_pixel_iterator operator -( const filter_pixel_iterator& i, distance_type delta ) noexcept { filter_pixel_iterator j( i ); j -= delta; return j; } /*! * Equality operator. Returns true if two iterators \a i and \a j point * to the same pixel. */ friend bool operator ==( const filter_pixel_iterator& i, const filter_pixel_iterator& j ) noexcept { return i.m_iterator[0] == j.m_iterator[0]; } /*! * Less than operator. Returns true if the specified iterator \a i * precedes another iterator \a j. */ friend bool operator <( const filter_pixel_iterator& i, const filter_pixel_iterator& j ) noexcept { return i.m_iterator[0] < j.m_iterator[0]; } }; // ------------------------------------------------------------------------- /*! * \class pcl::GenericImage::const_filter_pixel_iterator * \brief Immutable filter pixel iterator. * * An immutable pixel iterator provides read-only, random access to all * pixels in an image. A filter pixel iterator uses a predicate object to * filter pixels within its iteration range. * * Filter Predicates * * For an immutable filter pixel iterator, the class template argument F * must provide a function call operator of the form: * * \code bool F::operator()( const GenericVector\& f ) const \endcode * * Each component of the passed vector \a f is a pointer to a constant pixel * sample in the corresponding channel of the iterated image. The function * call operator must return true if the pointed pixel is valid in the * context of the filter iterator. The filter iterator stores a private * instance of F to validate pixels, so the F class must provide also copy * constructor semantics, either explicitly or implicitly. * * Filter Iterators * * Each time a filter iterator is constructed, incremented, decremented, or * moved forward or backward, the filter predicate is called to validate the * pixel or pixel sample pointed to by the iterator. If the pointed item is * not valid, the iterator is incremented or decremented (depending on the * operation performed initially) until it finds a valid one, or until it * reaches the end of the iteration range, whichever happens first. In this * way a filter iterator gives access only to valid items in a completely * automatic and transparent fashion. * * \ingroup image_iterators */ template class const_filter_pixel_iterator : public filter_pixel_iterator_base, const_pixel_iterator, const sample*, F> { public: /*! * Represents the type of the iterated image. */ typedef GenericImage

image_type; /*! * Represents the pixel traits class used by the iterated image. */ typedef typename image_type::pixel_traits pixel_traits; /*! * Represents a pixel sample of the iterated image. */ typedef typename image_type::sample sample; /*! * Represents the type of the unary predicate used by this filter * iterator. */ typedef F filter_type; typedef filter_pixel_iterator_base, const_pixel_iterator, const sample*, F> iterator_base; /*! * Default constructor. Initializes an invalid iterator. */ const_filter_pixel_iterator() = default; /*! * Constructs an immutable filter pixel iterator. * * \param image The constant image to iterate. * * \param filter Reference to a predicate object that will be used to * filter pixels. */ const_filter_pixel_iterator( const image_type& image, const F& filter ) : iterator_base( image, filter ) { } /*! * Constructs an immutable filter pixel iterator from an immutable pixel * iterator and the specified \a filter. */ const_filter_pixel_iterator( const const_pixel_iterator& i, const F& filter ) : iterator_base( i, filter ) { } /*! * Copy constructor. */ const_filter_pixel_iterator( const const_filter_pixel_iterator& ) = default; /*! * Copy assignment operator. Returns a reference to this iterator. */ const_filter_pixel_iterator& operator =( const const_filter_pixel_iterator& ) = default; /*! * Assigns an immutable pixel iterator to this object. Returns a * reference to this iterator. */ const_filter_pixel_iterator& operator =( const const_pixel_iterator& i ) noexcept { (void)iterator_base::operator =( i ); return *this; } /*! * Returns true only if this iterator is valid. A valid iterator defines * a valid iterated image and a non-null position. */ bool IsValid() const noexcept { return this->m_image != nullptr && !this->m_iterator.IsEmpty(); } /*! * Returns a reference to the constant image being iterated by this * object. */ const image_type& Image() const noexcept { return *this->m_image; } /*! * Returns a reference to the immutable predicate object used by this * filter iterator. */ const filter_type& Filter() const noexcept { return this->m_filter; } /*! * Returns a reference to the mutable predicate object used by this * filter iterator. */ filter_type& Filter() noexcept { return this->m_filter; } /*! * Returns a pointer to the constant pixel sample pointed to by this * iterator in the specified \a channel. */ const sample* Position( int channel ) const noexcept { return this->m_iterator[channel]; } /*! * Boolean type conversion operator. Returns true if this iterator is * active. A pixel iterator is active if it has not reached (or * surpassed) its iteration limit. */ operator bool() const noexcept { return this->m_iterator[0] < this->m_end; } /*! * Array subscript operator. Returns a reference to the constant pixel * sample pointed to by this iterator in the specified \a channel. */ const sample& operator []( int channel ) const noexcept { return *this->m_iterator[channel]; } /*! * Pre-increment operator. Causes this iterator to point to the next * valid pixel in the iterated image. Returns a reference to this * iterator. */ const_filter_pixel_iterator& operator ++() noexcept { for ( int i = 0; i < this->m_iterator.Length(); ++i ) ++this->m_iterator[i]; this->JumpToNextValidSample(); return *this; } /*! * Post-increment operator. Causes this iterator to point to the next * valid pixel in the iterated image channel. Returns a copy of the * iterator as it was before incrementing it. */ const_filter_pixel_iterator operator ++( int ) noexcept { const_filter_pixel_iterator i0( *this ); for ( int i = 0; i < this->m_iterator.Length(); ++i ) ++this->m_iterator[i]; this->JumpToNextValidSample(); return i0; } /*! * Pre-decrement operator. Causes this iterator to point to the previous * valid pixel in the iterated image channel, then returns a reference to * this iterator. */ const_filter_pixel_iterator& operator --() noexcept { for ( int i = 0; i < this->m_iterator.Length(); ++i ) --this->m_iterator[i]; this->JumpToPrevValidSample(); return *this; } /*! * Post-decrement operator. Causes this iterator to point to the previous * valid pixel in the iterated image channel. Returns a copy of the * iterator as it was before decrementing it. */ const_filter_pixel_iterator operator --( int ) noexcept { const_filter_pixel_iterator i0( *this ); for ( int i = 0; i < this->m_iterator.Length(); ++i ) --this->m_iterator[i]; this->JumpToPrevValidSample(); return i0; } /*! * Scalar assignment/addition operator. Increments this iterator by a * distance \a delta from its current position. Positive increments cause * this iterator to move forward by \a delta pixel samples. Negative * increments move this iterator backward by \a delta pixel samples. * Returns a reference to this iterator. */ const_filter_pixel_iterator& operator +=( distance_type delta ) noexcept { for ( int i = 0; i < this->m_iterator.Length(); ++i ) this->m_iterator[i] += delta; this->JumpToNextValidSample(); return *this; } /*! * Scalar assignment/subtraction operator. Decrements this iterator by a * distance \a delta from its current position. Positive increments cause * this iterator to move backward by \a delta pixel samples. Negative * increments move this iterator forward by \a delta pixel samples. * Returns a reference to this iterator. */ const_filter_pixel_iterator& operator -=( distance_type delta ) noexcept { for ( int i = 0; i < this->m_iterator.Length(); ++i ) this->m_iterator[i] -= delta; this->JumpToPrevValidSample(); return *this; } /*! * Moves this iterator on the iterated image by the specified horizontal * and vertical increments in pixels, \a dx and \a dy respectively, * relative to its current position. Positive (negative) \a dx increments * move the iterator rightwards (leftwards). Positive (negative) \a dy * increments move the iterator downwards (upwards). */ const_filter_pixel_iterator& MoveBy( int dx, int dy ) noexcept { distance_type d = distance_type( dy )*this->m_image->Width() + distance_type( dx ); for ( int i = 0; i < this->m_iterator.Length(); ++i ) this->m_iterator[i] += d; if ( d >= 0 ) this->JumpToNextValidSample(); else this->JumpToPrevValidSample(); return *this; } /*! * Scalar-to-iterator addition operator. Returns an iterator equivalent * to the specified iterator \a i incremented by a distance \a delta. */ friend const_filter_pixel_iterator operator +( const const_filter_pixel_iterator& i, distance_type delta ) noexcept { const_filter_pixel_iterator j( i ); j += delta; return j; } /*! * Iterator-to-scalar addition operator. This operator implements the * commutative property of scalar-to-iterator addition. */ friend const_filter_pixel_iterator operator +( distance_type delta, const const_filter_pixel_iterator& i ) noexcept { const_filter_pixel_iterator j( i ); j += delta; return j; } /*! * Scalar-to-iterator subtraction operator. Returns an iterator equal to * the specified iterator \a i decremented by a distance \a delta. */ friend const_filter_pixel_iterator operator -( const const_filter_pixel_iterator& i, distance_type delta ) noexcept { const_filter_pixel_iterator j( i ); j -= delta; return j; } /*! * Equality operator. Returns true if two iterators \a i and \a j point * to the same pixel. */ friend bool operator ==( const const_filter_pixel_iterator& i, const const_filter_pixel_iterator& j ) noexcept { return i.m_iterator[0] == j.m_iterator[0]; } /*! * Less than operator. Returns true if the specified iterator \a i * precedes another iterator \a j. */ friend bool operator <( const const_filter_pixel_iterator& i, const const_filter_pixel_iterator& j ) noexcept { return i.m_iterator[0] < j.m_iterator[0]; } }; // ------------------------------------------------------------------------- template class roi_filter_pixel_iterator_base : public roi_pixel_iterator_base { protected: typedef roi_pixel_iterator_base roi_iterator_base; filter_type m_filter; sample_pointer m_begin = nullptr; roi_filter_pixel_iterator_base() = default; roi_filter_pixel_iterator_base( image_type& image, const filter_type& filter, const Rect& rect ) : roi_iterator_base( image, rect ) , m_filter( filter ) , m_begin( roi_iterator_base::m_iterator ) { JumpToNextValidSample(); } roi_filter_pixel_iterator_base( const roi_iterator_base& i, const filter_type& filter ) : roi_iterator_base( i ) , m_filter( filter ) , m_begin( roi_iterator_base::m_iterator ) { JumpToNextValidSample(); } roi_filter_pixel_iterator_base( const roi_filter_pixel_iterator_base& ) = default; roi_filter_pixel_iterator_base& operator =( const roi_filter_pixel_iterator_base& ) = default; roi_filter_pixel_iterator_base& operator =( const roi_iterator_base& i ) noexcept { (void)roi_iterator_base::operator =( i ); JumpToNextValidSample(); return *this; } void JumpToNextValidSample() noexcept { while ( this->m_iterator[0] < this->m_end && !this->m_filter( this->m_iterator ) ) roi_iterator_base::Increment(); } void JumpToPrevValidSample() noexcept { while ( this->m_iterator[0] > this->m_begin && !this->m_filter( this->m_iterator ) ) roi_iterator_base::Decrement(); } }; // ------------------------------------------------------------------------- /*! * \class pcl::GenericImage::roi_filter_pixel_iterator * \brief Mutable region-of-interest, filter pixel iterator. * * A mutable, region-of-interest (ROI), filter pixel iterator combines the * capabilities of roi_pixel_iterator and filter_pixel_iterator in a single * iterator class. * * \ingroup image_iterators */ template class roi_filter_pixel_iterator : public roi_filter_pixel_iterator_base, sample*, F> { public: /*! * Represents the type of the iterated image. */ typedef GenericImage

image_type; /*! * Represents the pixel traits class used by the iterated image. */ typedef typename image_type::pixel_traits pixel_traits; /*! * Represents a pixel sample of the iterated image. */ typedef typename image_type::sample sample; /*! * Represents the type of the unary predicate used by this filter * iterator. */ typedef F filter_type; typedef roi_filter_pixel_iterator_base, sample*, F> iterator_base; /*! * Default constructor. Initializes an invalid iterator. */ roi_filter_pixel_iterator() = default; /*! * Constructs a mutable, region-of-interest (ROI), filter pixel iterator. * * \param image The image to iterate. * * \param filter Reference to a predicate object that will be used to * filter pixel sample values. * * \param rect Region of interest. If an empty rectangle is specified, * the current rectangular selection in the \a image will * be used. If the specified rectangle is not empty and * extends beyond image boundaries, only the intersection * with the image will be used. If that intersection does * not exist, then the resulting iterator will be invalid, * with an empty iteration range. The default value is an * empty rectangle. */ roi_filter_pixel_iterator( image_type& image, const F& filter, const Rect& rect = Rect( 0 ) ) : iterator_base( image.EnsureUnique(), filter, rect ) { } /*! * Constructs a mutable, region-of-interest (ROI), filter pixel iterator * from the specified ROI pixel iterator \a i and \a filter. */ roi_filter_pixel_iterator( const roi_pixel_iterator& i, const F& filter ) : iterator_base( i, filter ) { } /*! * Copy constructor. */ roi_filter_pixel_iterator( const roi_filter_pixel_iterator& ) = default; /*! * Copy assignment operator. Returns a reference to this object. */ roi_filter_pixel_iterator& operator =( const roi_filter_pixel_iterator& ) = default; /*! * Assigns a ROI pixel iterator. Returns a reference to this object. */ roi_filter_pixel_iterator& operator =( const roi_pixel_iterator& i ) noexcept { (void)iterator_base::operator =( i ); return *this; } /*! * Returns true only if this iterator is valid. A valid iterator defines * a valid iterated image and a non-null position. */ bool IsValid() const noexcept { return this->m_image != nullptr && !this->m_iterator.IsEmpty(); } /*! * Returns a reference to the image being iterated by this object. */ image_type& Image() const noexcept { return *this->m_image; } /*! * Returns a reference to the immutable predicate object used by this * filter iterator. */ const filter_type& Filter() const noexcept { return this->m_filter; } /*! * Returns a reference to the mutable predicate object used by this * filter iterator. */ filter_type& Filter() noexcept { return this->m_filter; } /*! * Returns a pointer to the pixel sample pointed to by this iterator in * the specified \a channel. */ sample* Position( int channel ) const noexcept { return this->m_iterator[channel]; } /*! * Boolean type conversion operator. Returns true if this iterator is * active. A ROI pixel iterator is active if it has not reached (or * surpassed) its iteration limit, i.e. the bottom right corner of its * iterated region of interest. */ operator bool() const noexcept { return this->m_iterator[0] < this->m_end; } /*! * Array subscript operator. Returns a reference to the pixel sample * pointed to by this iterator in the specified \a channel. */ sample& operator []( int channel ) const noexcept { return *this->m_iterator[channel]; } /*! * Pre-increment operator. Causes this iterator to point to the next * valid pixel in the iterated region of interest. Returns a reference to * this iterator. */ roi_filter_pixel_iterator& operator ++() noexcept { this->Increment(); this->JumpToNextValidSample(); return *this; } /*! * Post-increment operator. Causes this iterator to point to the next * valid pixel in the iterated region of interest. Returns a copy of the * iterator as it was before incrementing it. */ roi_filter_pixel_iterator operator ++( int ) noexcept { roi_filter_pixel_iterator i0( *this ); this->Increment(); this->JumpToNextValidSample(); return i0; } /*! * Pre-decrement operator. Causes this iterator to point to the previous * valid pixel in the iterated region of interest, then returns a * reference to this iterator. */ roi_filter_pixel_iterator& operator --() noexcept { this->Decrement(); this->JumpToPrevValidSample(); return *this; } /*! * Post-decrement operator. Causes this iterator to point to the previous * valid pixel in the iterated region of interest. Returns a copy of the * iterator as it was before decrementing it. */ roi_filter_pixel_iterator operator --( int ) noexcept { roi_filter_pixel_iterator i0( *this ); this->Decrement(); this->JumpToPrevValidSample(); return i0; } /*! * Scalar assignment/addition operator. Increments this iterator by a * distance \a delta from its current position, within the rectangular * region of interest being iterated. Positive increments cause this * iterator to move forward (rightwards and downwards) by \a delta pixel * samples. Negative increments move this iterator backward (leftwards * and upwards) by \a delta pixel samples. Returns a reference to this * iterator. */ roi_filter_pixel_iterator& operator +=( distance_type delta ) noexcept { int w = this->m_rowEnd - this->m_rowBegin; return MoveBy( delta%w, delta/w ); } /*! * Scalar assignment/subtraction operator. Decrements this iterator by a * distance \a delta from its current position, within the rectangular * region of interest being iterated. Positive increments cause this * iterator to move backward (leftwards and upwards) by \a delta pixel * samples. Negative increments move this iterator forward (rightwards * and downwards) by \a delta pixel samples. Returns a reference to this * iterator. */ roi_filter_pixel_iterator& operator -=( distance_type delta ) noexcept { int w = this->m_rowEnd - this->m_rowBegin; return MoveBy( -delta%w, -delta/w ); } /*! * Moves this iterator within its rectangular region of interest by the * specified horizontal and vertical increments in pixels, \a dx and * \a dy respectively, relative to its current position. Positive * (negative) \a dx increments move the iterator rightwards (leftwards). * Positive (negative) \a dy increments move the iterator downwards * (upwards). */ roi_filter_pixel_iterator& MoveBy( int dx, int dy ) noexcept { sample* __restrict__ i0 = this->m_iterator[0]; iterator_base::MoveBy( dx, dy ); if ( this->m_iterator[0] >= i0 ) this->JumpToNextValidSample(); else this->JumpToPrevValidSample(); return *this; } /*! * Scalar-to-iterator addition operator. Returns an iterator equivalent * to the specified iterator \a i incremented by a distance \a delta. */ friend roi_filter_pixel_iterator operator +( const roi_filter_pixel_iterator& i, distance_type delta ) noexcept { roi_filter_pixel_iterator j( i ); j += delta; return j; } /*! * Iterator-to-scalar addition operator. This operator implements the * commutative property of scalar-to-iterator addition. */ friend roi_filter_pixel_iterator operator +( distance_type delta, const roi_filter_pixel_iterator& i ) noexcept { roi_filter_pixel_iterator j( i ); j += delta; return j; } /*! * Scalar-to-iterator subtraction operator. Returns an iterator equal to * the specified iterator \a i decremented by a distance \a delta. */ friend roi_filter_pixel_iterator operator -( const roi_filter_pixel_iterator& i, distance_type delta ) noexcept { roi_filter_pixel_iterator j( i ); j -= delta; return j; } /*! * Equality operator. Returns true if two iterators \a i and \a j point * to the same pixel. */ friend bool operator ==( const roi_filter_pixel_iterator& i, const roi_filter_pixel_iterator& j ) noexcept { return i.m_iterator[0] == j.m_iterator[0]; } /*! * Less than operator. Returns true if the specified iterator \a i * precedes another iterator \a j. */ friend bool operator <( const roi_filter_pixel_iterator& i, const roi_filter_pixel_iterator& j ) noexcept { return i.m_iterator[0] < j.m_iterator[0]; } }; // ------------------------------------------------------------------------- /*! * \class pcl::GenericImage::const_roi_filter_pixel_iterator * \brief Immutable region-of-interest, filter pixel iterator. * * An immutable, region-of-interest (ROI), filter pixel iterator combines * the capabilities of const_roi_pixel_iterator and * const_filter_pixel_iterator in a single iterator class. * * \ingroup image_iterators */ template class const_roi_filter_pixel_iterator : public roi_filter_pixel_iterator_base, const sample*, F> { public: /*! * Represents the type of the iterated image. */ typedef GenericImage

image_type; /*! * Represents the pixel traits class used by the iterated image. */ typedef typename image_type::pixel_traits pixel_traits; /*! * Represents a pixel sample of the iterated image. */ typedef typename image_type::sample sample; /*! * Represents the type of the unary predicate used by this filter * iterator. */ typedef F filter_type; typedef roi_filter_pixel_iterator_base, const sample*, F> iterator_base; /*! * Default constructor. Initializes an invalid iterator. */ const_roi_filter_pixel_iterator() = default; /*! * Constructs an immutable, region-of-interest (ROI), filter pixel * iterator. * * \param image The constant image to iterate. * * \param filter Reference to a predicate object that will be used to * filter pixel sample values. * * \param rect Region of interest. If an empty rectangle is specified, * the current rectangular selection in the \a image will * be used. If the specified rectangle is not empty and * extends beyond image boundaries, only the intersection * with the image will be used. If that intersection does * not exist, then the resulting iterator will be invalid, * with an empty iteration range. The default value is an * empty rectangle. */ const_roi_filter_pixel_iterator( const image_type& image, const F& filter, const Rect& rect = Rect( 0 ) ) : iterator_base( image, filter, rect ) { } /*! * Constructs an immutable, region-of-interest (ROI), filter pixel * iterator from the specified ROI pixel iterator \a i and \a filter. */ const_roi_filter_pixel_iterator( const const_roi_pixel_iterator& i, const F& filter ) : iterator_base( i, filter ) { } /*! * Copy constructor. */ const_roi_filter_pixel_iterator( const const_roi_filter_pixel_iterator& ) = default; /*! * Copy assignment operator. Returns a reference to this object. */ const_roi_filter_pixel_iterator& operator =( const const_roi_filter_pixel_iterator& ) = default; /*! * Assigns a ROI pixel iterator. Returns a reference to this object. */ const_roi_filter_pixel_iterator& operator =( const const_roi_pixel_iterator& i ) noexcept { (void)iterator_base::operator =( i ); return *this; } /*! * Returns true only if this iterator is valid. A valid iterator defines * a valid iterated image and a non-null position. */ bool IsValid() const noexcept { return this->m_image != nullptr && !this->m_iterator.IsEmpty(); } /*! * Returns a reference to the constant image being iterated by this * object. */ const image_type& Image() const noexcept { return *this->m_image; } /*! * Returns a reference to the immutable predicate object used by this * filter iterator. */ const filter_type& Filter() const noexcept { return this->m_filter; } /*! * Returns a reference to the mutable predicate object used by this * filter iterator. */ filter_type& Filter() noexcept { return this->m_filter; } /*! * Returns a pointer to the constant pixel sample pointed to by this * iterator in the specified \a channel. */ const sample* Position( int channel ) const noexcept { return this->m_iterator[channel]; } /*! * Boolean type conversion operator. Returns true if this iterator is * active. A ROI pixel iterator is active if it has not reached (or * surpassed) its iteration limit, i.e. the bottom right corner of its * iterated region of interest. */ operator bool() const noexcept { return this->m_iterator[0] < this->m_end; } /*! * Array subscript operator. Returns a reference to the constant pixel * sample pointed to by this iterator in the specified \a channel. */ const sample& operator []( int channel ) const noexcept { return *this->m_iterator[channel]; } /*! * Pre-increment operator. Causes this iterator to point to the next * valid pixel in the iterated region of interest. Returns a reference to * this iterator. */ const_roi_filter_pixel_iterator& operator ++() noexcept { this->Increment(); this->JumpToNextValidSample(); return *this; } /*! * Post-increment operator. Causes this iterator to point to the next * valid pixel in the iterated region of interest. Returns a copy of the * iterator as it was before incrementing it. */ const_roi_filter_pixel_iterator operator ++( int ) noexcept { const_roi_filter_pixel_iterator i0( *this ); this->Increment(); this->JumpToNextValidSample(); return i0; } /*! * Pre-decrement operator. Causes this iterator to point to the previous * valid pixel in the iterated region of interest, then returns a * reference to this iterator. */ const_roi_filter_pixel_iterator& operator --() noexcept { this->Decrement(); this->JumpToPrevValidSample(); return *this; } /*! * Post-decrement operator. Causes this iterator to point to the previous * valid pixel in the iterated region of interest. Returns a copy of the * iterator as it was before decrementing it. */ const_roi_filter_pixel_iterator operator --( int ) noexcept { const_roi_filter_pixel_iterator i0( *this ); this->Decrement(); this->JumpToPrevValidSample(); return i0; } /*! * Scalar assignment/addition operator. Increments this iterator by a * distance \a delta from its current position, within the rectangular * region of interest being iterated. Positive increments cause this * iterator to move forward (rightwards and downwards) by \a delta pixel * samples. Negative increments move this iterator backward (leftwards * and upwards) by \a delta pixel samples. Returns a reference to this * iterator. */ const_roi_filter_pixel_iterator& operator +=( distance_type delta ) noexcept { int w = this->m_rowEnd - this->m_rowBegin; return MoveBy( delta%w, delta/w ); } /*! * Scalar assignment/subtraction operator. Decrements this iterator by a * distance \a delta from its current position, within the rectangular * region of interest being iterated. Positive increments cause this * iterator to move backward (leftwards and upwards) by \a delta pixel * samples. Negative increments move this iterator forward (rightwards * and downwards) by \a delta pixel samples. Returns a reference to this * iterator. */ const_roi_filter_pixel_iterator& operator -=( distance_type delta ) noexcept { int w = this->m_rowEnd - this->m_rowBegin; return MoveBy( -delta%w, -delta/w ); } /*! * Moves this iterator within its rectangular region of interest by the * specified horizontal and vertical increments in pixels, \a dx and * \a dy respectively, relative to its current position. Positive * (negative) \a dx increments move the iterator rightwards (leftwards). * Positive (negative) \a dy increments move the iterator downwards * (upwards). */ const_roi_filter_pixel_iterator& MoveBy( int dx, int dy ) noexcept { const sample* __restrict__ i0 = this->m_iterator[0]; iterator_base::MoveBy( dx, dy ); if ( this->m_iterator[0] >= i0 ) this->JumpToNextValidSample(); else this->JumpToPrevValidSample(); return *this; } /*! * Scalar-to-iterator addition operator. Returns an iterator equivalent * to the specified iterator \a i incremented by a distance \a delta. */ friend const_roi_filter_pixel_iterator operator +( const const_roi_filter_pixel_iterator& i, distance_type delta ) noexcept { const_roi_filter_pixel_iterator j( i ); j += delta; return j; } /*! * Iterator-to-scalar addition operator. This operator implements the * commutative property of scalar-to-iterator addition. */ friend const_roi_filter_pixel_iterator operator +( distance_type delta, const const_roi_filter_pixel_iterator& i ) noexcept { const_roi_filter_pixel_iterator j( i ); j += delta; return j; } /*! * Scalar-to-iterator subtraction operator. Returns an iterator equal to * the specified iterator \a i decremented by a distance \a delta. */ friend const_roi_filter_pixel_iterator operator -( const const_roi_filter_pixel_iterator& i, distance_type delta ) noexcept { const_roi_filter_pixel_iterator j( i ); j -= delta; return j; } /*! * Equality operator. Returns true if two iterators \a i and \a j point * to the same pixel. */ friend bool operator ==( const const_roi_filter_pixel_iterator& i, const const_roi_filter_pixel_iterator& j ) noexcept { return i.m_iterator[0] == j.m_iterator[0]; } /*! * Less than operator. Returns true if the specified iterator \a i * precedes another iterator \a j. */ friend bool operator <( const const_roi_filter_pixel_iterator& i, const const_roi_filter_pixel_iterator& j ) noexcept { return i.m_iterator[0] < j.m_iterator[0]; } }; // ------------------------------------------------------------------------- /*! * Returns true iff this %GenericImage template instantiation uses floating * point pixel samples; returns false if it uses integer samples. */ static bool IsFloatSample() noexcept { return pixel_traits::IsFloatSample(); } /*! * Returns true iff this %GenericImage template instantiation uses complex * pixel samples; returns false if it uses real samples. */ static bool IsComplexSample() noexcept { return pixel_traits::IsComplexSample(); } /*! * Returns the number of 8-bit bytes required to store a pixel sample in * this %GenericImage template instantiation. */ static int BytesPerSample() noexcept { return P::BytesPerSample(); } /*! * Returns the number of bits in a pixel sample of this %GenericImage * template instantiation. */ static int BitsPerSample() noexcept { return P::BitsPerSample(); } /*! * Returns true iff this object and another \a image use the same pixel * sample type: * * \li Same sample size in bits. * \li Both images using integer, floating point real, or complex samples. */ template bool SameSampleType( const GenericImage& image ) const noexcept { return image.BitsPerSample() == BitsPerSample() && image.IsFloatSample() == IsFloatSample() && image.IsComplexSample() == IsComplexSample(); } /*! * Always returns true, indicating that this object and the specified * \a image, both of the same template instantiation, use the same pixel * sample type. */ bool SameSampleType( const GenericImage& image ) const noexcept { return true; } // ------------------------------------------------------------------------- /*! * Constructs a default local image. This object will be initialized as an * empty grayscale image with local storage. */ GenericImage() { m_data = new Data( this ); } /*! * Copy constructor. Constructs a local image to store a copy of the * currently selected pixel samples in a local or shared image. The behavior * of this constructor depends on the current selections and storage type of * the specified source \a image: * * \li If the source object is a shared image, then this constructor will * create a duplicate of its current selection as a local image. * * \li If the source object is a completely selected local image * (see the note below), then this object will reference the same image * data, and hence no duplicate of the existing pixel data will be * generated. This enables the copy-on-write (or implicit data sharing) * functionality of %GenericImage. * * \li If the source image is only partially selected, then a local image * will be initialized to store in this object a copy of the selected pixel * samples in the source image. If the selected range of channels is smaller * than the required number of nominal channels for the source color space, * then this object will be in the grayscale space. Otherwise this object * will inherit the color space of the source \a image. * * \note In a completely selected image, the current rectangular selection * includes the entire image boundaries, and the range of selected channels * comprises all existing channels, including all nominal and alpha * channels. By default, all images are completely selected. A complete * selection is also enabled after calling the inherited * ImageGeometry::ResetSelections() member function. */ GenericImage( const GenericImage& image ) { if ( !image.IsShared() ) if ( image.IsCompletelySelected() ) { image.m_data->Attach( this ); m_data = image.m_data; m_status = image.m_status; ResetSelections(); return; } m_data = new Data( this ); (void)Assign( image ); } /*! * Move constructor. */ GenericImage( GenericImage&& image ) : AbstractImage( image ) , m_data( image.m_data ) { image.m_data = nullptr; } /*! * Constructs a local image to store a copy of the specified \a image, which * belongs to a different template instantiation. * * This constructor generates a uniquely referenced duplicate of the current * selection of pixel samples in the specified \a image, converted to the * sample type of this template instantiation. * * \note Don't confuse this constructor with the copy constructor for * %GenericImage. Note that the source \a image argument is a reference to a * different template instantiation of %GenericImage. */ template GenericImage( const GenericImage& image ) { m_data = new Data( this ); (void)Assign( image ); } /*! * Constructs a local image to store a copy of a subset of pixel samples in * the specified \a image. * * \param image Source image. * * \param rect Source rectangular selection. If this parameter defines an * empty rectangle, the current rectangular selection in the * source \a image will be used. If the specified rectangle * is not empty and extends beyond the boundaries of the * source \a image, only the intersection with the source * \a image will be used. If that intersection does not * exist, this constructor will initialize an empty image. * * \param firstChannel Zero-based index of the first channel to copy. If * a negative index is specified, the first selected channel * in the source \a image will be used. If the specified * index is larger than or equal to the number of existing * channels in the source \a image, this constructor will * initialize an empty image. The default value is -1. * * \param lastChannel Zero-based index of the last channel to copy. If * a negative index is specified, the last selected channel * in the source \a image will be used. If the specified * index is larger than or equal to the number of existing * channels in the source \a image, this constructor will * initialize an empty image. The default value is -1. * * If the specified source \a image and this object use different pixel * sample types, this constructor will use pixel traits primitives to * perform all the necessary data type conversions between different pixel * sample types transparently. * * \note This constructor will always generate a uniquely referenced, local * image, irrespective of whether the \a rect, \a firstChannel and * \a lastChannel parameters define a complete or partial selection for the * specified source \a image. To make use of the implicit data sharing * mechanism of %GenericImage, use the copy constructor and make sure that * the source \a image is completely selected. */ template GenericImage( const GenericImage& image, const Rect& rect, int firstChannel = -1, int lastChannel = -1 ) { m_data = new Data( this ); (void)Assign( image, rect, firstChannel, lastChannel ); } /*! * Constructs a local image with the specified dimensions and color space. * * \param width Width in pixels of the newly created image. * * \param height Height in pixels of the newly created image. * * \param colorSpace Color space. See the ColorSpace namespace for * symbolic constants. If this parameter is not specified, a * grayscale image will be constructed. * * The number of channels in the constructed image will depend on the * specified color space. Grayscale images will have a single channel. In * general, color images will have three channels. * * \note Pixel samples in the newly constructed image are not initialized by * this constructor, so the image will contain unpredictable values. */ GenericImage( int width, int height, color_space colorSpace = ColorSpace::Gray ) { m_data = new Data( this ); m_data->Allocate( width, height, ColorSpace::NumberOfNominalChannels( colorSpace ), colorSpace ); ResetSelections(); } /*! * Constructs a local image with data read from a raw-storage stream. * * \param stream Reference to an input stream where a raw image has been * previously stored (e.g. with the Write() member function). * * The input \a stream must provide sample values in the sample data type of * this image. The sample data type of an image is invariantly defined by * its template instantiation, and thus cannot be changed. * * \warning If the image stored in the input \a stream and this image use * different pixel sample data types, this function will throw an Error * exception with the appropriate error message. */ explicit GenericImage( File& stream ) { m_data = new Data( this ); Read( stream ); } /*! * Constructs a shared, client-side managed image corresponding to a * server-side image living in the PixInsight core application. * * \param handle A low-level API handle to an existing image in the * PixInsight core application. * * Shared images are alias objects referencing server-side images living * within the PixInsight core application. Shared images allocate pixel data * in the core application's heap, which we call shared memory. * * Local images exist within the user module's local heap, including all of * their pixel data. * * PCL implements an abstraction mechanism that allows you to handle local * and shared images transparently. Except in very few and special cases, * you don't have to know whether you're working with a shared or a local * image; your code will be exactly the same under both situations. */ explicit GenericImage( void* handle ) { m_data = new Data( this, handle ); ResetSelections(); } /*! * Constructs a new shared image with the specified geometry and color. * * \param width Width in pixels of the newly created image. * * \param height Height in pixels of the newly created image. * * \param colorSpace Color space. See the ColorSpace namespace for * symbolic constants. If this parameter is not specified, a * grayscale image will be constructed. * * The first \c void* parameter is strictly formal. Its value will be * ignored as its sole purpose is to distinguish this constructor from the * corresponding local storage constructor. * * The number of channels in the constructed image will depend on the * specified color space. Grayscale images will have a single channel. In * general, color images will have three channels. * * A shared image created with this constructor can be used for intermodule * communication tasks, such as file I/O operations with the FileFormat and * FileFormatInstance classes, and execution of processes with Process and * ProcessInstance. * * \note Pixel samples in the newly constructed image are not initialized by * this constructor, so the image will contain unpredictable values. */ GenericImage( void*, int width, int height, color_space colorSpace = ColorSpace::Gray ) { m_data = new Data( this, width, height, ColorSpace::NumberOfNominalChannels( colorSpace ), colorSpace ); ResetSelections(); } /*! * Destroys a %GenericImage instance. If this is a nonempty local or shared * image, the image data associated with this object are dereferenced. If * the image data become unreferenced, they are also destroyed. * * The internal allocator object does all the necessary clean-up work for * both local and shared images transparently: * * \li When a local image becomes unreferenced, all the internal data * structures are destroyed and the memory space in use to store pixel * samples is released immediately in the local heap of the calling module. * * \li When a shared image becomes unreferenced, the server-side image is * dereferenced. When the server-side shared image becomes unreferenced, it * is garbage-collected and eventually destroyed by the PixInsight core * application. */ virtual ~GenericImage() { if ( m_data != nullptr ) { DetachFromData(); m_data = nullptr; } } // ------------------------------------------------------------------------- /*! * Returns true iff this object is a shared image. Returns false if this is * a local image. * * Shared images are alias objects referencing actual images living within * the PixInsight core application. Shared images allocate pixel data in * the core application's heap, which we call shared memory. * * Local images exist within the user module's local heap, including all of * their pixel data. * * PCL implements an abstraction mechanism that allows you to handle local * and shared images transparently. Except in very few and special cases, * you don't have to know whether you're working with a shared or a local * image; your code will be exactly the same under both situations. */ bool IsShared() const noexcept { return m_data->IsShared(); } /*! * Returns true iff this object uniquely references its pixel data. Returns * false if this object is sharing its pixel data with other %GenericImage * instances. * * \note Don't confuse uniqueness of reference with local or shared storage * of images. A %GenericImage can share its data with other instances living * in the local heap of the calling module, irrespective of whether the * pixel data is local or shared. */ bool IsUnique() const noexcept { return m_data->IsUnique(); } /*! * Ensures that this image uses local pixel data storage. Returns a * reference to this image. * * If this object is already a local image, then calling this member * function has no effect. * * If the image is shared, then this function creates a local duplicate, * dereferences the previously referenced pixel data, and references the * newly created local duplicate. Upon returning, this image will uniquely * reference its pixel data. */ GenericImage& EnsureLocal() { if ( m_data->IsShared() ) { GenericImage local_; GenericImage* local = &local_; // ### Workaround to GCC 7's 'dereferencing type-punned pointer' bugs local->m_data->Allocate( m_width, m_height, m_numberOfChannels, m_colorSpace ); local->m_RGBWS = m_RGBWS; local->m_selected = m_selected; local->m_savedSelections = m_savedSelections; local->m_status = m_status; for ( int c = 0; c < m_numberOfChannels; ++c ) P::Copy( (*local)[c], m_channelData( c ), NumberOfPixels() ); local->m_data->Attach( this ); DetachFromData(); m_data = local->m_data; } return *this; } /*! * Ensures that this object uniquely references its pixel data, making a * unique copy of its referenced pixel data if necessary. Returns a * reference to this image. * * If this object is not sharing its pixel data with other %GenericImage * instances, then calling this member function has no effect. * * If this object is sharing its pixel data with other instances, then the * previously referenced pixel data are dereferenced, and a newly created * copy of the pixel data is uniquely referenced by this image. In this case * this object will always be a \e local image after calling this function: * newly created duplicates of existing pixel data are always allocated in * the local heap of the calling module. */ GenericImage& EnsureUnique() { if ( !m_data->IsUnique() ) { Data* newData = m_data->Clone( this ); DetachFromData(); m_data = newData; } return *this; } /*! * Returns a reference to the internal pixel allocator object associated * with this image. * * The type of the allocator object is a template specialization of the * PixelAllocator class for the sample data type of this image. The * allocator is responsible for dynamic allocation of pixel data, including * channel slots (pointers to pixel sample blocks) and pixel sample * blocks (used to store channel pixel data). * * The allocator must be able to handle dynamic allocation for local and * shared memory transparently. This means that the allocator object knows * whether this is a local image (allocated in the calling module's heap) or * a shared image (allocated in the PixInsight core's heap), and allows you * to manage and allocate pixel data without distinction in both cases. * * \warning \e Never use the \c new and \c delete operators with pixel data * memory blocks. Always call the Allocator() member function of * %GenericImage to get a reference to its PixelAllocator object, then use * its public member functions for all allocations and deallocations of * pixel data. */ pixel_allocator& Allocator() const noexcept { return m_allocator; } /*! * \internal Synchronizes this object with the server-side shared image, if * this is a shared client-side image. Does nothing if this is a local * image allocated in the calling module's heap. */ void Synchronize() { m_data->SynchronizeWithSharedImage(); ResetSelections(); } /*! * Allocates new pixel data with the specified geometry and color space. * Returns a reference to this image. * * \param width New image width in pixels. * * \param height New image height in pixels. * * \param numberOfChannels Number of channels. Must be greater than or * equal to the number of nominal channels corresponding to * the specified color space: one for grayscale images; three * for color images. If this parameter is not specified, a * single grayscale channel will be allocated. * * \param colorSpace New color space. See the ColorSpace namespace * for symbolic constants. If this parameter is not * specified, new image data will be allocated in the * grayscale space. * * If this object uniquely references its pixel data, then the previously * existing pixel data are destroyed, deallocated, and replaced with a newly * allocated local or shared image, preserving the storage space of this * instance. * * If this object is not unique, then the previously referenced pixel data * are dereferenced, and a newly created local image is uniquely referenced * by this object and allocated with the specified parameters. * * All image selections are reset to the default full selection after * calling this function. * * \note Newly allocated pixel samples are not initialized, so the image * will contain unpredictable values after calling this member function. */ GenericImage& AllocateData( int width, int height, int numberOfChannels = 1, color_space colorSpace = ColorSpace::Gray ) { if ( !m_data->IsUnique() ) { Data* newData = new Data( this ); DetachFromData(); m_data = newData; } m_data->Allocate( width, height, numberOfChannels, colorSpace ); ResetSelections(); return *this; } /*! * Allocates new pixel data with the specified geometry and color space. * Returns a reference to this image. * * This member function is equivalent to: * * \code * AllocateData( rect.Width(), rect.Height(), numberOfChannels, colorSpace ); * \endcode */ GenericImage& AllocateData( const Rect& rect, int numberOfChannels = 1, color_space colorSpace = ColorSpace::Gray ) { return AllocateData( rect.Width(), rect.Height(), numberOfChannels, colorSpace ); } /*! * Causes this object to reference an empty image. Returns a reference to * this image. * * If this object uniquely references its pixel data, then the pixel data * are destroyed and deallocated immediately. * * If this object is not unique, then the previously referenced pixel data * are dereferenced, and a newly created empty local image is uniquely * referenced by this object. */ GenericImage& FreeData() { if ( !m_data->IsEmpty() ) if ( m_data->IsUnique() ) m_data->Deallocate(); else { Data* newData = new Data( this ); DetachFromData(); m_data = newData; } ResetSelections(); return *this; } /*! * Forces this image to acquire the specified pixel data. Returns a * reference to this image. * * \param data Starting address of the acquired pixel data. Must be * either a null pointer, or a pointer to an array of * contiguous blocks of pixel samples. * * \param width Width in pixels of the acquired pixel data. * * \param height Height in pixels of the acquired pixel data. * * \param numberOfChannels Number of channels in the acquired data. Must * be greater than or equal to the number of nominal channels * corresponding to the specified color space: one for * grayscale images; three for color images. If this * parameter is not specified, a single grayscale channel is * assumed. * * \param colorSpace Color space of the acquired data. See the * ColorSpace namespace for symbolic constants. If this * parameter is not specified, the grayscale space is assumed * for the acquired data. * * If this object uniquely references its pixel data, then the previously * existing pixel data are destroyed, deallocated, and replaced with the * specified local or shared \a data, preserving the storage space of this * instance. * * If this object is a non-unique local image, then the previously * referenced pixel data are dereferenced, and a newly created local image * is uniquely referenced by this object and forced to own the specified * local \a data. * * If this object is a non-unique shared image, then this member function * throws an Error exception with the appropriate error message: An aliased * shared image cannot be forced to store externally allocated pixel data. * * When successful, this function resets all image selections to the default * full selection. * * \warning When calling this member function for a shared image, always * make sure that the passed \a data array and the pointed data blocks have * been allocated as shared memory. If you pass a locally-allocated array or * block to this function (e.g., a \c new'ed block), you may cause severe * heap corruption in the PixInsight core application. Similarly, if the * image is local, only locally-allocated arrays and blocks must be used. In * other words: shared and local pixel data and images cannot be mixed. * * \warning \e Never use the \c new and \c delete operators with pixel data * memory blocks. Always call the Allocator() member function of * %GenericImage to get a reference to its PixelAllocator object, then use * its public member functions for all allocations and deallocations of * pixel data. */ GenericImage& ImportData( sample** data, int width, int height, int numberOfChannels = 1, color_space colorSpace = ColorSpace::Gray ) { if ( !m_data->IsUnique() ) { if ( m_data->IsShared() ) throw Error( "GenericImage::ImportData(): Invalid operation for an aliased shared image" ); Data* newData = new Data( this ); DetachFromData(); m_data = newData; } m_data->Import( data, width, height, numberOfChannels, colorSpace ); ResetSelections(); return *this; } /*! * Releases ownership of the pixel data allocated by this image. * * Returns an array of pointers to the channel data blocks that were owned * by the image before calling this function. The returned array is a * sequence of pointers p0, p1, ..., pn-1, where each pi is the starting * address of a contiguous block containing the sample data of an image * channel, and n is the number of channels in the image, including all * nominal and alpha channels. * * After calling this function the image will be empty, but the returned * data will not be deallocated; the caller will be responsible for * deallocating it when it is no longer needed. * * If this member function is called for an aliased image, it throws an * Error exception with the appropriate error message: Pixel data cannot be * released if two or more %GenericImage instances are referencing them. * The reason for this limitation is that if the data of a non-unique image * were released, either all instances sharing the same data would be * affected, which violates the implicit data sharing paradigm, or the newly * allocated data of a unique clone would be released, which makes no sense. * * \warning When calling ReleaseData() for a shared image, always be aware * that the returned \c sample** value points to a \e shared memory block. * If you handle such a block as if it was a block within your local heap * (e.g., if you call the \c delete operator to deallocate it), you may * cause severe heap corruption. * * \warning \e Never use the \c new and \c delete operators with pixel data * memory blocks. Always call the Allocator() member function of * %GenericImage to get a reference to its PixelAllocator object, then use * its public member functions for all allocations and deallocations of * pixel data. */ sample** ReleaseData() { if ( !m_data->IsUnique() ) throw Error( "GenericImage::ReleaseData(): Invalid operation for an aliased image" ); sample** data = m_data->Release(); ResetSelections(); return data; } // ------------------------------------------------------------------------- /*! * Returns the size in bytes of a row of pixels in this image (also known as * a scan line). */ size_type LineSize() const noexcept { return BytesPerSample() * size_type( m_width ); } /*! * Returns the size in bytes of a channel of this image. This is equal to * the area in square pixels multiplied by BytesPerSample(). */ size_type ChannelSize() const noexcept { return BytesPerSample() * NumberOfPixels(); } /*! * Returns the size in bytes of all memory blocks required to store the * pixel data in this image, including nominal and alpha channels. This is * equal to the area in square pixels multiplied by the number of channels, * multiplied by BytesPerSample(). */ size_type ImageSize() const noexcept { return ChannelSize() * size_type( m_numberOfChannels ); } /*! * Returns the size in bytes of all memory blocks required to store the * pixel data in the nominal channels of this image (i.e., \e excluding * alpha channels). */ size_type NominalSize() const noexcept { return ChannelSize() * NumberOfNominalChannels(); } /*! * Returns the size in bytes of all memory blocks required to store the * pixel data in the alpha channels of this image (i.e., \e excluding * nominal channels). Returns zero if this image has no alpha channels. */ size_type AlphaSize() const noexcept { return ChannelSize() * NumberOfAlphaChannels(); } /*! * Returns a pointer to the first pixel sample in the specified \a channel. * * \param channel Channel index, 0 ≤ \a channel < \a n, where \a n is * the number of channels in this image, including nominal * and alpha channels. The default value is zero. * * The returned value is the address of the pixel at coordinates x=y=0 in * the specified \a channel. * * If this image is aliased, it is made unique before returning from this * member function. This involves making a local duplicate of the previously * referenced pixel data. */ sample* PixelData( int channel = 0 ) { PCL_PRECONDITION( 0 <= channel && channel < m_numberOfChannels ) EnsureUnique(); return m_channelData( channel ); } /*! * Returns a pointer to the constant first pixel sample in the specified * \a channel. * * This member function is the immutable counterpart of PixelData( int ). */ const sample* PixelData( int channel = 0 ) const noexcept { PCL_PRECONDITION( 0 <= channel && channel < m_numberOfChannels ) return m_channelData( channel ); } /*! * Returns true only is this is a valid, nonempty image. */ operator bool() const noexcept { return m_data != nullptr && !m_data->IsEmpty(); } /*! * Returns a pointer to the first pixel sample in the specified \a channel. * * \param channel Channel index, 0 ≤ \a channel < \a n, where \a n is * the number of channels in this image, including nominal * and alpha channels. * * This operator is a convenience synonym for the PixelData( int ) member * function. */ sample* operator []( int channel ) { PCL_PRECONDITION( 0 <= channel && channel < m_numberOfChannels ) return PixelData( channel ); } /*! * Returns a pointer to the constant first pixel sample in the specified * \a channel. * * This operator function is the immutable counterpart of operator []( int ). */ const sample* operator []( int channel ) const noexcept { PCL_PRECONDITION( 0 <= channel && channel < m_numberOfChannels ) return PixelData( channel ); } /*! * Returns a pointer to the first pixel sample in the first channel of this * image. * * For grayscale images, this function can be more readable and elegant than * operator []( int ). */ sample* operator *() { PCL_PRECONDITION( 0 < m_numberOfChannels ) return PixelData( 0 ); } /*! * Returns a pointer to the constant first pixel sample in the first channel * of this image. * * This operator function is the immutable counterpart of operator *(). */ const sample* operator *() const noexcept { PCL_PRECONDITION( 0 < m_numberOfChannels ) return PixelData( 0 ); } /*! * Returns the starting address of a scan line (i.e., a row of * pixels) in this image. * * \param y Vertical coordinate (or row index) of the desired scan * line, such that 0 ≤ \a y < \a h, where \a h is the * height in pixels of this image. * * \param channel Channel index, 0 ≤ \a channel < \a n, where \a n is * the number of channels in this image, including nominal * and alpha channels. The default value is zero. * * The returned value is a pointer to the sample at coordinates (x=0,y) in * the specified \a channel. * * If this image is aliased, it is made unique before returning from this * member function. This involves making a local duplicate of the previously * referenced pixel data. */ sample* ScanLine( int y, int channel = 0 ) { PCL_PRECONDITION( 0 <= channel && channel < m_numberOfChannels ) PCL_PRECONDITION( 0 <= y && y < m_height ) EnsureUnique(); return m_channelData( channel ) + RowOffset( y ); } /*! * Returns the starting address of a constant scan line (i.e., a * row of pixels) in this image. * * This member function is the immutable counterpart of ScanLine( int, int ). */ const sample* ScanLine( int y, int channel = 0 ) const noexcept { PCL_PRECONDITION( 0 <= channel && channel < m_numberOfChannels ) PCL_PRECONDITION( 0 <= y && y < m_height ) return m_channelData( channel ) + RowOffset( y ); } /*! * Returns the address of a pixel sample in this image, given by its pixel * coordinates and channel index. * * \param x Horizontal coordinate (or column index) of the desired * pixel, 0 ≤ \a x < \a w, where \a w is the width in * pixels of this image. * * \param y Vertical coordinate (or row index) of the desired scan * line, 0 ≤ \a y < \a h, where \a h is the height in * pixels of this image. * * \param channel Channel index, 0 ≤ \a channel < \a n, where \a n is * the number of channels in this image, including nominal * and alpha channels. The default value is zero. * * The returned value is a pointer to the sample at coordinates (x,y) in the * specified \a channel. * * If this image is aliased, it is made unique before returning from this * member function. This involves making a local duplicate of the previously * referenced pixel data. */ sample* PixelAddress( int x, int y, int channel = 0 ) { PCL_PRECONDITION( 0 <= channel && channel < m_numberOfChannels ) PCL_PRECONDITION( 0 <= x && x < m_width ) PCL_PRECONDITION( 0 <= y && y < m_height ) EnsureUnique(); return m_channelData( channel ) + PixelOffset( x, y ); } /*! * Returns the address of a constant pixel sample in this image, given by * its pixel coordinates and channel index. * * This member function is the immutable counterpart of PixelAddress( int, int, int ). */ const sample* PixelAddress( int x, int y, int channel = 0 ) const noexcept { PCL_PRECONDITION( 0 <= channel && channel < m_numberOfChannels ) PCL_PRECONDITION( 0 <= x && x < m_width ) PCL_PRECONDITION( 0 <= y && y < m_height ) return m_channelData( channel ) + PixelOffset( x, y ); } /*! * Returns the address of a pixel sample in this image, given by its pixel * position and channel index. * * \param p Position of the desired pixel in image coordinates. The * specified point must be included in the image bounds * rectangle, that is, its coordinates must refer to an * existing pixel in this image. * * \param channel Channel index, 0 ≤ \a channel < \a n, where \a n is * the number of channels in this image, including nominal * and alpha channels. The default value is zero. * * The returned value is a pointer to the sample at coordinates (p.x,p.y) in * the specified \a channel. * * If this image is aliased, it is made unique before returning from this * member function. This involves making a local duplicate of the previously * referenced pixel data. */ sample* PixelAddress( const Point& p, int channel = 0 ) { return PixelAddress( p.x, p.y, channel ); } /*! * Returns the address of a constant pixel sample in this image, given by * its pixel position and channel index. * * This member function is the immutable counterpart of * PixelAddress( const Point&, int ). */ const sample* PixelAddress( const Point& p, int channel = 0 ) const noexcept { return PixelAddress( p.x, p.y, channel ); } /*! * Returns a reference to a pixel sample in this image, given by its pixel * coordinates and channel index. * * \param x Horizontal coordinate (or column index) of the desired * pixel, 0 ≤ \a x < \a w, where \a w is the width in * pixels of this image. * * \param y Vertical coordinate (or row index) of the desired scan * line, 0 ≤ \a y < \a h, where \a h is the height in * pixels of this image. * * \param channel Channel index, 0 ≤ \a channel < \a n, where \a n is * the number of channels in this image, including nominal * and alpha channels. The default value is zero. * * If this image is aliased, it is made unique before returning from this * member function. This involves making a local duplicate of the previously * referenced pixel data. */ sample& operator ()( int x, int y, int channel = 0 ) { return *PixelAddress( x, y, channel ); } /*! * Returns a pixel sample value, given by its pixel coordinates and channel * index. * * \param x Horizontal coordinate (or column index) of the desired * pixel, 0 ≤ \a x < \a w, where \a w is the width in * pixels of this image. * * \param y Vertical coordinate (or row index) of the desired scan * line, 0 ≤ \a y < \a h, where \a h is the height in * pixels of this image. * * \param channel Channel index, 0 ≤ \a channel < \a n, where \a n is * the number of channels in this image, including nominal * and alpha channels. The default value is zero. */ sample operator ()( int x, int y, int channel = 0 ) const noexcept { return *PixelAddress( x, y, channel ); } /*! * Returns a reference to a pixel sample in this image, given by its pixel * position and channel index. * * \param p Position of the desired pixel in image coordinates. The * specified point must be included in the image bounds * rectangle, that is, its coordinates must refer to an * existing pixel in this image. * * \param channel Channel index, 0 ≤ \a channel < \a n, where \a n is * the number of channels in this image, including nominal * and alpha channels. The default value is zero. * * If this image is aliased, it is made unique before returning from this * member function. This involves making a local duplicate of the previously * referenced pixel data. */ sample& operator ()( const Point& p, int channel = 0 ) { return *PixelAddress( p, channel ); } /*! * Returns a pixel sample value, given by its pixel position and channel * index. * * \param p Position of the desired pixel in image coordinates. The * specified point must be included in the image bounds * rectangle, that is, its coordinates must refer to an * existing pixel in this image. * * \param channel Channel index, 0 ≤ \a channel < \a n, where \a n is * the number of channels in this image, including nominal * and alpha channels. The default value is zero. */ sample operator ()( const Point& p, int channel = 0 ) const noexcept { return *PixelAddress( p, channel ); } /*! * Returns a reference to a pixel sample in this image, given by its pixel * coordinates and channel index. * * \deprecated This member function has been deprecated. Use * GenericImage::operator()( int, int, int ) in newly produced code. */ sample& Pixel( int x, int y, int channel = 0 ) { return operator()( x, y, channel ); } /*! * Returns a pixel sample value, given by its pixel coordinates and channel * index. * * \deprecated This member function has been deprecated. Use * GenericImage::operator()( int, int, int ) const in newly produced code. */ sample Pixel( int x, int y, int channel = 0 ) const noexcept { return operator()( x, y, channel ); } /*! * Returns a reference to a pixel sample in this image, given by its pixel * position and channel index. * * \deprecated This member function has been deprecated. Use * GenericImage::operator()( const Point&, int ) in newly produced code. */ sample& Pixel( const Point& p, int channel = 0 ) { return operator()( p, channel ); } /*! * Returns a pixel sample value, given by its pixel position and channel * index. * * \deprecated This member function has been deprecated. Use * GenericImage::operator()( const Point&, int ) const in newly produced * code. */ sample Pixel( const Point& p, int channel = 0 ) const noexcept { return operator()( p, channel ); } // ------------------------------------------------------------------------- /*! * Associates an RGB working space \a RGBWS with this image. * * \note For shared images (i.e. images living in the PixInsight core * application), the RGB working space cannot be changed by calling this * member function. */ void SetRGBWorkingSpace( const RGBColorSystem& RGBWS ) override { if ( !IsShared() ) { EnsureUnique(); m_RGBWS = RGBWS; } } // ------------------------------------------------------------------------- /*! * Copies pixel samples from the specified source \a image to this image. * Returns a reference to this image. * * \param image Source image. * * \param rect Source rectangular selection. If this parameter defines an * empty rectangle, the current rectangular selection in the * source \a image will be used. If the specified rectangle * is not empty and extends beyond source \a image * boundaries, only the intersection with the source \a image * will be used. If that intersection does not exist, then * this image will be empty after calling this member * function. The default value is an empty rectangle. * * \param firstChannel Zero-based index of the first channel to copy. If a * negative value is specified, the first selected channel in * the source \a image will be used. If the specified value * is larger than or equal to the number of existing channels * in the source \a image, then this image will be empty * after calling this function. The default value is -1. * * \param lastChannel Zero-based index of the last channel to copy. If a * negative value is specified, the last selected channel in * the source \a image will be used. If the specified value * is larger than or equal to the number of existing channels * in the source \a image, then this image will be empty * after calling this function. The default value is -1. * * Previously referenced data in this image will be dereferenced, and * immediately destroyed if they become unreferenced. * * If this image and the specified source \a image are (a) compatible in * terms of pixel sample types, (b) both are local images, and (c) the * source \a image is completely selected, then this member function just * references the same pixel data, enabling the implicit data sharing * mechanism of %GenericImage. In this case, existing pixel data in the * source \a image are not duplicated, and no allocation is performed. * * If this member function causes a reallocation of pixel data, the newly * allocated data will be uniquely referenced by this image. * * The source \a image can be an instance of any supported template * instantiation of %GenericImage, not necessarily of the same instantiation * than this image. All the necessary data type conversions are performed * transparently. If pixel sample types are different, source data are * transformed to the data type of this image. This involves conversions * from the source data type P1::sample to the target data type P::sample, * as per pixel traits primitives. * * \note It is very important to point out that the storage class of an * image cannot be changed with this member function, even if this image and * the source image belong to the same template instantiation. For example: * * \code * Image aSharedImage( (void*)0, 0, 0 ); * ... * Image local; * ... * local.Assign( aSharedImage ); // local continues being a local image. * // It now stores a copy of aSharedImage's * // pixel data. * local = aSharedImage; // Same as above, since the assignment operator is * // just an alias for the Assign() function. * * ImageVariant otherShared; * otherShared.CreateSharedFloatImage(); * otherShared.AssignImage( local ); // otherShared is still a shared image. * \endcode */ template GenericImage& Assign( const GenericImage& image, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { m_status = image.Status(); Rect r = rect; if ( !image.ParseSelection( r, firstChannel, lastChannel ) ) { FreeData(); return *this; } int n = 1 + lastChannel - firstChannel; AllocateData( r, n, (firstChannel == 0 && n >= ColorSpace::NumberOfNominalChannels( image.ColorSpace() )) ? image.ColorSpace() : ColorSpace::Gray ); if ( !IsShared() ) // ### cannot modify a shared image's RGBWS m_RGBWS = image.RGBWorkingSpace(); ResetSelections(); if ( r == image.Bounds() ) for ( int c = 0; firstChannel <= lastChannel; ++c, ++firstChannel ) P::Copy( m_channelData( c ), image[firstChannel], NumberOfPixels() ); else for ( int c = 0; firstChannel <= lastChannel; ++c, ++firstChannel ) { sample* f = m_channelData( c ); const typename P1::sample* g = image.PixelAddress( r.LeftTop(), firstChannel ); for ( int y = 0; y < m_height; ++y, f += m_width, g += image.Width() ) P::Copy( f, g, m_width ); } return *this; } GenericImage& Assign( const GenericImage& image, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { m_status = image.Status(); Rect r = rect; if ( !image.ParseSelection( r, firstChannel, lastChannel ) ) { FreeData(); return *this; } #define completeSelection (firstChannel == 0 && lastChannel == image.m_numberOfChannels-1 && r == image.Bounds()) if ( m_data == image.m_data ) { // Self-assignment if ( !completeSelection ) { GenericImage result( image, r, firstChannel, lastChannel ); // ### implicit recursion result.m_data->Attach( this ); DetachFromData(); m_data = result.m_data; } ResetSelections(); return *this; } if ( !IsShared() ) if ( !image.IsShared() ) if ( completeSelection ) { image.m_data->Attach( this ); DetachFromData(); m_data = image.m_data; ResetSelections(); return *this; } #undef completeSelection int n = 1 + lastChannel - firstChannel; AllocateData( r, n, (firstChannel == 0 && n >= ColorSpace::NumberOfNominalChannels( image.ColorSpace() )) ? image.ColorSpace() : ColorSpace::Gray ); if ( !IsShared() ) // ### cannot modify a shared image's RGBWS m_RGBWS = image.m_RGBWS; ResetSelections(); if ( r == image.Bounds() ) for ( int c = 0; firstChannel <= lastChannel; ++c, ++firstChannel ) P::Copy( m_channelData( c ), image[firstChannel], NumberOfPixels() ); else for ( int c = 0; firstChannel <= lastChannel; ++c, ++firstChannel ) { sample* f = m_channelData( c ); const sample* g = image.PixelAddress( r.LeftTop(), firstChannel ); for ( int y = 0; y < m_height; ++y, f += m_width, g += image.m_width ) P::Copy( f, g, m_width ); } return *this; } /*! * Copies pixel samples from the specified source \a image to this image. * Returns a reference to this image. * * This operator is a convenience synonym for Assign(). */ template GenericImage& operator =( const GenericImage& image ) { return Assign( image ); } /*! * Copy assignment operator. Copies pixel samples from the specified source * \a image to this image. Returns a reference to this image. * * This operator is a synonym for Assign(), where both this object and the * source \a image are instances of the same template instantiation of * %GenericImage. The declaration of this operator is necessary for * optimization purposes. It also provides a pure copy assignment operator * for the %GenericImage template class. */ GenericImage& operator =( const GenericImage& image ) { return Assign( image ); } #define TRANSFER_BODY() \ if ( m_data != image.m_data ) \ { \ DetachFromData(); \ m_data = image.m_data; \ (void)AbstractImage::operator =( image ); \ image.m_data = nullptr; \ } \ return *this /*! * Transfers data from another \a image to this object. Returns a reference * to this image. * * Decrements the reference counter of the current image data, and destroys * it if it becomes unreferenced. * * Transfers the source image to this object, leaving empty and invalid the * source \a image, which cannot be used and must be destroyed immediately * after calling this function. */ GenericImage& Transfer( GenericImage& image ) { TRANSFER_BODY(); } /*! * Transfers data from another \a image to this object. Returns a reference * to this image. * * Decrements the reference counter of the current image data, and destroys * it if it becomes unreferenced. * * Transfers the source image to this object, leaving empty and invalid the * source \a image, which cannot be used and must be destroyed immediately * after calling this function. */ GenericImage& Transfer( GenericImage&& image ) { TRANSFER_BODY(); } #undef TRANSFER_BODY /*! * Move assignment operator. Returns a reference to this image. * * This operator is equivalent to Transfer( GenericImage& ). */ GenericImage& operator =( GenericImage&& image ) { return Transfer( image ); } /*! * Fills the current selection (selection rectangle and channel range) with * a \a scalar. Returns a reference to this image. * * This operator is a convenience synonym for Fill(). */ GenericImage& operator =( sample scalar ) { return Fill( scalar ); } /*! * Exchanges two images \a x1 and \a x2 of the same template instantiation. */ friend void Swap( GenericImage& x1, GenericImage& x2 ) noexcept { x1.AbstractImage::Swap( x2 ); pcl::Swap( x1.m_data, x2.m_data ); } // ------------------------------------------------------------------------- #ifndef __PCL_NO_VECTOR_IMAGE_CONVERSION /*! * Generates a vector of pixel samples from a row of this image. * * \param y Vertical coordinate (or row index) of the desired scan * line, 0 ≤ \a y < \a h, where \a h is the height in * pixels of this image. * * \param channel Channel index. If this parameter is negative, the * currently selected channel will be used. The default * value is -1. * * The number of components in the returned vector will be equal to the * width in pixels of this image. * * If this image is empty, if the specified vertical coordinate is out of * range, or if a nonexistent channel is specified, this member function * returns an empty vector. */ sample_vector RowVector( int y, int channel = -1 ) const { if ( channel < 0 ) channel = m_channel; if ( y < 0 || y >= m_height || channel < 0 || channel >= m_numberOfChannels ) return sample_vector(); sample_vector row( m_width ); P::Get( row.Begin(), ScanLine( y, channel ), m_width ); return row; } /*! * Generates a vector of pixel samples from a column of this image. * * \param x Horizontal coordinate (or column index) of the desired * column, 0 ≤ \a x < \a w, where \a w is the width in * pixels of this image. * * \param channel Channel index. If this parameter is negative, the * currently selected channel will be used. The default * value is -1. * * The number of components in the returned vector will be equal to the * height in pixels of this image. * * If this image is empty, if the specified horizontal coordinate is out of * range, or if a nonexistent channel is specified, this member function * returns an empty vector. */ sample_vector ColumnVector( int x, int channel = -1 ) const { if ( channel < 0 ) channel = m_channel; if ( x < 0 || x >= m_width || channel < 0 || channel >= m_numberOfChannels ) return sample_vector(); sample_vector col( m_height ); const sample* v = PixelAddress( x, 0, channel ); for ( int y = 0; y < m_height; ++y, v += m_width ) col[y] = *v; return col; } /*! * A convenience synonym for ColumnVector( int, int ) const. */ sample_vector ColVector( int x, int channel = 0 ) const { return ColumnVector( x, channel ); } #endif // !__PCL_NO_VECTOR_IMAGE_CONVERSION /*! * Stores a duplicate of a row of pixel samples in the specified array, * with implicit data type conversion. * * \param[out] buffer Starting address of a contiguous block where samples * from the source row will be converted and copied. The * length of this block must be at least equal to the * width in pixels of this image. * * \param y Vertical coordinate (or row index) of the desired scan * line, 0 ≤ \a y < \a h, where \a h is the height in * pixels of this image. * * \param channel Channel index. If this parameter is negative, the * currently selected channel will be used. The default * value is -1. * * If this image is empty, if the specified vertical coordinate is out of * range, or if a nonexistent channel is specified, this member function * does nothing. */ template void GetRow( T* buffer, int y, int channel = -1 ) const { PCL_PRECONDITION( buffer != 0 ) if ( channel < 0 ) channel = m_channel; if ( y >= 0 && y < m_height && channel >= 0 && channel < m_numberOfChannels ) P::Get( buffer, ScanLine( y, channel ), m_width ); } /*! * Stores a duplicate of a column of pixel samples in the specified array, * with implicit data type conversion. * * \param[out] buffer Starting address of a contiguous block where samples * from the source column will be converted and copied. * The length of this block must be at least equal to the * height in pixels of this image. * * \param x Horizontal coordinate (or column index) of the desired * column, 0 ≤ \a x < \a w, where \a w is the width in * pixels of this image. * * \param channel Channel index. If this parameter is negative, the * currently selected channel will be used. The default * value is -1. * * If this image is empty, if the specified horizontal coordinate is out of * range, or if a nonexistent channel is specified, this member function * does nothing. */ template void GetColumn( T* buffer, int x, int channel = -1 ) const { PCL_PRECONDITION( buffer != 0 ) if ( channel < 0 ) channel = m_channel; if ( x >= 0 && x < m_width && channel >= 0 && channel < m_numberOfChannels ) { const sample* v = PixelAddress( x, 0, channel ); for ( int y = 0; y < m_height; ++y, ++buffer, v += m_width ) P::FromSample( *buffer, *v ); } } /*! * Sets a row of pixel samples from values in the specified array, with * implicit data type conversion. Returns a reference to this image. * * \param buffer Starting address of a contiguous block with source * values that will be converted and copied to the target * row in this image. The length of this block must be at * least equal to the width in pixels of this image. * * \param y Vertical coordinate (or row index) of the desired scan * line, 0 ≤ \a y < \a h, where \a h is the height in * pixels of this image. * * \param channel Channel index. If this parameter is negative, the * currently selected channel will be used. The default * value is -1. * * If this image is empty, if the specified vertical coordinate is out of * range, or if a nonexistent channel is specified, this member function * does nothing. */ template GenericImage& SetRow( const T* buffer, int y, int channel = -1 ) { PCL_PRECONDITION( buffer != 0 ) if ( channel < 0 ) channel = m_channel; if ( y >= 0 && y < m_height && channel >= 0 && channel < m_numberOfChannels ) P::Copy( ScanLine( y, channel ), buffer, m_width ); return *this; } /*! * Sets a column of pixel samples from values in the specified array, with * implicit data type conversion. Returns a reference to this image. * * \param buffer Starting address of a contiguous block with source * values that will be converted and copied to the target * column in this image. The length of this block must be * at least equal to the height in pixels of this image. * * \param x Horizontal coordinate (or column index) of the desired * column, 0 ≤ \a x < \a w, where \a w is the width in * pixels of this image. * * \param channel Channel index. If this parameter is negative, the * currently selected channel will be used. The default * value is -1. * * If this image is empty, if the specified horizontal coordinate is out of * range, or if a nonexistent channel is specified, this member function * does nothing. */ template GenericImage& SetColumn( const T* buffer, int x, int channel = -1 ) { PCL_PRECONDITION( buffer != 0 ) if ( channel < 0 ) channel = m_channel; if ( x >= 0 && x < m_width && channel >= 0 && channel < m_numberOfChannels ) { sample* v = PixelAddress( x, 0, channel ); for ( int y = 0; y < m_height; ++y, ++buffer, v += m_width ) *v = P::ToSample( *buffer ); } return *this; } // ------------------------------------------------------------------------- /*! * Creates \a n new alpha channels in this image. Alpha channels are those * in excess of nominal channels, e.g. a fourth channel in an RGB color * image, or a second channel in a grayscale image. Returns a reference to * this image. * * Newly created channels are not initialized, so their pixel samples will * contain unpredictable values. */ GenericImage& CreateAlphaChannels( int n ) { if ( n > 0 && m_numberOfChannels > 0 ) { EnsureUnique(); sample** oldData = m_pixelData; sample** newData = nullptr; try { newData = m_allocator.AllocateChannelSlots( m_numberOfChannels+n ); for ( int i = 0; i < m_numberOfChannels; ++i ) newData[i] = oldData[i]; for ( int i = 0; i < n; ++i ) newData[m_numberOfChannels+i] = m_allocator.AllocatePixels( m_width, m_height ); } catch ( ... ) { if ( newData != nullptr ) { for ( int i = 0; i < n; ++i ) if ( newData[m_numberOfChannels+i] != nullptr ) m_allocator.Deallocate( newData[m_numberOfChannels+i] ); m_allocator.Deallocate( newData ); } throw; } m_allocator.SetSharedData( m_pixelData = newData ); m_allocator.SetSharedGeometry( m_width, m_height, m_numberOfChannels += n ); m_allocator.Deallocate( oldData ); } return *this; } /*! * Forces this image to acquire the specified pixel sample data as a new * alpha channel. Returns a reference to this image. * * \param data Starting address of the acquired pixel data. If this * parameter is a null pointer, a single new alpha channel * will be added to the image. * * The image will own the acquired pixel \a data. The new alpha channel is * appended to the existing channels. * * \warning See the ImportData() member function documentation for an * important warning notice that applies also to this function. */ GenericImage& AddAlphaChannel( sample* data = nullptr ) { // $$$ WARNING $$$ // * If this is a shared image, data must either be null or point to a // shared memory block. // * If this is a local image, data must either be null or point to a // block allocated in the local heap. if ( data == nullptr ) CreateAlphaChannels( 1 ); else if ( m_numberOfChannels > 0 ) { EnsureUnique(); sample** oldData = m_pixelData; sample** newData = nullptr; try { newData = m_allocator.AllocateChannelSlots( m_numberOfChannels+1 ); for ( int i = 0; i < m_numberOfChannels; ++i ) newData[i] = oldData[i]; newData[m_numberOfChannels] = data; } catch ( ... ) { if ( newData != nullptr ) m_allocator.Deallocate( newData ); throw; } m_allocator.SetSharedData( m_pixelData = newData ); m_allocator.SetSharedGeometry( m_width, m_height, ++m_numberOfChannels ); m_allocator.Deallocate( oldData ); } return *this; } /*! * Releases an alpha channel into another image. Returns a reference to this * image. * * \param image Image that will receive the data from the released alpha * channel. Its previous contents will be dereferenced and * eventually destroyed. * * \param channel Zero-based index of the alpha channel to be released, * 0 ≤ \a channel < \a n, where \a n is the number of * existing alpha channels before calling this function. * * \note The specified \a channel index is an alpha channel index, * not a normal channel index comprising nominal channels. That is, the * first alpha channel is indexed as zero by this function. * * \warning If this image is a shared instance, then the specified target * \a image must also be a reference to a shared image. Correspondingly, if * this is a local image, the target \a image must also be local. Pixel data * blocks cannot be transferred between local and shared images. If this * rule is violated, this member function will throw an Error exception. */ GenericImage& ReleaseAlphaChannel( GenericImage& image, int channel ) { if ( IsShared() != image.IsShared() ) throw Error( "GenericImage::ReleaseAlphaChannel(): Cannot release pixel data between local and shared images" ); if ( channel < 0 || channel >= NumberOfAlphaChannels() ) { image.FreeData(); return *this; } int c = NumberOfNominalChannels() + channel; sample** newData = nullptr; try { newData = image.m_allocator.AllocateChannelSlots( 1 ); *newData = m_pixelData[c]; } catch ( ... ) { if ( newData != nullptr ) image.m_allocator.Deallocate( newData ); throw; } image.FreeData(); image.m_pixelData = newData; image.m_width = m_width; image.m_height = m_height; image.m_numberOfChannels = 1; image.m_colorSpace = ColorSpace::Gray; image.m_data->UpdateSharedImage(); image.ResetSelections(); m_pixelData[c] = nullptr; ForgetAlphaChannel( channel ); return *this; } /*! * Destroys an alpha channel. Returns a reference to this image. * * \param channel Zero-based index of the alpha channel to be destroyed, * 0 ≤ \a c < \a n, where \a n is the number of existing * alpha channels before calling this function. * * \note The specified \a channel index is an alpha channel index, * not a normal channel index comprising nominal channels. That is, the * first alpha channel is indexed as zero by this function. */ GenericImage& DeleteAlphaChannel( int channel ) { if ( channel >= 0 && channel < NumberOfAlphaChannels() ) { EnsureUnique(); int c = NumberOfNominalChannels() + channel; m_allocator.Deallocate( m_pixelData[c] ); m_pixelData[c] = nullptr; ForgetAlphaChannel( channel ); } return *this; } /*! * Forces this image to discard an existing alpha channel without * deallocating it. Returns a reference to this image. * * \param channel Zero-based index of the alpha channel to be forgotten, * 0 ≤ \a c < \a n, where \a n is the number of existing * alpha channels before calling this function. * * \warning You should have gained control over the pixel data corresponding * to the forgotten \a channel before calling this function, in order to be * able to deallocate it when appropriate, or you'll induce a memory leak. * * \note The specified \a channel index is an alpha channel index, * not a normal channel index comprising nominal channels. That is, the * first alpha channel is indexed as zero by this function. */ GenericImage& ForgetAlphaChannel( int channel ) { if ( channel >= 0 && channel < NumberOfAlphaChannels() ) { EnsureUnique(); sample** oldData = m_pixelData; sample** newData = m_allocator.AllocateChannelSlots( m_numberOfChannels-1 ); int n0 = NumberOfNominalChannels(); int c = n0 + channel; for ( int i = 0; i < c; ++i ) newData[i] = oldData[i]; for ( int i = c, j = c; ++j < m_numberOfChannels; ++i ) newData[i] = oldData[j]; m_allocator.SetSharedData( m_pixelData = newData ); m_allocator.SetSharedGeometry( m_width, m_height, --m_numberOfChannels ); if ( m_channel >= n0 || m_lastChannel >= n0 ) ResetChannelRange(); m_allocator.Deallocate( oldData ); } return *this; } /*! * Destroys all existing alpha channels in this image. Returns a reference * to this image. */ GenericImage& DeleteAlphaChannels() { int n0 = NumberOfNominalChannels(); int n = m_numberOfChannels; if ( n > n0 ) { EnsureUnique(); do m_allocator.Deallocate( m_pixelData[--n] ), m_pixelData[n] = nullptr; while ( n > n0 ); ForgetAlphaChannels(); } return *this; } /*! * Forces this image to discard all existing alpha channels without * deallocating them. Returns a reference to this image. * * \warning You should have gained control over the pixel data for all alpha * channels in this image before calling this function, in order to be able * to deallocate them when appropriate, or you'll induce a memory leak. */ GenericImage& ForgetAlphaChannels() { int n0 = NumberOfNominalChannels(); if ( m_numberOfChannels > n0 ) { EnsureUnique(); sample** oldData = m_pixelData; sample** newData = m_allocator.AllocateChannelSlots( n0 ); for ( int i = 0; i < n0; ++i ) newData[i] = oldData[i]; m_allocator.SetSharedData( m_pixelData = newData ); m_allocator.SetSharedGeometry( m_width, m_height, m_numberOfChannels = n0 ); if ( m_channel >= n0 || m_lastChannel >= n0 ) ResetChannelRange(); m_allocator.Deallocate( oldData ); } return *this; } // ------------------------------------------------------------------------- /*! * Fills a subset of pixel samples of this image with the specified * \a scalar. Returns a reference to this image. * * \param scalar Scalar value to fill with. The specified value will be * converted to the sample data type of this image. * * \param rect Target rectangular region. If this parameter defines an * empty rectangle, the current rectangular selection will be * used. If the specified rectangle is not empty and extends * beyond image boundaries, only the intersection with this * image will be used. If that intersection does not exist, * then this member function has no effect. The default value * is an empty rectangle. * * \param firstChannel Zero-based index of the first channel to fill. If a * negative index is specified, the first selected channel * will be used. If the specified index is larger than or * equal to the number of channels in this image, then this * member function has no effect. The default value is -1. * * \param lastChannel Zero-based index of the last channel to fill. If a * negative index is specified, the last selected channel * will be used. If the specified index is larger than or * equal to the number of channels in this image, then this * member function has no effect. The default value is -1. * * Previously referenced data in this image will be dereferenced, and * immediately destroyed if they become unreferenced. * * If this member function causes a reallocation of pixel data, the newly * allocated data will be local and uniquely referenced by this image. * * \note Increments the status monitoring object by the number of selected * pixel samples. */ template GenericImage& Fill( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { Rect r = rect; if ( !ParseSelection( r, firstChannel, lastChannel ) ) return *this; EnsureUnique(); size_type N = size_type( r.Width() )*size_type( r.Height() ); if ( m_status.IsInitializationEnabled() ) m_status.Initialize( "Filling image", N*(1 + lastChannel - firstChannel) ); sample v = P::ToSample( scalar ); if ( r == Bounds() ) for ( int i = firstChannel; i <= lastChannel; ++i, m_status += N ) P::Fill( m_pixelData[i], v, N ); else for ( int i = firstChannel, w = r.Width(), h = r.Height(); i <= lastChannel; ++i, m_status += N ) { sample* __restrict__ f = PixelAddress( r.LeftTop(), i ); PCL_IVDEP for ( int j = 0; j < h; ++j, f += m_width ) P::Fill( f, v, w ); } return *this; } /*! * Fills a subset of pixel samples of this image with the scalar components * of a vector of per-channel filling \a values. Returns a reference to this * image. * * \param values %Vector of filling values. Each component of this vector * is a filling value for the corresponding channel in the * pixel sample selection (i.e., the first component is the * filling value for the first selected channel, the second * component for the second selected channel, etc.). If the * vector has less components than channels in the selection, * the missing values are replaced with the minimum sample * value in the native range of the image (usually zero). * * The rest of parameters of this function are identical to those of * Fill( T, const Rect&, int, int ). */ template GenericImage& Fill( const GenericVector& values, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { Rect r = rect; if ( !ParseSelection( r, firstChannel, lastChannel ) ) return *this; EnsureUnique(); size_type N = size_type( r.Width() )*size_type( r.Height() ); if ( m_status.IsInitializationEnabled() ) m_status.Initialize( "Filling image", N*(1 + lastChannel - firstChannel) ); if ( r == Bounds() ) for ( int i = firstChannel, c = 0; i <= lastChannel; ++i, ++c, m_status += N ) { sample v = (c < values.Length()) ? P::ToSample( values[c] ) : P::MinSampleValue(); P::Fill( m_pixelData[i], v, N ); } else for ( int i = firstChannel, c = 0, w = r.Width(), h = r.Height(); i <= lastChannel; ++i, ++c, m_status += N ) { sample* __restrict__ f = PixelAddress( r.LeftTop(), i ); sample v = (c < values.Length()) ? P::ToSample( values[c] ) : P::MinSampleValue(); PCL_IVDEP for ( int j = 0; j < h; ++j, f += m_width ) P::Fill( f, v, w ); } return *this; } /*! * Returns a local image with a subset of pixel samples from this image, * filled with the specified \a scalar. * * For information on the parameters of this member function, see the * documentation for Fill( T, const Rect&, int, int ). * * \note Increments the status monitoring object of the returned image by * the number of selected pixel samples. */ template GenericImage Filled( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) const { GenericImage result( *this, rect, firstChannel, lastChannel ); (void)result.Fill( scalar ); return result; } /*! * Returns a local image with a subset of pixel samples from this image, * filled with the scalar components of a vector of per-channel filling * \a values. * * For information on the parameters of this member function, see the * documentation for Fill( const GenericVector&, const Rect&, int, int ). * * \note Increments the status monitoring object of the returned image by * the number of selected pixel samples. */ template GenericImage Filled( const GenericVector& values, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) const { GenericImage result( *this, rect, firstChannel, lastChannel ); (void)result.Fill( values ); return result; } /*! * Fills a subset of pixel samples with the constant value resulting from * converting the floating point scalar zero (0.0) to the sample data type * of the image. Returns a reference to this image. * * This member function is a convenience shortcut for: * * \code * Fill( P::ToSample( 0.0 ), rect, firstChannel, lastChannel ); * \endcode * * \note Increments the status monitoring object by the number of selected * pixel samples. */ GenericImage& Zero( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { return Fill( P::ToSample( 0.0 ), rect, firstChannel, lastChannel ); } /*! * Fills a subset of pixel samples with the constant value resulting from * converting the floating point scalar one (1.0) to the sample data type of * the image. Returns a reference to this image. * * This member function is a convenience shortcut for: * * \code * Fill( P::ToSample( 1.0 ), rect, firstChannel, lastChannel ); * \endcode * * \note Increments the status monitoring object by the number of selected * pixel samples. */ GenericImage& One( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { return Fill( P::ToSample( 1.0 ), rect, firstChannel, lastChannel ); } /*! * Fills a subset of pixel samples with the minimum sample value in the * native range of the image. Returns a reference to this image. * * This member function is a convenience shortcut for: * * \code * Fill( P::MinSampleValue(), rect, firstChannel, lastChannel ); * \endcode * * \note Increments the status monitoring object by the number of selected * pixel samples. */ GenericImage& Black( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { return Fill( P::MinSampleValue(), rect, firstChannel, lastChannel ); } /*! * Fills a subset of pixel samples with the maximum sample value in the * native range of the image. Returns a reference to this image. * * This member function is a convenience shortcut for: * * \code * Fill( P::MaxSampleValue(), rect, firstChannel, lastChannel ); * \endcode * * \note Increments the status monitoring object by the number of selected * pixel samples. */ GenericImage& White( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { return Fill( P::MaxSampleValue(), rect, firstChannel, lastChannel ); } // ------------------------------------------------------------------------- /*! * Inverts a subset of pixel samples with respect to the specified * \a scalar. Returns a reference to this image. * * The inverse of a pixel sample \a v with respect to a scalar \a r is equal * to \a r - \a v. Normally (but not necessarily) the reference scalar used * is the maximum sample value in the native range of the image. * * For information on the rest of parameters of this member function, see * the documentation for Fill(). * * \note Increments the status monitoring object by the number of selected * pixel samples. */ template GenericImage& Invert( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { Rect r = rect; if ( !ParseSelection( r, firstChannel, lastChannel ) ) return *this; EnsureUnique(); size_type N = size_type( r.Width() )*size_type( r.Height() ); if ( m_status.IsInitializationEnabled() ) m_status.Initialize( "Inverting pixel samples", N*(1 + lastChannel - firstChannel) ); sample v = P::ToSample( scalar ); if ( r == Bounds() ) for ( int i = firstChannel; i <= lastChannel; ++i, m_status += N ) { sample* __restrict__ f = m_pixelData[i]; PCL_IVDEP for ( size_type j = 0; j < N; ++j, ++f ) *f = v - *f; } else for ( int i = firstChannel, w = r.Width(), h = r.Height(); i <= lastChannel; ++i, m_status += N ) { sample* __restrict__ f = PixelAddress( r.LeftTop(), i ); PCL_IVDEP for ( int j = 0; j < h; ++j, f += m_width-w ) { PCL_IVDEP for ( int k = 0; k < w; ++k, ++f ) *f = v - *f; } } return *this; } /*! * Returns a local image with a subset of pixel samples from this image, * inverted with respect to the specified \a scalar. * * For information on the rest of parameters of this member function, see * the documentation for Fill(). * * \note Increments the status monitoring object of the returned image by * the number of selected pixel samples. */ template GenericImage Inverted( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) const { GenericImage result( *this, rect, firstChannel, lastChannel ); (void)result.Invert( scalar ); return result; } /*! * Inverts a subset of pixel samples with respect to the maximum sample * value in the native range of the image. Returns a reference to this * image. * * This member function is a convenience shortcut for: * * \code * Invert( P::MaxSampleValue(), rect, firstChannel, lastChannel ); * \endcode * * For information on the parameters of this member function, see the * documentation for Fill(). * * \note Increments the status monitoring object by the number of selected * pixel samples. */ GenericImage& Invert( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { return Invert( P::MaxSampleValue(), rect, firstChannel, lastChannel ); } /*! * Returns a local image with a subset of pixel samples from this image, * inverted with respect to the maximum sample value in the native range of * the image. * * For information on the parameters of this member function, see the * documentation for Fill(). * * \note Increments the status monitoring object of the returned image by * the number of selected pixel samples. */ GenericImage Inverted( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) const { GenericImage result( *this, rect, firstChannel, lastChannel ); (void)result.Invert(); return result; } /*! * Returns an inverted duplicate of this image. The inversion operation is * performed on the current pixel sample selection with respect to the * maximum sample value in the native range of the image. * * \note Increments the status monitoring object of the returned image by * the number of selected pixel samples. */ GenericImage operator ~() const { GenericImage result( *this ); (void)result.Invert(); return result; } /*! * Replaces a subset of pixel samples with their bitwise logical NOT values. * Returns a reference to this image. * * For information on the parameters of this member function, see the * documentation for Fill(). * * \note Increments the status monitoring object by the number of selected * pixel samples. */ GenericImage& Not( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { Rect r = rect; if ( !ParseSelection( r, firstChannel, lastChannel ) ) return *this; EnsureUnique(); size_type N = size_type( r.Width() )*size_type( r.Height() ); if ( m_status.IsInitializationEnabled() ) m_status.Initialize( "Bitwise Not", N*(1 + lastChannel - firstChannel) ); if ( r == Bounds() ) for ( int i = firstChannel; i <= lastChannel; ++i, m_status += N ) { sample* __restrict__ f = m_pixelData[i]; PCL_IVDEP for ( size_type j = 0; j < N; ++j, ++f ) P::Not( *f ); } else for ( int i = firstChannel, w = r.Width(), h = r.Height(); i <= lastChannel; ++i, m_status += N ) { sample* __restrict__ f = PixelAddress( r.LeftTop(), i ); PCL_IVDEP for ( int j = 0; j < h; ++j, f += m_width-w ) { PCL_IVDEP for ( int k = 0; k < w; ++k, ++f ) P::Not( *f ); } } return *this; } /*! * Truncates a subset of pixel samples to the specified range of values. * Returns a reference to this image. * * \param lowerBound Lower bound of the truncation range. * \param upperBound Upper bound of the truncation range. * * The specified truncation bounds are converted to the sample data type of * this image, using pixel traits primitives. * * For information on the rest of parameters of this member function, see * the documentation for Fill(). * * \note Increments the status monitoring object by the number of selected * pixel samples. */ template GenericImage& Truncate( T lowerBound, T upperBound, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { Rect r = rect; if ( !ParseSelection( r, firstChannel, lastChannel ) ) return *this; EnsureUnique(); size_type N = size_type( r.Width() )*size_type( r.Height() ); if ( m_status.IsInitializationEnabled() ) m_status.Initialize( "Truncating pixel samples", N*(1 + lastChannel - firstChannel) ); sample b0 = P::ToSample( lowerBound ); sample b1 = P::ToSample( upperBound ); if ( b1 < b0 ) pcl::Swap( b0, b1 ); if ( r == Bounds() ) for ( int i = firstChannel; i <= lastChannel; ++i, m_status += N ) { sample* __restrict__ f = m_pixelData[i]; PCL_IVDEP for ( size_type j = 0; j < N; ++j, ++f ) if ( *f < b0 ) *f = b0; else if ( b1 < *f ) *f = b1; } else for ( int i = firstChannel, w = r.Width(), h = r.Height(); i <= lastChannel; ++i, m_status += N ) { sample* __restrict__ f = PixelAddress( r.LeftTop(), i ); PCL_IVDEP for ( int j = 0; j < h; ++j, f += m_width-w ) { PCL_IVDEP for ( int k = 0; k < w; ++k, ++f ) if ( *f < b0 ) *f = b0; else if ( b1 < *f ) *f = b1; } } return *this; } /*! * Returns a local image with a subset of pixel samples from this image, * truncated to the specified range of values. * * \param lowerBound Lower bound of the truncation range. * \param upperBound Upper bound of the truncation range. * * For information on the rest of parameters of this member function, see * the documentation for Fill(). * * \note Increments the status monitoring object of the returned image by * the number of selected pixel samples. */ template GenericImage Truncated( T lowerBound, T upperBound, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) const { GenericImage result( *this, rect, firstChannel, lastChannel ); (void)result.Truncate( lowerBound, upperBound ); return result; } /*! * Truncates a subset of pixel samples to the native range of the image. * Returns a reference to this image. * * This member function is a convenience shortcut for: * * \code * Truncate( P::MinSampleValue(), P::MaxSampleValue(), rect, firstChannel, lastChannel ); * \endcode * * For information on the parameters of this member function, see the * documentation for Fill(). * * Note that this function only makes sense for floating point real or * complex images, where pixel samples can take arbitrary values. Integer * pixel samples are constrained to their native range by nature, so calling * this function for integer images has no effect. * * \note Increments the status monitoring object by the number of selected * pixel samples. */ GenericImage& Truncate( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { return Truncate( P::MinSampleValue(), P::MaxSampleValue(), rect, firstChannel, lastChannel ); } /*! * Returns a local image with a subset of pixel samples from this image, * truncated to the native range of the image. * * For information on the parameters of this member function, see the * documentation for Fill(). * * \note Increments the status monitoring object of the returned image by * the number of selected pixel samples. */ GenericImage Truncated( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) const { GenericImage result( *this, rect, firstChannel, lastChannel ); (void)result.Truncate(); return result; } /*! * Rescales a subset of pixel samples to the specified range of values. * Returns a reference to this image. * * \param lowerBound Lower bound of the rescaling range. * \param upperBound Upper bound of the rescaling range. * * The specified rescaling bounds are converted to the sample data type of * this image, using pixel traits primitives. * * For information on the rest of parameters of this member function, see * the documentation for Fill(). * * The rescaling operation is as follows. Given the following two ranges: * * \li \c m, \c M : The range of existing sample values in the selected set of * pixel samples, * \li \c r0, \c r1 : The rescaling range, * * the rescaled value \c r for a given sample \c s is: * *

    * r = r0 + (s - m)*(r1 - r0)/(M - m)
    * 
* * \note Increments the status monitoring object by the number of selected * pixel samples. */ template GenericImage& Rescale( T lowerBound, T upperBound, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { Rect r = rect; if ( !ParseSelection( r, firstChannel, lastChannel ) ) return *this; size_type N = size_type( r.Width() )*size_type( r.Height() ); size_type Ns = N*(1 + lastChannel - firstChannel); if ( m_status.IsInitializationEnabled() ) m_status.Initialize( "Rescaling pixel samples", Ns ); sample b0 = P::ToSample( lowerBound ); sample b1 = P::ToSample( upperBound ); if ( b1 < b0 ) pcl::Swap( b0, b1 ); sample v0, v1; GetExtremePixelValues( v0, v1, r, firstChannel, lastChannel ); if ( v0 == b0 && v1 == b1 ) { m_status += Ns; return *this; } EnsureUnique(); double d = 0; if ( b0 != b1 ) if ( v0 != v1 ) d = (double( b1 ) - double( b0 ))/(double( v1 ) - double( v0 )); if ( r == Bounds() ) for ( int i = firstChannel; i <= lastChannel; ++i, m_status += N ) { sample* __restrict__ f = m_pixelData[i]; if ( v0 != v1 ) { if ( b0 != b1 ) { if ( b0 == sample( 0 ) ) { PCL_IVDEP for ( size_type j = 0; j < N; ++j, ++f ) *f = P::FloatToSample( d*(*f - v0) ); } else { PCL_IVDEP for ( size_type j = 0; j < N; ++j, ++f ) *f = P::FloatToSample( d*(*f - v0) + b0 ); } } else P::Fill( f, b0, N ); } else P::Fill( f, pcl::Range( v0, b0, b1 ), N ); } else for ( int i = firstChannel, w = r.Width(), h = r.Height(); i <= lastChannel; ++i, m_status += N ) { sample* __restrict__ f = PixelAddress( r.LeftTop(), i ); if ( v0 != v1 ) { if ( b0 != b1 ) { if ( b0 == sample( 0 ) ) { PCL_IVDEP for ( int j = 0; j < h; ++j, f += m_width-w ) { PCL_IVDEP for ( int k = 0; k < w; ++k, ++f ) *f = P::FloatToSample( d*(*f - v0) ); } } else { PCL_IVDEP for ( int j = 0; j < h; ++j, f += m_width-w ) { PCL_IVDEP for ( int k = 0; k < w; ++k, ++f ) *f = P::FloatToSample( d*(*f - v0) + b0 ); } } } else { PCL_IVDEP for ( int j = 0; j < h; ++j, f += m_width ) P::Fill( f, b0, w ); } } else { sample v = pcl::Range( v0, b0, b1 ); PCL_IVDEP for ( int j = 0; j < h; ++j, f += m_width ) P::Fill( f, v, w ); } } return *this; } /*! * Returns a local image with a subset of pixel samples from this image, * rescaled to the specified range of values. * * \param lowerBound Lower bound of the rescaling range. * \param upperBound Upper bound of the rescaling range. * * For information on the rest of parameters of this member function, see * the documentation for Fill(). * * \note Increments the status monitoring object of the returned image by * the number of selected pixel samples. */ template GenericImage Rescaled( T lowerBound, T upperBound, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) const { GenericImage result( *this, rect, firstChannel, lastChannel ); (void)result.Rescale( lowerBound, upperBound ); return result; } /*! * Rescales a subset of pixel samples to the native range of the image. * Returns a reference to this image. * * This member function is a convenience shortcut for: * * \code * Rescale( P::MinSampleValue(), P::MaxSampleValue(), rect, firstChannel, lastChannel ); * \endcode * * For information on the parameters of this member function, see the * documentation for Fill(). * * \note Increments the status monitoring object by the number of selected * pixel samples. */ GenericImage& Rescale( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { return Rescale( P::MinSampleValue(), P::MaxSampleValue(), rect, firstChannel, lastChannel ); } /*! * Returns a local image with a subset of pixel samples from this image, * rescaled to the native range of the image. * * For information on the parameters of this member function, see the * documentation for Fill(). * * \note Increments the status monitoring object of the returned image by * the number of selected pixel samples. */ GenericImage Rescaled( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) const { GenericImage result( *this, rect, firstChannel, lastChannel ); (void)result.Rescale(); return result; } /*! * Normalizes a subset of pixel samples to the specified range of values. * Returns a reference to this image. * * \param lowerBound Lower bound of the normalizing range. * \param upperBound Upper bound of the normalizing range. * * The specified normalizing bounds are converted to the sample data type of * this image, using pixel traits primitives. * * For information on the rest of parameters of this member function, see * the documentation for Fill(). * * Normalization is a conditional rescaling operation: Pixel sample values * are rescaled to the specified normalizing range only if the existing * extreme sample values exceed that range. Given the following two ranges: * * \li \c m, \c M : The range of existing sample values in the selected set of * pixel samples, * \li \c n0, \c n1 : The normalizing range, * * the normalized value \c n for a given sample \c s is: * *
    * if m < n0 or M > n1
    *    n = n0 + (s - m)*(n1 - n0)/(M - m)
    * else
    *    n = s
    * endif
    * 
* * \note Increments the status monitoring object by the number of selected * pixel samples. */ template GenericImage& Normalize( T lowerBound, T upperBound, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { Rect r = rect; if ( !ParseSelection( r, firstChannel, lastChannel ) ) return *this; size_type N = size_type( r.Width() )*size_type( r.Height() ); size_type Ns = N*(1 + lastChannel - firstChannel); if ( m_status.IsInitializationEnabled() ) m_status.Initialize( "Normalizing pixel samples", Ns ); sample b0 = P::ToSample( lowerBound ); sample b1 = P::ToSample( upperBound ); if ( b1 < b0 ) pcl::Swap( b0, b1 ); sample v0, v1; GetExtremePixelValues( v0, v1, r, firstChannel, lastChannel ); if ( v0 >= b0 && v1 <= b1 ) { m_status += Ns; return *this; } EnsureUnique(); double d = 0; if ( b0 != b1 ) if ( v0 != v1 ) d = (double( b1 ) - double( b0 ))/(double( v1 ) - double( v0 )); if ( r == Bounds() ) for ( int i = firstChannel; i <= lastChannel; ++i, m_status += N ) { sample* __restrict__ f = m_pixelData[i]; if ( v0 != v1 ) { if ( b0 != b1 ) { if ( b0 == sample( 0 ) ) { PCL_IVDEP for ( size_type j = 0; j < N; ++j, ++f ) *f = P::FloatToSample( d*(*f - v0) ); } else { PCL_IVDEP for ( size_type j = 0; j < N; ++j, ++f ) *f = P::FloatToSample( d*(*f - v0) + b0 ); } } else P::Fill( f, b0, N ); } else P::Fill( f, pcl::Range( v0, b0, b1 ), N ); } else for ( int i = firstChannel, w = r.Width(), h = r.Height(); i <= lastChannel; ++i, m_status += N ) { sample* __restrict__ f = PixelAddress( r.LeftTop(), i ); if ( v0 != v1 ) { if ( b0 != b1 ) { if ( b0 == sample( 0 ) ) { PCL_IVDEP for ( int j = 0; j < h; ++j, f += m_width-w ) { PCL_IVDEP for ( int k = 0; k < w; ++k, ++f ) *f = P::FloatToSample( d*(*f - v0) ); } } else { PCL_IVDEP for ( int j = 0; j < h; ++j, f += m_width-w ) { PCL_IVDEP for ( int k = 0; k < w; ++k, ++f ) *f = P::FloatToSample( d*(*f - v0) + b0 ); } } } else { PCL_IVDEP for ( int j = 0; j < h; ++j, f += m_width ) P::Fill( f, b0, w ); } } else { sample v = pcl::Range( v0, b0, b1 ); PCL_IVDEP for ( int j = 0; j < h; ++j, f += m_width ) P::Fill( f, v, w ); } } return *this; } /*! * Returns a local image with a subset of pixel samples from this image, * normalized to the specified range of values. * * \param lowerBound Lower bound of the normalizing range. * \param upperBound Upper bound of the normalizing range. * * For information on the rest of parameters of this member function, see * the documentation for Fill(). * * \note Increments the status monitoring object of the returned image by * the number of selected pixel samples. */ template GenericImage Normalized( T lowerBound, T upperBound, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) const { GenericImage result( *this, rect, firstChannel, lastChannel ); (void)result.Normalize( lowerBound, upperBound ); return result; } /*! * Normalizes a subset of pixel samples to the native range of the image. * Returns a reference to this image. * * This member function is a convenience shortcut for: * * \code * Normalize( P::MinSampleValue(), P::MaxSampleValue(), rect, firstChannel, lastChannel ); * \endcode * * For information on the parameters of this member function, see the * documentation for Fill(). * * \note Increments the status monitoring object by the number of selected * pixel samples. */ GenericImage& Normalize( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { return Normalize( P::MinSampleValue(), P::MaxSampleValue(), rect, firstChannel, lastChannel ); } /*! * Returns a local image with a subset of pixel samples from this image, * normalized to the native range of the image. * * For information on the parameters of this member function, see the * documentation for Fill(). * * \note Increments the status monitoring object of the returned image by * the number of selected pixel samples. */ GenericImage Normalized( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) const { GenericImage result( *this, rect, firstChannel, lastChannel ); (void)result.Normalize(); return result; } /*! * Binarizes a subset of pixel samples with respect to the specified * \a threshold value. Returns a reference to this image. * * \param threshold Binarizing threshold. * * The specified binarizing threshold is converted to the sample data type * of this image, using pixel traits primitives. * * For information on the rest of parameters of this member function, see * the documentation for Fill(). * * The binarized value \c b of a sample \c s is given by: * *
    * if s < t
    *    b = 0
    * else
    *    b = 1
    * endif
    * 
* * where \c t is the binarizing threshold value, and 0 and 1 represent, * respectively, the minimum and maximum pixel sample values in the native * range of the image. * * \note Increments the status monitoring object by the number of selected * pixel samples. */ template GenericImage& Binarize( T threshold, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { Rect r = rect; if ( !ParseSelection( r, firstChannel, lastChannel ) ) return *this; EnsureUnique(); size_type N = size_type( r.Width() )*size_type( r.Height() ); if ( m_status.IsInitializationEnabled() ) m_status.Initialize( "Binarizing pixel samples", N*(1 + lastChannel - firstChannel) ); sample t = P::ToSample( threshold ); if ( r == Bounds() ) for ( int i = firstChannel; i <= lastChannel; ++i, m_status += N ) { sample* __restrict__ f = m_pixelData[i]; PCL_IVDEP for ( size_type j = 0; j < N; ++j, ++f ) *f = (*f < t) ? P::MinSampleValue() : P::MaxSampleValue(); } else for ( int c = firstChannel, w = r.Width(), h = r.Height(); c <= lastChannel; ++c, m_status += N ) { sample* __restrict__ f = PixelAddress( r.LeftTop(), c ); PCL_IVDEP for ( int j = 0; j < h; ++j, f += m_width-w ) { PCL_IVDEP for ( int k = 0; k < w; ++k, ++f ) *f = (*f < t) ? P::MinSampleValue() : P::MaxSampleValue(); } } return *this; } /*! * Returns a local image with a subset of pixel samples from this image, * binarized with respect to the specified \a threshold value. * * For information on the rest of parameters of this member function, see * the documentation for Fill(). * * \note Increments the status monitoring object of the returned image by * the number of selected pixel samples. */ template GenericImage Binarized( T threshold, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) const { GenericImage result( *this, rect, firstChannel, lastChannel ); (void)result.Binarize( threshold ); return result; } /*! * Binarizes a subset of pixel samples with respect to the central value of * the native range of the image. Returns a reference to this image. * * This member function is a convenience shortcut for: * * \code * Binarize( (P::MinSampleValue() + P::MaxSampleValue())/2, rect, firstChannel, lastChannel ); * \endcode * * For information on the parameters of this member function, see the * documentation for Fill(). * * \note Increments the status monitoring object by the number of selected * pixel samples. */ GenericImage& Binarize( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { return Binarize( (P::MinSampleValue() + P::MaxSampleValue())/2, rect, firstChannel, lastChannel ); } /*! * Returns a local image with a subset of pixel samples from this image, * binarized with respect to the central value of the native range of the * image. * * For information on the parameters of this member function, see the * documentation for Fill(). * * \note Increments the status monitoring object of the returned image by * the number of selected pixel samples. */ GenericImage Binarized( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) const { GenericImage result( *this, rect, firstChannel, lastChannel ); (void)result.Binarize(); return result; } /*! * Replaces a subset of pixel samples with their absolute values. Returns a * reference to this image. * * For information on the parameters of this member function, see the * documentation for Fill(). * * \note Note that this member function is a no-op for unsigned integer * images (e.g., UInt8Image, UInt16Image and UInt32Image). * * \note Increments the status monitoring object by the number of selected * pixel samples. */ GenericImage& SetAbsoluteValue( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { Rect r = rect; if ( !ParseSelection( r, firstChannel, lastChannel ) ) return *this; EnsureUnique(); size_type N = size_type( r.Width() )*size_type( r.Height() ); if ( m_status.IsInitializationEnabled() ) m_status.Initialize( "Computing absolute value", N*(1 + lastChannel - firstChannel) ); if ( r == Bounds() ) for ( int i = firstChannel; i <= lastChannel; ++i, m_status += N ) { sample* __restrict__ f = m_pixelData[i]; PCL_IVDEP for ( size_type j = 0; j < N; ++j, ++f ) *f = pcl::Abs( *f ); } else for ( int i = firstChannel, w = r.Width(), h = r.Height(); i <= lastChannel; ++i, m_status += N ) { sample* __restrict__ f = PixelAddress( r.LeftTop(), i ); PCL_IVDEP for ( int j = 0; j < h; ++j, f += m_width-w ) { PCL_IVDEP for ( int k = 0; k < w; ++k, ++f ) *f = pcl::Abs( *f ); } } return *this; } /*! * A synonym for SetAbsoluteValue(). */ GenericImage& Abs( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { return SetAbsoluteValue( rect, firstChannel, lastChannel ); } /*! * Returns a local image with a subset of pixel samples from this image, * replaced with their absolute values. * * For information on the parameters of this member function, see the * documentation for Fill(). * * \note Increments the status monitoring object of the returned image by * the number of selected pixel samples. */ GenericImage AbsoluteValue( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) const { GenericImage result( *this, rect, firstChannel, lastChannel ); (void)result.SetAbsoluteValue(); return result; } // ------------------------------------------------------------------------- /* * Implementation of Apply( scalar ) member functions. We have to have a * separate implementation function to prevent infinite recursion in several * template specializations of the Apply() member function. */ template GenericImage& ApplyScalar( T scalar, image_op op = ImageOp::Mov, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { if ( op == ImageOp::Div ) if ( 1 + scalar == 1 ) throw Error( "Division by zero or insignificant scalar" ); Rect r = rect; if ( !ParseSelection( r, firstChannel, lastChannel ) ) return *this; EnsureUnique(); size_type N = size_type( r.Width() )*size_type( r.Height() ); if ( m_status.IsInitializationEnabled() ) m_status.Initialize( "Applying scalar: " + ImageOp::Id( op ) + ' ' + String( scalar ), N*(1 + lastChannel - firstChannel) ); if ( r == Bounds() ) for ( int i = firstChannel; i <= lastChannel; ++i, m_status += N ) { sample* __restrict__ f = m_pixelData[i]; #define ITERATE( Op ) \ PCL_IVDEP \ for ( size_type j = 0; j < N; ++j, ++f ) \ P::Op( *f, scalar ) switch ( op ) { case ImageOp::Mov: ITERATE( Mov ); break; case ImageOp::Add: ITERATE( Add ); break; case ImageOp::Sub: ITERATE( Sub ); break; case ImageOp::Mul: ITERATE( Mul ); break; case ImageOp::Div: ITERATE( Div ); break; case ImageOp::Pow: ITERATE( Pow ); break; case ImageOp::Dif: ITERATE( Dif ); break; case ImageOp::Min: ITERATE( Min ); break; case ImageOp::Max: ITERATE( Max ); break; case ImageOp::Not: ITERATE( Not ); break; case ImageOp::Or: ITERATE( Or ); break; case ImageOp::Nor: ITERATE( Nor ); break; case ImageOp::And: ITERATE( And ); break; case ImageOp::Nand: ITERATE( Nand ); break; case ImageOp::Xor: ITERATE( Xor ); break; case ImageOp::Xnor: ITERATE( Xnor ); break; case ImageOp::ColorBurn: ITERATE( ColorBurn ); break; case ImageOp::LinearBurn: ITERATE( LinearBurn ); break; case ImageOp::Screen: ITERATE( Screen ); break; case ImageOp::ColorDodge: ITERATE( ColorDodge ); break; case ImageOp::Overlay: ITERATE( Overlay ); break; case ImageOp::SoftLight: ITERATE( SoftLight ); break; case ImageOp::HardLight: ITERATE( HardLight ); break; case ImageOp::VividLight: ITERATE( VividLight ); break; case ImageOp::LinearLight: ITERATE( LinearLight ); break; case ImageOp::PinLight: ITERATE( PinLight ); break; case ImageOp::Exclusion: ITERATE( Exclusion ); break; default: break; } #undef ITERATE } else for ( int i = firstChannel, w = r.Width(), h = r.Height(); i <= lastChannel; ++i, m_status += N ) { sample* __restrict__ f = PixelAddress( r.LeftTop(), i ); #define ITERATE( Op ) \ PCL_IVDEP \ for ( int j = 0; j < h; ++j, f += m_width-w ) \ { \ PCL_IVDEP \ for ( int k = 0; k < w; ++k, ++f ) \ P::Op( *f, scalar ); \ } switch ( op ) { case ImageOp::Mov: ITERATE( Mov ); break; case ImageOp::Add: ITERATE( Add ); break; case ImageOp::Sub: ITERATE( Sub ); break; case ImageOp::Mul: ITERATE( Mul ); break; case ImageOp::Div: ITERATE( Div ); break; case ImageOp::Pow: ITERATE( Pow ); break; case ImageOp::Dif: ITERATE( Dif ); break; case ImageOp::Min: ITERATE( Min ); break; case ImageOp::Max: ITERATE( Max ); break; case ImageOp::Not: ITERATE( Not ); break; case ImageOp::Or: ITERATE( Or ); break; case ImageOp::Nor: ITERATE( Nor ); break; case ImageOp::And: ITERATE( And ); break; case ImageOp::Nand: ITERATE( Nand ); break; case ImageOp::Xor: ITERATE( Xor ); break; case ImageOp::Xnor: ITERATE( Xnor ); break; case ImageOp::ColorBurn: ITERATE( ColorBurn ); break; case ImageOp::LinearBurn: ITERATE( LinearBurn ); break; case ImageOp::Screen: ITERATE( Screen ); break; case ImageOp::ColorDodge: ITERATE( ColorDodge ); break; case ImageOp::Overlay: ITERATE( Overlay ); break; case ImageOp::SoftLight: ITERATE( SoftLight ); break; case ImageOp::HardLight: ITERATE( HardLight ); break; case ImageOp::VividLight: ITERATE( VividLight ); break; case ImageOp::LinearLight: ITERATE( LinearLight ); break; case ImageOp::PinLight: ITERATE( PinLight ); break; case ImageOp::Exclusion: ITERATE( Exclusion ); break; default: break; } #undef ITERATE } return *this; } /*! * Replaces a subset of pixel samples with the result of an arithmetic, * bitwise logical or pixel composition operation with a scalar. Returns a * reference to this image. * * \param scalar Right-hand operand value. * * \param op Identifies an arithmetic, bitwise logical or pixel * composition operator. For a list of supported operators, * see the ImageOp namespace. The default value is * ImageOp::Mov (replace target with source). * * For information on the rest of parameters of this member function, see * the documentation for Fill(). * * If the specified \a scalar is zero or insignificant, and the \a op * argument is ImageOp::Div, this function throws an Error exception to * prevent a division by zero. * * \note Increments the status monitoring object by the number of selected * pixel samples. */ template GenericImage& Apply( T scalar, image_op op = ImageOp::Mov, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { return ApplyScalar( scalar, op, rect, firstChannel, lastChannel ); } GenericImage& Apply( float scalar, image_op op = ImageOp::Mov, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { if ( ImageOp::IsArithmeticOperator( op ) || ImageOp::IsPixelCompositionOperator( op ) ) return ApplyScalar( scalar, op, rect, firstChannel, lastChannel ); return ApplyScalar( P::ToSample( scalar ), op, rect, firstChannel, lastChannel ); } GenericImage& Apply( double scalar, image_op op = ImageOp::Mov, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { if ( ImageOp::IsArithmeticOperator( op ) || ImageOp::IsPixelCompositionOperator( op ) ) return ApplyScalar( scalar, op, rect, firstChannel, lastChannel ); return ApplyScalar( P::ToSample( scalar ), op, rect, firstChannel, lastChannel ); } GenericImage& Apply( pcl::Complex scalar, image_op op = ImageOp::Mov, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { if ( ImageOp::IsArithmeticOperator( op ) || ImageOp::IsPixelCompositionOperator( op ) ) return ApplyScalar( scalar, op, rect, firstChannel, lastChannel ); return ApplyScalar( P::ToSample( scalar ), op, rect, firstChannel, lastChannel ); } GenericImage& Apply( pcl::Complex scalar, image_op op = ImageOp::Mov, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { if ( ImageOp::IsArithmeticOperator( op ) || ImageOp::IsPixelCompositionOperator( op ) ) return ApplyScalar( scalar, op, rect, firstChannel, lastChannel ); return ApplyScalar( P::ToSample( scalar ), op, rect, firstChannel, lastChannel ); } /*! * Creates a local image with a subset of pixel samples from this image, and * performs an arithmetic or bitwise logical operation with a scalar. * Returns the resulting image. * * \param scalar Right-hand operand value. * * \param op Identifies an arithmetic or bitwise logical operator. For * a list of supported operators, see the ImageOp namespace. * The default value is ImageOp::Mov (replace target with * source). * * For information on the rest of parameters of this member function, see * the documentation for Fill(). * * \note Increments the status monitoring object of the returned image by * the number of selected pixel samples. */ template GenericImage Applied( T scalar, image_op op = ImageOp::Mov, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) const { GenericImage result( *this, rect, firstChannel, lastChannel ); (void)result.Apply( scalar, op ); return result; } // ------------------------------------------------------------------------- /*! * Performs an arithmetic or bitwise logical operation between a subset of * pixel samples in this image and the corresponding samples of another * operand \a image. Returns a reference to this image. * * \param image Right-hand operand image. * * \param op Identifies an arithmetic or bitwise logical operator. For * a list of supported operators, see the ImageOp namespace. * The default value is ImageOp::Mov (replace target with * source). * * \param point Target point in image coordinates. This is the upper left * corner of the target rectangular region where the source * \a image will be applied to this image. If one or both * point coordinates are equal to \c int_max (the maximum * possible value of type \c int), then the current anchor * point selection will be used. The default value is * Point( int_max ). * * \param channel Target channel index (zero-based). This is the index of * the first channel where source pixel samples will be * applied. If this parameter is negative, then the currently * selected channel will be used. The default value is -1. * * For information on the rest of parameters of this member function, see * the documentation for Fill(). * * The specified operation is performed with samples in the intersection * between the specified (or implicit) rectangular selections for both * images. Each channel in the specified channel range selection for this * image is operated with its counterpart channel in the right-hand operand * \a image, starting from the first selected channel in the right-hand * operand \a image. * * The operation may involve conversions to the appropriate floating point * data type for intermediate results, or LUT table lookups, as necessary * to ensure preservation of the full native range of the image. Floating * point conversions may take place even if both images share the same * sample data type, depending on the selected operator. * * \note Increments the status monitoring object by the number of pixel * samples in the intersection between both operand images. */ template GenericImage& Apply( const GenericImage& image, image_op op = ImageOp::Mov, const Point& point = Point( int_max ), int channel = -1, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { Rect r = rect; if ( !image.ParseSelection( r, firstChannel, lastChannel ) ) return *this; if ( !ParseChannel( channel ) ) return *this; Point p = point; if ( p.x == int_max || p.y == int_max ) p = m_point; if ( p.x < 0 ) { if ( (r.x0 -= p.x) >= r.x1 ) return *this; p.x = 0; } else if ( p.x >= m_width ) return *this; if ( p.y < 0 ) { if ( (r.y0 -= p.y) >= r.y1 ) return *this; p.y = 0; } else if ( p.y >= m_height ) return *this; r.ResizeTo( pcl::Min( m_width - p.x, r.Width() ), pcl::Min( m_height - p.y, r.Height() ) ); lastChannel = pcl::Min( lastChannel, firstChannel + m_numberOfChannels - channel - 1 ); EnsureUnique(); size_type N = size_type( r.Width() )*size_type( r.Height() ); if ( m_status.IsInitializationEnabled() ) m_status.Initialize( "Applying image, op=" + ImageOp::Id( op ), N*(1 + lastChannel - firstChannel) ); if ( r == Bounds() && r == image.Bounds() ) for ( int i = channel, j = firstChannel; j <= lastChannel; ++i, ++j, m_status += N ) { sample* __restrict__ f = m_pixelData[i]; const typename P1::sample* __restrict__ g = image[j]; #define ITERATE( Op ) \ PCL_IVDEP \ for ( size_type k = 0; k < N; ++k, ++f, ++g ) \ P::Op( *f, *g ) switch ( op ) { case ImageOp::Mov: P::Copy( f, g, N ); break; case ImageOp::Min: P::CopyMin( f, g, N ); break; case ImageOp::Max: P::CopyMax( f, g, N ); break; default: switch ( op ) { case ImageOp::Add: ITERATE( Add ); break; case ImageOp::Sub: ITERATE( Sub ); break; case ImageOp::Mul: ITERATE( Mul ); break; case ImageOp::Div: for ( size_type j = 0; j < N; ++j, ++f, ++g ) if ( *g != 0 ) P::Div( *f, *g ); else *f = P::MaxSampleValue(); break; case ImageOp::Pow: ITERATE( Pow ); break; case ImageOp::Dif: ITERATE( Dif ); break; case ImageOp::Not: ITERATE( Not ); break; case ImageOp::Or: ITERATE( Or ); break; case ImageOp::Nor: ITERATE( Nor ); break; case ImageOp::And: ITERATE( And ); break; case ImageOp::Nand: ITERATE( Nand ); break; case ImageOp::Xor: ITERATE( Xor ); break; case ImageOp::Xnor: ITERATE( Xnor ); break; case ImageOp::ColorBurn: ITERATE( ColorBurn ); break; case ImageOp::LinearBurn: ITERATE( LinearBurn ); break; case ImageOp::Screen: ITERATE( Screen ); break; case ImageOp::ColorDodge: ITERATE( ColorDodge ); break; case ImageOp::Overlay: ITERATE( Overlay ); break; case ImageOp::SoftLight: ITERATE( SoftLight ); break; case ImageOp::HardLight: ITERATE( HardLight ); break; case ImageOp::VividLight: ITERATE( VividLight ); break; case ImageOp::LinearLight: ITERATE( LinearLight ); break; case ImageOp::PinLight: ITERATE( PinLight ); break; case ImageOp::Exclusion: ITERATE( Exclusion ); break; default: break; } #undef ITERATE break; } } else for ( int i = channel, j = firstChannel, w = r.Width(), h = r.Height(); j <= lastChannel; ++i, ++j, m_status += N ) { sample* __restrict__ f = PixelAddress( p, i ); const typename P1::sample* __restrict__ g = image.PixelAddress( r.LeftTop(), j ); #define ITERATE( Op ) \ PCL_IVDEP \ for ( int k = 0; k < h; ++k, f += m_width-w, g += image.Width()-w ) \ { \ PCL_IVDEP \ for ( int l = 0; l < w; ++l, ++f, ++g ) \ P::Op( *f, *g ); \ } switch ( op ) { case ImageOp::Mov: for ( int k = 0; k < h; ++k, f += m_width, g += image.Width() ) P::Copy( f, g, w ); break; case ImageOp::Min: for ( int k = 0; k < h; ++k, f += m_width, g += image.Width() ) P::CopyMin( f, g, w ); break; case ImageOp::Max: for ( int k = 0; k < h; ++k, f += m_width, g += image.Width() ) P::CopyMax( f, g, w ); break; case ImageOp::Add: ITERATE( Add ); break; case ImageOp::Sub: ITERATE( Sub ); break; case ImageOp::Mul: ITERATE( Mul ); break; case ImageOp::Div: PCL_IVDEP for ( int k = 0; k < h; ++k, f += m_width-w, g += image.Width()-w ) { PCL_IVDEP for ( int l = 0; l < w; ++l, ++f, ++g ) if ( *g != 0 ) P::Div( *f, *g ); else *f = P::MaxSampleValue(); } break; case ImageOp::Pow: ITERATE( Pow ); break; case ImageOp::Dif: ITERATE( Dif ); break; case ImageOp::Not: ITERATE( Not ); break; case ImageOp::Or: ITERATE( Or ); break; case ImageOp::Nor: ITERATE( Nor ); break; case ImageOp::And: ITERATE( And ); break; case ImageOp::Nand: ITERATE( Nand ); break; case ImageOp::Xor: ITERATE( Xor ); break; case ImageOp::Xnor: ITERATE( Xnor ); break; case ImageOp::ColorBurn: ITERATE( ColorBurn ); break; case ImageOp::LinearBurn: ITERATE( LinearBurn ); break; case ImageOp::Screen: ITERATE( Screen ); break; case ImageOp::ColorDodge: ITERATE( ColorDodge ); break; case ImageOp::Overlay: ITERATE( Overlay ); break; case ImageOp::SoftLight: ITERATE( SoftLight ); break; case ImageOp::HardLight: ITERATE( HardLight ); break; case ImageOp::VividLight: ITERATE( VividLight ); break; case ImageOp::LinearLight: ITERATE( LinearLight ); break; case ImageOp::PinLight: ITERATE( PinLight ); break; case ImageOp::Exclusion: ITERATE( Exclusion ); break; break; default: break; } #undef ITERATE } return *this; } /*! * Creates a local image with a subset of pixel samples from this image, and * performs an arithmetic or bitwise logical operation with the * corresponding samples of another \a image. Returns the resulting image. * * For information on the parameters of this member function, see the * documentation for Apply(). * * \note Increments the status monitoring object of the returned image by * the number of selected pixel samples. */ template GenericImage Applied( const GenericImage& image, image_op op = ImageOp::Mov, const Point& point = Point( int_max ), int channel = -1, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) const { int c0 = (channel < 0) ? m_channel : channel; int c1 = c0 + ((firstChannel < 0) ? image.FirstSelectedChannel() : firstChannel); int c2 = c0 + ((lastChannel < 0) ? image.LastSelectedChannel() : lastChannel); GenericImage result( *this, rect.MovedTo( point ), c1, c2 ); (void)result.Apply( image, op, Point( 0 ), 0, rect, firstChannel, lastChannel ); return result; } // ------------------------------------------------------------------------- /*! * Applies an image transformation to a subset of pixel samples. Returns a * reference to this image. * * \param transformation An image transformation that will be applied to * the selected subset of pixel samples. * * For information on the rest of parameters of this member function, see * the documentation for Fill(). * * If the applied \a transformation is bidirectional, then this function * represents an inverse transformation, usually requiring a * previous direct transformation. See the Transform() member * function. * * Bidirectional transformations such as FourierTransform and * ATrousWaveletTransform usually ignore the specified (or implicit) * selections of a rectangular region and channel range. Other * non-invertible transformations, especially geometric transformations such * as Resample or Rotation, also ignore pixel sample selections. * * \note In general, the applied transformation increments the status * monitoring object proportionally to the number of selected pixel samples. * However, the specific increments depend on the applied transformations. * * \warning This member function is not thread safe, even if the * subset of pixel samples is selected directly with the \a rect, * \a firstChannel and \a lastChannel parameters. This is because image * transformations always use the current image selection, which this * function changes temporarily. * * \sa ImageTransformation */ GenericImage& Apply( const ImageTransformation& transformation, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ); // ### Implemented in pcl/ImageTransformation.h (because of mutual dependencies) /*! * Creates a local image with a subset of pixel samples from this image, and * applies an image transformation to a subset of pixel samples. Returns the * resulting image. * * \param transformation An image transformation that will be applied to * the selected subset of pixel samples. * * For information on the rest of parameters of this member function, see * the documentation for Fill(). For information on image transformations, * see Apply( const ImageTransformation& ) and ImageTransformation. * * \warning This member function is not thread safe, even if the * subset of pixel samples is selected directly with the \a rect, * \a firstChannel and \a lastChannel parameters. This is because image * transformations always use the current image selection, which this * function changes temporarily. */ GenericImage Applied( const ImageTransformation& transformation, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) const { GenericImage result( *this, rect, firstChannel, lastChannel ); (void)result.Apply( transformation ); return result; } /*! * Applies a \e direct image transformation to a subset of pixel samples. * * \param transform A bidirectional image transformation that will be * applied to the selected subset of pixel samples. * * For information on the rest of parameters of this member function, see * the documentation for Fill(). * * \note In general, the applied transformation increments the status * monitoring object proportionally to the number of selected pixel samples. * However, the specific increments depend on the applied transformations. * * \warning This member function is not thread safe, even if the * subset of pixel samples is selected directly with the \a rect, * \a firstChannel and \a lastChannel parameters. This is because image * transformations always use the current image selection, which this * function changes temporarily. * * \sa ImageTransformation */ void Transform( BidirectionalImageTransformation& transform, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) const; // ### Implemented in pcl/ImageTransformation.h (because of mutual dependencies) // ------------------------------------------------------------------------- #ifndef __PCL_IMAGE_NO_BITMAP /*! * Blends a rectangular region of a 32-bit RGBA \a bitmap with this image. * Returns a reference to this image. * * \param bitmap Reference to a constant source bitmap. * * \param point Target point in image coordinates. This is the upper left * corner of the target rectangular region where the source * \a bitmap will be applied to this image. If one or both * point coordinates are equal to \c int_max (the maximum * possible value of type \c int), then the current anchor * point selection will be used. The default value is * Point( int_max ). * * \param rect Source rectangular region in bitmap coordinates. If this * parameter defines an empty rectangle, the whole source * bitmap will be applied. If the specified rectangle is not * empty and extends beyond image boundaries, only the * intersection with the source bitmap will be used. If that * intersection does not exist, then this member function has * no effect. The default value is an empty rectangle. * * The blending operation involves only nominal image channels. The alpha * channel of the source bitmap defines a transparency mask for combining * bitmap pixel values and image pixel sample values. The resulting pixel * sample value \c V1 after the blending operation is given in the * normalized real range [0,1] by the following expression: * *
    * V1 = V*(1 - A/255) + B * A/255
    * 
* * where \c V is the initial pixel value of this image, \c A is the alpha * value of the source bitmap pixel, and \c B is one of the red, green or * blue values in the source bitmap pixel. The expression above is applied * respectively for each nominal sample of this image. * * \note Increments the status monitoring object by the number of pixels in * the intersection between this image and the specified rectangular region * of the source bitmap. */ GenericImage& Blend( const Bitmap& bitmap, const Point& point = Point( int_max ), const Rect& rect = Rect( 0 ) ) { Rect r = rect; if ( r.IsRect() ) r = bitmap.Bounds().Intersection( r ); else r = bitmap.Bounds(); if ( !r.IsRect() ) return *this; Point p = point; if ( p.x == int_max || p.y == int_max ) p = m_point; if ( p.x < 0 ) { if ( (r.x0 -= p.x) >= r.x1 ) return *this; p.x = 0; } else if ( p.x >= m_width ) return *this; if ( p.y < 0 ) { if ( (r.y0 -= p.y) >= r.y1 ) return *this; p.y = 0; } else if ( p.y >= m_height ) return *this; r.ResizeTo( pcl::Min( m_width - p.x, r.Width() ), pcl::Min( m_height - p.y, r.Height() ) ); bool hasAlpha = HasAlphaChannels(); int w = r.Width(); int h = r.Height(); EnsureUnique(); if ( IsColor() ) { if ( m_status.IsInitializationEnabled() ) m_status.Initialize( "Blending RGBA bitmap", size_type( w )*size_type( h ) ); sample* __restrict__ fR = nullptr; sample* __restrict__ fG = nullptr; sample* __restrict__ fB = nullptr; sample* __restrict__ fA = nullptr; for ( int i = 0; i < h; ++i, ++p.y, m_status += w ) { fR = PixelAddress( p, 0 ); fG = PixelAddress( p, 1 ); fB = PixelAddress( p, 2 ); if ( hasAlpha ) fA = PixelAddress( p, 3 ); const RGBA* __restrict__ g = bitmap.ScanLine( r.y0 + i ) + r.x0; for ( int j = 0; j < w; ++j, ++fR, ++fG, ++fB, ++fA, ++g ) { RGBA rgba = *g; uint8 A = Alpha( rgba ); if ( hasAlpha ) { P::Mov( *fR, Red( rgba ) ); P::Mov( *fG, Green( rgba ) ); P::Mov( *fB, Blue( rgba ) ); P::Mov( *fA, A ); } else if ( A != 0 ) { if ( A == 0xFF ) { P::Mov( *fR, Red( rgba ) ); P::Mov( *fG, Green( rgba ) ); P::Mov( *fB, Blue( rgba ) ); } else { double k = PTLUT->pDLUTA[A]; double k1 = PTLUT->p1DLUT8[A]; double v; P::FromSample( v, *fR ), P::Mov( *fR, k*Red( rgba ) + k1*v ); P::FromSample( v, *fG ), P::Mov( *fG, k*Green( rgba ) + k1*v ); P::FromSample( v, *fB ), P::Mov( *fB, k*Blue( rgba ) + k1*v ); } } } } } else { if ( m_status.IsInitializationEnabled() ) m_status.Initialize( "Blending grayscale bitmap", size_type( w )*size_type( h ) ); sample* __restrict__ fK = nullptr; sample* __restrict__ fA = nullptr; for ( int i = 0; i < h; ++i, ++p.y, m_status += w ) { fK = PixelAddress( p, 0 ); if ( hasAlpha ) fA = PixelAddress( p, 1 ); const RGBA* __restrict__ g = bitmap.ScanLine( r.y0 + i ) + r.x0; for ( int j = 0; j < w; ++j, ++fK, ++fA, ++g ) { RGBA rgba = *g; uint8 R = Red( rgba ); uint8 G = Green( rgba ); uint8 B = Blue( rgba ); uint8 A = Alpha( rgba ); double K = 0.5*(pcl::Min( pcl::Min( R, G ), B ) + pcl::Max( pcl::Max( R, G ), B )); if ( hasAlpha ) { P::Mov( *fK, K/255 ); P::Mov( *fA, A ); } else if ( A != 0 ) { if ( A == 0xFF ) P::Mov( *fK, K/255 ); else { double v; P::FromSample( v, *fK ); P::Mov( *fK, PTLUT->pDLUTA[A]*K + PTLUT->p1DLUT8[A]*v ); } } } } } return *this; } /*! * Returns a local duplicate of this image blended with a rectangular region * of a 32-bit RGBA \a bitmap. * * For information on the parameters of this member function, see the * documentation for Blend(). * * \note Increments the status monitoring object of the returned image by * the number of pixels in the intersection between this image and the * specified rectangular region of the source bitmap. */ GenericImage Blended( const Bitmap& bitmap, const Point& point = Point( int_max ), const Rect& rect = Rect( 0 ) ) const { GenericImage result( *this, Bounds(), 0, m_numberOfChannels-1 ); (void)result.Blend( bitmap, point, rect ); return result; } #endif // !__PCL_IMAGE_NO_BITMAP // ------------------------------------------------------------------------- /*! * Replaces a subset of pixel samples in this image with the specified * constant \a scalar. Returns a reference to this image. * * This member function is equivalent to: * * \code * Apply( scalar, ImageOp::Mov, rect, firstChannel, lastChannel ); * \endcode */ template GenericImage& Move( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { return Apply( scalar, ImageOp::Mov, rect, firstChannel, lastChannel ); } /*! * A synonym for Move(). */ template GenericImage& Mov( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { return Move( scalar, rect, firstChannel, lastChannel ); } /*! * Adds the specified \a scalar to a subset of pixel samples in this image. * Returns a reference to this image. * * This member function is equivalent to: * * \code * Apply( scalar, ImageOp::Add, rect, firstChannel, lastChannel ); * \endcode */ template GenericImage& Add( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { return Apply( scalar, ImageOp::Add, rect, firstChannel, lastChannel ); } /*! * A synonym for Add(). */ template GenericImage& operator +=( T scalar ) { return Add( scalar ); } /*! * Creates a local image with a subset of pixel samples from this image, and * adds a \a scalar. Returns the resulting image. * * For information on the parameters of this member function, see the * documentation for Fill(). * * \note Increments the status monitoring object of the returned image by * the number of selected pixel samples. */ template GenericImage Added( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) const { GenericImage result( *this, rect, firstChannel, lastChannel ); (void)result.Add( scalar ); return result; } /*! * Subtracts the specified \a scalar from a subset of pixel samples in this * image. Returns a reference to this image. * * This member function is equivalent to: * * \code * Apply( scalar, ImageOp::Sub, rect, firstChannel, lastChannel ); * \endcode */ template GenericImage& Subtract( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { return Apply( scalar, ImageOp::Sub, rect, firstChannel, lastChannel ); } /*! * A synonym for Subtract(). */ template GenericImage& Sub( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { return Subtract( scalar, rect, firstChannel, lastChannel ); } /*! * A synonym for Subtract(). */ template GenericImage& operator -=( T scalar ) { return Subtract( scalar ); } /*! * Creates a local image with a subset of pixel samples from this image, and * subtracts a \a scalar. Returns the resulting image. * * For information on the parameters of this member function, see the * documentation for Fill(). * * \note Increments the status monitoring object of the returned image by * the number of selected pixel samples. */ template GenericImage Subtracted( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) const { GenericImage result( *this, rect, firstChannel, lastChannel ); (void)result.Subtract( scalar ); return result; } /*! * Multiplies a subset of pixel samples in this image by the specified * \a scalar. Returns a reference to this image. * * This member function is equivalent to: * * \code * Apply( scalar, ImageOp::Mul, rect, firstChannel, lastChannel ); * \endcode */ template GenericImage& Multiply( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { return Apply( scalar, ImageOp::Mul, rect, firstChannel, lastChannel ); } /*! * A synonym for Multiply(). */ template GenericImage& Mul( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { return Multiply( scalar, rect, firstChannel, lastChannel ); } /*! * A synonym for Multiply(). */ template GenericImage& operator *=( T scalar ) { return Multiply( scalar ); } /*! * Creates a local image with a subset of pixel samples from this image, and * multiplies it by a \a scalar. Returns the resulting image. * * For information on the parameters of this member function, see the * documentation for Fill(). * * \note Increments the status monitoring object of the returned image by * the number of selected pixel samples. */ template GenericImage Multiplied( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) const { GenericImage result( *this, rect, firstChannel, lastChannel ); (void)result.Multiply( scalar ); return result; } /*! * Divides a subset of pixel samples in this image by the specified * \a scalar. Returns a reference to this image. * * This member function is equivalent to: * * \code * Apply( scalar, ImageOp::Div, rect, firstChannel, lastChannel ); * \endcode * * \warning Make sure that the specified \a scalar is not zero or * insignificant. */ template GenericImage& Divide( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { return Apply( scalar, ImageOp::Div, rect, firstChannel, lastChannel ); } /*! * A synonym for Divide(). */ template GenericImage& Div( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { return Divide( scalar, rect, firstChannel, lastChannel ); } /*! * A synonym for Divide(). */ template GenericImage& operator /=( T scalar ) { return Divide( scalar ); } /*! * Creates a local image with a subset of pixel samples from this image, and * divides it by a \a scalar. Returns the resulting image. * * For information on the parameters of this member function, see the * documentation for Fill(). * * \note Increments the status monitoring object of the returned image by * the number of selected pixel samples. * * \warning Make sure that the specified \a scalar is not zero or * insignificant. */ template GenericImage Divided( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) const { GenericImage result( *this, rect, firstChannel, lastChannel ); (void)result.Divide( scalar ); return result; } /*! * Raises (exponentiation operator) a subset of pixel samples in this image * to the specified \a scalar. Returns a reference to this image. * * This member function is equivalent to: * * \code * Apply( scalar, ImageOp::Pow, rect, firstChannel, lastChannel ); * \endcode */ template GenericImage& Raise( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { return Apply( scalar, ImageOp::Pow, rect, firstChannel, lastChannel ); } /*! * A synonym for Raise(). */ template GenericImage& Pow( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { return Raise( scalar, rect, firstChannel, lastChannel ); } /*! * A synonym for Raise(). */ template GenericImage& operator ^=( T scalar ) { return Raise( scalar ); } /*! * Creates a local image with a subset of pixel samples from this image, and * raises it to a \a scalar. Returns the resulting image. * * For information on the parameters of this member function, see the * documentation for Fill(). * * \note Increments the status monitoring object of the returned image by * the number of selected pixel samples. */ template GenericImage Raised( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) const { GenericImage result( *this, rect, firstChannel, lastChannel ); (void)result.Raise( scalar ); return result; } /*! * Replaces a subset of pixel samples in this image with the absolute * values of their differences with the specified \a scalar. Returns a * reference to this image. * * This member function is equivalent to: * * \code * Apply( scalar, ImageOp::Dif, rect, firstChannel, lastChannel ); * \endcode */ template GenericImage& SetAbsoluteDifference( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { return Apply( scalar, ImageOp::Dif, rect, firstChannel, lastChannel ); } /*! * A synonym for SetAbsoluteDifference(). */ template GenericImage& Dif( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { return SetAbsoluteDifference( scalar, rect, firstChannel, lastChannel ); } /*! * Creates a local image with a subset of pixel samples from this image, and * computes the absolute values of their differences with the specified * \a scalar. Returns the resulting image. * * For information on the parameters of this member function, see the * documentation for Fill(). * * \note Increments the status monitoring object of the returned image by * the number of selected pixel samples. */ template GenericImage AbsoluteDifference( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) const { GenericImage result( *this, rect, firstChannel, lastChannel ); (void)result.SetAbsoluteDifference( scalar ); return result; } /*! * Replaces a subset of pixel samples in this image with the minimum of each * sample and the specified \a scalar. Returns a reference to this image. * * This member function is equivalent to: * * \code * Apply( scalar, ImageOp::Min, rect, firstChannel, lastChannel ); * \endcode */ template GenericImage& SetMinimum( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { return Apply( scalar, ImageOp::Min, rect, firstChannel, lastChannel ); } /*! * A synonym for SetMinimum(). */ template GenericImage& Min( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { return SetMinimum( scalar, rect, firstChannel, lastChannel ); } /*! * Creates a local image with a subset of pixel samples from this image, and * computes the minimum of each sample and the specified \a scalar. Returns * the resulting image. * * For information on the parameters of this member function, see the * documentation for Fill(). * * \note Increments the status monitoring object of the returned image by * the number of selected pixel samples. */ template GenericImage Minimum( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) const { GenericImage result( *this, rect, firstChannel, lastChannel ); (void)result.SetMinimum( scalar ); return result; } /*! * Replaces a subset of pixel samples in this image with the maximum of each * sample and the specified \a scalar. Returns a reference to this image. * * This member function is equivalent to: * * \code * Apply( scalar, ImageOp::Max, rect, firstChannel, lastChannel ); * \endcode */ template GenericImage& SetMaximum( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { return Apply( scalar, ImageOp::Max, rect, firstChannel, lastChannel ); } /*! * A synonym for SetMaximum(). */ template GenericImage& Max( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { return SetMaximum( scalar, rect, firstChannel, lastChannel ); } /*! * Creates a local image with a subset of pixel samples from this image, and * computes the maximum of each sample and the specified \a scalar. Returns * the resulting image. * * For information on the parameters of this member function, see the * documentation for Fill(). * * \note Increments the status monitoring object of the returned image by * the number of selected pixel samples. */ template GenericImage Maximum( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) const { GenericImage result( *this, rect, firstChannel, lastChannel ); (void)result.SetMaximum( scalar ); return result; } /*! * Performs a bitwise OR (inclusive OR) operation between a subset of pixel * samples in this image and the specified \a scalar. Returns a reference to * this image. * * This member function is equivalent to: * * \code * Apply( scalar, ImageOp::Or, rect, firstChannel, lastChannel ); * \endcode */ template GenericImage& Or( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { return Apply( scalar, ImageOp::Or, rect, firstChannel, lastChannel ); } /*! * Performs a bitwise AND operation between a subset of pixel samples in * this image and the specified \a scalar. Returns a reference to this * image. * * This member function is equivalent to: * * \code * Apply( scalar, ImageOp::And, rect, firstChannel, lastChannel ); * \endcode */ template GenericImage& And( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { return Apply( scalar, ImageOp::And, rect, firstChannel, lastChannel ); } /*! * Performs a bitwise XOR (exclusive OR) operation between a subset of pixel * samples in this image and the specified \a scalar. Returns a reference to * this image. * * This member function is equivalent to: * * \code * Apply( scalar, ImageOp::Or, rect, firstChannel, lastChannel ); * \endcode */ template GenericImage& Xor( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { return Apply( scalar, ImageOp::Xor, rect, firstChannel, lastChannel ); } /*! * Performs a bitwise NOR (NOT inclusive OR) operation between a subset of * pixel samples in this image and the specified \a scalar. Returns a * reference to this image. * * This member function is equivalent to: * * \code * Apply( scalar, ImageOp::Nor, rect, firstChannel, lastChannel ); * \endcode */ template GenericImage& Nor( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { return Apply( scalar, ImageOp::Nor, rect, firstChannel, lastChannel ); } /*! * Performs a bitwise NAND (NOT AND) operation between a subset of pixel * samples in this image and the specified \a scalar. Returns a reference to * this image. * * This member function is equivalent to: * * \code * Apply( scalar, ImageOp::Nand, rect, firstChannel, lastChannel ); * \endcode */ template GenericImage& Nand( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { return Apply( scalar, ImageOp::Nand, rect, firstChannel, lastChannel ); } /*! * Performs a bitwise XNOR (exclusive NOR) operation between a subset of * pixel samples in this image and the specified \a scalar. Returns a * reference to this image. * * This member function is equivalent to: * * \code * Apply( scalar, ImageOp::Xnor, rect, firstChannel, lastChannel ); * \endcode */ template GenericImage& Xnor( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { return Apply( scalar, ImageOp::Xnor, rect, firstChannel, lastChannel ); } // ------------------------------------------------------------------------- /*! * Replaces a subset of pixel samples in this image with samples from the * specified source \a image. Returns a reference to this image. * * This member function is equivalent to: * * \code * Apply( image, ImageOp::Mov, point, channel, rect, firstChannel, lastChannel ); * \endcode */ template GenericImage& Move( const GenericImage& image, const Point& point = Point( int_max ), int channel = -1, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { return Apply( image, ImageOp::Mov, point, channel, rect, firstChannel, lastChannel ); } /*! * A synonym for Move(). */ template GenericImage& Mov( const GenericImage& image, const Point& point = Point( int_max ), int channel = -1, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { return Move( image, point, channel, rect, firstChannel, lastChannel ); } /*! * Adds pixel samples of the specified \a image to a subset of pixel samples * in this image. Returns a reference to this image. * * This member function is equivalent to: * * \code * Apply( image, ImageOp::Add, point, channel, rect, firstChannel, lastChannel ); * \endcode */ template GenericImage& Add( const GenericImage& image, const Point& point = Point( int_max ), int channel = -1, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { return Apply( image, ImageOp::Add, point, channel, rect, firstChannel, lastChannel ); } /*! * A synonym for Add(). */ template GenericImage& operator +=( const GenericImage& image ) { return Add( image ); } /*! * Subtracts pixel samples of the specified \a image from a subset of pixel * samples in this image. Returns a reference to this image. * * This member function is equivalent to: * * \code * Apply( image, ImageOp::Sub, point, channel, rect, firstChannel, lastChannel ); * \endcode */ template GenericImage& Subtract( const GenericImage& image, const Point& point = Point( int_max ), int channel = -1, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { return Apply( image, ImageOp::Sub, point, channel, rect, firstChannel, lastChannel ); } /*! * A synonym for Subtract(). */ template GenericImage& Sub( const GenericImage& image, const Point& point = Point( int_max ), int channel = -1, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { return Subtract( image, point, channel, rect, firstChannel, lastChannel ); } /*! * A synonym for Subtract(). */ template GenericImage& operator -=( const GenericImage& image ) { return Subtract( image ); } /*! * Multiplies a subset of pixel samples in this image by samples from the * specified source \a image. Returns a reference to this image. * * This member function is equivalent to: * * \code * Apply( image, ImageOp::Mul, point, channel, rect, firstChannel, lastChannel ); * \endcode */ template GenericImage& Multiply( const GenericImage& image, const Point& point = Point( int_max ), int channel = -1, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { return Apply( image, ImageOp::Mul, point, channel, rect, firstChannel, lastChannel ); } /*! * A synonym for Multiply(). */ template GenericImage& Mul( const GenericImage& image, const Point& point = Point( int_max ), int channel = -1, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { return Multiply( image, point, channel, rect, firstChannel, lastChannel ); } /*! * A synonym for Multiply(). */ template GenericImage& operator *=( const GenericImage& image ) { return Multiply( image ); } /*! * Divides a subset of pixel samples in this image by samples from the * specified source \a image. Returns a reference to this image. * * This member function is equivalent to: * * \code * Apply( image, ImageOp::Div, point, channel, rect, firstChannel, lastChannel ); * \endcode * * When a source pixel sample of the specified \a image is zero or * insignificant, the corresponding target pixel sample is set to the * maximum sample value in the native range of this image. */ template GenericImage& Divide( const GenericImage& image, const Point& point = Point( int_max ), int channel = -1, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { return Apply( image, ImageOp::Div, point, channel, rect, firstChannel, lastChannel ); } /*! * A synonym for Divide(). */ template GenericImage& Div( const GenericImage& image, const Point& point = Point( int_max ), int channel = -1, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { return Divide( image, point, channel, rect, firstChannel, lastChannel ); } /*! * A synonym for Divide(). */ template GenericImage& operator /=( const GenericImage& image ) { return Divide( image ); } /*! * Raises (exponentiation operator) a subset of pixel samples in this image * to samples from the specified source \a image. Returns a reference to * this image. * * This member function is equivalent to: * * \code * Apply( image, ImageOp::Pow, point, channel, rect, firstChannel, lastChannel ); * \endcode */ template GenericImage& Raise( const GenericImage& image, const Point& point = Point( int_max ), int channel = -1, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { return Apply( image, ImageOp::Pow, point, channel, rect, firstChannel, lastChannel ); } /*! * A synonym for Raise(). */ template GenericImage& Pow( const GenericImage& image, const Point& point = Point( int_max ), int channel = -1, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { return Raise( image, point, channel, rect, firstChannel, lastChannel ); } /*! * A synonym for Raise(). */ template GenericImage& operator ^=( const GenericImage& image ) { return Raise( image ); } /*! * Replaces a subset of pixel samples in this image with the absolute values * of their differences with samples from the specified source \a image. * Returns a reference to this image. * * This member function is equivalent to: * * \code * Apply( image, ImageOp::Dif, point, channel, rect, firstChannel, lastChannel ); * \endcode */ template GenericImage& SetAbsoluteDifference( const GenericImage& image, const Point& point = Point( int_max ), int channel = -1, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { return Apply( image, ImageOp::Dif, point, channel, rect, firstChannel, lastChannel ); } /*! * A synonym for SetAbsoluteDifference(). */ template GenericImage& Dif( const GenericImage& image, const Point& point = Point( int_max ), int channel = -1, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { return SetAbsoluteDifference( image, point, channel, rect, firstChannel, lastChannel ); } /*! * Replaces a subset of pixel samples in this image with the minimum of each * target sample and the corresponding source sample of the specified * \a image. Returns a reference to this image. * * This member function is equivalent to: * * \code * Apply( image, ImageOp::Min, point, channel, rect, firstChannel, lastChannel ); * \endcode */ template GenericImage& SetMinimum( const GenericImage& image, const Point& point = Point( int_max ), int channel = -1, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { return Apply( image, ImageOp::Min, point, channel, rect, firstChannel, lastChannel ); } /*! * A synonym for SetMinimum(). */ template GenericImage& Min( const GenericImage& image, const Point& point = Point( int_max ), int channel = -1, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { return SetMinimum( image, point, channel, rect, firstChannel, lastChannel ); } /*! * Replaces a subset of pixel samples in this image with the maximum of each * target sample and the corresponding source sample of the specified * \a image. Returns a reference to this image. * * This member function is equivalent to: * * \code * Apply( image, ImageOp::Min, point, channel, rect, firstChannel, lastChannel ); * \endcode */ template GenericImage& SetMaximum( const GenericImage& image, const Point& point = Point( int_max ), int channel = -1, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { return Apply( image, ImageOp::Max, point, channel, rect, firstChannel, lastChannel ); } /*! * A synonym for SetMaximum(). */ template GenericImage& Max( const GenericImage& image, const Point& point = Point( int_max ), int channel = -1, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { return SetMaximum( image, point, channel, rect, firstChannel, lastChannel ); } /*! * Performs a bitwise OR (inclusive OR) operation between a subset of pixel * samples in this image and the corresponding samples of the specified * source \a image. Returns a reference to this image. * * This member function is equivalent to: * * \code * Apply( image, ImageOp::Or, point, channel, rect, firstChannel, lastChannel ); * \endcode */ template GenericImage& Or( const GenericImage& image, const Point& point = Point( int_max ), int channel = -1, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { return Apply( image, ImageOp::Or, point, channel, rect, firstChannel, lastChannel ); } /*! * Performs a bitwise AND operation between a subset of pixel samples in * this image and the corresponding samples of the specified source * \a image. Returns a reference to this image. * * This member function is equivalent to: * * \code * Apply( image, ImageOp::And, point, channel, rect, firstChannel, lastChannel ); * \endcode */ template GenericImage& And( const GenericImage& image, const Point& point = Point( int_max ), int channel = -1, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { return Apply( image, ImageOp::And, point, channel, rect, firstChannel, lastChannel ); } /*! * Performs a bitwise XOR (exclusive OR) operation between a subset of pixel * samples in this image and the corresponding samples of the specified * source \a image. Returns a reference to this image. * * This member function is equivalent to: * * \code * Apply( image, ImageOp::Xor, point, channel, rect, firstChannel, lastChannel ); * \endcode */ template GenericImage& Xor( const GenericImage& image, const Point& point = Point( int_max ), int channel = -1, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { return Apply( image, ImageOp::Xor, point, channel, rect, firstChannel, lastChannel ); } /*! * Performs a bitwise NOR (NOT inclusive OR) operation between a subset of * pixel samples in this image and the corresponding samples of the * specified source \a image. Returns a reference to this image. * * This member function is equivalent to: * * \code * Apply( image, ImageOp::Nor, point, channel, rect, firstChannel, lastChannel ); * \endcode */ template GenericImage& Nor( const GenericImage& image, const Point& point = Point( int_max ), int channel = -1, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { return Apply( image, ImageOp::Nor, point, channel, rect, firstChannel, lastChannel ); } /*! * Performs a bitwise NAND (NOT AND) operation between a subset of pixel * samples in this image and the corresponding samples of the specified * source \a image. Returns a reference to this image. * * This member function is equivalent to: * * \code * Apply( image, ImageOp::Nand, point, channel, rect, firstChannel, lastChannel ); * \endcode */ template GenericImage& Nand( const GenericImage& image, const Point& point = Point( int_max ), int channel = -1, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { return Apply( image, ImageOp::Nand, point, channel, rect, firstChannel, lastChannel ); } /*! * Performs a bitwise XNOR (NOT exclusive OR) operation between a subset of * pixel samples in this image and the corresponding samples of the * specified source \a image. Returns a reference to this image. * * This member function is equivalent to: * * \code * Apply( image, ImageOp::Xnor, point, channel, rect, firstChannel, lastChannel ); * \endcode */ template GenericImage& Xnor( const GenericImage& image, const Point& point = Point( int_max ), int channel = -1, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { return Apply( image, ImageOp::Xnor, point, channel, rect, firstChannel, lastChannel ); } // ------------------------------------------------------------------------- /*! * Exchanges a subset of pixel samples between this image and another * operand \a image. Returns a reference to this image. * * \param image Operand image, whose pixel samples will be exchanged with * samples from this image. * * \param point Target point in image coordinates. This is the upper left * corner of the target rectangular region where the source * \a image will be exchanged with this image. If one or both * point coordinates are equal to \c int_max (the maximum * possible value of type \c int), then the current anchor * point selection will be used. The default value is * Point( int_max ). * * \param channel Target channel index (zero-based). This is the index of * the first channel of this image where pixel samples will * be exchanged. If this parameter is negative, then the * currently selected channel will be used. The default value * is -1. * * For information on the rest of parameters of this member function, see * the documentation for Fill(). * * The specified operation is performed with samples in the intersection * between the specified (or implicit) rectangular selections for both * images. Each channel in the specified channel range selection for this * image is operated with its counterpart channel in the operand \a image, * starting from the first selected channel in the operand \a image. * * The operation may involve conversions to the appropriate floating point * data type for intermediate results, or LUT table lookups, as necessary * to ensure preservation of the full native range of both images. * * \note Increments the status monitoring object by the number of pixel * samples in the intersection between both operand images. */ template GenericImage& Exchange( GenericImage& image, const Point& point = Point( int_max ), int channel = -1, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { Rect r = rect; if ( !image.ParseSelection( r, firstChannel, lastChannel ) ) return *this; if ( !ParseChannel( channel ) ) return *this; Point p = point; if ( p.x == int_max || p.y == int_max ) p = m_point; if ( p.x < 0 ) { if ( (r.x0 -= p.x) >= r.x1 ) return *this; p.x = 0; } else if ( p.x >= m_width ) return *this; if ( p.y < 0 ) { if ( (r.y0 -= p.y) >= r.y1 ) return *this; p.y = 0; } else if ( p.y >= m_height ) return *this; r.ResizeTo( pcl::Min( m_width - p.x, r.Width() ), pcl::Min( m_height - p.y, r.Height() ) ); lastChannel = pcl::Min( lastChannel, firstChannel + m_numberOfChannels - channel - 1 ); EnsureUnique(); image.EnsureUnique(); size_type N = size_type( r.Width() )*size_type( r.Height() ); if ( m_status.IsInitializationEnabled() ) m_status.Initialize( "Exchanging pixel samples", N*(1 + lastChannel - firstChannel) ); if ( r == Bounds() && r == image.Bounds() ) for ( int i = channel, j = firstChannel; j <= lastChannel; ++i, ++j, m_status += N ) { sample* __restrict__ f = m_pixelData[i]; typename P1::sample* __restrict__ g = image[j]; PCL_IVDEP for ( size_type k = 0; k < N; ++k, ++f, ++g ) { sample t = *f; P1::FromSample( *f, *g ); P::FromSample( *g, t ); } } else for ( int i = channel, j = firstChannel, w = r.Width(), h = r.Height(); j <= lastChannel; ++i, ++j, m_status += N ) { sample* __restrict__ f = PixelAddress( p, i ); typename P1::sample* __restrict__ g = image.PixelAddress( r.LeftTop(), j ); PCL_IVDEP for ( int k = 0; k < h; ++k, f += m_width-w, g += image.Width()-w ) { PCL_IVDEP for ( int l = 0; l < w; ++l, ++f, ++g ) { sample t = *f; P1::FromSample( *f, *g ); P::FromSample( *g, t ); } } } return *this; } /*! * A synonym for Exchange(). */ template GenericImage& Xchg( GenericImage& image, const Point& point = Point( int_max ), int channel = -1, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) { return Exchange( image, point, channel, rect, firstChannel, lastChannel ); } // ------------------------------------------------------------------------- /*! * Returns the minimum value among a subset of pixel samples of this image. * * \param maxProcessors If a value greater than zero is specified, it is * the maximum number of concurrent threads that this function can * execute. If zero or a negative value is specified, the current * thread limit for this image will be used instead (see * AbstractImage::SetMaxProcessors()). The default value is zero. * * For information on the rest of parameters of this member function, see * the documentation for Fill(). * * If range clipping is enabled for this image, only pixel samples within * the current clipping range will be taken into account to search for the * minimum sample value. * * \note Increments the status monitoring object by the number of selected * pixel samples. */ sample MinimumSampleValue( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1, int maxProcessors = 0 ) const { Rect r = rect; if ( !ParseSelection( r, firstChannel, lastChannel ) ) return 0; size_type N = size_type( r.Width() )*size_type( r.Height() )*(1 + lastChannel - firstChannel); if ( m_status.IsInitializationEnabled() ) m_status.Initialize( "Computing minimum pixel sample value", N ); Array L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors ); bool useAffinity = m_parallel && Thread::IsRootThread(); ReferenceArray threads; for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) ) threads.Add( new MinThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ) ) ); if ( threads.Length() > 1 ) { int n = 0; for ( MinThread& thread : threads ) thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 ); for ( MinThread& thread : threads ) thread.Wait(); } else threads[0].Run(); sample min = P::MinSampleValue(); for ( size_type i = 0; i < threads.Length(); ++i ) if ( threads[i].count > 0 ) { min = threads[i].min; while ( ++i < threads.Length() ) if ( threads[i].count > 0 ) if ( threads[i].min < min ) min = threads[i].min; break; } threads.Destroy(); m_status += N; return min; } /*! * \deprecated This member function has been deprecated. It is still part of * PCL only to keep existing code alive. In newly produced code, use * MinimumSampleValue() instead. */ sample MinimumPixelValue( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1, int maxProcessors = 0 ) const { return MinimumSampleValue( rect, firstChannel, lastChannel, maxProcessors ); } /*! * Returns the maximum value among a subset of pixel samples of this image. * * \param maxProcessors If a value greater than zero is specified, it is * the maximum number of concurrent threads that this function can * execute. If zero or a negative value is specified, the current * thread limit for this image will be used instead (see * AbstractImage::SetMaxProcessors()). The default value is zero. * * For information on the rest of parameters of this member function, see * the documentation for Fill(). * * If range clipping is enabled for this image, only pixel samples within * the current clipping range will be taken into account to search for the * maximum sample value. * * \note Increments the status monitoring object by the number of selected * pixel samples. */ sample MaximumSampleValue( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1, int maxProcessors = 0 ) const { Rect r = rect; if ( !ParseSelection( r, firstChannel, lastChannel ) ) return 0; size_type N = size_type( r.Width() )*size_type( r.Height() )*(1 + lastChannel - firstChannel); if ( m_status.IsInitializationEnabled() ) m_status.Initialize( "Computing maximum pixel sample value", N ); Array L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors ); bool useAffinity = m_parallel && Thread::IsRootThread(); ReferenceArray threads; for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) ) threads.Add( new MaxThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ) ) ); if ( threads.Length() > 1 ) { int n = 0; for ( MaxThread& thread : threads ) thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 ); for ( MaxThread& thread : threads ) thread.Wait(); } else threads[0].Run(); sample max = P::MinSampleValue(); for ( size_type i = 0; i < threads.Length(); ++i ) if ( threads[i].count > 0 ) { max = threads[i].max; while ( ++i < threads.Length() ) if ( threads[i].count > 0 ) if ( max < threads[i].max ) max = threads[i].max; break; } threads.Destroy(); m_status += N; return max; } /*! * \deprecated This member function has been deprecated. It is still part of * PCL only to keep existing code alive. In newly produced code, use * MaximumSampleValue() instead. */ sample MaximumPixelValue( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1, int maxProcessors = 0 ) const { return MaximumSampleValue( rect, firstChannel, lastChannel, maxProcessors ); } /*! * Obtains the extreme values among a subset of pixel samples of this image. * * \param[out] min Minimum sample value. * * \param[out] max Maximum sample value. * * \param maxProcessors If a value greater than zero is specified, it is * the maximum number of concurrent threads that this function can * execute. If zero or a negative value is specified, the current * thread limit for this image will be used instead (see * AbstractImage::SetMaxProcessors()). The default value is zero. * * For information on the rest of parameters of this member function, see * the documentation for Fill(). * * If necessary, the output values will be converted from the pixel sample * data type of this image using pixel traits primitives. * * If range clipping is enabled for this image, only pixel samples within * the current clipping range will be taken into account to search for the * extreme sample values. * * \note Increments the status monitoring object by the number of selected * pixel samples. */ template void GetExtremeSampleValues( T& min, T& max, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1, int maxProcessors = 0 ) const { Rect r = rect; if ( !ParseSelection( r, firstChannel, lastChannel ) ) { // ### Required for compatibility with PCL 1.x P::FromSample( min, P::MinSampleValue() ); max = min; return; } size_type N = size_type( r.Width() )*size_type( r.Height() )*(1 + lastChannel - firstChannel); if ( m_status.IsInitializationEnabled() ) m_status.Initialize( "Computing extreme pixel sample values", N ); Array L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors ); bool useAffinity = m_parallel && Thread::IsRootThread(); ReferenceArray threads; for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) ) threads.Add( new MinMaxThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ) ) ); if ( threads.Length() > 1 ) { int n = 0; for ( MinMaxThread& thread : threads ) thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 ); for ( MinMaxThread& thread : threads ) thread.Wait(); } else threads[0].Run(); sample vmin = P::MinSampleValue(); sample vmax = P::MinSampleValue(); for ( size_type i = 0; i < threads.Length(); ++i ) if ( threads[i].count > 0 ) { vmin = threads[i].min; vmax = threads[i].max; while ( ++i < threads.Length() ) if ( threads[i].count > 0 ) { if ( threads[i].min < vmin ) vmin = threads[i].min; if ( vmax < threads[i].max ) vmax = threads[i].max; } break; } threads.Destroy(); m_status += N; P::FromSample( min, vmin ); P::FromSample( max, vmax ); } /*! * \deprecated This member function has been deprecated. It is still part of * PCL only to keep existing code alive. In newly produced code, use * GetExtremeSampleValues() instead. */ void GetExtremePixelValues( sample& min, sample& max, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1, int maxProcessors = 0 ) const { GetExtremeSampleValues( min, max, rect, firstChannel, lastChannel, maxProcessors ); } /*! * Returns the minimum value among a subset of pixel samples of this image, * and obtains the image coordinates of its first occurrence. * * \param[out] xmin Horizontal coordinate of the first occurrence of the * returned minimum pixel sample value. * * \param[out] ymin Vertical coordinate of the first occurrence of the * returned minimum pixel sample value. * * \param maxProcessors If a value greater than zero is specified, it is * the maximum number of concurrent threads that this function can * execute. If zero or a negative value is specified, the current * thread limit for this image will be used instead (see * AbstractImage::SetMaxProcessors()). The default value is zero. * * For information on the rest of parameters of this member function, see * the documentation for Fill(). * * If range clipping is enabled for this image, only pixel samples within * the current clipping range will be taken into account to search for the * minimum sample value. * * \note Increments the status monitoring object by the number of selected * pixel samples. */ sample LocateMinimumSampleValue( int& xmin, int& ymin, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1, int maxProcessors = 0 ) const { Rect r = rect; if ( !ParseSelection( r, firstChannel, lastChannel ) ) { // ### Required for compatibility with PCL 1.x xmin = ymin = 0; return P::MinSampleValue(); } size_type N = size_type( r.Width() )*size_type( r.Height() )*(1 + lastChannel - firstChannel); if ( m_status.IsInitializationEnabled() ) m_status.Initialize( "Locating minimum pixel sample value", N ); Array L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors ); bool useAffinity = m_parallel && Thread::IsRootThread(); ReferenceArray threads; for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) ) threads.Add( new MinPosThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ) ) ); if ( threads.Length() > 1 ) { int n = 0; for ( MinPosThread& thread : threads ) thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 ); for ( MinPosThread& thread : threads ) thread.Wait(); } else threads[0].Run(); xmin = ymin = -1; sample min = P::MinSampleValue(); for ( size_type i = 0; i < threads.Length(); ++i ) if ( threads[i].count > 0 ) { min = threads[i].min; xmin = threads[i].pmin.x; ymin = threads[i].pmin.y; while ( ++i < threads.Length() ) if ( threads[i].count > 0 ) if ( threads[i].min < min ) { min = threads[i].min; xmin = threads[i].pmin.x; ymin = threads[i].pmin.y; } break; } threads.Destroy(); m_status += N; return min; } /*! * Returns the minimum value among a subset of pixel samples of this image, * and obtains the image coordinates of its first occurrence. * * \param[out] pmin Coordinates of the first occurrence of the returned * minimum pixel sample value. * * \param maxProcessors If a value greater than zero is specified, it is * the maximum number of concurrent threads that this function can * execute. If zero or a negative value is specified, the current * thread limit for this image will be used instead (see * AbstractImage::SetMaxProcessors()). The default value is zero. * * For information on the parameters of this member function, see the * documentation for Fill(). * * This member function is equivalent to: * * \code * LocateMinimumSampleValue( pmin.x, pmin.y, rect, firstChannel, lastChannel ); * \endcode */ sample LocateMinimumSampleValue( Point& pmin, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1, int maxProcessors = 0 ) const { return LocateMinimumSampleValue( pmin.x, pmin.y, rect, firstChannel, lastChannel, maxProcessors ); } /*! * \deprecated This member function has been deprecated. It is still part of * PCL only to keep existing code alive. In newly produced code, use * LocateMinimumSampleValue() instead. */ sample LocateMinimumPixelValue( int& xmin, int& ymin, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1, int maxProcessors = 0 ) const { return LocateMinimumSampleValue( xmin, ymin, rect, firstChannel, lastChannel, maxProcessors ); } /*! * \deprecated This member function has been deprecated. It is still part of * PCL only to keep existing code alive. In newly produced code, use * LocateMinimumSampleValue() instead. */ sample LocateMinimumPixelValue( Point& pmin, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1, int maxProcessors = 0 ) const { return LocateMinimumSampleValue( pmin, rect, firstChannel, lastChannel, maxProcessors ); } /*! * Returns the maximum value among a subset of pixel samples of this image, * and obtains the image coordinates of its first occurrence. * * \param[out] xmax Horizontal coordinate of the first occurrence of the * returned maximum pixel sample value. * * \param[out] ymax Vertical coordinate of the first occurrence of the * returned maximum pixel sample value. * * \param maxProcessors If a value greater than zero is specified, it is * the maximum number of concurrent threads that this function can * execute. If zero or a negative value is specified, the current * thread limit for this image will be used instead (see * AbstractImage::SetMaxProcessors()). The default value is zero. * * If range clipping is enabled for this image, only pixel samples within * the current clipping range will be taken into account to search for the * maximum sample value. * * For information on the rest of parameters of this member function, see * the documentation for Fill(). * * \note Increments the status monitoring object by the number of selected * pixel samples. */ sample LocateMaximumSampleValue( int& xmax, int& ymax, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1, int maxProcessors = 0 ) const { Rect r = rect; if ( !ParseSelection( r, firstChannel, lastChannel ) ) { // ### Required for compatibility with PCL 1.x xmax = ymax = 0; return P::MaxSampleValue(); } size_type N = size_type( r.Width() )*size_type( r.Height() )*(1 + lastChannel - firstChannel); if ( m_status.IsInitializationEnabled() ) m_status.Initialize( "Locating maximum pixel sample value", N ); Array L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors ); bool useAffinity = m_parallel && Thread::IsRootThread(); ReferenceArray threads; for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) ) threads.Add( new MaxPosThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ) ) ); if ( threads.Length() > 1 ) { int n = 0; for ( MaxPosThread& thread : threads ) thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 ); for ( MaxPosThread& thread : threads ) thread.Wait(); } else threads[0].Run(); xmax = ymax = -1; sample max = P::MinSampleValue(); for ( size_type i = 0; i < threads.Length(); ++i ) if ( threads[i].count > 0 ) { max = threads[i].max; xmax = threads[i].pmax.x; ymax = threads[i].pmax.y; while ( ++i < threads.Length() ) if ( threads[i].count > 0 ) if ( max < threads[i].max ) { max = threads[i].max; xmax = threads[i].pmax.x; ymax = threads[i].pmax.y; } break; } threads.Destroy(); m_status += N; return max; } /*! * Returns the maximum value among a subset of pixel samples of this image, * and obtains the image coordinates of its first occurrence. * * \param[out] pmax Coordinates of the first occurrence of the returned * maximum pixel sample value. * * \param maxProcessors If a value greater than zero is specified, it is * the maximum number of concurrent threads that this function can * execute. If zero or a negative value is specified, the current * thread limit for this image will be used instead (see * AbstractImage::SetMaxProcessors()). The default value is zero. * * For information on the parameters of this member function, see the * documentation for Fill(). * * This member function is equivalent to: * * \code * LocateMaximumSampleValue( pmax.x, pmax.y, rect, firstChannel, lastChannel ); * \endcode */ sample LocateMaximumSampleValue( Point& pmax, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1, int maxProcessors = 0 ) const { return LocateMaximumSampleValue( pmax.x, pmax.y, rect, firstChannel, lastChannel, maxProcessors ); } /*! * \deprecated This member function has been deprecated. It is still part of * PCL only to keep existing code alive. In newly produced code, use * LocateMaximumSampleValue() instead. */ sample LocateMaximumPixelValue( int& xmax, int& ymax, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1, int maxProcessors = 0 ) const { return LocateMaximumSampleValue( xmax, ymax, rect, firstChannel, lastChannel, maxProcessors ); } /*! * \deprecated This member function has been deprecated. It is still part of * PCL only to keep existing code alive. In newly produced code, use * LocateMaximumSampleValue() instead. */ sample LocateMaximumPixelValue( Point& pmax, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1, int maxProcessors = 0 ) const { return LocateMaximumSampleValue( pmax, rect, firstChannel, lastChannel, maxProcessors ); } /*! * Obtains the extreme values, and the image coordinates of their first * occurrences, among a subset of pixel samples of this image. * * \param[out] xmin Horizontal coordinate of the first occurrence of the * minimum pixel sample value. * * \param[out] ymin Vertical coordinate of the first occurrence of the * minimum pixel sample value. * * \param[out] min Minimum pixel sample value in the selected subset. * * \param[out] xmax Horizontal coordinate of the first occurrence of the * maximum pixel sample value. * * \param[out] ymax Vertical coordinate of the first occurrence of the * maximum pixel sample value. * * \param[out] max Maximum pixel sample value in the selected subset. * * \param maxProcessors If a value greater than zero is specified, it is * the maximum number of concurrent threads that this function can * execute. If zero or a negative value is specified, the current * thread limit for this image will be used instead (see * AbstractImage::SetMaxProcessors()). The default value is zero. * * For information on the rest of parameters of this member function, see * the documentation for Fill(). * * If range clipping is enabled for this image, only pixel samples within * the current clipping range will be taken into account to search for the * extreme sample values. * * \note Increments the status monitoring object by the number of selected * pixel samples. */ template void LocateExtremeSampleValues( int& xmin, int& ymin, T& min, int& xmax, int& ymax, T& max, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1, int maxProcessors = 0 ) const { Rect r = rect; if ( !ParseSelection( r, firstChannel, lastChannel ) ) { // ### Required for compatibility with PCL 1.x xmin = ymin = xmax = ymax = 0; P::FromSample( min, P::MinSampleValue() ); max = min; return; } size_type N = size_type( r.Width() )*size_type( r.Height() )*(1 + lastChannel - firstChannel); if ( m_status.IsInitializationEnabled() ) m_status.Initialize( "Locating extreme pixel sample values", N ); Array L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors ); bool useAffinity = m_parallel && Thread::IsRootThread(); ReferenceArray threads; for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) ) threads.Add( new MinMaxPosThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ) ) ); if ( threads.Length() > 1 ) { int n = 0; for ( MinMaxPosThread& thread : threads ) thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 ); for ( MinMaxPosThread& thread : threads ) thread.Wait(); } else threads[0].Run(); xmin = ymin = xmax = ymax = -1; sample vmin = P::MinSampleValue(); sample vmax = P::MinSampleValue(); for ( size_type i = 0; i < threads.Length(); ++i ) if ( threads[i].count > 0 ) { vmin = threads[i].min; xmin = threads[i].pmin.x; ymin = threads[i].pmin.y; vmax = threads[i].max; xmax = threads[i].pmax.x; ymax = threads[i].pmax.y; while ( ++i < threads.Length() ) if ( threads[i].count > 0 ) { if ( threads[i].min < vmin ) { vmin = threads[i].min; xmin = threads[i].pmin.x; ymin = threads[i].pmin.y; } if ( vmax < threads[i].max ) { vmax = threads[i].max; xmax = threads[i].pmax.x; ymax = threads[i].pmax.y; } } break; } threads.Destroy(); m_status += N; P::FromSample( min, vmin ); P::FromSample( max, vmax ); } /*! * Obtains the extreme values, and the image coordinates of their first * occurrences, among a subset of pixel samples of this image. * * \param[out] pmin Coordinates of the first occurrence of the minimum * pixel sample value. * * \param[out] min Minimum pixel sample value in the selected subset. * * \param[out] pmax Coordinates of the first occurrence of the maximum * pixel sample value. * * \param[out] max Maximum pixel sample value in the selected subset. * * \param maxProcessors If a value greater than zero is specified, it is * the maximum number of concurrent threads that this function can * execute. If zero or a negative value is specified, the current * thread limit for this image will be used instead (see * AbstractImage::SetMaxProcessors()). The default value is zero. * * For information on the rest of parameters of this member function, see * the documentation for Fill(). * * If range clipping is enabled for this image, only pixel samples within * the current clipping range will be taken into account to search for the * extreme sample values. */ template void LocateExtremeSampleValues( Point& pmin, T& min, Point& pmax, T& max, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1, int maxProcessors = 0 ) const { LocateExtremeSampleValues( pmin.x, pmin.y, min, pmax.x, pmax.y, max, rect, firstChannel, lastChannel, maxProcessors ); } /*! * Returns the number of pixel samples selectable for statistics calculation * from a subset of pixel samples. * * \param maxProcessors If a value greater than zero is specified, it is * the maximum number of concurrent threads that this function can * execute. If zero or a negative value is specified, the current * thread limit for this image will be used instead (see * AbstractImage::SetMaxProcessors()). The default value is zero. * * For information on the rest of parameters of this member function, see * the documentation for Fill(). * * If range clipping is enabled for this image, only pixel samples within * the current clipping range will be taken into account. If range clipping * is disabled, the result of this member function will be the volume of the * specified (or implicitly selected) region. * * When range clipping is enabled and this is a complex-valued image, this * function uses the magnitudes (or absolute values) of all selected samples * to compare them with the bounds of the current clipping range. * * \note Increments the status monitoring object by the number of selected * pixel samples. */ size_type Count( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1, int maxProcessors = 0 ) const { Rect r = rect; if ( !ParseSelection( r, firstChannel, lastChannel ) ) return 0; size_type N = size_type( r.Width() )*size_type( r.Height() )*(1 + lastChannel - firstChannel); if ( m_status.IsInitializationEnabled() ) m_status.Initialize( "Counting pixel samples", N ); if ( !this->IsRangeClippingEnabled() ) { m_status += N; return size_type( r.Width() ) * size_type( r.Height() ) * size_type( 1 + lastChannel - firstChannel ); } Array L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors ); bool useAffinity = m_parallel && Thread::IsRootThread(); ReferenceArray threads; for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) ) threads.Add( new CountThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ) ) ); if ( threads.Length() > 1 ) { int n = 0; for ( CountThread& thread : threads ) thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 ); for ( CountThread& thread : threads ) thread.Wait(); } else threads[0].Run(); size_type count = 0; for ( const CountThread& thread : threads ) count += thread.count; threads.Destroy(); m_status += N; return count; } /*! * Returns the arithmetic mean of a subset of pixel samples. * * \param maxProcessors If a value greater than zero is specified, it is * the maximum number of concurrent threads that this function can * execute. If zero or a negative value is specified, the current * thread limit for this image will be used instead (see * AbstractImage::SetMaxProcessors()). The default value is zero. * * For information on the rest of parameters of this member function, see * the documentation for Fill(). * * This member function returns the mean in the normalized range [0,1], * irrespective of the sample data type of the image. * * For complex-valued images, this function returns the mean of the * magnitudes (or absolute values) of all selected samples. * * If range clipping is enabled for this image, only pixel samples within * the current clipping range will be taken into account for calculation of * the mean. * * This function implements a numerically stable summation algorithm to * reduce roundoff errors to the machine's floating point precision. * * \note Increments the status monitoring object by the number of selected * pixel samples. */ double Mean( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1, int maxProcessors = 0 ) const { Rect r = rect; if ( !ParseSelection( r, firstChannel, lastChannel ) ) return 0; size_type N = size_type( r.Width() )*size_type( r.Height() )*(1 + lastChannel - firstChannel); if ( m_status.IsInitializationEnabled() ) m_status.Initialize( "Computing mean pixel sample value", N ); Array L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors ); bool useAffinity = m_parallel && Thread::IsRootThread(); ReferenceArray threads; for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) ) threads.Add( new SumThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ) ) ); if ( threads.Length() > 1 ) { int n = 0; for ( SumThread& thread : threads ) thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 ); for ( SumThread& thread : threads ) thread.Wait(); } else threads[0].Run(); double s = 0; double e = 0; size_type n = 0; for ( const SumThread& thread : threads ) { double y = thread.s - e; double t = s + y; e = (t - s) - y; s = t; n += thread.n; } threads.Destroy(); m_status += N; if ( n == 0 ) return 0; return s/n; } /*! * Returns the median of a subset of pixel samples. * * \param maxProcessors If a value greater than zero is specified, it is * the maximum number of concurrent threads that this function can * execute. If zero or a negative value is specified, the current * thread limit for this image will be used instead (see * AbstractImage::SetMaxProcessors()). The default value is zero. * * For information on the rest of parameters of this member function, see * the documentation for Fill(). * * This member function returns the median in the normalized range [0,1], * irrespective of the sample data type of the image. * * For complex-valued images, this function returns the median of the * magnitudes (or absolute values) of all selected samples. * * If range clipping is enabled for this image, only pixel samples within * the current clipping range will be taken into account for calculation of * the median. * * \note Increments the status monitoring object by the number of selected * pixel samples. */ double Median( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1, int maxProcessors = 0 ) const { Rect r = rect; if ( !ParseSelection( r, firstChannel, lastChannel ) ) return 0; size_type N = size_type( r.Width() )*size_type( r.Height() )*(1 + lastChannel - firstChannel); if ( m_status.IsInitializationEnabled() ) m_status.Initialize( "Computing median pixel sample value", N ); if ( N <= 2560000 ) { SmpThread S( *this, r, firstChannel, lastChannel, 0, r.Height() ); S.Run(); if ( S.n == 0 ) { m_status += N; return 0; } double m = double( *pcl::Select( S.samples.Begin(), S.samples.At( S.n ), S.n >> 1 ) )/double( P::MaxSampleValue() ); if ( S.n & 1 ) { m_status += N; return m; } m = (m + double( *pcl::Select( S.samples.Begin(), S.samples.At( S.n ), (S.n >> 1)-1 ) )/double( P::MaxSampleValue() ))/2; m_status += N; return m; } Array L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors, 160*1024/*overheadLimitPx*/ ); bool useAffinity = m_parallel && Thread::IsRootThread(); double low, high; size_type count = 0; { ReferenceArray threads; for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) ) threads << new MinMaxThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ) ); if ( threads.Length() > 1 ) { int i = 0; for ( MinMaxThread& thread : threads ) thread.Start( ThreadPriority::DefaultMax, useAffinity ? i++ : -1 ); for ( MinMaxThread& thread : threads ) thread.Wait(); } else threads[0].Run(); sample slow = 0, shigh = 0; for ( size_type i = 0; i < threads.Length(); ++i ) if ( threads[i].count > 0 ) { slow = threads[i].min; shigh = threads[i].max; count = threads[i].count; while ( ++i < threads.Length() ) if ( threads[i].count > 0 ) { if ( threads[i].min < slow ) slow = threads[i].min; if ( shigh < threads[i].max ) shigh = threads[i].max; count += threads[i].count; } break; } threads.Destroy(); low = double( slow ); high = double( shigh ); } const double eps = P::IsComplexSample() ? 2*std::numeric_limits::epsilon() : (P::IsFloatSample() ? 2*std::numeric_limits::epsilon() : 0.5/Pow2( double( P::BitsPerSample() ) )); if ( count == 0 ) { m_status += N; return 0; } if ( high - low < eps ) { m_status += N; return low/double( P::MaxSampleValue() ); } ReferenceArray threads; for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) ) threads << new HistogramThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ), low, high ); double mh = 0, l0 = low, h0 = high; SzVector H0; for ( size_type n = 0, n2 = count >> 1, step = 0, it = 0;; ++it ) { SzVector H; if ( it == 0 && step ) H = H0; else { if ( threads.Length() > 1 ) { int i = 0; for ( HistogramThread& thread : threads ) thread.Start( ThreadPriority::DefaultMax, useAffinity ? i++ : -1 ); for ( HistogramThread& thread : threads ) thread.Wait(); } else threads[0].Run(); H = threads[0].H; for ( size_type i = 1; i < threads.Length(); ++i ) H += threads[i].H; if ( it == 0 ) if ( (count & 1) == 0 ) H0 = H; } for ( int i = 0; ; n += H[i++] ) if ( n + H[i] > n2 ) { double range = high - low; high = (range * (i + 1))/(__PCL_MEDIAN_HISTOGRAM_LENGTH - 1) + low; low = (range * i)/(__PCL_MEDIAN_HISTOGRAM_LENGTH - 1) + low; if ( high - low < eps ) { if ( count & 1 ) { threads.Destroy(); m_status += N; return low/double( P::MaxSampleValue() ); } if ( step ) { threads.Destroy(); m_status += N; return (low + mh)/2/double( P::MaxSampleValue() ); } mh = low; low = l0; high = h0; n = 0; --n2; ++step; it = 0; } break; } } } /*! * Returns an order statistic computed for a subset of pixel samples. * * \param k Selection point in the [0,1] range, where 0 corresponds to * the minimum value in the ordered subset of selected pixel * samples and 1 corresponds to the maximum value. If the value of * this parameter is outside the [0,1] range, this function returns * zero conventionally. * * \param maxProcessors If a value greater than zero is specified, it is * the maximum number of concurrent threads that this function can * execute. If zero or a negative value is specified, the current * thread limit for this image will be used instead (see * AbstractImage::SetMaxProcessors()). The default value is zero. * * For information on the rest of parameters of this member function, see * the documentation for Fill(). * * This function returns the requested order statistic in the normalized * range [0,1], irrespective of the sample data type of the image. * * For complex-valued images, this function returns the requested order * statistic for the set of magnitudes (or absolute values) of all selected * samples. * * If range clipping is enabled for this image, only pixel samples within * the current clipping range will be taken into account. * * \note Increments the status monitoring object by the number of selected * pixel samples. */ double OrderStatistic( double k, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1, int maxProcessors = 0 ) const { if ( k < 0 || k > 1 ) return 0; Rect r = rect; if ( !ParseSelection( r, firstChannel, lastChannel ) ) return 0; size_type N = size_type( r.Width() )*size_type( r.Height() )*(1 + lastChannel - firstChannel); if ( m_status.IsInitializationEnabled() ) m_status.Initialize( "Computing order statistic", N ); if ( N <= 2560000 ) { SmpThread S( *this, r, firstChannel, lastChannel, 0, r.Height() ); S.Run(); m_status += N; if ( S.n == 0 ) return 0; return double( *pcl::Select( S.samples.Begin(), S.samples.At( S.n ), distance_type( k*(S.n - 1) ) ) )/double( P::MaxSampleValue() ); } Array L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors, 160*1024/*overheadLimitPx*/ ); bool useAffinity = m_parallel && Thread::IsRootThread(); double low, high; size_type count = 0; { ReferenceArray threads; for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) ) threads << new MinMaxThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ) ); if ( threads.Length() > 1 ) { int i = 0; for ( MinMaxThread& thread : threads ) thread.Start( ThreadPriority::DefaultMax, useAffinity ? i++ : -1 ); for ( MinMaxThread& thread : threads ) thread.Wait(); } else threads[0].Run(); sample slow = 0, shigh = 0; for ( size_type i = 0; i < threads.Length(); ++i ) if ( threads[i].count > 0 ) { slow = threads[i].min; shigh = threads[i].max; count = threads[i].count; while ( ++i < threads.Length() ) if ( threads[i].count > 0 ) { if ( threads[i].min < slow ) slow = threads[i].min; if ( shigh < threads[i].max ) shigh = threads[i].max; count += threads[i].count; } break; } threads.Destroy(); low = double( slow ); high = double( shigh ); } const double eps = P::IsComplexSample() ? 2*std::numeric_limits::epsilon() : (P::IsFloatSample() ? 2*std::numeric_limits::epsilon() : 0.5/Pow2( double( P::BitsPerSample() ) )); if ( count == 0 ) { m_status += N; return 0; } if ( k == 0 || high - low < eps ) { m_status += N; return low/double( P::MaxSampleValue() ); } if ( k == 1 ) { m_status += N; return high/double( P::MaxSampleValue() ); } size_type index = size_type( k*(count - 1) ); ReferenceArray threads; for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) ) threads << new HistogramThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ), low, high ); for ( size_type n = 0;; ) { if ( threads.Length() > 1 ) { int i = 0; for ( HistogramThread& thread : threads ) thread.Start( ThreadPriority::DefaultMax, useAffinity ? i++ : -1 ); for ( HistogramThread& thread : threads ) thread.Wait(); } else threads[0].Run(); SzVector H = threads[0].H; for ( size_type i = 1; i < threads.Length(); ++i ) H += threads[i].H; for ( int i = 0; ; n += H[i++] ) if ( n + H[i] > index ) { double range = high - low; high = (range * (i + 1))/(__PCL_MEDIAN_HISTOGRAM_LENGTH - 1) + low; low = (range * i)/(__PCL_MEDIAN_HISTOGRAM_LENGTH - 1) + low; if ( high - low < eps ) { threads.Destroy(); m_status += N; return low/double( P::MaxSampleValue() ); } break; } } } /*! * Returns the variance from the mean of a subset of pixel samples. * * \param maxProcessors If a value greater than zero is specified, it is * the maximum number of concurrent threads that this function can * execute. If zero or a negative value is specified, the current * thread limit for this image will be used instead (see * AbstractImage::SetMaxProcessors()). The default value is zero. * * For information on the rest of parameters of this member function, see * the documentation for Fill(). * * This member function returns the variance in the normalized range [0,1], * irrespective of the sample data type of the image. * * For complex-valued images, this function returns the variance of the * magnitudes (or absolute values) of all selected samples. * * This implementation uses a two-pass compensated summation algorithm to * minimize roundoff errors (see References). * * If range clipping is enabled for this image, only pixel samples within * the current clipping range will be taken into account for calculation of * the variance. * * \b References * * William H. Press et al., Numerical Recipes in C: The Art of * Scientific Computing, Second Edition (1997 reprint) Cambridge * University Press, page 613. * * \note Increments the status monitoring object by the number of selected * pixel samples. */ double Variance( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1, int maxProcessors = 0 ) const { Rect r = rect; if ( !ParseSelection( r, firstChannel, lastChannel ) ) return 0; size_type N = size_type( r.Width() )*size_type( r.Height() )*(1 + lastChannel - firstChannel); if ( m_status.IsInitializationEnabled() ) m_status.Initialize( "Computing variance", N ); Array L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors ); bool useAffinity = m_parallel && Thread::IsRootThread(); ReferenceArray sumThreads; for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) ) sumThreads.Add( new SumThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ) ) ); if ( sumThreads.Length() > 1 ) { int n = 0; for ( SumThread& thread : sumThreads ) thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 ); for ( SumThread& thread : sumThreads ) thread.Wait(); } else sumThreads[0].Run(); double s = 0; double e = 0; size_type n = 0; for ( const SumThread& thread : sumThreads ) { double y = thread.s - e; double t = s + y; e = (t - s) - y; s = t; n += thread.n; } sumThreads.Destroy(); if ( n < 2 ) return 0; double mean = s/n; ReferenceArray varThreads; for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) ) varThreads.Add( new VarThread( *this, mean, r, firstChannel, lastChannel, n, n + int( L[i] ) ) ); if ( varThreads.Length() > 1 ) { int n = 0; for ( VarThread& thread : varThreads ) thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 ); for ( VarThread& thread : varThreads ) thread.Wait(); } else varThreads[0].Run(); double var = 0, eps = 0; for ( const VarThread& thread : varThreads ) var += thread.var, eps += thread.eps; varThreads.Destroy(); m_status += N; return (var - eps*eps/n)/(n - 1); } /*! * Returns the standard deviation from the mean of a subset of pixel * samples. * * \param maxProcessors If a value greater than zero is specified, it is * the maximum number of concurrent threads that this function can * execute. If zero or a negative value is specified, the current * thread limit for this image will be used instead (see * AbstractImage::SetMaxProcessors()). The default value is zero. * * For information on the rest of parameters of this member function, see * the documentation for Fill(). * * This member function returns the standard deviation in the normalized * range [0,1], irrespective of the sample data type of the image. * * For complex-valued images, this function returns the standard deviation * of the magnitudes (or absolute values) of all selected samples. * * This implementation uses a two-pass compensated summation algorithm to * minimize roundoff errors (see References). * * If range clipping is enabled for this image, only pixel samples within * the current clipping range will be taken into account for calculation of * the variance. * * \b References * * William H. Press et al., Numerical Recipes in C: The Art of * Scientific Computing, Second Edition (1997 reprint) Cambridge * University Press, page 613. */ double StdDev( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1, int maxProcessors = 0 ) const { return pcl::Sqrt( Variance( rect, firstChannel, lastChannel, maxProcessors ) ); } /*! * Returns the mean absolute deviation of a subset of pixel samples with * respect to the specified \a center value. * * \param center Reference central value for computation of the mean * absolute deviation in the normalized range [0,1]. * Typically, this parameter is set to the median value of * the set of pixel samples used for calculation. * * \param maxProcessors If a value greater than zero is specified, it is * the maximum number of concurrent threads that this function can * execute. If zero or a negative value is specified, the current * thread limit for this image will be used instead (see * AbstractImage::SetMaxProcessors()). The default value is zero. * * For information on the rest of parameters of this member function, see * the documentation for Fill(). * * This member function returns the mean absolute deviation with respect to * \a center in the normalized range [0,1], irrespective of the sample data * type of the image. * * For complex-valued images, this function returns the average absolute * deviation of the magnitudes (or absolute values) of all selected samples. * * If range clipping is enabled for this image, only pixel samples within * the current clipping range will be taken into account for calculation of * the average absolute deviation. * * This function implements a numerically stable summation algorithm to * reduce roundoff errors to the machine's floating point precision. * * \note To make the average absolute deviation about the median consistent * with the standard deviation of a normal distribution, it must be * multiplied by the constant 1.2533. * * \note Increments the status monitoring object by the number of selected * pixel samples. */ double AvgDev( double center, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1, int maxProcessors = 0 ) const { Rect r = rect; if ( !ParseSelection( r, firstChannel, lastChannel ) ) return 0; size_type N = size_type( r.Width() )*size_type( r.Height() )*(1 + lastChannel - firstChannel); if ( m_status.IsInitializationEnabled() ) m_status.Initialize( "Computing average absolute deviation", N ); Array L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors ); bool useAffinity = m_parallel && Thread::IsRootThread(); ReferenceArray threads; for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) ) threads.Add( new SumAbsDevThread( *this, center, r, firstChannel, lastChannel, n, n + int( L[i] ) ) ); if ( threads.Length() > 1 ) { int n = 0; for ( SumAbsDevThread& thread : threads ) thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 ); for ( SumAbsDevThread& thread : threads ) thread.Wait(); } else threads[0].Run(); double s = 0; double e = 0; size_type n = 0; for ( const SumAbsDevThread& thread : threads ) { double y = thread.s - e; double t = s + y; e = (t - s) - y; s = t; n += thread.n; } threads.Destroy(); m_status += N; if ( n == 0 ) return 0; return s/n; } /*! * Returns the two-sided mean absolute deviation of a subset of pixel * samples with respect to the specified \a center value. * * \param center Reference central value for computation of the mean * absolute deviation in the normalized range [0,1]. * Typically, the value of this parameter is the median of * the set of pixel samples used for calculation. * * \param maxProcessors If a value greater than zero is specified, it is * the maximum number of concurrent threads that this function can * execute. If zero or a negative value is specified, the current * thread limit for this image will be used instead (see * AbstractImage::SetMaxProcessors()). The default value is zero. * * See AvgDev() for complete information and references. * * \note Increments the status monitoring object by the number of selected * pixel samples. */ TwoSidedEstimate TwoSidedAvgDev( double center, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1, int maxProcessors = 0 ) const { Rect r = rect; if ( !ParseSelection( r, firstChannel, lastChannel ) ) return 0; size_type N = size_type( r.Width() )*size_type( r.Height() )*(1 + lastChannel - firstChannel); if ( m_status.IsInitializationEnabled() ) m_status.Initialize( "Computing two-sided average absolute deviation", N ); Array L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors ); bool useAffinity = m_parallel && Thread::IsRootThread(); ReferenceArray threads; for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) ) threads.Add( new TwoSidedSumAbsDevThread( *this, center, r, firstChannel, lastChannel, n, n + int( L[i] ) ) ); if ( threads.Length() > 1 ) { int n = 0; for ( TwoSidedSumAbsDevThread& thread : threads ) thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 ); for ( TwoSidedSumAbsDevThread& thread : threads ) thread.Wait(); } else threads[0].Run(); double s0 = 0, s1 = 0; double e0 = 0, e1 = 0; size_type n0 = 0, n1 = 0; for ( const TwoSidedSumAbsDevThread& thread : threads ) { double y = thread.s0 - e0; double t = s0 + y; e0 = (t - s0) - y; s0 = t; n0 += thread.n0; y = thread.s1 - e1; t = s1 + y; e1 = (t - s1) - y; s1 = t; n1 += thread.n1; } threads.Destroy(); m_status += N; return { (n0 > 0) ? s0/n0 : 0.0, (n1 > 0) ? s1/n1 : 0.0 }; } /*! * Returns the median absolute deviation (MAD) of a subset of pixel samples * with respect to the specified \a center value. * * \param center Reference central value for computation of MAD in the * normalized range [0,1]. Normally, this value should be the * median of the selected pixel samples, or the median of the * whole image. * * \param maxProcessors If a value greater than zero is specified, it is * the maximum number of concurrent threads that this function can * execute. If zero or a negative value is specified, the current * thread limit for this image will be used instead (see * AbstractImage::SetMaxProcessors()). The default value is zero. * * For information on the rest of parameters of this member function, see * the documentation for Fill(). * * This member function returns MAD in the normalized range [0,1] with * respect to \a center, irrespective of the sample data type of the image. * * For complex-valued images, this function returns the MAD of the * magnitudes (or absolute values) of all selected samples. * * If range clipping is enabled for this image, only pixel samples within * the current clipping range will be taken into account for calculation of * the median absolute deviation. * * \note To make the MAD estimator consistent with the standard deviation of * a normal distribution, it must be multiplied by the constant 1.4826. * * \note Increments the status monitoring object by the number of selected * pixel samples. */ double MAD( double center, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1, int maxProcessors = 0 ) const { Rect r = rect; if ( !ParseSelection( r, firstChannel, lastChannel ) ) return 0; size_type N = size_type( r.Width() )*size_type( r.Height() )*(1 + lastChannel - firstChannel); if ( m_status.IsInitializationEnabled() ) m_status.Initialize( "Computing median absolute deviation", N ); if ( N <= 2560000 ) { AbsDevSmpThread S( *this, center, r, firstChannel, lastChannel, 0, r.Height() ); S.Run(); if ( S.n == 0 ) { m_status += N; return 0; } double m = *pcl::Select( S.values.Begin(), S.values.At( S.n ), S.n >> 1 ); if ( S.n & 1 ) { m_status += N; return m; } m = (m + *pcl::Select( S.values.Begin(), S.values.At( S.n ), (S.n >> 1)-1 ))/2; m_status += N; return m; } Array L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors, 160*1024/*overheadLimitPx*/ ); bool useAffinity = m_parallel && Thread::IsRootThread(); double low, high; size_type count = 0; { ReferenceArray threads; for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) ) threads << new ExtremeAbsDevThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ), center ); if ( threads.Length() > 1 ) { int n = 0; for ( ExtremeAbsDevThread& thread : threads ) thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 ); for ( ExtremeAbsDevThread& thread : threads ) thread.Wait(); } else threads[0].Run(); for ( size_type i = 0; i < threads.Length(); ++i ) if ( threads[i].count > 0 ) { low = threads[i].minAbsDev; high = threads[i].maxAbsDev; count += threads[i].count; while ( ++i < threads.Length() ) if ( threads[i].count > 0 ) { if ( threads[i].minAbsDev < low ) low = threads[i].minAbsDev; if ( threads[i].maxAbsDev > high ) high = threads[i].maxAbsDev; count += threads[i].count; } break; } threads.Destroy(); } const double eps = 2*std::numeric_limits::epsilon(); if ( count == 0 || high - low < eps ) { m_status += N; return 0; } ReferenceArray threads; for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) ) threads << new AbsDevHistogramThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ), center, low, high ); double mh = 0, l0 = low, h0 = high; SzVector H0; for ( size_type n = 0, n2 = count >> 1, step = 0, it = 0;; ++it ) { SzVector H; if ( it == 0 && step ) H = H0; else { if ( threads.Length() > 1 ) { int i = 0; for ( AbsDevHistogramThread& thread : threads ) thread.Start( ThreadPriority::DefaultMax, useAffinity ? i++ : -1 ); for ( AbsDevHistogramThread& thread : threads ) thread.Wait(); } else threads[0].Run(); H = threads[0].H; for ( size_type i = 1; i < threads.Length(); ++i ) H += threads[i].H; if ( it == 0 ) if ( (count & 1) == 0 ) H0 = H; } for ( int i = 0; ; n += H[i++] ) if ( n + H[i] > n2 ) { double range = high - low; high = (range * (i + 1))/(__PCL_MEDIAN_HISTOGRAM_LENGTH - 1) + low; low = (range * i)/(__PCL_MEDIAN_HISTOGRAM_LENGTH - 1) + low; if ( high - low < eps ) { if ( count & 1 ) { threads.Destroy(); m_status += N; return low; } if ( step ) { threads.Destroy(); m_status += N; return (low + mh)/2; } mh = low; low = l0; high = h0; n = 0; --n2; ++step; it = 0; } break; } } } /*! * Returns the two-sided median absolute deviation (MAD) of a subset of * pixel samples with respect to the specified \a center value. * * \param center Reference central value for computation of MAD in the * normalized range [0,1]. Normally, this value should be the * median of the selected pixel samples, or the median of the * whole image. * * \param maxProcessors If a value greater than zero is specified, it is * the maximum number of concurrent threads that this function can * execute. If zero or a negative value is specified, the current * thread limit for this image will be used instead (see * AbstractImage::SetMaxProcessors()). The default value is zero. * * See MAD() for more information and references. * * \note Increments the status monitoring object by the number of selected * pixel samples. */ TwoSidedEstimate TwoSidedMAD( double center, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1, int maxProcessors = 0 ) const { Rect r = rect; if ( !ParseSelection( r, firstChannel, lastChannel ) ) return 0; size_type N = size_type( r.Width() )*size_type( r.Height() )*(1 + lastChannel - firstChannel); if ( m_status.IsInitializationEnabled() ) m_status.Initialize( "Computing two-sided median absolute deviation", N ); if ( N <= 2560000 ) { TwoSidedAbsDevSmpThread S( *this, center, r, firstChannel, lastChannel, 0, r.Height() ); S.Run(); m_status += N; return { pcl::Median( S.values.Begin(), S.p ), pcl::Median( S.q, S.values.End() ) }; } Array L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors, 160*1024/*overheadLimitPx*/ ); bool useAffinity = m_parallel && Thread::IsRootThread(); double minLow = 0, minHigh = 0, maxLow = 0, maxHigh = 0; size_type nLow = 0, nHigh = 0; { ReferenceArray threads; for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) ) threads << new TwoSidedExtremeAbsDevThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ), center ); if ( threads.Length() > 1 ) { int n = 0; for ( TwoSidedExtremeAbsDevThread& thread : threads ) thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 ); for ( TwoSidedExtremeAbsDevThread& thread : threads ) thread.Wait(); } else threads[0].Run(); for ( size_type i = 0; i < threads.Length(); ++i ) if ( threads[i].nLow > 0 ) { minLow = threads[i].minAbsDevLow; maxLow = threads[i].maxAbsDevLow; nLow = threads[i].nLow; while ( ++i < threads.Length() ) if ( threads[i].nLow > 0 ) { if ( threads[i].minAbsDevLow < minLow ) minLow = threads[i].minAbsDevLow; if ( threads[i].maxAbsDevLow > maxLow ) maxLow = threads[i].maxAbsDevLow; nLow += threads[i].nLow; } break; } for ( size_type i = 0; i < threads.Length(); ++i ) if ( threads[i].nHigh > 0 ) { minHigh = threads[i].minAbsDevHigh; maxHigh = threads[i].maxAbsDevHigh; nHigh = threads[i].nHigh; while ( ++i < threads.Length() ) if ( threads[i].nHigh > 0 ) { if ( threads[i].minAbsDevHigh < minHigh ) minHigh = threads[i].minAbsDevHigh; if ( threads[i].maxAbsDevHigh > maxHigh ) maxHigh = threads[i].maxAbsDevHigh; nHigh += threads[i].nHigh; } break; } threads.Destroy(); } int side; double sideLow, sideHigh; ReferenceArray threads; for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) ) threads << new TwoSidedAbsDevHistogramThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ), center, side, sideLow, sideHigh ); const double eps = 2*std::numeric_limits::epsilon(); double mad[ 2 ]; for ( side = 0; side < 2; ++side ) { size_type n = side ? nHigh : nLow; if ( n < 2 ) { mad[side] = 0; continue; } sideLow = side ? minHigh : minLow; sideHigh = side ? maxHigh : maxLow; if ( sideHigh - sideLow < eps ) { mad[side] = 0; continue; } double mh = 0, h0 = sideHigh; SzVector H0; for ( size_type count = 0, n2 = n >> 1, step = 0, it = 0;; ++it ) { SzVector H; if ( it == 0 && step ) H = H0; else { if ( threads.Length() > 1 ) { int i = 0; for ( TwoSidedAbsDevHistogramThread& thread : threads ) thread.Start( ThreadPriority::DefaultMax, useAffinity ? i++ : -1 ); for ( TwoSidedAbsDevHistogramThread& thread : threads ) thread.Wait(); } else threads[0].Run(); H = threads[0].H; for ( size_type i = 1; i < threads.Length(); ++i ) H += threads[i].H; if ( it == 0 ) if ( (n & 1) == 0 ) H0 = H; } for ( int i = 0; ; count += H[i++] ) if ( count + H[i] > n2 ) { double range = sideHigh - sideLow; sideHigh = (range * (i + 1))/(__PCL_MEDIAN_HISTOGRAM_LENGTH - 1) + sideLow; sideLow = (range * i)/(__PCL_MEDIAN_HISTOGRAM_LENGTH - 1) + sideLow; if ( sideHigh - sideLow < eps ) { if ( n & 1 ) { mad[side] = sideLow; goto __madNextSide; } if ( step ) { mad[side] = (sideLow + mh)/2; goto __madNextSide; } mh = sideLow; sideLow = 0; sideHigh = h0; count = 0; --n2; ++step; it = 0; } break; } } __madNextSide: ; } threads.Destroy(); m_status += N; return { mad[0], mad[1] }; } /*! * Returns a biweight midvariance (BWMV) for a subset of pixel samples. * * \param center Reference center value in the [0,1] range. Normally, the * median of the selected pixel samples should be used. * * \param sigma A reference estimate of dispersion in the [0,1] range. * Normally, the median absolute deviation from the median * (MAD) of the selected pixel samples should be used. * * \param k Rejection limit in sigma units. The default value is k=9. * * \param maxProcessors If a value greater than zero is specified, it is * the maximum number of concurrent threads that this function can * execute. If zero or a negative value is specified, the current * thread limit for this image will be used instead (see * AbstractImage::SetMaxProcessors()). The default value is zero. * * For information on the rest of parameters of this member function, see * the documentation for Fill(). * * The square root of the biweight midvariance is a robust estimator of * scale. It is an efficient estimator with respect to many statistical * distributions (about 87% Gaussian efficiency), and appears to have a * breakdown point close to 0.5 (the same as MAD). * * This member function returns BWMV in the normalized range [0,1], * irrespective of the sample data type of the image. * * For complex-valued images, this function returns a BWMV for the * magnitudes (or absolute values) of all selected samples. * * If range clipping is enabled for this image, only pixel samples within * the current clipping range will be taken into account for calculation of * the biweight midvariance. * * \b References * * Rand R. Wilcox (2012), Introduction to Robust Estimation and Hypothesis * Testing, 3rd Edition, Elsevier Inc., Section 3.12.1. * * \note Increments the status monitoring object by the number of selected * pixel samples. */ double BiweightMidvariance( double center, double sigma, int k = 9, bool reducedLength = false, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1, int maxProcessors = 0 ) const { Rect r = rect; if ( !ParseSelection( r, firstChannel, lastChannel ) ) return 0; size_type N = size_type( r.Width() )*size_type( r.Height() )*(1 + lastChannel - firstChannel); double kd = k * sigma; if ( kd < 0 || 1 + kd == 1 ) { m_status += N; return 0; } if ( m_status.IsInitializationEnabled() ) m_status.Initialize( "Computing biweight midvariance", N ); Array L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors ); bool useAffinity = m_parallel && Thread::IsRootThread(); ReferenceArray threads; for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) ) threads.Add( new BWMVThread( *this, center, kd, r, firstChannel, lastChannel, n, n + int( L[i] ) ) ); if ( threads.Length() > 1 ) { int n = 0; for ( BWMVThread& thread : threads ) thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 ); for ( BWMVThread& thread : threads ) thread.Wait(); } else threads[0].Run(); double num = 0, den = 0; size_type n = 0, nr = 0; for ( const BWMVThread& thread : threads ) { num += thread.num; den += thread.den; n += thread.n; nr += thread.nr; } threads.Destroy(); m_status += N; den *= den; return (n >= 2 && 1 + den != 1) ? (reducedLength ? nr : n)*num/den : 0.0; } /*! * Returns a two-sided biweight midvariance (BWMV) for a subset of pixel * samples. * * \param center Reference center value in the [0,1] range. Normally, the * median of the selected pixel samples should be used. * * \param sigma Reference estimates of dispersion in the [0,1] range, * respectively for samples below and above the reference \a center * value (low and high members of the TwoSidedEstimate structure, * respectively). Normally, a two-sided median absolute deviation * from the median (two-sided MAD) for the selected pixel samples * should be specified. * * \param k Rejection limit in sigma units. The default value is k=9. * * \param maxProcessors If a value greater than zero is specified, it is * the maximum number of concurrent threads that this function can * execute. If zero or a negative value is specified, the current * thread limit for this image will be used instead (see * AbstractImage::SetMaxProcessors()). The default value is zero. * * See the BiweightMidvariance() member function for more information and * references. * * \note Increments the status monitoring object by the number of selected * pixel samples. */ TwoSidedEstimate TwoSidedBiweightMidvariance( double center, const TwoSidedEstimate& sigma, int k = 9, bool reducedLength = false, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1, int maxProcessors = 0 ) const { Rect r = rect; if ( !ParseSelection( r, firstChannel, lastChannel ) ) return 0; size_type N = size_type( r.Width() )*size_type( r.Height() )*(1 + lastChannel - firstChannel); double kd0 = k * sigma.low; double kd1 = k * sigma.high; if ( kd0 < 0 || 1 + kd0 == 1 || kd1 < 0 || 1 + kd1 == 1 ) { m_status += N; return 0; } if ( m_status.IsInitializationEnabled() ) m_status.Initialize( "Computing two-sided biweight midvariance", N ); Array L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors ); bool useAffinity = m_parallel && Thread::IsRootThread(); ReferenceArray threads; for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) ) threads.Add( new TwoSidedBWMVThread( *this, center, kd0, kd1, r, firstChannel, lastChannel, n, n + int( L[i] ) ) ); if ( threads.Length() > 1 ) { int n = 0; for ( TwoSidedBWMVThread& thread : threads ) thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 ); for ( TwoSidedBWMVThread& thread : threads ) thread.Wait(); } else threads[0].Run(); double num0 = 0, den0 = 0, num1 = 0, den1 = 0; size_type n0 = 0, n1 = 0, nr0 = 0, nr1 = 0; for ( const TwoSidedBWMVThread& thread : threads ) { num0 += thread.num0; den0 += thread.den0; num1 += thread.num1; den1 += thread.den1; n0 += thread.n0; n1 += thread.n1; nr0 += thread.nr0; nr1 += thread.nr1; } threads.Destroy(); m_status += N; den0 *= den0; den1 *= den1; return { (n0 >= 2 && 1 + den0 != 1) ? (reducedLength ? nr0 : n0)*num0/den0 : 0.0, (n1 >= 2 && 1 + den1 != 1) ? (reducedLength ? nr1 : n1)*num1/den1 : 0.0 }; } /*! * Returns a percentage bend midvariance (PBMV) for a subset of pixel * samples. * * \param center Reference center value in the [0,1] range. Normally, the * median of the selected pixel samples should be used. * * \param beta Rejection parameter in the [0,0.5] range. Higher values * improve robustness to outliers (i.e., increase the * breakdown point of the estimator) at the expense of lower * efficiency. The default value is beta=0.2. * * \param maxProcessors If a value greater than zero is specified, it is * the maximum number of concurrent threads that this function can * execute. If zero or a negative value is specified, the current * thread limit for this image will be used instead (see * AbstractImage::SetMaxProcessors()). The default value is zero. * * For information on the rest of parameters of this member function, see * the documentation for Fill(). * * The square root of the percentage bend midvariance is a robust estimator * of scale. With the default beta=0.2, its Gaussian efficiency is 67%. With * beta=0.1, its efficiency is 85% but its breakdown is only 0.1. * * This member function returns PBMV in the normalized range [0,1], * irrespective of the sample data type of the image. * * For complex-valued images, this function returns a PBMV for the * magnitudes (or absolute values) of all selected samples. * * If range clipping is enabled for this image, only pixel samples within * the current clipping range will be taken into account for calculation of * the percentage bend midvariance. * * \b References * * Rand R. Wilcox (2012), Introduction to Robust Estimation and Hypothesis * Testing, 3rd Edition, Elsevier Inc., Section 3.12.3. * * \note Increments the status monitoring object by the number of selected * pixel samples. */ double BendMidvariance( double center, double beta = 0.2, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1, int maxProcessors = 0 ) const { Rect r = rect; if ( !ParseSelection( r, firstChannel, lastChannel ) ) return 0; size_type N = size_type( r.Width() )*size_type( r.Height() )*(1 + lastChannel - firstChannel); if ( m_status.IsInitializationEnabled() ) m_status.Initialize( "Computing percentage bend midvariance", N ); Array L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors ); bool useAffinity = m_parallel && Thread::IsRootThread(); ReferenceArray threads; for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) ) threads.Add( new DSmpThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ) ) ); if ( threads.Length() > 1 ) { int n = 0; for ( DSmpThread& thread : threads ) thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 ); for ( DSmpThread& thread : threads ) thread.Wait(); } else threads[0].Run(); Array values; for ( DSmpThread& thread : threads ) if ( !thread.values.IsEmpty() ) { values.Add( thread.values.Begin(), thread.values.At( thread.n ) ); thread.values.Clear(); } threads.Destroy(); double pbmv = pcl::BendMidvariance( values.Begin(), values.End(), center, beta ); m_status += N; return pbmv; } /*! * Sn scale estimator of Rousseeuw and Croux for a subset of pixel samples. * * \param maxProcessors If a value greater than zero is specified, it is * the maximum number of concurrent threads that this function can * execute. If zero or a negative value is specified, the current * thread limit for this image will be used instead (see * AbstractImage::SetMaxProcessors()). The default value is zero. * * For information on the rest of parameters of this member function, see * the documentation for Fill(). * * For a sample {x0,...,x_n-1}, the Sn estimator is given by: * * Sn = c * low_median( high_median( |x_i - x_j| ) ) * * where low_median() is the order statistic of rank (n + 1)/2, and * high_median() is the order statistic of rank n/2 + 1. * * This member function returns Sn in the normalized range [0,1], * irrespective of the sample data type of the image. * * For complex-valued images, this function returns the Sn estimator of the * magnitudes (or absolute values) of all selected samples. * * If range clipping is enabled for this image, only pixel samples within * the current clipping range will be taken into account for calculation of * the Sn estimator. * * The constant c = 1.1926 must be used to make the Sn estimator converge to * the standard deviation of a pure normal distribution. However, this * implementation does not apply it (it uses c=1 implicitly), for * consistency with other implementations of scale estimators. * * \b References * * P.J. Rousseeuw and C. Croux (1993), Alternatives to the Median Absolute * Deviation, Journal of the American Statistical Association, Vol. 88, * pp. 1273-1283. * * \note Increments the status monitoring object by the number of selected * pixel samples. */ double Sn( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1, int maxProcessors = 0 ) const { Rect r = rect; if ( !ParseSelection( r, firstChannel, lastChannel ) ) return 0; size_type N = size_type( r.Width() )*size_type( r.Height() )*(1 + lastChannel - firstChannel); if ( m_status.IsInitializationEnabled() ) m_status.Initialize( "Computing Sn scale estimate", N ); Array L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors ); bool useAffinity = m_parallel && Thread::IsRootThread(); ReferenceArray threads; for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) ) threads.Add( new DSmpThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ) ) ); if ( threads.Length() > 1 ) { int n = 0; for ( DSmpThread& thread : threads ) thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 ); for ( DSmpThread& thread : threads ) thread.Wait(); } else threads[0].Run(); Array values; for ( DSmpThread& thread : threads ) if ( !thread.values.IsEmpty() ) { values.Add( thread.values.Begin(), thread.values.At( thread.n ) ); thread.values.Clear(); } threads.Destroy(); double sn = pcl::Sn( values.Begin(), values.End() ); m_status += N; return sn; } /*! * Qn scale estimator of Rousseeuw and Croux for a subset of pixel samples. * * \param maxProcessors If a value greater than zero is specified, it is * the maximum number of concurrent threads that this function can * execute. If zero or a negative value is specified, the current * thread limit for this image will be used instead (see * AbstractImage::SetMaxProcessors()). The default value is zero. * * For information on the rest of parameters of this member function, see * the documentation for Fill(). * * For a sample {x0,...,x_n-1}, the Qn estimator is given by: * * Qn = c * first_quartile( |x_i - x_j| : i < j ) * * where first_quartile() is the order statistic of rank (n + 1)/4. * * This member function returns Qn in the normalized range [0,1], * irrespective of the sample data type of the image. * * For complex-valued images, this function returns the Qn estimator of the * magnitudes (or absolute values) of all selected samples. * * If range clipping is enabled for this image, only pixel samples within * the current clipping range will be taken into account for calculation of * the Qn estimator. * * The constant c = 2.2219 must be used to make the Qn estimator converge to * the standard deviation of a pure normal distribution. However, this * implementation does not apply it (it uses c=1 implicitly), for * consistency with other implementations of scale estimators. * * \b References * * P.J. Rousseeuw and C. Croux (1993), Alternatives to the Median Absolute * Deviation, Journal of the American Statistical Association, Vol. 88, * pp. 1273-1283. * * \note Increments the status monitoring object by the number of selected * pixel samples. */ double Qn( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1, int maxProcessors = 0 ) const { Rect r = rect; if ( !ParseSelection( r, firstChannel, lastChannel ) ) return 0; size_type N = size_type( r.Width() )*size_type( r.Height() )*(1 + lastChannel - firstChannel); if ( m_status.IsInitializationEnabled() ) m_status.Initialize( "Computing Qn scale estimate", N ); Array L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors ); bool useAffinity = m_parallel && Thread::IsRootThread(); ReferenceArray threads; for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) ) threads.Add( new DSmpThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ) ) ); if ( threads.Length() > 1 ) { int n = 0; for ( DSmpThread& thread : threads ) thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 ); for ( DSmpThread& thread : threads ) thread.Wait(); } else threads[0].Run(); Array values; for ( DSmpThread& thread : threads ) if ( !thread.values.IsEmpty() ) { values.Add( thread.values.Begin(), thread.values.At( thread.n ) ); thread.values.Clear(); } threads.Destroy(); double qn = pcl::Qn( values.Begin(), values.End() ); m_status += N; return qn; } /*! * Returns the norm of a subset of pixel samples. * * \param maxProcessors If a value greater than zero is specified, it is * the maximum number of concurrent threads that this function can * execute. If zero or a negative value is specified, the current * thread limit for this image will be used instead (see * AbstractImage::SetMaxProcessors()). The default value is zero. * * For information on the rest of parameters of this member function, see * the documentation for Fill(). * * The norm is the sum of all selected pixel sample values. This member * function returns the norm in the normalized range [0,1], irrespective of * the sample data type of the image. * * For real-valued integer images, this function returns the sum of all * selected samples converted to the normalized range [0,1]. * * For floating-point real images, this function simply sums all selected * samples, assuming that no selected samples have negative values. If there * are some negative values, the returned value may make not sense, * depending on the application. * * For complex-valued images, this function returns the sum of the * magnitudes (or absolute values) of all selected samples. * * If range clipping is enabled for this image, only pixel samples within * the current clipping range will be taken into account for calculation of * the norm. * * This function implements a numerically stable summation algorithm to * reduce roundoff errors to the machine's floating point precision. * * \note Increments the status monitoring object by the number of selected * pixel samples. */ double Norm( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1, int maxProcessors = 0 ) const { Rect r = rect; if ( !ParseSelection( r, firstChannel, lastChannel ) ) return 0; size_type N = size_type( r.Width() )*size_type( r.Height() )*(1 + lastChannel - firstChannel); if ( m_status.IsInitializationEnabled() ) m_status.Initialize( "Computing norm", N ); Array L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors ); bool useAffinity = m_parallel && Thread::IsRootThread(); ReferenceArray threads; for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) ) threads.Add( new SumThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ) ) ); if ( threads.Length() > 1 ) { int n = 0; for ( SumThread& thread : threads ) thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 ); for ( SumThread& thread : threads ) thread.Wait(); } else threads[0].Run(); double s = 0; double e = 0; for ( const SumThread& thread : threads ) { double y = thread.s - e; double t = s + y; e = (t - s) - y; s = t; } threads.Destroy(); m_status += N; return (1 + s != 1) ? s : 0.0; // don't return insignificant nonzero values } /*! * Returns the modulus of a subset of pixel samples. * * \param maxProcessors If a value greater than zero is specified, it is * the maximum number of concurrent threads that this function can * execute. If zero or a negative value is specified, the current * thread limit for this image will be used instead (see * AbstractImage::SetMaxProcessors()). The default value is zero. * * For information on the rest of parameters of this member function, see * the documentation for Fill(). * * The modulus is the sum of the absolute values of all selected pixel * sample values. This member function returns the modulus in the normalized * range [0,1], irrespective of the sample data type of the image. * * This member function only makes sense for real-valued floating point * images. For the rest of image types, including unsigned integer and * complex valued images, this function is always equivalent to Norm(). * * If range clipping is enabled for this image, only pixel samples within * the current clipping range will be taken into account for calculation of * the modulus. * * This function implements a numerically stable summation algorithm to * reduce roundoff errors to the machine's floating point precision. * * \note Increments the status monitoring object by the number of selected * pixel samples. */ double Modulus( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1, int maxProcessors = 0 ) const { Rect r = rect; if ( !ParseSelection( r, firstChannel, lastChannel ) ) return 0; size_type N = size_type( r.Width() )*size_type( r.Height() )*(1 + lastChannel - firstChannel); if ( m_status.IsInitializationEnabled() ) m_status.Initialize( "Computing modulus", N ); Array L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors ); bool useAffinity = m_parallel && Thread::IsRootThread(); ReferenceArray threads; for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) ) threads.Add( new SumAbsThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ) ) ); if ( threads.Length() > 1 ) { int n = 0; for ( SumAbsThread& thread : threads ) thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 ); for ( SumAbsThread& thread : threads ) thread.Wait(); } else threads[0].Run(); double s = 0; double e = 0; for ( const SumAbsThread& thread : threads ) { double y = thread.s - e; double t = s + y; e = (t - s) - y; s = t; } threads.Destroy(); m_status += N; return (1 + s != 1) ? s : 0.0; // don't return insignificant nonzero values } /*! * Returns the sum of the squares of a subset of pixel samples. * * \param maxProcessors If a value greater than zero is specified, it is * the maximum number of concurrent threads that this function can * execute. If zero or a negative value is specified, the current * thread limit for this image will be used instead (see * AbstractImage::SetMaxProcessors()). The default value is zero. * * For information on the rest of parameters of this member function, see * the documentation for Fill(). * * This member function returns the sum of squares in the normalized range * [0,1], irrespective of the sample data type of the image. * * For complex-valued images, this function returns the sum of the * squares of the magnitudes (or absolute values) of all selected samples. * * If range clipping is enabled for this image, only pixel samples within * the current clipping range will be taken into account for calculation of * the sum of squares. * * This function implements a numerically stable summation algorithm to * reduce roundoff errors to the machine's floating point precision. * * \note Increments the status monitoring object by the number of selected * pixel samples. */ double SumOfSquares( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1, int maxProcessors = 0 ) const { Rect r = rect; if ( !ParseSelection( r, firstChannel, lastChannel ) ) return 0; size_type N = size_type( r.Width() )*size_type( r.Height() )*(1 + lastChannel - firstChannel); if ( m_status.IsInitializationEnabled() ) m_status.Initialize( "Computing sum of squares", N ); Array L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors ); bool useAffinity = m_parallel && Thread::IsRootThread(); ReferenceArray threads; for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) ) threads.Add( new SumSqrThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ) ) ); if ( threads.Length() > 1 ) { int n = 0; for ( SumSqrThread& thread : threads ) thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 ); for ( SumSqrThread& thread : threads ) thread.Wait(); } else threads[0].Run(); double s = 0; double e = 0; for ( const SumSqrThread& thread : threads ) { double y = thread.s - e; double t = s + y; e = (t - s) - y; s = t; } threads.Destroy(); m_status += N; return (1 + s != 1) ? s : 0.0; // don't return insignificant nonzero values } /*! * Returns the mean of the squares of a subset of pixel samples. * * \param maxProcessors If a value greater than zero is specified, it is * the maximum number of concurrent threads that this function can * execute. If zero or a negative value is specified, the current * thread limit for this image will be used instead (see * AbstractImage::SetMaxProcessors()). The default value is zero. * * For information on the rest of parameters of this member function, see * the documentation for Fill(). * * This member function returns the mean of squares in the normalized range * [0,1], irrespective of the sample data type of the image. * * For complex-valued images, this function returns the mean of the * squares of the magnitudes (or absolute values) of all selected samples. * * If range clipping is enabled for this image, only pixel samples within * the current clipping range will be taken into account for calculation of * the mean of squares. * * This function implements a numerically stable summation algorithm to * reduce roundoff errors to the machine's floating point precision. * * \note Increments the status monitoring object by the number of selected * pixel samples. */ double MeanOfSquares( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1, int maxProcessors = 0 ) const { Rect r = rect; if ( !ParseSelection( r, firstChannel, lastChannel ) ) return 0; size_type N = size_type( r.Width() )*size_type( r.Height() )*(1 + lastChannel - firstChannel); if ( m_status.IsInitializationEnabled() ) m_status.Initialize( "Computing mean of squares", N ); Array L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors ); bool useAffinity = m_parallel && Thread::IsRootThread(); ReferenceArray threads; for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) ) threads.Add( new SumSqrThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ) ) ); if ( threads.Length() > 1 ) { int n = 0; for ( SumSqrThread& thread : threads ) thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 ); for ( SumSqrThread& thread : threads ) thread.Wait(); } else threads[0].Run(); double s = 0; double e = 0; size_type n = 0; for ( const SumSqrThread& thread : threads ) { double y = thread.s - e; double t = s + y; e = (t - s) - y; s = t; n += thread.n; } threads.Destroy(); m_status += N; if ( n < 1 ) return 0; return s/n; } /*! * Returns a vector of norms for a subset of pixel samples. * * \param maxDegree Maximum degree ≥ 1 of the computed vector of * norms. The default value is 2, hence the L1 and L2 norms are * computed if this parameter is not specified. * * \param maxProcessors If a value greater than zero is specified, it is * the maximum number of concurrent threads that this function can * execute. If zero or a negative value is specified, the current * thread limit for this image will be used instead (see * AbstractImage::SetMaxProcessors()). The default value is zero. * * For information on the rest of parameters of this member function, see * the documentation for Fill(). * * This function computes a series of image norms, up to \a maxDegree, in a * single multithreaded operation for improved efficiency. The norms are * returned as a vector. The first vector component is the L1 norm, which is * the sum of all selected pixel sample values. The second vector component * is the L2 norm, or the sum of squared pixel sample values, and so on. * * For the sake of performance, this function assumes that no negative * values exist in the selected subset of pixel samples; otherwise, odd * norms will provide meaningless results. All norms are returned expressed * in the normalized range [0,1], irrespective of the sample data type of * the image. * * This function implements a numerically stable summation algorithm to * reduce roundoff errors to the machine's floating point precision. * * See the Norm() and SumOfSquares() member functions for more information * and important implementation details. * * \note Increments the status monitoring object by the number of selected * pixel samples. */ Vector Norms( int maxDegree = 2, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1, int maxProcessors = 0 ) const { PCL_PRECONDITION( maxDegree > 0 ) maxDegree = pcl::Max( 1, maxDegree ); Rect r = rect; if ( !ParseSelection( r, firstChannel, lastChannel ) ) return 0; size_type N = size_type( r.Width() )*size_type( r.Height() )*(1 + lastChannel - firstChannel); if ( m_status.IsInitializationEnabled() ) m_status.Initialize( "Computing norms", N ); Array L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors ); bool useAffinity = m_parallel && Thread::IsRootThread(); ReferenceArray threads; for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) ) threads.Add( new NormThread( *this, maxDegree, r, firstChannel, lastChannel, n, n + int( L[i] ) ) ); if ( threads.Length() > 1 ) { int n = 0; for ( NormThread& thread : threads ) thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 ); for ( NormThread& thread : threads ) thread.Wait(); } else threads[0].Run(); Vector R( 0.0, maxDegree ); Vector e( 0.0, maxDegree ); for ( const NormThread& thread : threads ) for ( int i = 0; i < maxDegree; ++i ) { double y = thread.R[i] - e[i]; double t = R[i] + y; e[i] = (t - R[i]) - y; R[i] = t; } for ( int i = 0; i < maxDegree; ++i ) if ( 1 + R[i] == 1 ) // don't return insignificant nonzero values R[i] = 0; threads.Destroy(); m_status += N; return R; } /*! * Returns a 64-bit non-cryptographic hash value computed for the specified * \a channel of this image. * * This function calls pcl::Hash64() for the internal pixel sample buffer. * * The \a seed parameter can be used to generate repeatable hash values. It * can also be set to a random value in compromised environments. * * For information on the rest of parameters of this member function, see * the documentation for Fill(). */ uint64 Hash64( int channel = -1, uint64 seed = 0 ) const noexcept { if ( !ParseChannel( channel ) ) return 0; return pcl::Hash64( m_channelData( channel ), ChannelSize(), seed ); } /*! * Returns a 32-bit non-cryptographic hash value computed for the specified * \a channel of this image. * * This function calls pcl::Hash32() for the internal pixel sample buffer. * * The \a seed parameter can be used to generate repeatable hash values. It * can also be set to a random value in compromised environments. * * For information on the rest of parameters of this member function, see * the documentation for Fill(). */ uint32 Hash32( int channel = -1, uint32 seed = 0 ) const noexcept { if ( !ParseChannel( channel ) ) return 0; return pcl::Hash32( m_channelData( channel ), ChannelSize(), seed ); } /*! * Returns a non-cryptographic hash value computed for the specified * \a channel of this image. This function is a synonym for Hash64(). */ uint64 Hash( int channel = -1, uint64 seed = 0 ) const noexcept { return Hash64( channel, seed ); } // ------------------------------------------------------------------------- /*! * Writes a subset of pixel samples to a raw-storage output stream. Returns * a reference to this image. * * \param file Output stream to write to. * * The generated raw-storage can be used to generate a new image with the * Read() member function. * * \note Increments the status monitoring object by the number of pixel * samples written to the specified \a file. * * \note This member function, together with the Read() function, implements * a lightweight image reading/writing system with limited error detection * and handling capabilities. For more powerful and sophisticated ways to * serialize image data, see the ImageVariant class. */ GenericImage& Write( File& file, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) const { Rect r = rect; if ( !ParseSelection( r, firstChannel, lastChannel ) ) return const_cast( *this ); size_type N = size_type( r.Width() )*size_type( r.Height() ); int numberOfChannels = 1 + lastChannel - firstChannel; if ( m_status.IsInitializationEnabled() ) m_status.Initialize( "Writing to raw-storage image stream", N*numberOfChannels ); file.WriteUI32( r.Width() ); file.WriteUI32( r.Height() ); file.WriteUI32( numberOfChannels ); file.WriteUI32( (firstChannel == 0 && lastChannel >= 2) ? m_colorSpace : ColorSpace::Gray ); if ( r == Bounds() ) { for ( int i = firstChannel; i <= lastChannel; ++i, m_status += N ) file.Write( (const void*)m_pixelData[i], fsize_type( ChannelSize() ) ); } else { for ( int i = firstChannel, w = r.Width(), h = r.Height(); i <= lastChannel; ++i ) { const sample* p = PixelAddress( r.LeftTop(), i ); for ( int j = 0; j < h; ++j, p += m_width, m_status += w ) file.Write( (const void*)p, w*BytesPerSample() ); } } return const_cast( *this ); } /*! * Writes a subset of pixel samples to a raw-storage file. Returns a * reference to this image. * * \param filePath File path where a new file will be created with pixel * data from this image. * * This member function is just a wrapper to the more general version: * Write( File&, const Rect&, int, int ). */ GenericImage& Write( const String& filePath, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) const { File file = File::CreateFileForWriting( filePath ); return Write( file, rect, firstChannel, lastChannel ); } /*! * Reads an image from a raw-storage stream. Returns a reference to this * image. * * \param file Input stream to read from. * * If this object uniquely references its pixel data, then the previously * existing pixel data are destroyed, deallocated, and replaced with a newly * allocated local or shared image, preserving the storage space of this * instance. * * The input stream must provide pixel sample values in the sample data type * of this image. The sample data type of an image is invariantly defined by * its template instantiation, and thus cannot be changed. If the image * stored in the input stream uses a different sample data type than this * image, this function will lead to unpredictable results, most likely an * exception being thrown by the stream I/O routines. * * If this object is not unique, then the previously referenced pixel data * are dereferenced, and a newly created local image is uniquely referenced * by this object. * * All image selections are reset to the default full selection after * calling this function. * * The data read from the specified \a file should have been generated by a * previous call to Write(). * * \note Increments the status monitoring object by the number of pixel * samples read from the specified \a file. * * \note This function, together with the Write() function, implements a * lightweight image reading/writing system with limited error detection and * handling capabilities. For more powerful and sophisticated ways to * serialize image data, see the ImageVariant class. */ GenericImage& Read( File& file ) { int width, height, numberOfChannels, colorSpace; file.ReadUI32( width ); file.ReadUI32( height ); file.ReadUI32( numberOfChannels ); file.ReadUI32( colorSpace ); AllocateData( width, height, numberOfChannels, color_space( colorSpace ) ); ResetSelections(); size_type N = NumberOfPixels(); if ( N > 0 ) { if ( m_status.IsInitializationEnabled() ) m_status.Initialize( "Reading from raw-storage image stream", N*m_numberOfChannels ); for ( int i = 0; i < m_numberOfChannels; ++i, m_status += N ) file.Read( (void*)m_pixelData[i], fsize_type( ChannelSize() ) ); } return *this; } /*! * Reads an image from a raw-storage file. Returns a reference to this * image. * * \param filePath Path to an existing file from which new pixel data will * be read. The contents of the existing file will not be * modified in any way. * * This member function is just a wrapper to the more general version: * Read( File& ). */ GenericImage& Read( const String& filePath ) { File file = File::OpenFileForReading( filePath ); return Read( file ); } // ------------------------------------------------------------------------- /*! * Crops or extends the entire image by applying the specified margins. * Returns a reference to this image. * * \param left Left margin in pixels. * * \param top Top margin in pixels. * * \param right Right margin in pixels. * * \param bottom Bottom margin in pixels. * * \param fillValues %Vector of filling sample values for extended areas, * used when one or more margins are greater than zero. * Each vector component is used for its corresponding * channel (e.g., the first component is used to fill the * red channel of an RGB image, the second component to * fill the green channel, etc.). If there are less vector * components than channels in the image, the minimum * sample value in the native range of the image (usually * zero or black) is used for the missing filling values. * The default parameter value is an empty vector, so the * extended areas are filled with black by default. * * Negative margins crop the image at their respective sides. Positive * margin values add extra space. * * \note Increments the status monitoring object by the area in square * pixels of the cropping rectangle. */ template GenericImage& CropBy( int left, int top, int right, int bottom, const GenericVector& fillValues ) { if ( left == 0 && top == 0 && right == 0 && bottom == 0 ) return *this; if ( m_width+left+right <= 0 || m_height+top+bottom <= 0 ) { FreeData(); return *this; } Rect r( -left, -top, m_width+right, m_height+bottom ); int width = r.Width(); int height = r.Height(); if ( !Intersects( r ) ) return AllocateData( width, height, m_numberOfChannels, m_colorSpace ).Fill( fillValues ); size_type N = size_type( width )*size_type( height ); EnsureUnique(); if ( m_status.IsInitializationEnabled() ) m_status.Initialize( String().Format( "Crop margins: %+d, %+d, %+d, %+d", left, top, right, bottom ), N*m_numberOfChannels ); sample** newData = nullptr; try { newData = m_allocator.AllocateChannelSlots( m_numberOfChannels ); for ( int c = 0; c < m_numberOfChannels; ++c, m_status += N ) { sample* __restrict__ f = newData[c] = m_allocator.AllocatePixels( N ); sample v = (c < fillValues.Length()) ? P::ToSample( fillValues[c] ) : P::MinSampleValue(); for ( int i = r.y0, j; i < r.y1; ) { for ( ; i < 0; ++i ) { PCL_IVDEP for ( int j = 0; j < width; ++j ) *f++ = v; } PCL_IVDEP for ( j = r.x0; j < 0; ++j ) *f++ = v; for ( const sample* f0 = PixelAddress( j, i, c ); j < r.x1; ) { *f++ = *f0++; if ( ++j == m_width ) { PCL_IVDEP for ( ; j < r.x1; ++j ) *f++ = v; } } if ( ++i == m_height ) for ( ; i < r.y1; ++i ) { PCL_IVDEP for ( int j = 0; j < width; ++j ) *f++ = v; } } } try { for ( int c = 0; c < m_numberOfChannels; ++c ) { m_allocator.Deallocate( m_pixelData[c] ); m_pixelData[c] = newData[c]; } m_allocator.Deallocate( newData ); newData = nullptr; m_allocator.SetSharedGeometry( m_width = width, m_height = height, m_numberOfChannels ); ResetPoint(); ResetSelection(); return *this; } catch ( ... ) { m_data->Deallocate(); ResetSelections(); throw; } } catch ( ... ) { if ( newData != nullptr ) { for ( int i = 0; i < m_numberOfChannels; ++i ) if ( newData[i] != nullptr ) m_allocator.Deallocate( newData[i] ), newData[i] = nullptr; m_allocator.Deallocate( newData ); } throw; } } /*! * Crops or extends the entire image by applying the specified margins, * filling extended regions with the minimum sample value in the native * range of the image. Returns a reference to this image. * * This member function is equivalent to: * * \code * CropBy( left, top, right, bottom, sample_vector() ); * \endcode */ GenericImage& CropBy( int left, int top, int right, int bottom ) { return CropBy( left, top, right, bottom, sample_vector() ); } /*! * Crops the image to the specified rectangular region. Returns a reference * to this image. Returns a reference to this image. * * This member function is equivalent to: * * \code * CropBy( -rect.x0, -rect.y0, rect.x1 - m_width, rect.y1 - m_height, fillValues ); * \endcode */ template GenericImage& CropTo( const Rect& rect, const GenericVector& fillValues ) { Rect r = rect.Ordered(); return CropBy( -r.x0, -r.y0, r.x1 - m_width, r.y1 - m_height, fillValues ); } /*! * Crops the image to the specified rectangular region, filling extended * regions with the minimum sample value in the native range of the image. * Returns a reference to this image. * * This member function is equivalent to: * * \code * CropTo( rect, sample_vector() ); * \endcode */ GenericImage& CropTo( const Rect& rect ) { return CropTo( rect, sample_vector() ); } /*! * Crops the image to the specified rectangular region. Returns a reference * to this image. * * This member function is equivalent to: * * \code * CropTo( Rect( x0, y0, x1, y1 ), fillValues ); * \endcode */ template GenericImage& CropTo( int x0, int y0, int x1, int y1, const GenericVector& fillValues ) { return CropTo( Rect( x0, y0, x1, y1 ), fillValues ); } /*! * Crops the image to the specified rectangular region, filling extended * regions with the minimum sample value in the native range of the image. * Returns a reference to this image. * * This member function is equivalent to: * * \code * CropTo( x0, y0, x1, y1, sample_vector() ); * \endcode */ GenericImage& CropTo( int x0, int y0, int x1, int y1 ) { return CropTo( x0, y0, x1, y1, sample_vector() ); } /*! * Crops the image to its current rectangular selection. Returns a reference * to this image. * * This member function is equivalent to: * * \code * CropTo( SelectedRectangle(), fillValues ); * \endcode */ template GenericImage& Crop( const GenericVector& fillValues ) { return CropTo( m_rectangle, fillValues ); } /*! * Crops the image to its current rectangular selection, filling extended * regions with the minimum sample value in the native range of the image. * Returns a reference to this image. * * This member function is equivalent to: * * \code * Crop( sample_vector() ); * \endcode */ GenericImage& Crop() { return Crop( sample_vector() ); } /*! * Shifts (translates) this image by the specified increments in pixels. * Returns a reference to this image. * * \param dx Horizontal displacement in pixels. * * \param dy Vertical displacement in pixels. * * \param fillValues %Vector of filling sample values for extended areas. * See the documentation for CropBy() for more * information. * * Positive increment values shift the image toward the right and bottom * sides. Negative increments shift toward the left and top sides. * * This member function is equivalent to: * * \code * CropBy( dx, dy, -dx, -dy, fillValues ); * \endcode */ template GenericImage& ShiftBy( int dx, int dy, const GenericVector& fillValues ) { return CropBy( dx, dy, -dx, -dy, fillValues ); } /*! * Shifts (translates) this image by the specified increments in pixels, * filling extended regions with the minimum sample value in the native * range of the image. Returns a reference to this image. * * This member function is equivalent to: * * \code * ShiftBy( dx, dy, sample_vector() ); * \endcode */ GenericImage& ShiftBy( int dx, int dy ) { return ShiftBy( dx, dy, sample_vector() ); } /*! * Shifts (translates) the image to the specified coordinates. Returns a * reference to this image. * * A synonym for ShiftBy( int, int, sample ). */ template GenericImage& ShiftTo( int x, int y, const GenericVector& fillValues ) { return ShiftBy( x, y, fillValues ); } /*! * Shifts (translates) the image to the specified coordinates, filling * extended regions with the minimum sample value in the native range of the * image. Returns a reference to this image. * * This member function is equivalent to: * * \code * ShiftTo( x, y, sample_vector() ); * \endcode */ GenericImage& ShiftTo( int x, int y ) { return ShiftTo( x, y, sample_vector() ); } /*! * Shifts (translates) the image to the specified coordinates. Returns a * reference to this image. * * This member function is equivalent to: * * \code * ShiftBy( p.x, p.y, fillValues ); * \endcode */ template GenericImage& ShiftTo( const Point& p, const GenericVector& fillValues ) { return ShiftBy( p.x, p.y, fillValues ); } /*! * Shifts (translates) the image to the specified coordinates, filling * extended regions with the minimum sample value in the native range of the * image. Returns a reference to this image. * * This member function is equivalent to: * * \code * ShiftTo( p, sample_vector() ); * \endcode */ GenericImage& ShiftTo( const Point& p ) { return ShiftTo( p, sample_vector() ); } /*! * Extends or crops the image centered within the specified \a width and * \a height in pixels. Returns a reference to this image. */ template GenericImage& ShiftToCenter( int width, int height, const GenericVector& fillValues ) { int dx2 = (width - m_width) >> 1; int dy2 = (height - m_height) >> 1; return CropBy( dx2, dy2, width-m_width-dx2, height-m_height-dy2, fillValues ); } /*! * Extends or crops the image centered within the specified \a width and * \a height in pixels, filling extended regions with the minimum sample * value in the native range of the image. Returns a reference to this * image. * * This member function is equivalent to: * * \code * ShiftToCenter( width, height, sample_vector() ); * \endcode */ GenericImage& ShiftToCenter( int width, int height ) { return ShiftToCenter( width, height, sample_vector() ); } /*! * Extends or crops the image at its right and bottom sides to the specified * \a width and \a height in pixels. Returns a reference to this image. */ template GenericImage& ShiftToTopLeft( int width, int height, const GenericVector& fillValues ) { int dx = width - m_width; int dy = height - m_height; return CropBy( 0, 0, dx, dy, fillValues ); } /*! * Extends or crops the image at its right and bottom sides to the specified * \a width and \a height in pixels, filling extended regions with the * minimum sample value in the native range of the image. Returns a * reference to this image. * * This member function is equivalent to: * * \code * ShiftToTopLeft( width, height, sample_vector() ); * \endcode */ GenericImage& ShiftToTopLeft( int width, int height ) { return ShiftToTopLeft( width, height, sample_vector() ); } /*! * Extends or crops the image at its left and bottom sides to the specified * \a width and \a height in pixels. Returns a reference to this image. */ template GenericImage& ShiftToTopRight( int width, int height, const GenericVector& fillValues ) { int dx = width - m_width; int dy = height - m_height; return CropBy( dx, 0, 0, dy, fillValues ); } /*! * Extends or crops the image at its left and bottom sides to the specified * \a width and \a height in pixels, filling extended regions with the * minimum sample value in the native range of the image. Returns a * reference to this image. * * This member function is equivalent to: * * \code * ShiftToTopRight( width, height, sample_vector() ); * \endcode */ GenericImage& ShiftToTopRight( int width, int height ) { return ShiftToTopRight( width, height, sample_vector() ); } /*! * Extends or crops the image at its right and top sides to the specified * \a width and \a height in pixels. Returns a reference to this image. */ template GenericImage& ShiftToBottomLeft( int width, int height, const GenericVector& fillValues ) { int dx = width - m_width; int dy = height - m_height; return CropBy( 0, dy, dx, 0, fillValues ); } /*! * Extends or crops the image at its right and top sides to the specified * \a width and \a height in pixels, filling extended regions with the * minimum sample value in the native range of the image. Returns a * reference to this image. * * This member function is equivalent to: * * \code * ShiftToBottomLeft( width, height, sample_vector() ); * \endcode */ GenericImage& ShiftToBottomLeft( int width, int height ) { return ShiftToBottomLeft( width, height, sample_vector() ); } /*! * Extends or crops the image at its left and top sides to the specified * \a width and \a height in pixels. Returns a reference to this image. */ template GenericImage& ShiftToBottomRight( int width, int height, const GenericVector& fillValues ) { int dx = width - m_width; int dy = height - m_height; return CropBy( dx, dy, 0, 0, fillValues ); } /*! * Extends or crops the image at its left and top sides to the specified * \a width and \a height in pixels, filling extended regions with the * minimum sample value in the native range of the image. Returns a * reference to this image. * * This member function is equivalent to: * * \code * ShiftToBottomRight( width, height, sample_vector() ); * \endcode */ GenericImage& ShiftToBottomRight( int width, int height ) { return ShiftToBottomRight( width, height, sample_vector() ); } /*! * Shifts (translates) the image to its current anchor point selection. * Returns a reference to this image. * * This member function is equivalent to: * * \code * ShiftTo( SelectedPoint(), fillValues ); * \endcode */ template GenericImage& Shift( const GenericVector& fillValues ) { return ShiftTo( m_point, fillValues ); } /*! * Shifts (translates) the image to its current anchor point selection, * filling extended regions with the minimum sample value in the native * range of the image. Returns a reference to this image. * * This member function is equivalent to: * * \code * Shift( sample_vector() ); * \endcode */ GenericImage& Shift() { return Shift( sample_vector() ); } // ------------------------------------------------------------------------- /*! * Converts the image to the specified color space. Returns a reference to * this object. * * \param colorSpace Specifies a color space to convert the image to. See * the ColorSpace namespace for symbolic constants * corresponding to all supported color spaces in this * version of PCL. * * \param maxProcessors If a value greater than zero is specified, it is * the maximum number of concurrent threads that this function can * execute. If zero or a negative value is specified, the current * thread limit for this image will be used instead (see * AbstractImage::SetMaxProcessors()). The default value is zero. * * Color space conversion is performed in the current RGB working space * (RGBWS) associated with this image. * * This function operates on the entire image; current selections are * neither used nor modified (except the channel range selection, which is * reset to defaults when the color space conversion involves a change in * the number of nominal channels). Existing alpha channels are never * modified by this member function. * * Before performing the requested color space conversion, this function * ensures that the image data is uniquely referenced by this object. If * necessary, the previously referenced data are dereferenced (and destroyed * immediately if they become unreferenced), and a new local image is * allocated and referenced. * * \note Increments the status monitoring object by the number of pixels * (\e not samples) in the image. */ GenericImage& SetColorSpace( color_space colorSpace, int maxProcessors = 0 ) { size_type N = NumberOfPixels(); if ( N == 0 ) return *this; if ( colorSpace == m_colorSpace ) { m_status += N; return *this; } EnsureUnique(); if ( m_status.IsInitializationEnabled() ) m_status.Initialize( "In-place color space conversion: " + ColorSpace::Name( m_colorSpace ) + " -> " + ColorSpace::Name( colorSpace ), N ); int n = m_numberOfChannels; if ( m_colorSpace == ColorSpace::Gray ) { sample** oldData = m_pixelData; sample** newData = nullptr; try { // Make room for the G and B channels. newData = m_allocator.AllocateChannelSlots( 2+n ); // Put the nominal gray channel into the R slot newData[0] = oldData[0]; // Allocate and copy the G and B channels newData[1] = m_allocator.AllocatePixels( N ); ::memcpy( newData[1], oldData[0], ChannelSize() ); newData[2] = m_allocator.AllocatePixels( N ); ::memcpy( newData[2], oldData[0], ChannelSize() ); // Put existing alpha channels in their corresponding slots for ( int i = 1; i < n; ++i ) newData[i+2] = oldData[i]; try { m_pixelData = newData; newData = nullptr; m_numberOfChannels += 2; m_colorSpace = ColorSpace::RGB; m_data->UpdateSharedImage(); ResetChannelRange(); m_allocator.Deallocate( oldData ); } catch ( ... ) { m_data->Deallocate(); ResetSelections(); throw; } } catch ( ... ) { if ( newData != nullptr ) { newData[0] = nullptr; if ( newData[1] != nullptr ) m_allocator.Deallocate( newData[1] ), newData[1] = nullptr; if ( newData[2] != nullptr ) m_allocator.Deallocate( newData[2] ), newData[2] = nullptr; m_allocator.Deallocate( newData ); throw; } } if ( colorSpace == ColorSpace::RGB ) { m_status += N; return *this; } n += 2; } Array L = Thread::OptimalThreadLoads( N, 16u/*overheadLimit*/, m_parallel ? ((maxProcessors > 0) ? maxProcessors : m_maxProcessors) : 1 ); ThreadData data( *this, N ); ReferenceArray threads; for ( size_type i = 0, n = 0; i < L.Length(); n += L[i++] ) threads.Add( new ColorSpaceConversionThread( *this, data, colorSpace, n, n + L[i] ) ); RunThreads( threads, data ); threads.Destroy(); m_status = data.status; if ( colorSpace == ColorSpace::Gray ) { sample** oldData = m_pixelData; sample** newData = nullptr; try { newData = m_allocator.AllocateChannelSlots( n-2 ); newData[0] = oldData[0]; for ( int i = 3; i < n; ++i ) newData[i-2] = oldData[i]; m_pixelData = newData; newData = nullptr; m_numberOfChannels -= 2; m_colorSpace = ColorSpace::Gray; m_data->UpdateSharedImage(); ResetChannelRange(); m_allocator.Deallocate( oldData[1] ); m_allocator.Deallocate( oldData[2] ); m_allocator.Deallocate( oldData ); } catch ( ... ) { m_data->Deallocate(); ResetSelections(); if ( newData != nullptr ) m_allocator.Deallocate( newData ); throw; } } else { m_allocator.SetSharedColor( m_colorSpace = colorSpace, m_RGBWS ); } return *this; } // ------------------------------------------------------------------------- /*! * Computes the CIE Y component for a subset of pixel samples and stores it * in a grayscale image. * * \param[out] Y Reference to a destination image, where the computed * CIE Y component will be stored. This image can be an * instance of any supported %GenericImage template * instantiation; all the necessary data type conversions * are carried out transparently. * * \param maxProcessors If a value greater than zero is specified, it is * the maximum number of concurrent threads that this function can * execute. If zero or a negative value is specified, the current * thread limit for this image will be used instead (see * AbstractImage::SetMaxProcessors()). The default value is zero. * * This member function computes the Y component of the CIE XYZ space. CIE Y * component values are calculated in the RGB working space (RGBWS) * currently associated with the image. * * If this image is in the grayscale color space, gray source samples are * simply copied to the destination image, after conversion to the * destination sample data type, if necessary, using pixel traits * primitives. * * If this image is in the CIE XYZ color space, samples from the luminance * channel (source channel #1) are simply copied to the destination image, * after conversion to the destination sample data type, if necessary. * * For other color spaces, CIE Y values are obtained by means of the * simplest possible intermediate transformation (usually to the CIE XYZ * space), converted to the destination sample data type if necessary, and * transferred to the destination image. * * The luminance is computed for the specified subset of pixel samples, or * for the current rectangular selection, depending on parameter values. For * more information on the rest of parameters of this function, see the * documentation for Fill(). The current channel range selection is always * ignored by this function. * * The previous contents of the destination image will be dereferenced (and * immediately destroyed if they become unreferenced) and replaced with the * computed, uniquely referenced CIE Y data. The destination image will be * in the grayscale color space; it will have a single channel corresponding * to the luminance of the specified pixel sample subset in this image. The * dimensions of the destination image will be the same ones of the * specified pixel sample subset, and it will be associated with the same * RGBWS as this image. * * \note In general, computing luminance components as integer values is an * error. The GenericPixelTraits instantiation P1 should correspond to a * floating point real pixel sample type, either \c float or \c double, * depending on the source data type. The recommended types are:\n * \n * \li FloatPixelTraits for 8-bit and 16-bit integer images, as well as for * 32-bit floating point real and complex images.\n * \li DoublePixelTraits for 32-bit integer images, as well as for 64-bit * floating point real and complex images. * * \note Increments the status monitoring object by the number of selected * pixel samples. * * \warning Please note that the computed lightness is meaningful as such * \e exclusively in the RGB working space of this image. */ template void GetLuminance( GenericImage& Y, const Rect& rect = Rect( 0 ), int maxProcessors = 0 ) const { Rect r = rect; if ( !ParseRect( r ) ) { Y.FreeData(); return; } Y.AllocateData( r ); if ( !Y.IsShared() ) Y.SetRGBWorkingSpace( m_RGBWS ); size_type N = Y.NumberOfPixels(); if ( m_colorSpace == ColorSpace::Gray || m_colorSpace == ColorSpace::CIEXYZ ) { if ( m_status.IsInitializationEnabled() ) m_status.Initialize( "Transferring pixel data", N ); typename GenericImage::sample* __restrict__ g = *Y; int cY = (m_colorSpace == ColorSpace::Gray) ? 0 : 1; if ( r == Bounds() ) { const sample* __restrict__ f = m_pixelData[cY]; P1::Copy( g, f, N ); } else { const sample* __restrict__ f = PixelAddress( r.LeftTop(), cY ); for ( int i = 0; i < Y.Height(); ++i, f += m_width, g += Y.Width() ) P1::Copy( g, f, Y.Width() ); } m_status += N; } else { if ( m_status.IsInitializationEnabled() ) m_status.Initialize( "Computing CIE Y component", N ); Array L = OptimalThreadRows( Y.Height(), Y.Width(), maxProcessors ); ThreadData data( *this, N ); ReferenceArray > threads; for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) ) threads.Add( new GetLuminanceThread( Y, *this, data, r, n, n + int( L[i] ) ) ); RunThreads( threads, data ); threads.Destroy(); m_status = data.status; } } /*! * Computes the CIE Y component for a subset of pixel samples and stores it * in a grayscale image, transported by an ImageVariant object. * * If the specified %ImageVariant does not transport an image, a new * floating point image will be created with the appropriate sample type * (either 32-bit or 64-bit floating point, depending on the sample data * type of this image). */ void GetLuminance( ImageVariant& Y, const Rect& rect = Rect( 0 ), int maxProcessors = 0 ) const; // Implemented in pcl/ImageVariant.h /*! * Computes the CIE L* component for a subset of pixel samples and stores it * in a grayscale image. * * \param[out] L Reference to a destination image, where the computed * CIE L* component will be stored. This image can be an * instance of any supported %GenericImage template * instantiation; all the necessary data type conversions * are carried out transparently. * * \param maxProcessors If a value greater than zero is specified, it is * the maximum number of concurrent threads that this function can * execute. If zero or a negative value is specified, the current * thread limit for this image will be used instead (see * AbstractImage::SetMaxProcessors()). The default value is zero. * * This member function computes the L* component of the CIE L*a*b* space. * CIE L* component values are calculated in the RGB working space (RGBWS) * currently associated with this image. * * If this image is in the grayscale color space, gray source samples are * simply copied to the destination image, after conversion to the * destination sample data type, if necessary, using pixel traits * primitives. * * If this image is in the CIE L*a*b* color space, samples from the * lightness channel (source channel #0) are simply copied to the * destination image, after conversion to the destination sample data type, * if necessary. * * For other color spaces, CIE L* values are obtained by means of the * simplest possible intermediate transformation (usually to the CIE L*a*b* * space), converted to the destination sample data type if necessary, and * transferred to the destination image. * * The lightness is computed for the specified subset of pixel samples, or * for the current rectangular selection, depending on parameter values. For * more information on the rest of parameters of this function, see the * documentation for Fill(). The current channel range selection is always * ignored by this function. * * The previous contents of the destination image will be dereferenced (and * immediately destroyed if they become unreferenced) and replaced with the * computed, uniquely referenced CIE L* data. The destination image will be * in the grayscale color space; it will have a single channel corresponding * to the lightness of the specified pixel sample subset in this image. The * dimensions of the destination image will be the same ones of the * specified pixel sample subset, and it will be associated with the same * RGBWS as this image. * * \note In general, computing lightness components as integer values is an * error. The GenericPixelTraits instantiation P1 should correspond to a * floating point real pixel sample type, either \c float or \c double, * depending on the source data type. The recommended types are:\n * \n * \li FloatPixelTraits for 8-bit and 16-bit integer images, as well as for * 32-bit floating point real and complex images.\n * \li DoublePixelTraits for 32-bit integer images, as well as for 64-bit * floating point real and complex images. * * \note Increments the status monitoring object by the number of modified * pixels (\e not samples). * * \warning Please note that the computed lightness is meaningful as such * \e exclusively in the RGB working space of this image. */ template void GetLightness( GenericImage& L, const Rect& rect = Rect( 0 ), int maxProcessors = 0 ) const { Rect r = rect; if ( !ParseRect( r ) ) { L.FreeData(); return; } L.AllocateData( r ); if ( !L.IsShared() ) L.SetRGBWorkingSpace( m_RGBWS ); size_type N = L.NumberOfPixels(); if ( m_colorSpace == ColorSpace::Gray || m_colorSpace == ColorSpace::CIELab || m_colorSpace == ColorSpace::CIELch ) { if ( m_status.IsInitializationEnabled() ) m_status.Initialize( "Transferring pixel data", N ); typename GenericImage::sample* g = *L; if ( r == Bounds() ) { const sample* __restrict__ f = *m_pixelData; P1::Copy( g, f, N ); } else { const sample* __restrict__ f = PixelAddress( r.LeftTop() ); for ( int i = 0; i < L.Height(); ++i, f += m_width, g += L.Width() ) P1::Copy( g, f, L.Width() ); } m_status += N; } else { if ( m_status.IsInitializationEnabled() ) m_status.Initialize( "Computing CIE L* component", N ); Array R = OptimalThreadRows( L.Height(), L.Width(), maxProcessors ); ThreadData data( *this, N ); ReferenceArray > threads; for ( int i = 0, n = 0; i < int( R.Length() ); n += int( R[i++] ) ) threads.Add( new GetLightnessThread( L, *this, data, r, n, n + int( R[i] ) ) ); RunThreads( threads, data ); threads.Destroy(); m_status = data.status; } } /*! * Computes the CIE L* component for a subset of pixel samples and stores it * in a grayscale image, transported by an ImageVariant object. * * If the specified %ImageVariant does not transport an image, a new * floating point image will be created with the appropriate sample type * (either 32-bit or 64-bit floating point, depending on the sample data * type of this image). */ void GetLightness( ImageVariant& L, const Rect& rect = Rect( 0 ), int maxProcessors = 0 ) const; // Implemented in pcl/ImageVariant.h /*! * Computes the intensity HSI component for a subset of pixel samples and * stores it in a grayscale image. * * \param[out] I Reference to a destination image, where the computed * intensity component will be stored. This image can be * an instance of any supported %GenericImage template * instantiation; all the necessary data type conversions * are carried out transparently. * * \param maxProcessors If a value greater than zero is specified, it is * the maximum number of concurrent threads that this function can * execute. If zero or a negative value is specified, the current * thread limit for this image will be used instead (see * AbstractImage::SetMaxProcessors()). The default value is zero. * * This member function computes the I intensity component in the HSI color * ordering scheme. The intensity is useful for applications where a * colorimetrically correct component, such as luminance or lightness, is * not necessary. For example, the intensity component can be used for * feature detection (e.g., star detection) on RGB color images. * * The intensity component I of an RGB pixel is given by: * *
    * I = (Max(R,G,B) + Min(R,G,B))/2
    * 
* * where R, G and B are the red, green and blue components of the RGB pixel, * respectively. * * If this image is in the grayscale or HSI spaces, gray or intensity source * samples are simply copied to the destination image, after conversion to * the destination sample data type, if necessary, using pixel traits * primitives. * * If this image is in the RGB color space, the intensity components for * each pixel is calculated as described above. For other color spaces * intermediate RGB components are computed on the fly. * * The intensity is computed for the specified subset of pixel samples, or * for the current rectangular selection, depending on parameter values. For * more information on the rest of parameters of this function, see the * documentation for Fill(). The current channel range selection is always * ignored by this function. * * The previous contents of the destination image will be dereferenced (and * immediately destroyed if they become unreferenced) and replaced with the * computed, uniquely referenced intensity data. The destination image will * be in the grayscale color space; it will have a single channel * corresponding to the intensity of the specified pixel sample subset in * this image. The dimensions of the destination image will be the same ones * of the specified pixel sample subset, and it will be associated with the * same RGBWS as this image. * * \note Increments the status monitoring object by the number of modified * pixels (\e not samples). */ template void GetIntensity( GenericImage& I, const Rect& rect = Rect( 0 ), int maxProcessors = 0 ) const { Rect r = rect; if ( !ParseRect( r ) ) { I.FreeData(); return; } I.AllocateData( r ); if ( !I.IsShared() ) I.SetRGBWorkingSpace( m_RGBWS ); size_type N = I.NumberOfPixels(); if ( m_colorSpace == ColorSpace::Gray || m_colorSpace == ColorSpace::HSI ) { if ( m_status.IsInitializationEnabled() ) m_status.Initialize( "Transferring pixel data", N ); typename GenericImage::sample* __restrict__ g = *I; int cI = (m_colorSpace == ColorSpace::Gray) ? 0 : 2; if ( r == Bounds() ) { const sample* __restrict__ f = m_pixelData[cI]; P1::Copy( g, f, N ); } else { const sample* __restrict__ f = PixelAddress( r.LeftTop(), cI ); for ( int i = 0; i < I.Height(); ++i, f += m_width, g += I.Width() ) P1::Copy( g, f, I.Width() ); } m_status += N; } else { if ( m_status.IsInitializationEnabled() ) m_status.Initialize( "Computing intensity component", N ); Array L = OptimalThreadRows( I.Height(), I.Width(), maxProcessors ); ThreadData data( *this, N ); ReferenceArray > threads; for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) ) threads.Add( new GetIntensityThread( I, *this, data, r, n, n + int( L[i] ) ) ); RunThreads( threads, data ); threads.Destroy(); m_status = data.status; } } /*! * Computes the intensity HSI component for a subset of pixel samples and * stores it in a grayscale image, transported by an ImageVariant object. * * If the specified %ImageVariant does not transport an image, a new * floating point image will be created with the appropriate sample type * (either 32-bit or 64-bit floating point, depending on the sample data * type of this image). */ void GetIntensity( ImageVariant& I, const Rect& rect = Rect( 0 ), int maxProcessors = 0 ) const; // Implemented in pcl/ImageVariant.h // ------------------------------------------------------------------------- /*! * Replaces the CIE Y component of a subset of pixel samples with data * extracted from another image. * * \param Y Reference to a source image, from which the CIE Y * components will be obtained. This image can be in any * supported color space, and can be an instance of any * supported template instantiation of %GenericImage; all * the necessary color space and data type conversions are * carried out transparently. * * \param point Target point in image coordinates. This is the upper * left corner of the target rectangular region in this * image where CIE Y components will be replaced with * source values. If one or both point coordinates are * equal to \c int_max (the maximum possible value of type * \c int), then the current anchor point selection will * be used. The default value is Point( int_max ). * * \param rect Source rectangular region. If this parameter defines an * empty rectangle, the current rectangular selection in * the source image will be used. If the specified * rectangle is not empty and extends beyond source image * boundaries, only the intersection with the source image * will be used. If that intersection does not exist, then * this member function has no effect. The default value * is an empty rectangle. * * \param maxProcessors If a value greater than zero is specified, it is * the maximum number of concurrent threads that this function can * execute. If zero or a negative value is specified, the current * thread limit for this image will be used instead (see * AbstractImage::SetMaxProcessors()). The default value is zero. * * This member function computes and replaces the Y component of the CIE XYZ * color space. Source CIE Y component values are calculated in the RGB * working space (RGBWS) associated with the source image \a Y. Replacement * of CIE Y components in this (target) image is performed by * transformations in the RGBWS associated with this image. This ensures * that only pure illumination data is transferred between both images, * without any chrominance contamination. * * If the source image is in the grayscale or CIE XYZ color spaces, samples * from its first or second channel, respectively, are directly taken as * source values, requiring no additional conversion. * * Once a source CIE Y value is available, either by direct extraction or * through the simplest possible intermediate transformation (usually to the * CIE L*a*b* or CIE XYZ spaces), it is inserted in the corresponding * destination pixel by means of a series of transformations in the target * RGBWS, whose number and complexity depend on the destination color space. * Additional sample data type conversions may take place using pixel traits * primitives, if the source and destination data types differ. Roundoff * errors are minimized during the whole process. * * \note Increments the status monitoring object by the number of modified * pixels (\e not samples). */ template GenericImage& SetLuminance( const GenericImage& Y, const Point& point = Point( int_max ), const Rect& rect = Rect( 0 ), int maxProcessors = 0 ) { Rect r = rect; if ( !Y.ParseRect( r ) ) return *this; Point p = point; if ( p.x == int_max || p.y == int_max ) p = m_point; if ( p.x < 0 ) { if ( (r.x0 -= p.x) >= r.x1 ) return *this; p.x = 0; } else if ( p.x >= m_width ) return *this; if ( p.y < 0 ) { if ( (r.y0 -= p.y) >= r.y1 ) return *this; p.y = 0; } else if ( p.y >= m_height ) return *this; r.ResizeTo( pcl::Min( m_width - p.x, r.Width() ), pcl::Min( m_height - p.y, r.Height() ) ); size_type N = size_type( r.Width() )*size_type( r.Height() ); EnsureUnique(); if ( m_colorSpace == ColorSpace::Gray && (Y.ColorSpace() == ColorSpace::Gray || Y.ColorSpace() == ColorSpace::CIEXYZ) ) { if ( m_status.IsInitializationEnabled() ) m_status.Initialize( "Transferring pixel data", N ); int c0 = (Y.ColorSpace() == ColorSpace::Gray) ? 0 : 1; const typename GenericImage::sample* __restrict__ g = Y.PixelAddress( r.LeftTop(), c0 ); sample* __restrict__ f = PixelAddress( p ); for ( int i = 0, w = r.Width(), h = r.Height(); i < h; ++i, f += m_width, g += Y.Width(), m_status += w ) P::Copy( f, g, w ); } else { if ( m_status.IsInitializationEnabled() ) m_status.Initialize( "Importing CIE Y component", N ); Array L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors ); ThreadData data( *this, N ); ReferenceArray > threads; for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) ) threads.Add( new SetLuminanceThread( *this, Y, data, p, r, n, n + int( L[i] ) ) ); RunThreads( threads, data ); threads.Destroy(); m_status = data.status; } return *this; } /*! * Replaces the CIE Y component of a subset of pixel samples with data * extracted from another image, transported by an ImageVariant object. * * If the specified %ImageVariant does not transport an image, a new * floating point image will be created with the appropriate sample type * (either 32-bit or 64-bit floating point, depending on the sample data * type of this image). */ GenericImage& SetLuminance( const ImageVariant& Y, const Point& point = Point( int_max ), const Rect& rect = Rect( 0 ), int maxProcessors = 0 ); // Implemented in pcl/ImageVariant.h /*! * Replaces the CIE L* component of a subset of pixel samples with data * extracted from another image. * * \param L Reference to a source image, from which the CIE L* * components will be obtained. This image can be in any * supported color space, and can be an instance of any * supported template instantiation of %GenericImage; all * the necessary color space and data type conversions are * carried out transparently. * * \param point Target point in image coordinates. This is the upper * left corner of the target rectangular region in this * image where CIE L* components will be replaced with * source values. If one or both point coordinates are * equal to \c int_max (the maximum possible value of type * \c int), then the current anchor point selection will * be used. The default value is Point( int_max ). * * \param rect Source rectangular region. If this parameter defines an * empty rectangle, the current rectangular selection in * the source image will be used. If the specified * rectangle is not empty and extends beyond source image * boundaries, only the intersection with the source image * will be used. If that intersection does not exist, then * this member function has no effect. The default value * is an empty rectangle. * * \param maxProcessors If a value greater than zero is specified, it is * the maximum number of concurrent threads that this function can * execute. If zero or a negative value is specified, the current * thread limit for this image will be used instead (see * AbstractImage::SetMaxProcessors()). The default value is zero. * * This member function computes and replaces the L* component of the CIE * L*a*b* color space. Source CIE L* component values are calculated in the * RGB working space (RGBWS) associated with the source image \a L. * Replacement of CIE L* components in this (target) image is performed by * transformations in the RGBWS associated with this image. This ensures * that only pure illumination data is transferred between both images, * without any chrominance contamination. * * If the source image is in the grayscale, CIE L*a*b* or CIE L*c*h* color * spaces, samples from its first channel are directly taken as source * values, requiring no additional conversion. * * Once a source CIE L* value is available, either by direct extraction or * through the simplest possible intermediate transformation (usually to the * CIE L*a*b* or CIE XYZ spaces), it is inserted in the corresponding * destination pixel by means of a series of transformations in the target * RGBWS, whose number and complexity depend on the destination color space. * Additional sample data type conversions may take place using pixel traits * primitives, if the source and destination data types differ. Roundoff * errors are minimized during the whole process. * * \note Increments the status monitoring object by the number of modified * pixels (\e not samples). */ template GenericImage& SetLightness( const GenericImage& L, const Point& point = Point( int_max ), const Rect& rect = Rect( 0 ), int maxProcessors = 0 ) { Rect r = rect; if ( !L.ParseRect( r ) ) return *this; Point p = point; if ( p.x == int_max || p.y == int_max ) p = m_point; if ( p.x < 0 ) { if ( (r.x0 -= p.x) >= r.x1 ) return *this; p.x = 0; } else if ( p.x >= m_width ) return *this; if ( p.y < 0 ) { if ( (r.y0 -= p.y) >= r.y1 ) return *this; p.y = 0; } else if ( p.y >= m_height ) return *this; r.ResizeTo( pcl::Min( m_width - p.x, r.Width() ), pcl::Min( m_height - p.y, r.Height() ) ); size_type N = size_type( r.Width() )*size_type( r.Height() ); EnsureUnique(); if ( m_colorSpace == ColorSpace::Gray && (L.ColorSpace() == ColorSpace::Gray || L.ColorSpace() == ColorSpace::CIELab || L.ColorSpace() == ColorSpace::CIELch) ) { if ( m_status.IsInitializationEnabled() ) m_status.Initialize( "Transferring pixel data", N ); const typename GenericImage::sample* __restrict__ g = L.PixelAddress( r.LeftTop() ); sample* __restrict__ f = PixelAddress( p ); for ( int i = 0, w = r.Width(), h = r.Height(); i < h; ++i, f += m_width, g += L.Width(), m_status += w ) P::Copy( f, g, w ); } else { if ( m_status.IsInitializationEnabled() ) m_status.Initialize( "Importing CIE L* component", N ); Array R = OptimalThreadRows( r.Height(), r.Width(), maxProcessors ); ThreadData data( *this, N ); ReferenceArray > threads; for ( int i = 0, n = 0; i < int( R.Length() ); n += int( R[i++] ) ) threads.Add( new SetLightnessThread( *this, L, data, p, r, n, n + int( R[i] ) ) ); RunThreads( threads, data ); threads.Destroy(); m_status = data.status; } return *this; } /*! * Replaces the CIE L* component of a subset of pixel samples with data * extracted from another image, transported by an ImageVariant object. * * If the specified %ImageVariant does not transport an image, a new * floating point image will be created with the appropriate sample type * (either 32-bit or 64-bit floating point, depending on the sample data * type of this image). */ GenericImage& SetLightness( const ImageVariant& L, const Point& point = Point( int_max ), const Rect& rect = Rect( 0 ), int maxProcessors = 0 ); // Implemented in pcl/ImageVariant.h /*! * Compression of image pixel samples. * * \param compressor A compression algorithm that will be used to compress * pixel sample data from this image. * * \param rect A rectangular region in image pixel coordinates. This * region determines the subset of pixel samples that will * be compressed. If this parameter is not specified, or * if an empty rectangle is specified, this function will * compress pixel samples in the current rectangular * selection of this image, that is, SelectedRectangle(). * * \param channel Channel index. Must be the zero-based index of an * existing channel in this image, or an integer < 0. If * this parameter is not specified or a negative integer, * this function will compress pixel samples in the * currently selected channel of this image, that is, * SelectedChannel(). * * \param perf If non-null, pointer to a Compression::Performance * structure where performance data will be provided. * * Returns a dynamic array of compressed sub-blocks. Each array element is a * Compression::Subblock structure with the compressed data and the * corresponding uncompressed length in bytes. If compression succeeds, the * returned array will have at least one element. * * Data will be compressed for the intersection of the specified (or * implicitly selected) rectangular region with this image. All predefined * pixel sample types are supported, including integer, real and complex * pixels. * * If there is no intersection between the rectangular region and the image, * if an invalid channel index is specified, or if one or more sub-blocks * are not compressible with the specified compression algorithm, this * function returns an empty array. */ Compression::subblock_list Compress( const Compression& compressor, const Rect& rect = Rect( 0 ), int channel = -1, Compression::Performance* perf = nullptr ) const { Rect r = rect; if ( !ParseSelection( r, channel ) ) return Compression::subblock_list(); if ( r == Bounds() ) return compressor.Compress( m_channelData( channel ), ChannelSize(), perf ); GenericImage

subimage( *this, r, channel, channel ); return compressor.Compress( subimage[0], subimage.ChannelSize(), perf ); } // ------------------------------------------------------------------------- private: /*! * \struct Data * \internal * Reference-counted image data structure. */ struct Data : public ReferenceCounter { /*! * Pixel data * * Each element in the data array points to a contiguous block of pixel * samples that stores one channel of the image. This includes all * nominal and alpha channels. */ sample** data = nullptr; /*! * Pixel allocator * * The allocator is responsible for all allocations and deallocations of * pixel data and the associated structures. The allocator works on local * or external storage, depending on whether this instance is a local or * a shared image. For shared images, the allocator takes into account * ownership of the shared object for controlled destruction of shared * resources. */ pixel_allocator allocator; /*! * Image geometry */ Geometry geometry; /*! * Image color */ Color color; /*! * Constructs an empty local image. */ Data( GenericImage* image ) { LinkWithClientImage( image ); } /*! * Constructs an aliased shared image. */ Data( GenericImage* image, void* handle ) : allocator( handle ) { SynchronizeWithSharedImage(); LinkWithClientImage( image ); } /*! * Constructs a newly created shared image. */ Data( GenericImage* image, int width, int height, int numberOfChannels, int colorSpace ) : allocator( width, height, numberOfChannels, colorSpace ) { SynchronizeWithSharedImage(); LinkWithClientImage( image ); } void Attach( GenericImage* image ) { ReferenceCounter::Attach(); LinkWithClientImage( image ); } ~Data() { if ( IsShared() ) Reset(); else Deallocate(); } bool IsShared() const noexcept { return allocator.IsShared(); } bool IsEmpty() const noexcept { return data == nullptr; } void Allocate( int width, int height, int numberOfChannels, color_space colorSpace ) { if ( width <= 0 || height <= 0 || numberOfChannels <= 0 ) { Deallocate(); return; } if ( numberOfChannels < ColorSpace::NumberOfNominalChannels( colorSpace ) ) throw Error( "GenericImage::Data::Allocate(): Insufficient number of channels" ); if ( data != nullptr ) { if ( size_type( width )*size_type( height ) == geometry.NumberOfPixels() ) { if ( numberOfChannels != geometry.numberOfChannels ) { sample** newData = nullptr; int m = pcl::Min( geometry.numberOfChannels, numberOfChannels ); try { newData = allocator.AllocateChannelSlots( numberOfChannels ); for ( int i = 0; i < m; ++i ) newData[i] = data[i]; for ( int i = m; i < numberOfChannels; ++i ) newData[i] = allocator.AllocatePixels( width, height ); try { for ( int i = m; i < geometry.numberOfChannels; ++i ) if ( data[i] != nullptr ) allocator.Deallocate( data[i] ), data[i] = nullptr; allocator.Deallocate( data ); data = newData; newData = nullptr; } catch ( ... ) { Deallocate(); throw; } } catch ( ... ) { if ( newData != nullptr ) { for ( int i = m; i < numberOfChannels; ++i ) if ( newData[i] != nullptr ) allocator.Deallocate( newData[i] ), newData[i] = nullptr; allocator.Deallocate( newData ); } throw; } } } else { sample** newData = nullptr; try { newData = allocator.AllocateChannelSlots( numberOfChannels ); for ( int i = 0; i < numberOfChannels; ++i ) newData[i] = allocator.AllocatePixels( width, height ); try { for ( int i = 0; i < geometry.numberOfChannels; ++i ) if ( data[i] != nullptr ) allocator.Deallocate( data[i] ), data[i] = nullptr; allocator.Deallocate( data ); data = newData; newData = nullptr; } catch ( ... ) { Deallocate(); throw; } } catch ( ... ) { if ( newData != nullptr ) { for ( int i = 0; i < numberOfChannels; ++i ) if ( newData[i] != nullptr ) allocator.Deallocate( newData[i] ), newData[i] = nullptr; allocator.Deallocate( newData ); } throw; } } } else { try { data = allocator.AllocateChannelSlots( numberOfChannels ); for ( int i = 0; i < numberOfChannels; ++i ) data[i] = allocator.AllocatePixels( width, height ); } catch ( ... ) { if ( data != nullptr ) { for ( int i = 0; i < numberOfChannels; ++i ) if ( data[i] != nullptr ) allocator.Deallocate( data[i] ), data[i] = nullptr; allocator.Deallocate( data ); Reset(); } throw; } } geometry.width = width; geometry.height = height; geometry.numberOfChannels = numberOfChannels; color.colorSpace = colorSpace; UpdateSharedImage(); } void Import( sample** newData, int width, int height, int numberOfChannels, color_space colorSpace ) { if ( newData != data ) { Deallocate(); if ( newData != nullptr && width > 0 && height > 0 && numberOfChannels > 0 ) { if ( numberOfChannels < ColorSpace::NumberOfNominalChannels( colorSpace ) ) throw Error( "GenericImage::Data::Import(): Insufficient number of channels" ); data = newData; geometry.width = width; geometry.height = height; geometry.numberOfChannels = numberOfChannels; color.colorSpace = colorSpace; UpdateSharedImage(); } } } sample** Release() { sample** oldData = data; Reset(); UpdateSharedImage(); return oldData; } void Deallocate() { if ( data != nullptr ) { for ( int i = 0; i < geometry.numberOfChannels; ++i ) if ( data[i] != nullptr ) allocator.Deallocate( data[i] ), data[i] = nullptr; allocator.Deallocate( data ); Reset(); UpdateSharedImage(); } } Data* Clone( GenericImage* image ) const { Data* clone = nullptr; try { clone = new Data; if ( !IsEmpty() ) { clone->data = clone->allocator.AllocateChannelSlots( geometry.numberOfChannels ); for ( int i = 0; i < geometry.numberOfChannels; ++i ) { clone->data[i] = clone->allocator.AllocatePixels( geometry.width, geometry.height ); P::Copy( clone->data[i], data[i], geometry.NumberOfPixels() ); } clone->geometry.Assign( geometry ); clone->color.Assign( color ); } clone->LinkWithClientImage( image ); return clone; } catch ( ... ) { if ( clone != nullptr ) { clone->Deallocate(); delete clone; } throw; } } void Reset() { data = nullptr; geometry.Reset(); color.Reset(); } void UpdateSharedImage() { allocator.SetSharedData( data ); allocator.SetSharedGeometry( geometry.width, geometry.height, geometry.numberOfChannels ); allocator.SetSharedColor( color.colorSpace, color.RGBWS ); } void SynchronizeWithSharedImage() { data = allocator.GetSharedData(); allocator.GetSharedGeometry( geometry.width, geometry.height, geometry.numberOfChannels ); allocator.GetSharedColor( color.colorSpace, color.RGBWS ); } private: Data() = default; void LinkWithClientImage( GenericImage* image ) { if ( image != nullptr ) { image->m_geometry = &geometry; image->m_color = &color; } } }; /*! * \internal * The reference-counted image data. */ Data* m_data = nullptr; /*! * \internal * Dereferences image data and disposes it if it becomes garbage. */ void DetachFromData() { if ( !m_data->Detach() ) delete m_data; } // ------------------------------------------------------------------------- class RectThreadBase : public Thread { public: RectThreadBase( const GenericImage& image, const Rect& rect, int ch1, int ch2, int firstRow, int endRow ) : m_image( image ) , m_rect( rect ) , m_ch1( ch1 ) , m_ch2( ch2 ) , m_firstRow( firstRow ) , m_endRow( endRow ) { } virtual void Run() = 0; protected: const GenericImage& m_image; const Rect& m_rect; int m_ch1, m_ch2; int m_firstRow, m_endRow; template void Execute( F process ) noexcept { int w = m_rect.Width(); int dw = m_image.Width() - w; if ( m_image.IsLowRangeClippingEnabled() ) { sample clipLow = P::ToSample( m_image.RangeClipLow() ); if ( m_image.IsHighRangeClippingEnabled() ) { sample clipHigh = P::ToSample( m_image.RangeClipHigh() ); for ( int i = m_ch1; i <= m_ch2; ++i ) { const sample* __restrict__ f = m_image.PixelAddress( m_rect.x0, m_rect.y0+m_firstRow, i ); for ( int j = m_firstRow; j < m_endRow; ++j, f += dw ) { PCL_IVDEP for ( int k = 0; k < w; ++k, ++f ) if ( clipLow < *f ) if ( *f < clipHigh ) process( f ); } } } else { for ( int i = m_ch1; i <= m_ch2; ++i ) { const sample* __restrict__ f = m_image.PixelAddress( m_rect.x0, m_rect.y0+m_firstRow, i ); for ( int j = m_firstRow; j < m_endRow; ++j, f += dw ) { PCL_IVDEP for ( int k = 0; k < w; ++k, ++f ) if ( clipLow < *f ) process( f ); } } } } else if ( m_image.IsHighRangeClippingEnabled() ) { sample clipHigh = P::ToSample( m_image.RangeClipHigh() ); for ( int i = m_ch1; i <= m_ch2; ++i ) { const sample* __restrict__ f = m_image.PixelAddress( m_rect.x0, m_rect.y0+m_firstRow, i ); for ( int j = m_firstRow; j < m_endRow; ++j, f += dw ) { PCL_IVDEP for ( int k = 0; k < w; ++k, ++f ) if ( *f < clipHigh ) process( f ); } } } else { for ( int i = m_ch1; i <= m_ch2; ++i ) { const sample* __restrict__ f = m_image.PixelAddress( m_rect.x0, m_rect.y0+m_firstRow, i ); for ( int j = m_firstRow; j < m_endRow; ++j, f += dw ) { PCL_IVDEP for ( int k = 0; k < w; ++k, ++f ) process( f ); } } } } }; // ------------------------------------------------------------------------- class CountThread : public RectThreadBase { public: size_type count = 0; CountThread( const GenericImage& image, const Rect& rect, int ch1, int ch2, int firstRow, int endRow ) : RectThreadBase( image, rect, ch1, ch2, firstRow, endRow ) { } void Run() final { /* * N.B. These threads are only used when range clipping is enabled. */ int w = this->m_rect.Width(); int dw = this->m_image.Width() - w; if ( this->m_image.IsLowRangeClippingEnabled() ) { sample clipLow = P::ToSample( this->m_image.RangeClipLow() ); if ( this->m_image.IsHighRangeClippingEnabled() ) { sample clipHigh = P::ToSample( this->m_image.RangeClipHigh() ); for ( int i = this->m_ch1; i <= this->m_ch2; ++i ) { const sample* __restrict__ f = this->m_image.PixelAddress( this->m_rect.x0, this->m_rect.y0+this->m_firstRow, i ); for ( int j = this->m_firstRow; j < this->m_endRow; ++j, f += dw ) { PCL_IVDEP for ( int k = 0; k < w; ++k, ++f ) if ( clipLow < *f ) if ( *f < clipHigh ) ++count; } } } else { for ( int i = this->m_ch1; i <= this->m_ch2; ++i ) { const sample* __restrict__ f = this->m_image.PixelAddress( this->m_rect.x0, this->m_rect.y0+this->m_firstRow, i ); for ( int j = this->m_firstRow; j < this->m_endRow; ++j, f += dw ) { PCL_IVDEP for ( int k = 0; k < w; ++k, ++f ) if ( clipLow < *f ) ++count; } } } } else if ( this->m_image.IsHighRangeClippingEnabled() ) { sample clipHigh = P::ToSample( this->m_image.RangeClipHigh() ); for ( int i = this->m_ch1; i <= this->m_ch2; ++i ) { const sample* __restrict__ f = this->m_image.PixelAddress( this->m_rect.x0, this->m_rect.y0+this->m_firstRow, i ); for ( int j = this->m_firstRow; j < this->m_endRow; ++j, f += dw ) { PCL_IVDEP for ( int k = 0; k < w; ++k, ++f ) if ( *f < clipHigh ) ++count; } } } else // ?! this should not happen count = size_type( 1 + this->m_ch2 - this->m_ch1 ) * size_type( w ) * size_type( this->m_rect.Height() ); } }; // ------------------------------------------------------------------------- class MinThread : public RectThreadBase { public: sample min; size_type count; MinThread( const GenericImage& image, const Rect& rect, int ch1, int ch2, int firstRow, int endRow ) : RectThreadBase( image, rect, ch1, ch2, firstRow, endRow ) { } void Run() final { min = P::HighestSampleValue(); count = 0; this->Execute( [=]( const sample* f ) { if ( *f < min ) min = *f; ++count; } ); } }; // ------------------------------------------------------------------------- class MaxThread : public RectThreadBase { public: sample max; size_type count; MaxThread( const GenericImage& image, const Rect& rect, int ch1, int ch2, int firstRow, int endRow ) : RectThreadBase( image, rect, ch1, ch2, firstRow, endRow ) { } void Run() final { max = P::LowestSampleValue(); count = 0; this->Execute( [=]( const sample* f ) { if ( max < *f ) max = *f; ++count; } ); } }; // ------------------------------------------------------------------------- class MinMaxThread : public RectThreadBase { public: sample min; sample max; size_type count; MinMaxThread( const GenericImage& image, const Rect& rect, int ch1, int ch2, int firstRow, int endRow ) : RectThreadBase( image, rect, ch1, ch2, firstRow, endRow ) { } void Run() final { min = P::HighestSampleValue(); max = P::LowestSampleValue(); count = 0; this->Execute( [=]( const sample* f ) { if ( *f < min ) min = *f; if ( max < *f ) max = *f; ++count; } ); } }; // ------------------------------------------------------------------------- class ExtremePosThreadBase : public RectThreadBase { public: int cmin, cmax; Point pmin, pmax; size_type count; ExtremePosThreadBase( const GenericImage& image, const Rect& rect, int ch1, int ch2, int firstRow, int endRow ) : RectThreadBase( image, rect, ch1, ch2, firstRow, endRow ) { } void Run() final { count = 0; m_amin = m_amax = nullptr; DoExecute(); if ( count > 0 ) { if ( m_amin != 0 ) for ( int i = this->m_ch1; i <= this->m_ch2; ++i ) if ( m_amin >= this->m_image[i] && m_amin < (this->m_image[i] + this->m_image.NumberOfPixels()) ) { cmin = i; pmin.x = (m_amin - this->m_image[i]) % this->m_image.Width(); pmin.y = (m_amin - this->m_image[i]) / this->m_image.Width(); break; } if ( m_amax != 0 ) for ( int i = this->m_ch1; i <= this->m_ch2; ++i ) if ( m_amax >= this->m_image[i] && m_amax < (this->m_image[i] + this->m_image.NumberOfPixels()) ) { cmax = i; pmax.x = (m_amax - this->m_image[i]) % this->m_image.Width(); pmax.y = (m_amax - this->m_image[i]) / this->m_image.Width(); break; } } } protected: const sample* m_amin; const sample* m_amax; virtual void DoExecute() = 0; }; // ------------------------------------------------------------------------- class MinPosThread : public ExtremePosThreadBase { public: sample min; MinPosThread( const GenericImage& image, const Rect& rect, int ch1, int ch2, int firstRow, int endRow ) : ExtremePosThreadBase( image, rect, ch1, ch2, firstRow, endRow ) { } private: void DoExecute() override { min = P::HighestSampleValue(); this->Execute( [=]( const sample* f ) { if ( *f < min ) min = *(this->m_amin = f); ++this->count; } ); } }; // ------------------------------------------------------------------------- class MaxPosThread : public ExtremePosThreadBase { public: sample max; MaxPosThread( const GenericImage& image, const Rect& rect, int ch1, int ch2, int firstRow, int endRow ) : ExtremePosThreadBase( image, rect, ch1, ch2, firstRow, endRow ) { } private: void DoExecute() override { max = P::LowestSampleValue(); this->Execute( [=]( const sample* f ) { if ( max < *f ) max = *(this->m_amax = f); ++this->count; } ); } }; // ------------------------------------------------------------------------- class MinMaxPosThread : public ExtremePosThreadBase { public: sample min, max; MinMaxPosThread( const GenericImage& image, const Rect& rect, int ch1, int ch2, int firstRow, int endRow ) : ExtremePosThreadBase( image, rect, ch1, ch2, firstRow, endRow ) { } private: void DoExecute() override { min = P::HighestSampleValue(); max = P::LowestSampleValue(); this->Execute( [=]( const sample* f ) { if ( *f < min ) min = *(this->m_amin = f); if ( max < *f ) max = *(this->m_amax = f); ++this->count; } ); } }; // ------------------------------------------------------------------------- class SumThread : public RectThreadBase { public: double s; size_type n; SumThread( const GenericImage& image, const Rect& rect, int ch1, int ch2, int firstRow, int endRow ) : RectThreadBase( image, rect, ch1, ch2, firstRow, endRow ) { } void Run() final { s = e = 0; n = 0; DoExecute(); } protected: double e; void SumStep( double x ) noexcept { double y = x - e; double t = s + y; e = (t - s) - y; s = t; ++n; } virtual void DoExecute() { this->Execute( [=]( const sample* f ) { double v; P::FromSample( v, *f ); SumStep( v ); } ); } }; // ------------------------------------------------------------------------- class SumSqrThread : public SumThread { public: SumSqrThread( const GenericImage& image, const Rect& rect, int ch1, int ch2, int firstRow, int endRow ) : SumThread( image, rect, ch1, ch2, firstRow, endRow ) { } private: void DoExecute() override { this->Execute( [=]( const sample* f ) { double v; P::FromSample( v, *f ); this->SumStep( v*v ); } ); } }; // ------------------------------------------------------------------------- class SumAbsThread : public SumThread { public: SumAbsThread( const GenericImage& image, const Rect& rect, int ch1, int ch2, int firstRow, int endRow ) : SumThread( image, rect, ch1, ch2, firstRow, endRow ) { } private: void DoExecute() override { this->Execute( [=]( const sample* f ) { double v; P::FromSample( v, *f ); this->SumStep( pcl::Abs( v ) ); } ); } }; // ------------------------------------------------------------------------- class NormThread : public RectThreadBase { public: Vector R; size_type n; NormThread( const GenericImage& image, int degree, const Rect& rect, int ch1, int ch2, int firstRow, int endRow ) : RectThreadBase( image, rect, ch1, ch2, firstRow, endRow ) , R( degree ) , e( degree ) { } void Run() final { R = e = 0.0; n = 0; this->Execute( [=]( const sample* f ) { double v; P::FromSample( v, *f ); for ( int i = 0;; ) { NormStep( i, v ); if ( ++i == R.Length() ) break; v *= v; } ++n; } ); } protected: Vector e; void NormStep( int i, double x ) noexcept { double y = x - e[i]; double t = R[i] + y; e[i] = (t - R[i]) - y; R[i] = t; } }; // ------------------------------------------------------------------------- class HistogramThread : public RectThreadBase { public: SzVector H; HistogramThread( const GenericImage& image, const Rect& rect, int ch1, int ch2, int firstRow, int endRow, const double& low, const double& high ) : RectThreadBase( image, rect, ch1, ch2, firstRow, endRow ) , H( __PCL_MEDIAN_HISTOGRAM_LENGTH ) , m_low( low ) , m_high( high ) { } void Run() final { H = size_type( 0 ); m_range = m_high - m_low; // Workaround for clang compiler bug on macOS: // https://pixinsight.com/forum/index.php?threads/pi-always-crash-when-stf.15830/ #ifdef __PCL_MACOSX if ( 1 + m_range != 1 ) #endif this->Execute( [=]( const sample* f ) { if ( *f >= m_low ) if ( *f <= m_high ) ++H[TruncInt( (__PCL_MEDIAN_HISTOGRAM_LENGTH - 1) * (double( *f ) - m_low)/m_range )]; } ); } private: const double& m_low; const double& m_high; double m_range; }; // ------------------------------------------------------------------------- class ExtremeAbsDevThread : public RectThreadBase { public: double minAbsDev, maxAbsDev; size_type count; ExtremeAbsDevThread( const GenericImage& image, const Rect& rect, int ch1, int ch2, int firstRow, int endRow, double center ) : RectThreadBase( image, rect, ch1, ch2, firstRow, endRow ) , m_center( center ) { } void Run() final { minAbsDev = std::numeric_limits::max(); maxAbsDev = 0; count = 0; this->Execute( [=]( const sample* f ) { double d; P::FromSample( d, *f ); d = pcl::Abs( d - m_center ); if ( d < minAbsDev ) minAbsDev = d; if ( d > maxAbsDev ) maxAbsDev = d; ++count; } ); } private: double m_center; }; // ------------------------------------------------------------------------- class TwoSidedExtremeAbsDevThread : public RectThreadBase { public: double minAbsDevLow, minAbsDevHigh; double maxAbsDevLow, maxAbsDevHigh; size_type nLow, nHigh; TwoSidedExtremeAbsDevThread( const GenericImage& image, const Rect& rect, int ch1, int ch2, int firstRow, int endRow, double center ) : RectThreadBase( image, rect, ch1, ch2, firstRow, endRow ) , m_center( center ) { } void Run() final { minAbsDevLow = minAbsDevHigh = std::numeric_limits::max(); maxAbsDevLow = maxAbsDevHigh = 0; nLow = nHigh = 0; this->Execute( [=]( const sample* f ) { double x; P::FromSample( x, *f ); if ( x <= m_center ) { double d = m_center - x; if ( d < minAbsDevLow ) minAbsDevLow = d; if ( d > maxAbsDevLow ) maxAbsDevLow = d; ++nLow; } else { double d = x - m_center; if ( d < minAbsDevHigh ) minAbsDevHigh = d; if ( d > maxAbsDevHigh ) maxAbsDevHigh = d; ++nHigh; } } ); } private: double m_center; }; // ------------------------------------------------------------------------- class AbsDevHistogramThread : public RectThreadBase { public: SzVector H; AbsDevHistogramThread( const GenericImage& image, const Rect& rect, int ch1, int ch2, int firstRow, int endRow, double center, const double& low, const double& high ) : RectThreadBase( image, rect, ch1, ch2, firstRow, endRow ) , H( __PCL_MEDIAN_HISTOGRAM_LENGTH ) , m_center( center ) , m_low( low ) , m_high( high ) { } void Run() final { H = size_type( 0 ); m_range = m_high - m_low; // Workaround for clang compiler bug on macOS: // https://pixinsight.com/forum/index.php?threads/pi-always-crash-when-stf.15830/ #ifdef __PCL_MACOSX if ( 1 + m_range != 1 ) #endif this->Execute( [=]( const sample* f ) { double d; P::FromSample( d, *f ); d = pcl::Abs( d - m_center ); if ( d >= m_low ) if ( d <= m_high ) ++H[TruncInt( (__PCL_MEDIAN_HISTOGRAM_LENGTH - 1) * (d - m_low)/m_range )]; } ); } private: double m_center; const double& m_low; const double& m_high; double m_range; }; // ------------------------------------------------------------------------- class TwoSidedAbsDevHistogramThread : public RectThreadBase { public: SzVector H; TwoSidedAbsDevHistogramThread( const GenericImage& image, const Rect& rect, int ch1, int ch2, int firstRow, int endRow, double center, const int& side, const double& low, const double& high ) : RectThreadBase( image, rect, ch1, ch2, firstRow, endRow ) , H( __PCL_MEDIAN_HISTOGRAM_LENGTH ) , m_center( center ) , m_side( side ) , m_low( low ) , m_high( high ) { } void Run() final { H = size_type( 0 ); m_range = m_high - m_low; // Workaround for clang compiler bug on macOS: // https://pixinsight.com/forum/index.php?threads/pi-always-crash-when-stf.15830/ #ifdef __PCL_MACOSX if ( 1 + m_range != 1 ) #endif this->Execute( [=]( const sample* f ) { double x; P::FromSample( x, *f ); if ( m_side > 0 == x > m_center ) { double d = m_side ? x - m_center : m_center - x; if ( d >= m_low ) if ( d <= m_high ) ++H[TruncInt( (__PCL_MEDIAN_HISTOGRAM_LENGTH - 1) * (d - m_low)/m_range )]; } } ); } private: double m_center; const int& m_side; const double& m_low; const double& m_high; double m_range; }; // ------------------------------------------------------------------------- class VarThread : public RectThreadBase { public: double var, eps; VarThread( const GenericImage& image, double mean, const Rect& rect, int ch1, int ch2, int firstRow, int endRow ) : RectThreadBase( image, rect, ch1, ch2, firstRow, endRow ) , m_mean( mean ) { } void Run() final { var = eps = 0; this->Execute( [=]( const sample* f ) { double d; P::FromSample( d, *f ); d -= m_mean; var += d*d; eps += d; } ); } private: double m_mean; }; // ------------------------------------------------------------------------- class SumAbsDevThread : public SumThread { public: SumAbsDevThread( const GenericImage& image, double center, const Rect& rect, int ch1, int ch2, int firstRow, int endRow ) : SumThread( image, rect, ch1, ch2, firstRow, endRow ) , m_center( center ) { } private: double m_center; void DoExecute() override { this->Execute( [=]( const sample* f ) { double v; P::FromSample( v, *f ); this->SumStep( pcl::Abs( v - m_center ) ); } ); } }; // ------------------------------------------------------------------------- class TwoSidedSumAbsDevThread : public RectThreadBase { public: double s0, s1; size_type n0, n1; TwoSidedSumAbsDevThread( const GenericImage& image, double center, const Rect& rect, int ch1, int ch2, int firstRow, int endRow ) : RectThreadBase( image, rect, ch1, ch2, firstRow, endRow ) , m_center( center ) { } void Run() final { s0 = s1 = 0; n0 = n1 = 0; this->Execute( [=]( const sample* f ) { double v; P::FromSample( v, *f ); if ( v <= m_center ) { s0 += m_center - v; ++n0; } else { s1 += v - m_center; ++n1; } } ); } private: double m_center; }; // ------------------------------------------------------------------------- class BWMVThread : public RectThreadBase { public: double num, den; size_type n, nr; BWMVThread( const GenericImage& image, double center, double kd, const Rect& rect, int ch1, int ch2, int firstRow, int endRow ) : RectThreadBase( image, rect, ch1, ch2, firstRow, endRow ) , m_center( center ) , m_kd( kd ) { } void Run() final { num = den = 0; n = nr = 0; this->Execute( [=]( const sample* f ) { ++n; double xc; P::FromSample( xc, *f ); xc -= m_center; double y = xc/m_kd; if ( pcl::Abs( y ) < 1 ) { double y2 = y*y; double y21 = 1 - y2; num += xc*xc * y21*y21*y21*y21; den += y21 * (1 - 5*y2); ++nr; } } ); } private: double m_center; double m_kd; }; // ------------------------------------------------------------------------- class TwoSidedBWMVThread : public RectThreadBase { public: double num0, den0; double num1, den1; size_type n0, n1, nr0, nr1; TwoSidedBWMVThread( const GenericImage& image, double center, double kd0, double kd1, const Rect& rect, int ch1, int ch2, int firstRow, int endRow ) : RectThreadBase( image, rect, ch1, ch2, firstRow, endRow ) , m_center( center ) , m_kd0( kd0 ) , m_kd1( kd1 ) { } void Run() final { num0 = den0 = num1 = den1 = 0; n0 = n1 = nr0 = nr1 = 0; this->Execute( [=]( const sample* f ) { double xc; P::FromSample( xc, *f ); xc -= m_center; bool low = xc <= 0; if ( low ) ++n0; else ++n1; double y = xc/(low ? m_kd0 : m_kd1); if ( pcl::Abs( y ) < 1 ) { double y2 = y*y; double y21 = 1 - y2; double num = xc*xc * y21*y21*y21*y21; double den = y21 * (1 - 5*y2); if ( low ) { num0 += num; den0 += den; ++nr0; } else { num1 += num; den1 += den; ++nr1; } } } ); } private: double m_center; double m_kd0; double m_kd1; }; // ------------------------------------------------------------------------- class SmpThread : public RectThreadBase { public: Array samples; size_type n; SmpThread( const GenericImage& image, const Rect& rect, int ch1, int ch2, int firstRow, int endRow ) : RectThreadBase( image, rect, ch1, ch2, firstRow, endRow ) { size_type N = size_type( this->m_rect.Width() ) * size_type( this->m_endRow - this->m_firstRow ) * (1 + this->m_ch2 - this->m_ch1); samples = Array( N ); } void Run() final { n = 0; this->Execute( [=]( const sample* f ) { samples[n++] = *f; } ); } }; // ------------------------------------------------------------------------- class DSmpThread : public RectThreadBase { public: Array values; size_type n; DSmpThread( const GenericImage& image, const Rect& rect, int ch1, int ch2, int firstRow, int endRow ) : RectThreadBase( image, rect, ch1, ch2, firstRow, endRow ) { size_type N = size_type( this->m_rect.Width() ) * size_type( this->m_endRow - this->m_firstRow ) * (1 + this->m_ch2 - this->m_ch1); values = Array( N ); } void Run() final { n = 0; DoExecute(); } protected: virtual void DoExecute() { this->Execute( [=]( const sample* f ) { P::FromSample( values[n++], *f ); } ); } }; // ------------------------------------------------------------------------- class AbsDevSmpThread : public DSmpThread { public: AbsDevSmpThread( const GenericImage& image, double center, const Rect& rect, int ch1, int ch2, int firstRow, int endRow ) : DSmpThread( image, rect, ch1, ch2, firstRow, endRow ) , m_center( center ) { } private: double m_center; void DoExecute() override { this->Execute( [=]( const sample* f ) { double d; P::FromSample( d, *f ); this->values[this->n++] = pcl::Abs( d - m_center ); } ); } }; // ------------------------------------------------------------------------- class TwoSidedAbsDevSmpThread : public DSmpThread { public: Array::iterator p, q; TwoSidedAbsDevSmpThread( const GenericImage& image, double center, const Rect& rect, int ch1, int ch2, int firstRow, int endRow ) : DSmpThread( image, rect, ch1, ch2, firstRow, endRow ) , m_center( center ) { } private: double m_center; void DoExecute() override { p = this->values.Begin(); q = this->values.End(); this->Execute( [=]( const sample* f ) { double x; P::FromSample( x, *f ); if ( x <= m_center ) *p++ = m_center - x; else *--q = x - m_center; ++this->n; } ); } }; // ------------------------------------------------------------------------- class ColorSpaceConversionThread : public Thread { public: ColorSpaceConversionThread( GenericImage& image, ThreadData& data, color_space toColorSpace, size_type begin, size_type end ) : m_image( image ) , m_data( data ) , m_toColorSpace( toColorSpace ) , m_begin( begin ) , m_end( end ) { } void Run() final { INIT_THREAD_MONITOR() const RGBColorSystem& rgbws = m_image.RGBWorkingSpace(); typename P::sample* f0 = m_image[0] + m_begin; typename P::sample* f1 = m_image[1] + m_begin; typename P::sample* f2 = m_image[2] + m_begin; typename P::sample* fN = m_image[0] + m_end; for ( ; f0 < fN; ++f0, ++f1, ++f2 ) { RGBColorSystem::sample r0, r1, r2; P::FromSample( r0, *f0 ); P::FromSample( r1, *f1 ); P::FromSample( r2, *f2 ); switch ( m_image.ColorSpace() ) { case ColorSpace::RGB : switch ( m_toColorSpace ) { case ColorSpace::Gray : rgbws.RGBToGray( r0, r0, r1, r2 ); break; case ColorSpace::HSV : rgbws.RGBToHSV( r0, r1, r2, r0, r1, r2 ); break; case ColorSpace::HSI : rgbws.RGBToHSI( r0, r1, r2, r0, r1, r2 ); break; case ColorSpace::CIEXYZ : rgbws.RGBToCIEXYZ( r0, r1, r2, r0, r1, r2 ); break; case ColorSpace::CIELab : rgbws.RGBToCIELab( r0, r1, r2, r0, r1, r2 ); break; case ColorSpace::CIELch : rgbws.RGBToCIELch( r0, r1, r2, r0, r1, r2 ); break; default: break; } break; case ColorSpace::HSV : rgbws.HSVToRGB( r0, r1, r2, r0, r1, r2 ); switch ( m_toColorSpace ) { case ColorSpace::Gray : rgbws.RGBToGray( r0, r0, r1, r2 ); break; case ColorSpace::RGB : break; case ColorSpace::HSI : rgbws.RGBToHSI( r0, r1, r2, r0, r1, r2 ); break; case ColorSpace::CIEXYZ : rgbws.RGBToCIEXYZ( r0, r1, r2, r0, r1, r2 ); break; case ColorSpace::CIELab : rgbws.RGBToCIELab( r0, r1, r2, r0, r1, r2 ); break; case ColorSpace::CIELch : rgbws.RGBToCIELch( r0, r1, r2, r0, r1, r2 ); break; default: break; } break; case ColorSpace::HSI : rgbws.HSIToRGB( r0, r1, r2, r0, r1, r2 ); switch ( m_toColorSpace ) { case ColorSpace::Gray : rgbws.RGBToGray( r0, r0, r1, r2 ); break; case ColorSpace::RGB : break; case ColorSpace::HSV : rgbws.RGBToHSV( r0, r1, r2, r0, r1, r2 ); break; case ColorSpace::CIEXYZ : rgbws.RGBToCIEXYZ( r0, r1, r2, r0, r1, r2 ); break; case ColorSpace::CIELab : rgbws.RGBToCIELab( r0, r1, r2, r0, r1, r2 ); break; case ColorSpace::CIELch : rgbws.RGBToCIELch( r0, r1, r2, r0, r1, r2 ); break; default: break; } break; case ColorSpace::CIEXYZ : switch ( m_toColorSpace ) { case ColorSpace::Gray : rgbws.CIEXYZToRGB( r0, r1, r2, r0, r1, r2 ); rgbws.RGBToGray( r0, r0, r1, r2 ); break; case ColorSpace::RGB : rgbws.CIEXYZToRGB( r0, r1, r2, r0, r1, r2 ); break; case ColorSpace::HSV : rgbws.CIEXYZToRGB( r0, r1, r2, r0, r1, r2 ); rgbws.RGBToHSV( r0, r1, r2, r0, r1, r2 ); break; case ColorSpace::HSI : rgbws.CIEXYZToRGB( r0, r1, r2, r0, r1, r2 ); rgbws.RGBToHSI( r0, r1, r2, r0, r1, r2 ); break; case ColorSpace::CIELab : rgbws.CIEXYZToCIELab( r0, r1, r2, r0, r1, r2 ); break; case ColorSpace::CIELch : rgbws.CIEXYZToCIELab( r0, r1, r2, r0, r1, r2 ); rgbws.CIELabToCIELch( r0, r1, r2, r0, r1, r2 ); break; default: break; } break; case ColorSpace::CIELab : switch ( m_toColorSpace ) { case ColorSpace::Gray : rgbws.CIELabToRGB( r0, r1, r2, r0, r1, r2 ); rgbws.RGBToGray( r0, r0, r1, r2 ); break; case ColorSpace::RGB : rgbws.CIELabToRGB( r0, r1, r2, r0, r1, r2 ); break; case ColorSpace::HSV : rgbws.CIELabToRGB( r0, r1, r2, r0, r1, r2 ); rgbws.RGBToHSV( r0, r1, r2, r0, r1, r2 ); break; case ColorSpace::HSI : rgbws.CIELabToRGB( r0, r1, r2, r0, r1, r2 ); rgbws.RGBToHSI( r0, r1, r2, r0, r1, r2 ); break; case ColorSpace::CIEXYZ : rgbws.CIELabToCIEXYZ( r0, r1, r2, r0, r1, r2 ); break; case ColorSpace::CIELch : rgbws.CIELabToCIELch( r0, r1, r2, r0, r1, r2 ); break; default: break; } break; case ColorSpace::CIELch : switch ( m_toColorSpace ) { case ColorSpace::Gray : rgbws.CIELchToRGB( r0, r1, r2, r0, r1, r2 ); rgbws.RGBToGray( r0, r0, r1, r2 ); break; case ColorSpace::RGB : rgbws.CIELchToRGB( r0, r1, r2, r0, r1, r2 ); break; case ColorSpace::HSV : rgbws.CIELchToRGB( r0, r1, r2, r0, r1, r2 ); rgbws.RGBToHSV( r0, r1, r2, r0, r1, r2 ); break; case ColorSpace::HSI : rgbws.CIELchToRGB( r0, r1, r2, r0, r1, r2 ); rgbws.RGBToHSI( r0, r1, r2, r0, r1, r2 ); break; case ColorSpace::CIEXYZ : rgbws.CIELchToCIELab( r0, r1, r2, r0, r1, r2 ); rgbws.CIELabToCIEXYZ( r0, r1, r2, r0, r1, r2 ); break; case ColorSpace::CIELab : rgbws.CIELchToCIELab( r0, r1, r2, r0, r1, r2 ); break; default: break; } break; default: break; } *f0 = P::ToSample( r0 ); if ( m_toColorSpace != ColorSpace::Gray ) { *f1 = P::ToSample( r1 ); *f2 = P::ToSample( r2 ); } UPDATE_THREAD_MONITOR( 65536 ) } } private: GenericImage& m_image; ThreadData& m_data; color_space m_toColorSpace; size_type m_begin, m_end; }; // ------------------------------------------------------------------------- template class GetLuminanceThread : public Thread { public: GetLuminanceThread( GenericImage& luminance, const GenericImage& image, ThreadData& data, const Rect& rect, int firstRow, int endRow ) : m_luminance( luminance ) , m_image( image ) , m_data( data ) , m_rect( rect ) , m_firstRow( firstRow ) , m_endRow( endRow ) { } void Run() final { INIT_THREAD_MONITOR() const RGBColorSystem& rgbws = m_image.RGBWorkingSpace(); const typename P::sample* f0 = m_image.PixelAddress( m_rect.x0, m_rect.y0 + m_firstRow, 0 ); const typename P::sample* f1 = m_image.PixelAddress( m_rect.x0, m_rect.y0 + m_firstRow, 1 ); const typename P::sample* f2 = m_image.PixelAddress( m_rect.x0, m_rect.y0 + m_firstRow, 2 ); typename P1::sample* fY = m_luminance.ScanLine( m_firstRow ); for ( int y = m_firstRow, dw = m_image.Width()-m_rect.Width(); y < m_endRow; ++y, f0 += dw, f1 += dw, f2 += dw ) { const typename P::sample* fN = f0 + m_rect.Width(); for ( ; f0 < fN; ++f0, ++f1, ++f2, ++fY ) { RGBColorSystem::sample r0, r1, r2, rY; P::FromSample( r0, *f0 ); P::FromSample( r1, *f1 ); P::FromSample( r2, *f2 ); switch ( m_image.ColorSpace() ) { case ColorSpace::RGB: case ColorSpace::HSV: case ColorSpace::HSI: switch ( m_image.ColorSpace() ) { case ColorSpace::HSV: rgbws.HSVToRGB( r0, r1, r2, r0, r1, r2 ); break; case ColorSpace::HSI: rgbws.HSIToRGB( r0, r1, r2, r0, r1, r2 ); break; default: break; } rY = rgbws.CIEY( r0, r1, r2 ); break; case ColorSpace::CIELch: rgbws.CIELchToCIELab( r0, r1, r2, r0, r1, r2 ); // fall through case ColorSpace::CIELab: rgbws.CIELabToCIEXYZ( r0, rY, r2, r0, r1, r2 ); break; default: // ?! rY = 0; break; } *fY = P1::ToSample( rY ); UPDATE_THREAD_MONITOR( 65536 ) } } } private: GenericImage& m_luminance; const GenericImage& m_image; ThreadData& m_data; const Rect& m_rect; int m_firstRow, m_endRow; }; // ------------------------------------------------------------------------- template class GetLightnessThread : public Thread { public: GetLightnessThread( GenericImage& lightness, const GenericImage& image, ThreadData& data, const Rect& rect, int firstRow, int endRow ) : m_lightness( lightness ) , m_image( image ) , m_data( data ) , m_rect( rect ) , m_firstRow( firstRow ) , m_endRow( endRow ) { } void Run() final { INIT_THREAD_MONITOR() const RGBColorSystem& rgbws = m_image.RGBWorkingSpace(); const typename P::sample* f0 = m_image.PixelAddress( m_rect.x0, m_rect.y0 + m_firstRow, 0 ); const typename P::sample* f1 = m_image.PixelAddress( m_rect.x0, m_rect.y0 + m_firstRow, 1 ); const typename P::sample* f2 = m_image.PixelAddress( m_rect.x0, m_rect.y0 + m_firstRow, 2 ); typename P1::sample* fL = m_lightness.ScanLine( m_firstRow ); for ( int y = m_firstRow, dw = m_image.Width()-m_rect.Width(); y < m_endRow; ++y, f0 += dw, f1 += dw, f2 += dw ) { const typename P::sample* fN = f0 + m_rect.Width(); for ( ; f0 < fN; ++f0, ++f1, ++f2, ++fL ) { RGBColorSystem::sample r0, r1, r2, rL; P::FromSample( r0, *f0 ); P::FromSample( r1, *f1 ); P::FromSample( r2, *f2 ); switch ( m_image.ColorSpace() ) { case ColorSpace::RGB: case ColorSpace::HSV: case ColorSpace::HSI: case ColorSpace::CIEXYZ: switch ( m_image.ColorSpace() ) { case ColorSpace::HSV: rgbws.HSVToRGB( r0, r1, r2, r0, r1, r2 ); break; case ColorSpace::HSI: rgbws.HSIToRGB( r0, r1, r2, r0, r1, r2 ); break; case ColorSpace::CIEXYZ: rgbws.CIEXYZToRGB( r0, r1, r2, r0, r1, r2 ); break; default: break; } rL = rgbws.CIEL( r0, r1, r2 ); break; default: // ?! rL = 0; break; } *fL = P1::ToSample( rL ); UPDATE_THREAD_MONITOR( 65536 ) } } } private: GenericImage& m_lightness; const GenericImage& m_image; ThreadData& m_data; const Rect& m_rect; int m_firstRow, m_endRow; }; // ------------------------------------------------------------------------- template class GetIntensityThread : public Thread { public: GetIntensityThread( GenericImage& luminance, const GenericImage& image, ThreadData& data, const Rect& rect, int firstRow, int endRow ) : m_intensity( luminance ) , m_image( image ) , m_data( data ) , m_rect( rect ) , m_firstRow( firstRow ) , m_endRow( endRow ) { } void Run() final { INIT_THREAD_MONITOR() const RGBColorSystem& rgbws = m_image.RGBWorkingSpace(); const typename P::sample* f0 = m_image.PixelAddress( m_rect.x0, m_rect.y0 + m_firstRow, 0 ); const typename P::sample* f1 = m_image.PixelAddress( m_rect.x0, m_rect.y0 + m_firstRow, 1 ); const typename P::sample* f2 = m_image.PixelAddress( m_rect.x0, m_rect.y0 + m_firstRow, 2 ); typename P1::sample* fI = m_intensity.ScanLine( m_firstRow ); for ( int y = m_firstRow, dw = m_image.Width()-m_rect.Width(); y < m_endRow; ++y, f0 += dw, f1 += dw, f2 += dw ) { const typename P::sample* fN = f0 + m_rect.Width(); for ( ; f0 < fN; ++f0, ++f1, ++f2, ++fI ) { RGBColorSystem::sample r0, r1, r2; P::FromSample( r0, *f0 ); P::FromSample( r1, *f1 ); P::FromSample( r2, *f2 ); switch ( m_image.ColorSpace() ) { case ColorSpace::RGB: break; case ColorSpace::HSV: rgbws.HSVToRGB( r0, r1, r2, r0, r1, r2 ); break; case ColorSpace::CIEXYZ: rgbws.CIEXYZToRGB( r0, r1, r2, r0, r1, r2 ); break; case ColorSpace::CIELab: rgbws.CIELabToRGB( r0, r1, r2, r0, r1, r2 ); break; case ColorSpace::CIELch: rgbws.CIELchToRGB( r0, r1, r2, r0, r1, r2 ); break; default: break; } *fI = P1::ToSample( rgbws.Intensity( r0, r1, r2 ) ); UPDATE_THREAD_MONITOR( 65536 ) } } } private: GenericImage& m_intensity; const GenericImage& m_image; ThreadData& m_data; const Rect& m_rect; int m_firstRow, m_endRow; }; // ------------------------------------------------------------------------- template class SetLuminanceThread : public Thread { public: SetLuminanceThread( GenericImage& image, const GenericImage& luminance, ThreadData& data, const Point& target, const Rect& rect, int firstRow, int endRow ) : m_image( image ) , m_luminance( luminance ) , m_data( data ) , m_target( target ) , m_rect( rect ) , m_firstRow( firstRow ) , m_endRow( endRow ) { } void Run() final { INIT_THREAD_MONITOR() const RGBColorSystem& sourceRGBWS = m_luminance.RGBWorkingSpace(); const RGBColorSystem& targetRGBWS = m_image.RGBWorkingSpace(); typename P::sample* f0 = m_image.PixelAddress( m_target.x, m_target.y + m_firstRow, 0 ); typename P::sample* f1 = m_image.PixelAddress( m_target.x, m_target.y + m_firstRow, 1 ); typename P::sample* f2 = m_image.PixelAddress( m_target.x, m_target.y + m_firstRow, 2 ); if ( m_luminance.ColorSpace() == ColorSpace::Gray || m_luminance.ColorSpace() == ColorSpace::CIEXYZ ) { int cY = (m_luminance.ColorSpace() == ColorSpace::Gray) ? 0 : 1; const typename P1::sample* fY = m_luminance.PixelAddress( m_rect.x0, m_rect.y0 + m_firstRow, cY ); for ( int y = m_firstRow, dw = m_image.Width()-m_rect.Width(), dwY = m_luminance.Width()-m_rect.Width(); y < m_endRow; ++y, f0 += dw, f1 += dw, f2 += dw, fY += dwY ) { const typename P::sample* fN = f0 + m_rect.Width(); for ( ; f0 < fN; ++f0, ++f1, ++f2, ++fY ) { RGBColorSystem::sample r0, r1, r2, rY; P::FromSample( r0, *f0 ); P::FromSample( r1, *f1 ); P::FromSample( r2, *f2 ); P1::FromSample( rY, *fY ); switch ( m_image.ColorSpace() ) { case ColorSpace::RGB: case ColorSpace::HSV: case ColorSpace::HSI: switch ( m_image.ColorSpace() ) { case ColorSpace::HSV: targetRGBWS.HSVToRGB( r0, r1, r2, r0, r1, r2 ); break; case ColorSpace::HSI: targetRGBWS.HSIToRGB( r0, r1, r2, r0, r1, r2 ); break; default: // RGB break; } targetRGBWS.RGBToCIEab( r1, r2, r0, r1, r2 ); targetRGBWS.CIELabToRGB( r0, r1, r2, sourceRGBWS.CIEYToCIEL( rY ), r1, r2 ); switch ( m_image.ColorSpace() ) { case ColorSpace::HSV: targetRGBWS.RGBToHSV( r0, r1, r2, r0, r1, r2 ); break; case ColorSpace::HSI: targetRGBWS.RGBToHSI( r0, r1, r2, r0, r1, r2 ); break; default: // RGB break; } break; case ColorSpace::CIEXYZ: targetRGBWS.CIEXYZToCIELab( r0, r1, r2, r0, r1, r2 ); targetRGBWS.CIELabToCIEXYZ( r0, r1, r2, sourceRGBWS.CIEYToCIEL( rY ), r1, r2 ); break; case ColorSpace::CIELab: case ColorSpace::CIELch: r0 = sourceRGBWS.CIEYToCIEL( rY ); break; default: // ??? break; } *f0 = P::ToSample( r0 ); *f1 = P::ToSample( r1 ); *f2 = P::ToSample( r2 ); UPDATE_THREAD_MONITOR( 65536 ) } } } else { const typename P1::sample* g0 = m_luminance.PixelAddress( m_rect.x0, m_rect.y0 + m_firstRow, 0 ); const typename P1::sample* g1 = m_luminance.PixelAddress( m_rect.x0, m_rect.y0 + m_firstRow, 1 ); const typename P1::sample* g2 = m_luminance.PixelAddress( m_rect.x0, m_rect.y0 + m_firstRow, 2 ); for ( int y = m_firstRow, dw = m_image.Width()-m_rect.Width(), dwY = m_luminance.Width()-m_rect.Width(); y < m_endRow; ++y, f0 += dw, f1 += dw, f2 += dw, g0 += dwY, g1 += dwY, g2 += dwY ) { const typename P::sample* fN = f0 + m_rect.Width(); for ( ; f0 < fN; ++f0, ++f1, ++f2, ++g0, ++g1, ++g2 ) { typename RGBColorSystem::sample r0, r1, r2, s0, s1, s2; P::FromSample( r0, *f0 ); P::FromSample( r1, *f1 ); P::FromSample( r2, *f2 ); P1::FromSample( s0, *g0 ); P1::FromSample( s1, *g1 ); P1::FromSample( s2, *g2 ); switch ( m_image.ColorSpace() ) { case ColorSpace::RGB: case ColorSpace::HSV: case ColorSpace::HSI: case ColorSpace::CIEXYZ: case ColorSpace::CIELab: case ColorSpace::CIELch: switch ( m_image.ColorSpace() ) { case ColorSpace::RGB: case ColorSpace::HSV: case ColorSpace::HSI: switch ( m_image.ColorSpace() ) { case ColorSpace::HSV: targetRGBWS.HSVToRGB( r0, r1, r2, r0, r1, r2 ); break; case ColorSpace::HSI: targetRGBWS.HSIToRGB( r0, r1, r2, r0, r1, r2 ); break; default: break; } targetRGBWS.RGBToCIEab( r1, r2, r0, r1, r2 ); break; case ColorSpace::CIEXYZ: targetRGBWS.CIEXYZToCIELab( r0, r1, r2, r0, r1, r2 ); break; case ColorSpace::CIELab: // NOOP case ColorSpace::CIELch: // NOOP break; default: // ?! break; } switch ( m_luminance.ColorSpace() ) { case ColorSpace::RGB: case ColorSpace::HSV: case ColorSpace::HSI: switch ( m_luminance.ColorSpace() ) { case ColorSpace::HSV: sourceRGBWS.HSVToRGB( s0, s1, s2, s0, s1, s2 ); break; case ColorSpace::HSI: sourceRGBWS.HSIToRGB( s0, s1, s2, s0, s1, s2 ); break; default: break; } r0 = sourceRGBWS.CIEL( s0, s1, s2 ); break; case ColorSpace::CIEXYZ: sourceRGBWS.CIEXYZToCIELab( r0, s1, s2, s0, s1, s2 ); break; case ColorSpace::CIELab: case ColorSpace::CIELch: r0 = s0; break; default: // ?! break; } switch ( m_image.ColorSpace() ) { case ColorSpace::RGB: case ColorSpace::HSV: case ColorSpace::HSI: targetRGBWS.CIELabToRGB( r0, r1, r2, r0, r1, r2 ); switch ( m_image.ColorSpace() ) { case ColorSpace::HSV: targetRGBWS.RGBToHSV( r0, r1, r2, r0, r1, r2 ); break; case ColorSpace::HSI: targetRGBWS.RGBToHSI( r0, r1, r2, r0, r1, r2 ); break; default: // RGB break; } break; case ColorSpace::CIEXYZ: targetRGBWS.CIELabToCIEXYZ( r0, r1, r2, r0, r1, r2 ); break; case ColorSpace::CIELab: // NOOP case ColorSpace::CIELch: // NOOP break; default: // ?! break; } break; default: // ?! break; } *f0 = P::ToSample( r0 ); *f1 = P::ToSample( r1 ); *f2 = P::ToSample( r2 ); UPDATE_THREAD_MONITOR( 65536 ) } } } } private: GenericImage& m_image; const GenericImage& m_luminance; ThreadData& m_data; const Point& m_target; const Rect& m_rect; int m_firstRow, m_endRow; }; // ------------------------------------------------------------------------- template class SetLightnessThread : public Thread { public: SetLightnessThread( GenericImage& image, const GenericImage& lightness, ThreadData& data, const Point& target, const Rect& rect, int firstRow, int endRow ) : m_image( image ) , m_lightness( lightness ) , m_data( data ) , m_target( target ) , m_rect( rect ) , m_firstRow( firstRow ) , m_endRow( endRow ) { } void Run() final { INIT_THREAD_MONITOR() const RGBColorSystem& sourceRGBWS = m_lightness.RGBWorkingSpace(); const RGBColorSystem& targetRGBWS = m_image.RGBWorkingSpace(); typename P::sample* f0 = m_image.PixelAddress( m_target.x, m_target.y + m_firstRow, 0 ); typename P::sample* f1 = m_image.PixelAddress( m_target.x, m_target.y + m_firstRow, 1 ); typename P::sample* f2 = m_image.PixelAddress( m_target.x, m_target.y + m_firstRow, 2 ); if ( m_lightness.ColorSpace() == ColorSpace::Gray || m_lightness.ColorSpace() == ColorSpace::CIELab || m_lightness.ColorSpace() == ColorSpace::CIELch ) { const typename P1::sample* fL = m_lightness.PixelAddress( m_rect.x0, m_rect.y0 + m_firstRow, 0 ); for ( int y = m_firstRow, dw = m_image.Width()-m_rect.Width(), dwL = m_lightness.Width()-m_rect.Width(); y < m_endRow; ++y, f0 += dw, f1 += dw, f2 += dw, fL += dwL ) { const typename P::sample* fN = f0 + m_rect.Width(); for ( ; f0 < fN; ++f0, ++f1, ++f2, ++fL ) { RGBColorSystem::sample r0, r1, r2, rL; P::FromSample( r0, *f0 ); P::FromSample( r1, *f1 ); P::FromSample( r2, *f2 ); P1::FromSample( rL, *fL ); switch ( m_image.ColorSpace() ) { case ColorSpace::RGB: case ColorSpace::HSV: case ColorSpace::HSI: switch ( m_image.ColorSpace() ) { case ColorSpace::HSV: targetRGBWS.HSVToRGB( r0, r1, r2, r0, r1, r2 ); break; case ColorSpace::HSI: targetRGBWS.HSIToRGB( r0, r1, r2, r0, r1, r2 ); break; default: break; } targetRGBWS.RGBToCIEab( r1, r2, r0, r1, r2 ); targetRGBWS.CIELabToRGB( r0, r1, r2, rL, r1, r2 ); switch ( m_image.ColorSpace() ) { case ColorSpace::HSV: targetRGBWS.RGBToHSV( r0, r1, r2, r0, r1, r2 ); break; case ColorSpace::HSI: targetRGBWS.RGBToHSI( r0, r1, r2, r0, r1, r2 ); break; default: break; } break; case ColorSpace::CIEXYZ: r1 = targetRGBWS.CIELToCIEY( rL ); break; case ColorSpace::CIELab: case ColorSpace::CIELch: r0 = rL; break; default: // ??? break; } *f0 = P::ToSample( r0 ); *f1 = P::ToSample( r1 ); *f2 = P::ToSample( r2 ); UPDATE_THREAD_MONITOR( 65536 ) } } } else { const typename P1::sample* g0 = m_lightness.PixelAddress( m_rect.x0, m_rect.y0 + m_firstRow, 0 ); const typename P1::sample* g1 = m_lightness.PixelAddress( m_rect.x0, m_rect.y0 + m_firstRow, 1 ); const typename P1::sample* g2 = m_lightness.PixelAddress( m_rect.x0, m_rect.y0 + m_firstRow, 2 ); for ( int y = m_firstRow, dw = m_image.Width()-m_rect.Width(), dwL = m_lightness.Width()-m_rect.Width(); y < m_endRow; ++y, f0 += dw, f1 += dw, f2 += dw, g0 += dwL, g1 += dwL, g2 += dwL ) { const typename P::sample* fN = f0 + m_rect.Width(); for ( ; f0 < fN; ++f0, ++f1, ++f2, ++g0, ++g1, ++g2 ) { typename RGBColorSystem::sample r0, r1, r2, s0, s1, s2; P::FromSample( r0, *f0 ); P::FromSample( r1, *f1 ); P::FromSample( r2, *f2 ); P1::FromSample( s0, *g0 ); P1::FromSample( s1, *g1 ); P1::FromSample( s2, *g2 ); switch ( m_image.ColorSpace() ) { case ColorSpace::RGB: case ColorSpace::HSV: case ColorSpace::HSI: case ColorSpace::CIEXYZ: case ColorSpace::CIELab: case ColorSpace::CIELch: switch ( m_image.ColorSpace() ) { case ColorSpace::RGB: case ColorSpace::HSV: case ColorSpace::HSI: switch ( m_image.ColorSpace() ) { case ColorSpace::HSV: targetRGBWS.HSVToRGB( r0, r1, r2, r0, r1, r2 ); break; case ColorSpace::HSI: targetRGBWS.HSIToRGB( r0, r1, r2, r0, r1, r2 ); break; default: break; } targetRGBWS.RGBToCIEab( r1, r2, r0, r1, r2 ); break; case ColorSpace::CIEXYZ: targetRGBWS.CIEXYZToCIELab( r0, r1, r2, r0, r1, r2 ); break; case ColorSpace::CIELch: targetRGBWS.CIELchToCIELab( r0, r1, r2, r0, r1, r2 ); break; default: break; } switch ( m_lightness.ColorSpace() ) { case ColorSpace::RGB: case ColorSpace::HSV: case ColorSpace::HSI: switch ( m_lightness.ColorSpace() ) { case ColorSpace::HSV: sourceRGBWS.HSVToRGB( s0, s1, s2, s0, s1, s2 ); break; case ColorSpace::HSI: sourceRGBWS.HSIToRGB( s0, s1, s2, s0, s1, s2 ); break; default: break; } r0 = sourceRGBWS.CIEL( s0, s1, s2 ); break; case ColorSpace::CIEXYZ: r0 = sourceRGBWS.CIEYToCIEL( s1 ); break; default: // ??? break; } switch ( m_image.ColorSpace() ) { case ColorSpace::RGB: case ColorSpace::HSV: case ColorSpace::HSI: targetRGBWS.CIELabToRGB( r0, r1, r2, r0, r1, r2 ); switch ( m_image.ColorSpace() ) { case ColorSpace::HSV: targetRGBWS.RGBToHSV( r0, r1, r2, r0, r1, r2 ); break; case ColorSpace::HSI: targetRGBWS.RGBToHSI( r0, r1, r2, r0, r1, r2 ); break; default: break; } break; case ColorSpace::CIEXYZ: targetRGBWS.CIELabToCIEXYZ( r0, r1, r2, r0, r1, r2 ); break; case ColorSpace::CIELch: targetRGBWS.CIELabToCIELch( r0, r1, r2, r0, r1, r2 ); break; default: break; } default: // ??? break; } *f0 = P::ToSample( r0 ); *f1 = P::ToSample( r1 ); *f2 = P::ToSample( r2 ); UPDATE_THREAD_MONITOR( 65536 ) } } } } private: GenericImage& m_image; const GenericImage& m_lightness; ThreadData& m_data; const Point& m_target; const Rect& m_rect; int m_firstRow, m_endRow; }; // ------------------------------------------------------------------------- #ifdef __PCL_BUILDING_PIXINSIGHT_APPLICATION friend class pi::SharedImage; #endif }; #undef m_pixelData #undef m_channelData #undef m_allocator #undef m_width #undef m_height #undef m_numberOfChannels #undef m_colorSpace #undef m_RGBWS #undef m_channel #undef m_lastChannel #undef m_point #undef m_rectangle #undef m_clipLow #undef m_clipHigh #undef m_clippedLow #undef m_clippedHigh // ---------------------------------------------------------------------------- /*! * \defgroup image_scalar_operators Image Scalar Operators */ /*! * Returns the sum of an \a image and a \a scalar. The operation is performed * on the current selection of the specified \a image. * \ingroup image_scalar_operators */ template inline GenericImage

operator +( const GenericImage

& image, T scalar ) { GenericImage

result( image ); (void)(result += scalar); return result; } /*! * Returns the sum of a \a scalar and an \a image. The operation is performed * on the current selection of the specified \a image. * * This operator implements the commutative property of image scalar addition. * * \ingroup image_scalar_operators */ template inline GenericImage

operator +( T scalar, const GenericImage

& image ) { return image + scalar; } /*! * Returns the subtraction of a \a scalar from an \a image. The operation is * performed on the current selection of the specified \a image. * \ingroup image_scalar_operators */ template inline GenericImage

operator -( const GenericImage

& image, T scalar ) { GenericImage

result( image ); (void)(result -= scalar); return result; } /*! * Returns the product of an \a image by a \a scalar. The operation is * performed on the current selection of the specified \a image. * \ingroup image_scalar_operators */ template inline GenericImage

operator *( const GenericImage

& image, T scalar ) { GenericImage

result( image ); (void)(result *= scalar); return result; } /*! * Returns the product of a \a scalar by an \a image. The operation is * performed on the current selection of the specified \a image. * * This operator implements the commutative property of image scalar * multiplication. * * \ingroup image_scalar_operators */ template inline GenericImage

operator *( T scalar, const GenericImage

& image ) { return image * scalar; } /*! * Returns the result of dividing an \a image by a \a scalar. The operation is * performed on the current selection of the specified \a image. * \ingroup image_scalar_operators */ template inline GenericImage

operator /( const GenericImage

& image, T scalar ) { GenericImage

result( image ); (void)(result /= scalar); return result; } /*! * Returns the result of raising an \a image to a \a scalar. The operation is * performed on the current selection of the specified \a image. * \ingroup image_scalar_operators */ template inline GenericImage

operator ^( const GenericImage

& image, T scalar ) { GenericImage

result( image ); (void)(result ^= scalar); return result; } // ---------------------------------------------------------------------------- /*! * \defgroup image_operators Image Operators */ /*! * Returns the sum of two images. * * The image addition operation is performed for the current selection of the * right-hand operand image, and is applied at the current anchor point of the * left-hand image. * * The result is an image of the same template instantiation as the left-hand * operand image. * * \ingroup image_operators */ template inline GenericImage operator +( const GenericImage& image1, const GenericImage& image2 ) { GenericImage result( image1 ); (void)(result += image2); return result; } /*! * Returns the difference of two images. * * The image subtraction operation is performed for the current selection of * the right-hand operand image, and is applied at the current anchor point of * the left-hand image. * * The result is an image of the same template instantiation as the left-hand * operand image. * * \ingroup image_operators */ template inline GenericImage operator -( const GenericImage& image1, const GenericImage& image2 ) { GenericImage result( image1 ); (void)(result -= image2); return result; } /*! * Returns the product of two images. * * The image multiplication operation is performed for the current selection of * the right-hand operand image, and is applied at the current anchor point of * the left-hand image. * * The result is an image of the same template instantiation as the left-hand * operand image. * * \ingroup image_operators */ template inline GenericImage operator *( const GenericImage& image1, const GenericImage& image2 ) { GenericImage result( image1 ); (void)(result *= image2); return result; } /*! * Returns the result of dividing one image \a image1 by another image * \a image2. * * The image division operation is performed for the current selection of the * right-hand operand image, and is applied at the current anchor point of the * left-hand image. * * The result is an image of the same template instantiation as the left-hand * operand image. * * \ingroup image_operators */ template inline GenericImage operator /( const GenericImage& image1, const GenericImage& image2 ) { GenericImage result( image1 ); (void)(result /= image2); return result; } /*! * Returns the result of raising one image \a image1 to another image * \a image2. * * The image exponentiation operation is performed for the current selection of * the right-hand operand image, and is applied at the current anchor point of * the left-hand image. * * The result is an image of the same template instantiation as the left-hand * operand image. * * \ingroup image_operators */ template inline GenericImage operator ^( const GenericImage& image1, const GenericImage& image2 ) { GenericImage result( image1 ); (void)(result ^= image2); return result; } // ---------------------------------------------------------------------------- #ifndef __PCL_NO_IMAGE_INSTANTIATE /*! * \defgroup image_types_2d Image Types */ /*! * \class pcl::FImage * \ingroup image_types_2d * \brief 32-bit floating point real image. * * %FImage is a template instantiation of GenericImage for FloatPixelTraits. */ typedef GenericImage FImage; /*! * \class pcl::DImage * \ingroup image_types_2d * \brief 64-bit floating point real image. * * %DImage is a template instantiation of GenericImage for DoublePixelTraits. */ typedef GenericImage DImage; /*! * \class pcl::FComplexImage * \ingroup image_types_2d * \brief 32-bit floating point complex image. * * %FComplexImage is a template instantiation of GenericImage for the * ComplexPixelTraits class. */ typedef GenericImage FComplexImage; /*! * \class pcl::DComplexImage * \ingroup image_types_2d * \brief 64-bit floating point complex image. * * %DComplexImage is a template instantiation of GenericImage for the * DComplexPixelTraits class. */ typedef GenericImage DComplexImage; /*! * \class pcl::UInt8Image * \ingroup image_types_2d * \brief 8-bit unsigned integer image. * * %UInt8Image is a template instantiation of GenericImage for the * UInt8PixelTraits class. */ typedef GenericImage UInt8Image; /*! * \class pcl::UInt16Image * \ingroup image_types_2d * \brief 16-bit unsigned integer image. * * %UInt16Image is a template instantiation of GenericImage for the * UInt16PixelTraits class. */ typedef GenericImage UInt16Image; /*! * \class pcl::UInt32Image * \ingroup image_types_2d * \brief 32-bit unsigned integer image. * * %UInt32Image is a template instantiation of GenericImage for the * UInt32PixelTraits class. */ typedef GenericImage UInt32Image; /*! * \class pcl::Image * \ingroup image_types_2d * \brief 32-bit floating point real image. * * %Image is an alias for FImage. It is a template instantiation of * GenericImage for the FloatPixelTraits class. */ typedef FImage Image; /*! * \class pcl::ComplexImage * \ingroup image_types_2d * \brief 32-bit floating point complex image. * * %ComplexImage is an alias for FComplexImage. It is a template instantiation * of GenericImage for the ComplexPixelTraits class. */ typedef FComplexImage ComplexImage; #endif // __PCL_NO_IMAGE_INSTANTIATE // ---------------------------------------------------------------------------- } // pcl // ---------------------------------------------------------------------------- #ifndef __PCL_NO_IMAGE_VARIANT_AUTO #ifndef __PCL_ImageVariant_h #include #endif #endif // __PCL_NO_IMAGE_VARIANT_AUTO // ---------------------------------------------------------------------------- #endif // __PCL_Image_h // ---------------------------------------------------------------------------- // EOF pcl/Image.h - Released 2022-03-12T18:59:29Z