Files
tenmon/3rdparty/include/pcl/Image.h
T
2022-04-12 08:17:18 +02:00

17818 lines
629 KiB
C++

// ____ ______ __
// / __ \ / ____// /
// / /_/ // / / /
// / ____// /___ / /___ 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 <pcl/Defs.h>
#include <pcl/Diagnostics.h>
#include <pcl/AbstractImage.h>
#include <pcl/AutoLock.h>
#include <pcl/Complex.h>
#include <pcl/Compression.h>
#include <pcl/File.h>
#include <pcl/Mutex.h>
#include <pcl/PixelAllocator.h>
#include <pcl/PixelTraits.h>
#include <pcl/ReferenceArray.h>
#include <pcl/Vector.h>
#ifndef __PCL_IMAGE_NO_BITMAP
# ifdef __PCL_BUILDING_PIXINSIGHT_APPLICATION
# ifndef __PI_Bitmap_h
# include <API/Bitmap.h> // using server-side bitmaps
# endif
using namespace pi;
# else
# ifndef __PCL_Bitmap_h
# include <pcl/Bitmap.h> // using client-side bitmaps
# endif
# endif
# ifndef __PCL_Color_h
# include <pcl/Color.h> // 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:
*
* <table border="1" cellpadding="4" cellspacing="0">
* <tr><td>ImageOp::Nop</td> <td>No operation</td></tr>
* <tr><td>ImageOp::Mov</td> <td>Copy pixels</td></tr>
* <tr><td>ImageOp::Add</td> <td>Add</td></tr>
* <tr><td>ImageOp::Sub</td> <td>Subtract</td></tr>
* <tr><td>ImageOp::Mul</td> <td>Multiply</td></tr>
* <tr><td>ImageOp::Div</td> <td>Divide</td></tr>
* <tr><td>ImageOp::Pow</td> <td>Raise</td></tr>
* <tr><td>ImageOp::Dif</td> <td>Absolute difference</td></tr>
* <tr><td>ImageOp::Min</td> <td>Minimum value</td></tr>
* <tr><td>ImageOp::Max</td> <td>Maximum value</td></tr>
* <tr><td>ImageOp::Or</td> <td>Bitwise OR (inclusive OR)</td></tr>
* <tr><td>ImageOp::And</td> <td>Bitwise AND</td></tr>
* <tr><td>ImageOp::Xor</td> <td>Bitwise XOR (exclusive OR)</td></tr>
* <tr><td>ImageOp::Not</td> <td>Bitwise NOT</td></tr>
* <tr><td>ImageOp::Nor</td> <td>Bitwise NOR (inclusive NOR)</td></tr>
* <tr><td>ImageOp::Nand</td> <td>Bitwise NAND</td></tr>
* <tr><td>ImageOp::Xnor</td> <td>Bitwise XNOR (exclusive NOR)</td></tr>
* <tr><td>ImageOp::ColorBurn</td> <td>Color burn pixel composition operator</td></tr>
* <tr><td>ImageOp::LinearBurn</td> <td>Linear burn pixel composition operator</td></tr>
* <tr><td>ImageOp::Screen</td> <td>Screen pixel composition operator</td></tr>
* <tr><td>ImageOp::ColorDodge</td> <td>Color dodge pixel composition operator</td></tr>
* <tr><td>ImageOp::Overlay</td> <td>Overlay pixel composition operator</td></tr>
* <tr><td>ImageOp::SoftLight</td> <td>Soft light pixel composition operator</td></tr>
* <tr><td>ImageOp::HardLight</td> <td>Hard light pixel composition operator</td></tr>
* <tr><td>ImageOp::VividLight</td> <td>Vivid light pixel composition operator</td></tr>
* <tr><td>ImageOp::LinearLight</td> <td>Linear light pixel composition operator</td></tr>
* <tr><td>ImageOp::PinLight</td> <td>Pin light pixel composition operator</td></tr>
* <tr><td>ImageOp::Exclusion</td> <td>Exclusion pixel composition operator</td></tr>
* </table>
*/
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<sample*>( 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:
*
* <table border="1" cellpadding="4" cellspacing="0">
* <tr><td>pcl::Image</td> <td>32-bit floating point real image</td></tr>
* <tr><td>pcl::DImage</td> <td>64-bit floating point real image</td></tr>
* <tr><td>pcl::ComplexImage</td> <td>32-bit floating point complex image</td></tr>
* <tr><td>pcl::DComplexImage</td> <td>64-bit floating point complex image</td></tr>
* <tr><td>pcl::UInt8Image</td> <td>8-bit unsigned integer image</td></tr>
* <tr><td>pcl::UInt16Image</td> <td>16-bit unsigned integer image</td></tr>
* <tr><td>pcl::UInt32Image</td> <td>32-bit unsigned integer image</td></tr>
* </table>
*
* 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 P>
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<P> 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> sample_vector;
/*!
* A dynamic array of pixel sample values.
*/
typedef Array<sample> 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<P> 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<P> 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 image_type, class sample_pointer>
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<GenericImage<P>, sample*>
{
public:
/*!
* Represents the type of the iterated image.
*/
typedef GenericImage<P> 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<GenericImage<P>, 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 GenericImage<P>, const sample*>
{
public:
/*!
* Represents the type of the iterated image.
*/
typedef GenericImage<P> 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 GenericImage<P>, 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 image_type, class iterator_base, class sample_pointer, class filter_type>
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.
*
* <b>Filter Predicates</b>
*
* 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.
*
* <b>Filter Iterators</b>
*
* 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 F>
class filter_sample_iterator :
public filter_sample_iterator_base<GenericImage<P>, sample_iterator, sample*, F>
{
public:
/*!
* Represents the type of the iterated image.
*/
typedef GenericImage<P> 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<GenericImage<P>, 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.
*
* <b>Filter Predicates</b>
*
* 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.
*
* <b>Filter Iterators</b>
*
* 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 F>
class const_filter_sample_iterator :
public filter_sample_iterator_base<const GenericImage<P>, const_sample_iterator, const sample*, F>
{
public:
/*!
* Represents the type of the iterated image.
*/
typedef GenericImage<P> 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 GenericImage<P>, 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<F>& 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 image_type, class sample_pointer, class filter_type>
class roi_filter_sample_iterator_base : public roi_sample_iterator_base<image_type, sample_pointer>
{
protected:
typedef roi_sample_iterator_base<image_type, sample_pointer>
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 F>
class roi_filter_sample_iterator : public roi_filter_sample_iterator_base<GenericImage<P>, sample*, F>
{
public:
/*!
* Represents the type of the iterated image.
*/
typedef GenericImage<P> 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<GenericImage<P>, 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 F>
class const_roi_filter_sample_iterator : public roi_filter_sample_iterator_base<const GenericImage<P>, const sample*, F>
{
public:
/*!
* Represents the type of the iterated image.
*/
typedef GenericImage<P> 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 GenericImage<P>, 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<P> 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<sample*> 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<P> 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<const sample*> 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 image_type, class sample_pointer>
class roi_pixel_iterator_base
{
protected:
typedef GenericVector<sample_pointer> 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<GenericImage<P>, sample*>
{
public:
/*!
* Represents the type of the iterated image.
*/
typedef GenericImage<P> 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<GenericImage<P>, 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 GenericImage<P>, const sample*>
{
public:
/*!
* Represents the type of the iterated image.
*/
typedef GenericImage<P> 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 GenericImage<P>, 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 image_type, class iterator_base, class sample_pointer, class filter_type>
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.
*
* <b>Filter Predicates</b>
*
* 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\<sample*\>& 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.
*
* <b>Filter Iterators</b>
*
* 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 F>
class filter_pixel_iterator :
public filter_pixel_iterator_base<GenericImage<P>, pixel_iterator, sample*, F>
{
public:
/*!
* Represents the type of the iterated image.
*/
typedef GenericImage<P> 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<GenericImage<P>, 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.
*
* <b>Filter Predicates</b>
*
* 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\<const sample*\>& 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.
*
* <b>Filter Iterators</b>
*
* 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 F>
class const_filter_pixel_iterator :
public filter_pixel_iterator_base<const GenericImage<P>, const_pixel_iterator, const sample*, F>
{
public:
/*!
* Represents the type of the iterated image.
*/
typedef GenericImage<P> 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 GenericImage<P>, 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 image_type, class sample_pointer, class filter_type>
class roi_filter_pixel_iterator_base : public roi_pixel_iterator_base<image_type, sample_pointer>
{
protected:
typedef roi_pixel_iterator_base<image_type, sample_pointer>
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 F>
class roi_filter_pixel_iterator : public roi_filter_pixel_iterator_base<GenericImage<P>, sample*, F>
{
public:
/*!
* Represents the type of the iterated image.
*/
typedef GenericImage<P> 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<GenericImage<P>, 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 F>
class const_roi_filter_pixel_iterator : public roi_filter_pixel_iterator_base<const GenericImage<P>, const sample*, F>
{
public:
/*!
* Represents the type of the iterated image.
*/
typedef GenericImage<P> 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 GenericImage<P>, 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 <class P1>
bool SameSampleType( const GenericImage<P1>& 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 <em>completely selected</em> 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 <class P1>
GenericImage( const GenericImage<P1>& 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 <class P1>
GenericImage( const GenericImage<P1>& 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 <em>shared memory</em>.
*
* 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 <em>shared memory</em>.
*
* 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
* <em>channel slots</em> (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 <em>scan line</em>).
*/
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 &le; \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 &le; \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 <em>scan line</em> (i.e., a row of
* pixels) in this image.
*
* \param y Vertical coordinate (or row index) of the desired scan
* line, such that 0 &le; \a y < \a h, where \a h is the
* height in pixels of this image.
*
* \param channel Channel index, 0 &le; \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 <em>scan line</em> (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 &le; \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 &le; \a y < \a h, where \a h is the height in
* pixels of this image.
*
* \param channel Channel index, 0 &le; \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 &le; \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 &le; \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 &le; \a y < \a h, where \a h is the height in
* pixels of this image.
*
* \param channel Channel index, 0 &le; \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 &le; \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 &le; \a y < \a h, where \a h is the height in
* pixels of this image.
*
* \param channel Channel index, 0 &le; \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 &le; \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 &le; \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 <class P1>
GenericImage& Assign( const GenericImage<P1>& 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 <class P1>
GenericImage& operator =( const GenericImage<P1>& 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 &le; \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 &le; \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 &le; \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 <typename T>
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 &le; \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 <typename T>
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 &le; \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 <typename T>
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 &le; \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 <typename T>
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 &le; \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 <em>alpha channel index</em>,
* 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 &le; \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 <em>alpha channel index</em>,
* 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 &le; \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 <em>alpha channel index</em>,
* 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 <typename T>
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 <typename T>
GenericImage& Fill( const GenericVector<T>& 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 <typename T>
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 <typename T>
GenericImage Filled( const GenericVector<T>& 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 <typename T>
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 <typename T>
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 <typename T>
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 <typename T>
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:
*
* <pre>
* r = r0 + (s - m)*(r1 - r0)/(M - m)
* </pre>
*
* \note Increments the status monitoring object by the number of selected
* pixel samples.
*/
template <typename T>
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 <typename T>
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:
*
* <pre>
* if m < n0 or M > n1
* n = n0 + (s - m)*(n1 - n0)/(M - m)
* else
* n = s
* endif
* </pre>
*
* \note Increments the status monitoring object by the number of selected
* pixel samples.
*/
template <typename T>
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 <typename T>
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:
*
* <pre>
* if s < t
* b = 0
* else
* b = 1
* endif
* </pre>
*
* 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 <typename T>
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 <typename T>
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 <typename T>
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 <typename T>
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<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( pcl::Complex<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 );
}
/*!
* 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 <typename T>
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 <class P1>
GenericImage& Apply( const GenericImage<P1>& 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 <class P1>
GenericImage Applied( const GenericImage<P1>& 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 <em>inverse transformation</em>, usually requiring a
* previous <em>direct transformation</em>. 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 <b>not thread safe</b>, 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 <b>not thread safe</b>, 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 <b>not thread safe</b>, 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:
*
* <pre>
* V1 = V*(1 - A/255) + B * A/255
* </pre>
*
* 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 <typename T>
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 <typename T>
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 <typename T>
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 <typename T>
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 <typename T>
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 <typename T>
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 <typename T>
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 <typename T>
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 <typename T>
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 <typename T>
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 <typename T>
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 <typename T>
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 <typename T>
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 <typename T>
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 <typename T>
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 <typename T>
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 <typename T>
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 <typename T>
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 <typename T>
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 <typename T>
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 <typename T>
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 <typename T>
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 <typename T>
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 <typename T>
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 <typename T>
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 <typename T>
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 <typename T>
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 <typename T>
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 <typename T>
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 <typename T>
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 <typename T>
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 <typename T>
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 <typename T>
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 <typename T>
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 <typename T>
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 <typename T>
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 <class P1>
GenericImage& Move( const GenericImage<P1>& 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 <class P1>
GenericImage& Mov( const GenericImage<P1>& 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 <class P1>
GenericImage& Add( const GenericImage<P1>& 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 <class P1>
GenericImage& operator +=( const GenericImage<P1>& 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 <class P1>
GenericImage& Subtract( const GenericImage<P1>& 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 <class P1>
GenericImage& Sub( const GenericImage<P1>& 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 <class P1>
GenericImage& operator -=( const GenericImage<P1>& 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 <class P1>
GenericImage& Multiply( const GenericImage<P1>& 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 <class P1>
GenericImage& Mul( const GenericImage<P1>& 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 <class P1>
GenericImage& operator *=( const GenericImage<P1>& 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 <class P1>
GenericImage& Divide( const GenericImage<P1>& 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 <class P1>
GenericImage& Div( const GenericImage<P1>& 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 <class P1>
GenericImage& operator /=( const GenericImage<P1>& 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 <class P1>
GenericImage& Raise( const GenericImage<P1>& 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 <class P1>
GenericImage& Pow( const GenericImage<P1>& 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 <class P1>
GenericImage& operator ^=( const GenericImage<P1>& 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 <class P1>
GenericImage& SetAbsoluteDifference( const GenericImage<P1>& 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 <class P1>
GenericImage& Dif( const GenericImage<P1>& 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 <class P1>
GenericImage& SetMinimum( const GenericImage<P1>& 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 <class P1>
GenericImage& Min( const GenericImage<P1>& 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 <class P1>
GenericImage& SetMaximum( const GenericImage<P1>& 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 <class P1>
GenericImage& Max( const GenericImage<P1>& 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 <class P1>
GenericImage& Or( const GenericImage<P1>& 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 <class P1>
GenericImage& And( const GenericImage<P1>& 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 <class P1>
GenericImage& Xor( const GenericImage<P1>& 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 <class P1>
GenericImage& Nor( const GenericImage<P1>& 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 <class P1>
GenericImage& Nand( const GenericImage<P1>& 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 <class P1>
GenericImage& Xnor( const GenericImage<P1>& 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 <class P1>
GenericImage& Exchange( GenericImage<P1>& 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 <class P1>
GenericImage& Xchg( GenericImage<P1>& 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<size_type> L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors );
bool useAffinity = m_parallel && Thread::IsRootThread();
ReferenceArray<MinThread> 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<size_type> L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors );
bool useAffinity = m_parallel && Thread::IsRootThread();
ReferenceArray<MaxThread> 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 <typename T>
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<size_type> L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors );
bool useAffinity = m_parallel && Thread::IsRootThread();
ReferenceArray<MinMaxThread> 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<size_type> L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors );
bool useAffinity = m_parallel && Thread::IsRootThread();
ReferenceArray<MinPosThread> 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<size_type> L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors );
bool useAffinity = m_parallel && Thread::IsRootThread();
ReferenceArray<MaxPosThread> 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 <typename T>
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<size_type> L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors );
bool useAffinity = m_parallel && Thread::IsRootThread();
ReferenceArray<MinMaxPosThread> 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 <typename T>
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<size_type> L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors );
bool useAffinity = m_parallel && Thread::IsRootThread();
ReferenceArray<CountThread> 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<size_type> L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors );
bool useAffinity = m_parallel && Thread::IsRootThread();
ReferenceArray<SumThread> 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<size_type> L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors, 160*1024/*overheadLimitPx*/ );
bool useAffinity = m_parallel && Thread::IsRootThread();
double low, high;
size_type count = 0;
{
ReferenceArray<MinMaxThread> 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<double>::epsilon() :
(P::IsFloatSample() ? 2*std::numeric_limits<typename P::component>::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<HistogramThread> 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<size_type> L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors, 160*1024/*overheadLimitPx*/ );
bool useAffinity = m_parallel && Thread::IsRootThread();
double low, high;
size_type count = 0;
{
ReferenceArray<MinMaxThread> 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<double>::epsilon() :
(P::IsFloatSample() ? 2*std::numeric_limits<typename P::component>::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<HistogramThread> 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., <em>Numerical Recipes in C: The Art of
* Scientific Computing, Second Edition</em> (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<size_type> L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors );
bool useAffinity = m_parallel && Thread::IsRootThread();
ReferenceArray<SumThread> 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<VarThread> 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., <em>Numerical Recipes in C: The Art of
* Scientific Computing, Second Edition</em> (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<size_type> L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors );
bool useAffinity = m_parallel && Thread::IsRootThread();
ReferenceArray<SumAbsDevThread> 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<size_type> L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors );
bool useAffinity = m_parallel && Thread::IsRootThread();
ReferenceArray<TwoSidedSumAbsDevThread> 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<size_type> L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors, 160*1024/*overheadLimitPx*/ );
bool useAffinity = m_parallel && Thread::IsRootThread();
double low, high;
size_type count = 0;
{
ReferenceArray<ExtremeAbsDevThread> 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<double>::epsilon();
if ( count == 0 || high - low < eps )
{
m_status += N;
return 0;
}
ReferenceArray<AbsDevHistogramThread> 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<size_type> 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<TwoSidedExtremeAbsDevThread> 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<TwoSidedAbsDevHistogramThread> 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<double>::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), <em>Introduction to Robust Estimation and Hypothesis
* Testing, 3rd Edition</em>, 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<size_type> L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors );
bool useAffinity = m_parallel && Thread::IsRootThread();
ReferenceArray<BWMVThread> 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<size_type> L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors );
bool useAffinity = m_parallel && Thread::IsRootThread();
ReferenceArray<TwoSidedBWMVThread> 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), <em>Introduction to Robust Estimation and Hypothesis
* Testing, 3rd Edition</em>, 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<size_type> L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors );
bool useAffinity = m_parallel && Thread::IsRootThread();
ReferenceArray<DSmpThread> 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<double> 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), <em>Alternatives to the Median Absolute
* Deviation,</em> 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<size_type> L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors );
bool useAffinity = m_parallel && Thread::IsRootThread();
ReferenceArray<DSmpThread> 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<double> 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), <em>Alternatives to the Median Absolute
* Deviation,</em> 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<size_type> L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors );
bool useAffinity = m_parallel && Thread::IsRootThread();
ReferenceArray<DSmpThread> 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<double> 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<size_type> L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors );
bool useAffinity = m_parallel && Thread::IsRootThread();
ReferenceArray<SumThread> 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<size_type> L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors );
bool useAffinity = m_parallel && Thread::IsRootThread();
ReferenceArray<SumAbsThread> 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<size_type> L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors );
bool useAffinity = m_parallel && Thread::IsRootThread();
ReferenceArray<SumSqrThread> 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<size_type> L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors );
bool useAffinity = m_parallel && Thread::IsRootThread();
ReferenceArray<SumSqrThread> 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 &ge; 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<size_type> L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors );
bool useAffinity = m_parallel && Thread::IsRootThread();
ReferenceArray<NormThread> 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<GenericImage&>( *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<GenericImage&>( *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 <typename T>
GenericImage& CropBy( int left, int top, int right, int bottom, const GenericVector<T>& 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 <typename T>
GenericImage& CropTo( const Rect& rect, const GenericVector<T>& 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 <typename T>
GenericImage& CropTo( int x0, int y0, int x1, int y1, const GenericVector<T>& 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 <typename T>
GenericImage& Crop( const GenericVector<T>& 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 <typename T>
GenericImage& ShiftBy( int dx, int dy, const GenericVector<T>& 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 <typename T>
GenericImage& ShiftTo( int x, int y, const GenericVector<T>& 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 <typename T>
GenericImage& ShiftTo( const Point& p, const GenericVector<T>& 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 <typename T>
GenericImage& ShiftToCenter( int width, int height, const GenericVector<T>& 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 <typename T>
GenericImage& ShiftToTopLeft( int width, int height, const GenericVector<T>& 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 <typename T>
GenericImage& ShiftToTopRight( int width, int height, const GenericVector<T>& 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 <typename T>
GenericImage& ShiftToBottomLeft( int width, int height, const GenericVector<T>& 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 <typename T>
GenericImage& ShiftToBottomRight( int width, int height, const GenericVector<T>& 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 <typename T>
GenericImage& Shift( const GenericVector<T>& 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<size_type> L = Thread::OptimalThreadLoads( N,
16u/*overheadLimit*/,
m_parallel ? ((maxProcessors > 0) ? maxProcessors : m_maxProcessors) : 1 );
ThreadData data( *this, N );
ReferenceArray<ColorSpaceConversionThread> 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 <class P1>
void GetLuminance( GenericImage<P1>& 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<P1>::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<size_type> L = OptimalThreadRows( Y.Height(), Y.Width(), maxProcessors );
ThreadData data( *this, N );
ReferenceArray<GetLuminanceThread<P1> > threads;
for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
threads.Add( new GetLuminanceThread<P1>( 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 <class P1>
void GetLightness( GenericImage<P1>& 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<P1>::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<size_type> R = OptimalThreadRows( L.Height(), L.Width(), maxProcessors );
ThreadData data( *this, N );
ReferenceArray<GetLightnessThread<P1> > threads;
for ( int i = 0, n = 0; i < int( R.Length() ); n += int( R[i++] ) )
threads.Add( new GetLightnessThread<P1>( 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:
*
* <pre>
* I = (Max(R,G,B) + Min(R,G,B))/2
* </pre>
*
* 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 <class P1>
void GetIntensity( GenericImage<P1>& 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<P1>::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<size_type> L = OptimalThreadRows( I.Height(), I.Width(), maxProcessors );
ThreadData data( *this, N );
ReferenceArray<GetIntensityThread<P1> > threads;
for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
threads.Add( new GetIntensityThread<P1>( 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 <class P1>
GenericImage& SetLuminance( const GenericImage<P1>& 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<P1>::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<size_type> L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors );
ThreadData data( *this, N );
ReferenceArray<SetLuminanceThread<P1> > threads;
for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
threads.Add( new SetLuminanceThread<P1>( *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 <class P1>
GenericImage& SetLightness( const GenericImage<P1>& 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<P1>::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<size_type> R = OptimalThreadRows( r.Height(), r.Width(), maxProcessors );
ThreadData data( *this, N );
ReferenceArray<SetLightnessThread<P1> > threads;
for ( int i = 0, n = 0; i < int( R.Length() ); n += int( R[i++] ) )
threads.Add( new SetLightnessThread<P1>( *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<P> 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 <class F> 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<double>::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<double>::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<sample> 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<sample>( N );
}
void Run() final
{
n = 0;
this->Execute( [=]( const sample* f )
{
samples[n++] = *f;
} );
}
};
// -------------------------------------------------------------------------
class DSmpThread : public RectThreadBase
{
public:
Array<double> 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<double>( 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<double>::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 P1>
class GetLuminanceThread : public Thread
{
public:
GetLuminanceThread( GenericImage<P1>& 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<P1>& m_luminance;
const GenericImage& m_image;
ThreadData& m_data;
const Rect& m_rect;
int m_firstRow, m_endRow;
};
// -------------------------------------------------------------------------
template <class P1>
class GetLightnessThread : public Thread
{
public:
GetLightnessThread( GenericImage<P1>& 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<P1>& m_lightness;
const GenericImage& m_image;
ThreadData& m_data;
const Rect& m_rect;
int m_firstRow, m_endRow;
};
// -------------------------------------------------------------------------
template <class P1>
class GetIntensityThread : public Thread
{
public:
GetIntensityThread( GenericImage<P1>& 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<P1>& m_intensity;
const GenericImage& m_image;
ThreadData& m_data;
const Rect& m_rect;
int m_firstRow, m_endRow;
};
// -------------------------------------------------------------------------
template <class P1>
class SetLuminanceThread : public Thread
{
public:
SetLuminanceThread( GenericImage& image, const GenericImage<P1>& 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<P1>& m_luminance;
ThreadData& m_data;
const Point& m_target;
const Rect& m_rect;
int m_firstRow, m_endRow;
};
// -------------------------------------------------------------------------
template <class P1>
class SetLightnessThread : public Thread
{
public:
SetLightnessThread( GenericImage& image, const GenericImage<P1>& 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<P1>& 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 <class P, typename T> inline
GenericImage<P> operator +( const GenericImage<P>& image, T scalar )
{
GenericImage<P> 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 <class P, typename T> inline
GenericImage<P> operator +( T scalar, const GenericImage<P>& 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 <class P, typename T> inline
GenericImage<P> operator -( const GenericImage<P>& image, T scalar )
{
GenericImage<P> 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 <class P, typename T> inline
GenericImage<P> operator *( const GenericImage<P>& image, T scalar )
{
GenericImage<P> 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 <class P, typename T> inline
GenericImage<P> operator *( T scalar, const GenericImage<P>& 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 <class P, typename T> inline
GenericImage<P> operator /( const GenericImage<P>& image, T scalar )
{
GenericImage<P> 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 <class P, typename T> inline
GenericImage<P> operator ^( const GenericImage<P>& image, T scalar )
{
GenericImage<P> 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 <class P1, class P2> inline
GenericImage<P1> operator +( const GenericImage<P1>& image1, const GenericImage<P2>& image2 )
{
GenericImage<P1> 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 <class P1, class P2> inline
GenericImage<P1> operator -( const GenericImage<P1>& image1, const GenericImage<P2>& image2 )
{
GenericImage<P1> 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 <class P1, class P2> inline
GenericImage<P1> operator *( const GenericImage<P1>& image1, const GenericImage<P2>& image2 )
{
GenericImage<P1> 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 <class P1, class P2> inline
GenericImage<P1> operator /( const GenericImage<P1>& image1, const GenericImage<P2>& image2 )
{
GenericImage<P1> 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 <class P1, class P2> inline
GenericImage<P1> operator ^( const GenericImage<P1>& image1, const GenericImage<P2>& image2 )
{
GenericImage<P1> 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<FloatPixelTraits> 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<DoublePixelTraits> 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<ComplexPixelTraits> 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<DComplexPixelTraits> 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<UInt8PixelTraits> 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<UInt16PixelTraits> 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<UInt32PixelTraits> 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 <pcl/ImageVariant.h>
#endif
#endif // __PCL_NO_IMAGE_VARIANT_AUTO
// ----------------------------------------------------------------------------
#endif // __PCL_Image_h
// ----------------------------------------------------------------------------
// EOF pcl/Image.h - Released 2022-03-12T18:59:29Z