PCL
AbstractImage.h
Go to the documentation of this file.
1 // ____ ______ __
2 // / __ \ / ____// /
3 // / /_/ // / / /
4 // / ____// /___ / /___ PixInsight Class Library
5 // /_/ \____//_____/ PCL 02.01.12.0947
6 // ----------------------------------------------------------------------------
7 // pcl/AbstractImage.h - Released 2019-04-30T16:30:41Z
8 // ----------------------------------------------------------------------------
9 // This file is part of the PixInsight Class Library (PCL).
10 // PCL is a multiplatform C++ framework for development of PixInsight modules.
11 //
12 // Copyright (c) 2003-2019 Pleiades Astrophoto S.L. All Rights Reserved.
13 //
14 // Redistribution and use in both source and binary forms, with or without
15 // modification, is permitted provided that the following conditions are met:
16 //
17 // 1. All redistributions of source code must retain the above copyright
18 // notice, this list of conditions and the following disclaimer.
19 //
20 // 2. All redistributions in binary form must reproduce the above copyright
21 // notice, this list of conditions and the following disclaimer in the
22 // documentation and/or other materials provided with the distribution.
23 //
24 // 3. Neither the names "PixInsight" and "Pleiades Astrophoto", nor the names
25 // of their contributors, may be used to endorse or promote products derived
26 // from this software without specific prior written permission. For written
27 // permission, please contact info@pixinsight.com.
28 //
29 // 4. All products derived from this software, in any form whatsoever, must
30 // reproduce the following acknowledgment in the end-user documentation
31 // and/or other materials provided with the product:
32 //
33 // "This product is based on software from the PixInsight project, developed
34 // by Pleiades Astrophoto and its contributors (http://pixinsight.com/)."
35 //
36 // Alternatively, if that is where third-party acknowledgments normally
37 // appear, this acknowledgment must be reproduced in the product itself.
38 //
39 // THIS SOFTWARE IS PROVIDED BY PLEIADES ASTROPHOTO AND ITS CONTRIBUTORS
40 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
41 // TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
42 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL PLEIADES ASTROPHOTO OR ITS
43 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
44 // EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, BUSINESS
45 // INTERRUPTION; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; AND LOSS OF USE,
46 // DATA OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
47 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
48 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
49 // POSSIBILITY OF SUCH DAMAGE.
50 // ----------------------------------------------------------------------------
51 
52 #ifndef __PCL_AbstractImage_h
53 #define __PCL_AbstractImage_h
54 
56 
57 #include <pcl/Defs.h>
58 
59 #include <pcl/Array.h>
60 #include <pcl/ImageColor.h>
61 #include <pcl/ImageGeometry.h>
62 #include <pcl/ImageSelections.h>
63 #include <pcl/Mutex.h>
64 #include <pcl/ParallelProcess.h>
65 #include <pcl/ReferenceArray.h>
66 #include <pcl/StatusMonitor.h>
67 #include <pcl/Thread.h>
68 
69 #ifdef __PCL_BUILDING_PIXINSIGHT_APPLICATION
70 namespace pi
71 {
72 class SharedImage;
73 }
74 #endif
75 
76 namespace pcl
77 {
78 
79 // ----------------------------------------------------------------------------
80 
81 #define m_width m_geometry->width
82 #define m_height m_geometry->height
83 #define m_numberOfChannels m_geometry->numberOfChannels
84 
85 #define m_colorSpace m_color->colorSpace
86 #define m_RGBWS m_color->RGBWS
87 
88 // ----------------------------------------------------------------------------
89 
118 class PCL_CLASS AbstractImage : public ImageGeometry,
119  public ImageColor,
120  public ParallelProcess
121 {
122 public:
123 
128 
134  typedef ImageColor::color_space color_space;
135 
139  virtual ~AbstractImage()
140  {
141  }
142 
148  {
149  return ColorSpace::NumberOfNominalChannels( m_colorSpace );
150  }
151 
157  {
158  return NumberOfPixels() * NumberOfNominalChannels();
159  }
160 
167  bool HasAlphaChannels() const
168  {
169  PCL_PRECONDITION( NumberOfChannels() != 0 )
170  return NumberOfChannels() > NumberOfNominalChannels();
171  }
172 
177  {
178  PCL_PRECONDITION( NumberOfChannels() != 0 )
179  return NumberOfChannels() - NumberOfNominalChannels();
180  }
181 
188  {
189  return NumberOfPixels() * NumberOfAlphaChannels();
190  }
191 
192  // -------------------------------------------------------------------------
193 
200  void SelectChannel( int c ) const
201  {
202  PCL_PRECONDITION( 0 <= c && c < m_numberOfChannels )
203  m_selected.channel = m_selected.lastChannel = c;
204  ValidateChannelRange();
205  }
206 
215  int SelectedChannel() const
216  {
217  return m_selected.channel;
218  }
219 
227  void SelectChannelRange( int c0, int c1 ) const
228  {
229  PCL_PRECONDITION( 0 <= c0 && c0 < m_numberOfChannels )
230  PCL_PRECONDITION( 0 <= c1 && c1 < m_numberOfChannels )
231  m_selected.channel = c0;
232  m_selected.lastChannel = c1;
233  ValidateChannelRange();
234  }
235 
241  {
242  m_selected.channel = 0;
243  m_selected.lastChannel = NumberOfNominalChannels()-1;
244  ValidateChannelRange();
245  }
246 
254  void SelectAlphaChannels() const
255  {
256  m_selected.channel = NumberOfNominalChannels();
257  m_selected.lastChannel = m_numberOfChannels-1;
258  ValidateChannelRange();
259  }
260 
265  void ResetChannelRange() const
266  {
267  m_selected.channel = 0;
268  m_selected.lastChannel = pcl::Max( 0, m_numberOfChannels-1 );
269  }
270 
275  {
276  return 1 + m_selected.lastChannel - m_selected.channel;
277  }
278 
283  {
284  return m_selected.channel;
285  }
286 
291  {
292  return m_selected.lastChannel;
293  }
294 
302  void GetSelectedChannelRange( int& c0, int& c1 ) const
303  {
304  c0 = m_selected.channel;
305  c1 = m_selected.lastChannel;
306  }
307 
314  void SelectPoint( int x, int y ) const
315  {
316  m_selected.point.MoveTo( x, y );
317  }
318 
322  void SelectPoint( const Point& p ) const
323  {
324  m_selected.point = p;
325  }
326 
330  void ResetPoint() const
331  {
332  m_selected.point = 0;
333  }
334 
338  const Point& SelectedPoint() const
339  {
340  return m_selected.point;
341  }
342 
356  void SelectRectangle( int x0, int y0, int x1, int y1 ) const
357  {
358  m_selected.rectangle.Set( x0, y0, x1, y1 );
359  Clip( m_selected.rectangle );
360  }
361 
370  void SelectRectangle( const Point& p0, const Point& p1 ) const
371  {
372  SelectRectangle( p0.x, p0.y, p1.x, p1.y );
373  }
374 
379  void SelectRectangle( const Rect& r ) const
380  {
381  SelectRectangle( r.x0, r.y0, r.x1, r.y1 );
382  }
383 
387  void ResetSelection() const
388  {
389  m_selected.rectangle.Set( 0, 0, m_width, m_height );
390  }
391 
395  bool IsEmptySelection() const
396  {
397  return m_selected.rectangle.IsPointOrLine();
398  }
399 
404  bool IsFullSelection() const
405  {
406  return m_selected.rectangle.x0 <= 0 &&
407  m_selected.rectangle.y0 <= 0 &&
408  m_selected.rectangle.x1 >= m_width &&
409  m_selected.rectangle.y1 >= m_height;
410  }
411 
415  const Rect& SelectedRectangle() const
416  {
417  return m_selected.rectangle;
418  }
419 
428  bool IsCompletelySelected() const
429  {
430  return m_selected.channel == 0 &&
431  m_selected.lastChannel >= m_numberOfChannels-1 &&
432  m_selected.rectangle.x0 <= 0 &&
433  m_selected.rectangle.y0 <= 0 &&
434  m_selected.rectangle.x1 >= m_width &&
435  m_selected.rectangle.y1 >= m_height;
436  }
437 
443  {
444  return size_type( m_selected.rectangle.Width() ) * size_type( m_selected.rectangle.Height() );
445  // ### NB: Rect::Area() cannot be used here because it performs a
446  // *signed* multiplication of two 32-bit signed integers.
447  //return m_selected.rectangle.Area();
448  }
449 
456  {
457  return NumberOfSelectedPixels()*size_type( NumberOfSelectedChannels() );
458  }
459 
480  {
481  return m_selected.clipped;
482  }
483 
489  void EnableRangeClipping( bool enable = true ) const
490  {
491  m_selected.clipped = enable;
492  }
493 
499  void DisableRangeClipping( bool disable = true ) const
500  {
501  m_selected.clipped = !disable;
502  }
503 
509  double RangeClipLow() const
510  {
511  return m_selected.clipLow;
512  }
513 
519  double RangeClipHigh() const
520  {
521  return m_selected.clipHigh;
522  }
523 
529  void SetRangeClipLow( double clipLow ) const
530  {
531  m_selected.clipLow = clipLow;
532  if ( m_selected.clipHigh < m_selected.clipLow )
533  pcl::Swap( m_selected.clipLow, m_selected.clipHigh );
534  }
535 
541  void SetRangeClipHigh( double clipHigh ) const
542  {
543  m_selected.clipHigh = clipHigh;
544  if ( m_selected.clipHigh < m_selected.clipLow )
545  pcl::Swap( m_selected.clipLow, m_selected.clipHigh );
546  }
547 
554  void SetRangeClipping( double clipLow, double clipHigh ) const
555  {
556  if ( clipHigh < clipLow )
557  pcl::Swap( clipLow, clipHigh );
558  m_selected.clipLow = clipLow;
559  m_selected.clipHigh = clipHigh;
560  m_selected.clipped = true;
561  }
562 
570  void ResetRangeClipping() const
571  {
572  m_selected.clipLow = 0;
573  m_selected.clipHigh = 1;
574  m_selected.clipped = false;
575  }
576 
595  void ResetSelections() const
596  {
597  ResetChannelRange();
598  ResetPoint();
599  ResetSelection();
600  ResetRangeClipping();
601  }
602 
607  {
608  return m_selected;
609  }
610 
615  void PushSelections() const
616  {
617  m_savedSelections.Append( m_selected );
618  }
619 
628  void PopSelections() const
629  {
630  if ( CanPopSelections() )
631  {
632  selection_stack::iterator i = m_savedSelections.ReverseBegin();
633  m_selected = *i;
634  m_savedSelections.Remove( i );
635  }
636  }
637 
643  bool CanPopSelections() const
644  {
645  return !m_savedSelections.IsEmpty();
646  }
647 
660  bool ParseRect( Rect& rect ) const
661  {
662  if ( !rect.IsRect() )
663  {
664  rect = m_selected.rectangle;
665  if ( !rect.IsRect() )
666  return false;
667  }
668  if ( !Clip( rect ) )
669  return false;
670  return true;
671  }
672 
683  bool ParseChannel( int& channel ) const
684  {
685  if ( channel < 0 )
686  {
687  channel = m_selected.channel;
688  if ( channel < 0 )
689  return false;
690  }
691  if ( channel >= m_numberOfChannels )
692  return false;
693 
694  return true;
695  }
696 
721  bool ParseSelection( Rect& rect, int& firstChannel, int& lastChannel ) const
722  {
723  if ( !ParseRect( rect ) || !ParseChannel( firstChannel ) )
724  return false;
725 
726  if ( lastChannel < 0 )
727  {
728  lastChannel = m_selected.lastChannel;
729  if ( lastChannel < 0 )
730  return false;
731  }
732  if ( lastChannel >= m_numberOfChannels )
733  return false;
734 
735  if ( lastChannel < firstChannel )
736  pcl::Swap( firstChannel, lastChannel );
737 
738  return true;
739  }
740 
758  bool ParseSelection( Rect& rect, int& channel ) const
759  {
760  return ParseRect( rect ) && ParseChannel( channel );
761  }
762 
763  // -------------------------------------------------------------------------
764 
770  {
771  return m_status;
772  }
773 
779  {
780  return m_status.Callback();
781  }
782 
789  {
790  return StatusCallback();
791  }
792 
797  void SetStatusCallback( pcl::StatusCallback* callback ) const
798  {
799  m_status.SetCallback( callback );
800  }
801 
827  int NumberOfThreads( size_type count, int maxProcessors = 0, size_type overheadLimit = 16u ) const
828  {
829  return m_parallel ? pcl::Min( (maxProcessors > 0) ? maxProcessors : m_maxProcessors,
830  Thread::NumberOfThreads( count, overheadLimit ) ) : 1;
831  }
832 
861  int NumberOfThreadsForRows( int rowCount = 0, int rowWidth = 0, int maxProcessors = 0, size_type overheadLimitPx = 1024u ) const
862  {
863  return NumberOfThreads( (rowCount > 0) ? rowCount : Height(),
864  maxProcessors,
865  pcl::Max( size_type( 1 ), size_type( overheadLimitPx/((rowWidth > 0) ? rowWidth : Width()) ) ) );
866  }
867 
868  // -------------------------------------------------------------------------
869 
881  struct ThreadData
882  {
884  mutable Mutex mutex;
885  mutable size_type count = 0;
886  size_type total = 0;
887  size_type numThreads = 0;
888 
892  ThreadData() = default;
893 
903  ThreadData( const AbstractImage& image, size_type N ) :
904  status( image.Status() ),
905  total( N )
906  {
907  }
908 
917  ThreadData( const StatusMonitor& a_status, size_type N ) :
918  status( a_status ),
919  total( N )
920  {
921  }
922  };
923 
988  template <class thread>
989  static void RunThreads( ReferenceArray<thread>& threads, ThreadData& data, bool useAffinity = true )
990  {
991  if ( threads.IsEmpty() )
992  return;
993 
994  data.numThreads = threads.Length();
995  if ( data.numThreads == 1 )
996  {
997  try
998  {
999  threads[0].Run();
1000  return;
1001  }
1002  catch ( ... )
1003  {
1004  threads.Destroy();
1005  throw;
1006  }
1007  }
1008 
1009  if ( useAffinity )
1010  if ( !Thread::IsRootThread() )
1011  useAffinity = false;
1012 
1013  {
1014  int n = 0;
1015  for ( thread& t : threads )
1016  t.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 );
1017  }
1018 
1019  uint32 waitTime = StatusMonitor::RefreshRate() >> 1;
1020  waitTime += waitTime >> 2; // waitTime = 0.625 * StatusMonitor::RefreshRate()
1021 
1022  for ( size_type lastCount = 0; ; )
1023  {
1024  for ( typename ReferenceArray<thread>::iterator i = threads.Begin(); ; )
1025  {
1026  if ( !i->Wait( waitTime ) )
1027  break;
1028 
1029  if ( ++i == threads.End() )
1030  {
1031  if ( data.total > 0 )
1032  data.status += data.total - lastCount;
1033  return;
1034  }
1035  }
1036 
1037  if ( data.mutex.TryLock() )
1038  {
1039  try
1040  {
1041  if ( data.total > 0 )
1042  {
1043  data.status += data.count - lastCount;
1044  lastCount = data.count;
1045  }
1046  else
1047  ++data.status;
1048 
1049  data.mutex.Unlock();
1050  }
1051  catch ( ... )
1052  {
1053  data.mutex.Unlock();
1054  for ( thread& t : threads )
1055  t.Abort();
1056  for ( thread& t : threads )
1057  t.Wait();
1058  threads.Destroy();
1059  throw ProcessAborted();
1060  }
1061  }
1062  }
1063  }
1064 
1065 protected:
1066 
1067  mutable ImageSelections m_selected;
1068  mutable selection_stack m_savedSelections;
1069  mutable StatusMonitor m_status;
1070 
1071  AbstractImage() = default;
1072 
1073  AbstractImage( const AbstractImage& ) = default;
1074 
1075  AbstractImage& operator =( const AbstractImage& ) = default;
1076 
1077  void Swap( AbstractImage& image )
1078  {
1079  ImageGeometry::Swap( image );
1080  ImageColor::Swap( image );
1081  ParallelProcess::Swap( image );
1082  pcl::Swap( m_selected, image.m_selected );
1083  pcl::Swap( m_savedSelections, image.m_savedSelections );
1084  pcl::Swap( m_status, image.m_status );
1085  }
1086 
1087  void ValidateChannelRange() const
1088  {
1089  if ( m_numberOfChannels > 0 )
1090  {
1091  if ( m_selected.channel < 0 )
1092  m_selected.channel = 0;
1093  else if ( m_selected.channel >= m_numberOfChannels )
1094  m_selected.channel = m_numberOfChannels-1;
1095 
1096  if ( m_selected.lastChannel < 0 )
1097  m_selected.lastChannel = 0;
1098  else if ( m_selected.lastChannel >= m_numberOfChannels )
1099  m_selected.lastChannel = m_numberOfChannels-1;
1100 
1101  if ( m_selected.lastChannel < m_selected.channel )
1102  pcl::Swap( m_selected.channel, m_selected.lastChannel );
1103  }
1104  else
1105  {
1106  m_selected.channel = m_selected.lastChannel = 0;
1107  }
1108  }
1109 
1110 #ifdef __PCL_BUILDING_PIXINSIGHT_APPLICATION
1111  friend class pi::SharedImage;
1112 #endif
1113 };
1114 
1115 // ----------------------------------------------------------------------------
1116 
1117 #undef m_width
1118 #undef m_height
1119 #undef m_numberOfChannels
1120 #undef m_colorSpace
1121 #undef m_RGBWS
1122 
1123 // ----------------------------------------------------------------------------
1124 
1143 #define INIT_THREAD_MONITOR() \
1144  size_type ___n___ = 0, ___n1___ = 0;
1145 
1231 #define UPDATE_THREAD_MONITOR( N ) \
1232  if ( ++___n1___ == (N) ) \
1233  { \
1234  if ( this->m_data.numThreads > 1 ) \
1235  { \
1236  if ( this->TryIsAborted() ) \
1237  return; \
1238  ___n___ += (N); \
1239  if ( this->m_data.total > 0 ) \
1240  if ( this->m_data.mutex.TryLock() ) \
1241  { \
1242  this->m_data.count += ___n___; \
1243  this->m_data.mutex.Unlock(); \
1244  ___n___ = 0; \
1245  } \
1246  } \
1247  else \
1248  { \
1249  if ( this->m_data.total > 0 ) \
1250  this->m_data.status += (N); \
1251  else \
1252  ++this->m_data.status; \
1253  } \
1254  ___n1___ = 0; \
1255  }
1256 
1312 #define UPDATE_THREAD_MONITOR_CHUNK( N, chunkSize ) \
1313  if ( (___n1___ += (chunkSize)) == (N) ) \
1314  { \
1315  if ( this->m_data.numThreads > 1 ) \
1316  { \
1317  if ( this->TryIsAborted() ) \
1318  return; \
1319  ___n___ += (N); \
1320  if ( this->m_data.total > 0 ) \
1321  if ( this->m_data.mutex.TryLock() ) \
1322  { \
1323  this->m_data.count += ___n___; \
1324  this->m_data.mutex.Unlock(); \
1325  ___n___ = 0; \
1326  } \
1327  } \
1328  else \
1329  { \
1330  if ( this->m_data.total > 0 ) \
1331  this->m_data.status += (N); \
1332  else \
1333  ++this->m_data.status; \
1334  } \
1335  ___n1___ = 0; \
1336  }
1337 
1338 // ----------------------------------------------------------------------------
1339 
1340 } // pcl
1341 
1342 #endif // __PCL_AbstractImage_h
1343 
1344 // ----------------------------------------------------------------------------
1345 // EOF pcl/AbstractImage.h - Released 2019-04-30T16:30:41Z
bool ParseSelection(Rect &rect, int &channel) const
int NumberOfNominalChannels() const
void GetSelectedChannelRange(int &c0, int &c1) const
An exception class signaling the interruption of a process.
bool CanPopSelections() const
void SelectPoint(const Point &p) const
void ResetSelection() const
size_type count
current monitoring count.
void Swap(GenericPoint< T > &p1, GenericPoint< T > &p2)
Definition: Point.h:1290
void ResetPoint() const
Dynamic array of pointers to objects providing direct iteration and element access by reference...
size_type NumberOfSelectedSamples() const
Adaptive mutual exclusion lock variable.
Definition: Mutex.h:208
void ResetChannelRange() const
bool ParseSelection(Rect &rect, int &firstChannel, int &lastChannel) const
PCL root namespace.
Definition: AbstractImage.h:76
pcl::StatusCallback * GetStatusCallback() const
int channel
First selected channel.
Array< ImageSelections > selection_stack
Mutable ReferenceArray iterator.
static void RunThreads(ReferenceArray< thread > &threads, ThreadData &data, bool useAffinity=true)
void SelectPoint(int x, int y) const
void SelectChannelRange(int c0, int c1) const
const Rect & SelectedRectangle() const
void SetRangeClipLow(double clipLow) const
size_type NumberOfSelectedPixels() const
int NumberOfAlphaChannels() const
StatusMonitor status
Status monitoring object.
Implements color space properties of images.
Definition: ImageColor.h:96
void SelectRectangle(const Point &p0, const Point &p1) const
bool IsCompletelySelected() const
int LastSelectedChannel() const
size_t size_type
Definition: Defs.h:545
size_type NumberOfAlphaSamples() const
int NumberOfThreadsForRows(int rowCount=0, int rowWidth=0, int maxProcessors=0, size_type overheadLimitPx=1024u) const
void SelectRectangle(int x0, int y0, int x1, int y1) const
Mutex mutex
Mutual exclusion for synchronized thread access.
ThreadData(const StatusMonitor &a_status, size_type N)
constexpr const T & Max(const T &a, const T &b)
Definition: Utility.h:119
void PushSelections() const
int NumberOfThreads(size_type count, int maxProcessors=0, size_type overheadLimit=16u) const
bool HasAlphaChannels() const
virtual ~AbstractImage()
double RangeClipLow() const
constexpr const T & Min(const T &a, const T &b)
Definition: Utility.h:90
pcl::StatusCallback * StatusCallback() const
bool TryLock()
Definition: Mutex.h:362
void SelectAlphaChannels() const
Base class of all two-dimensional images in PCL.
int SelectedChannel() const
An asynchronous status monitoring system.
A structure used to store rectangular image selections, channel ranges, anchor points, and clipping ranges.
bool ParseChannel(int &channel) const
bool IsRangeClippingEnabled() const
void ResetSelections() const
size_type numThreads
Number of concurrent threads being executed (set by RunThreads()).
bool IsEmptySelection() const
size_type NumberOfNominalSamples() const
Provides status monitoring callback functions.
Definition: StatusMonitor.h:96
ImageColor::color_space color_space
void DisableRangeClipping(bool disable=true) const
void EnableRangeClipping(bool enable=true) const
bool ParseRect(Rect &rect) const
32-bit integer point on the plane.
size_type Length() const
int NumberOfSelectedChannels() const
StatusMonitor & Status() const
int lastChannel
Last selected channel.
void PopSelections() const
void SelectRectangle(const Rect &r) const
void SelectNominalChannels() const
void SetStatusCallback(pcl::StatusCallback *callback) const
int NumberOfNominalChannels(int colorSpace)
Definition: ColorSpace.h:102
size_type total
Total monitoring count.
double RangeClipHigh() const
void SelectChannel(int c) const
const Point & SelectedPoint() const
bool IsEmpty() const
ImageSelections & Selections() const
Implements geometric properties of two-dimensional images.
Definition: ImageGeometry.h:83
unsigned int uint32
Definition: Defs.h:602
32-bit integer rectangle on the plane.
void ResetRangeClipping() const
void SetRangeClipHigh(double clipHigh) const
Thread synchronization data for status monitoring of parallel image processing tasks.
void Unlock()
Definition: Mutex.h:310
void SetRangeClipping(double clipLow, double clipHigh) const
int FirstSelectedChannel() const
ThreadData(const AbstractImage &image, size_type N)
A process using multiple concurrent execution threads.
void Destroy(iterator i, size_type n=1)
bool IsFullSelection() const