PCL
Image.h
Go to the documentation of this file.
1 // ____ ______ __
2 // / __ \ / ____// /
3 // / /_/ // / / /
4 // / ____// /___ / /___ PixInsight Class Library
5 // /_/ \____//_____/ PCL 2.9.3
6 // ----------------------------------------------------------------------------
7 // pcl/Image.h - Released 2025-02-21T12:13:32Z
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-2025 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 (https://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_Image_h
53 #define __PCL_Image_h
54 
56 
57 #include <pcl/Defs.h>
58 #include <pcl/Diagnostics.h>
59 
60 #include <pcl/AbstractImage.h>
61 #include <pcl/AutoLock.h>
62 #include <pcl/Complex.h>
63 #include <pcl/Compression.h>
64 #include <pcl/File.h>
65 #include <pcl/Mutex.h>
66 #include <pcl/PixelAllocator.h>
67 #include <pcl/PixelTraits.h>
68 #include <pcl/ReferenceArray.h>
69 #include <pcl/Vector.h>
70 
71 #ifndef __PCL_IMAGE_NO_BITMAP
72 # ifdef __PCL_BUILDING_PIXINSIGHT_APPLICATION
73 # ifndef __PI_Bitmap_h
74 # include <API/Bitmap.h> // using server-side bitmaps
75 # endif
76 using namespace pi;
77 # else
78 # ifndef __PCL_Bitmap_h
79 # include <pcl/Bitmap.h> // using client-side bitmaps
80 # endif
81 # endif
82 # ifndef __PCL_Color_h
83 # include <pcl/Color.h> // for RGBA
84 # endif
85 #endif
86 
87 #ifdef __PCL_BUILDING_PIXINSIGHT_APPLICATION
88 namespace pi
89 {
90  class SharedImage;
91 }
92 #endif
93 
94 namespace pcl
95 {
96 
97 // ----------------------------------------------------------------------------
98 
136 namespace ImageOp
137 {
138  enum value_type
139  {
140  Nop, // No operation
141  Mov, // a = b
142  Add, // a + b
143  Sub, // a - b
144  Mul, // a * b
145  Div, // a / b
146  Pow, // Pow( a, b )
147  Dif, // |a - b|
148  Min, // Min( a, b )
149  Max, // Max( a, b )
150  Or, // a | b
151  And, // a & b
152  Xor, // a ^ b
153  Not, // ~a
154  Nor, // ~(a | b)
155  Nand, // ~(a & b)
156  Xnor, // ~(a ^ b)
157  ColorBurn, // 1 - Min( (1 - a)/b, 1 )
158  LinearBurn, // a + b - 1
159  Screen, // 1 - (1 - a)*(1 - b)
160  ColorDodge, // Min( a/(1 - b), 1 )
161  Overlay, // (a > 0.5) ? 1 - ((1 - 2*(a - 0.5)) * (1 - b)) : 2*a*b
162  SoftLight, // (b > 0.5) ? 1 - (1 - a)*(1 - b - 0.5) : a*(b + 0.5)
163  HardLight, // (b > 0.5) ? 1 - (1 - a)*(1 - 2*(b - 0.5)) : 2*a*b
164  VividLight, // (b > 0.5) ? 1 - Max( (1 - a)/(b - 0.5)/2, 1.0 ) : Min( a/(1 - 2*b ), 1.0 )
165  LinearLight, // (b > 0.5) ? Max( a + 2*(b - 0.5), 1.0 ) : Max( a + 2*b - 1, 1.0 )
166  PinLight, // (b > 0.5) ? Max( a, 2*(b - 0.5) ) : Min( a, 2*b )
167  Exclusion, // 0.5 - 2*(a - 0.5)*(b - 0.5)
168  NumberOfOperators
169  };
170 
174  inline bool IsArithmeticOperator( int op ) noexcept
175  {
176  return op >= Add && op <= Dif;
177  }
178 
183  inline bool IsBitwiseLogicalOperator( int op ) noexcept
184  {
185  return op >= Or && op <= Xnor;
186  }
187 
192  inline bool IsMoveOperator( int op ) noexcept
193  {
194  return op == Mov || op == Min || op == Max;
195  }
196 
201  inline bool IsPixelCompositionOperator( int op ) noexcept
202  {
203  return op >= ColorBurn && op <= Exclusion;
204  }
205 
209  String Id( value_type operation );
210 }
211 
212 // ----------------------------------------------------------------------------
213 
214 #define m_pixelData m_data->data
215 #define m_channelData( c ) reinterpret_cast<sample*>( PCL_ASSUME_ALIGNED_32( m_pixelData[c] ) )
216 #define m_allocator m_data->allocator
217 
218 #define m_width m_geometry->width
219 #define m_height m_geometry->height
220 #define m_numberOfChannels m_geometry->numberOfChannels
221 
222 #define m_colorSpace m_color->colorSpace
223 #define m_RGBWS m_color->RGBWS
224 
225 #define m_channel m_selected.channel
226 #define m_lastChannel m_selected.lastChannel
227 #define m_point m_selected.point
228 #define m_rectangle m_selected.rectangle
229 #define m_clipLow m_selected.clipLow
230 #define m_clipHigh m_selected.clipHigh
231 #define m_clippedLow m_selected.clippedLow
232 #define m_clippedHigh m_selected.clippedHigh
233 
234 // ----------------------------------------------------------------------------
235 
236 class PCL_CLASS ImageVariant;
237 class PCL_CLASS ImageTransformation;
238 class PCL_CLASS BidirectionalImageTransformation;
239 
240 // ----------------------------------------------------------------------------
241 
276 template <class P>
277 class PCL_CLASS GenericImage : public AbstractImage
278 {
279 public:
280 
288  using pixel_traits = P;
289 
300 
305  using sample = typename pixel_traits::sample;
306 
312  using color_space = AbstractImage::color_space;
313 
319 
325  using image_op = ImageOp::value_type;
326 
331 
336 
337  // -------------------------------------------------------------------------
338 
343  // -------------------------------------------------------------------------
344 
355  {
356  public:
357 
362 
367 
371  using sample = typename image_type::sample;
372 
376  sample_iterator() = default;
377 
391  sample_iterator( image_type& image, int channel = -1 )
392  : m_image( &image )
393  {
394  m_image->EnsureUnique();
395  if ( m_image->ParseChannel( channel ) )
396  {
397  m_iterator = (*m_image)[channel];
398  m_end = m_iterator + m_image->NumberOfPixels();
399  }
400  }
401 
419  : m_image( &image )
420  , m_iterator( i )
421  , m_end( j )
422  {
423  }
424 
428  sample_iterator( const sample_iterator& ) = default;
429 
433  sample_iterator& operator =( const sample_iterator& ) = default;
434 
439  bool IsValid() const noexcept
440  {
441  return m_image != nullptr && m_iterator != nullptr;
442  }
443 
447  image_type& Image() const noexcept
448  {
449  return *m_image;
450  }
451 
455  sample* Position() const noexcept
456  {
457  return m_iterator;
458  }
459 
467  operator bool() const noexcept
468  {
469  return m_iterator < m_end;
470  }
471 
476  sample& operator *() const noexcept
477  {
478  return *m_iterator;
479  }
480 
486  sample_iterator& operator ++() noexcept
487  {
488  ++m_iterator;
489  return *this;
490  }
491 
497  sample_iterator operator ++( int ) noexcept
498  {
499  return sample_iterator( *m_image, m_iterator++, m_end );
500  }
501 
507  sample_iterator& operator --() noexcept
508  {
509  --m_iterator;
510  return *this;
511  }
512 
518  sample_iterator operator --( int ) noexcept
519  {
520  return sample_iterator( *m_image, m_iterator--, m_end );
521  }
522 
530  sample_iterator& operator +=( distance_type delta ) noexcept
531  {
532  m_iterator += delta;
533  return *this;
534  }
535 
543  sample_iterator& operator -=( distance_type delta ) noexcept
544  {
545  m_iterator -= delta;
546  return *this;
547  }
548 
556  sample_iterator& MoveBy( int dx, int dy ) noexcept
557  {
558  m_iterator += distance_type( dy )*m_image->Width() + distance_type( dx );
559  return *this;
560  }
561 
566  friend sample_iterator operator +( const sample_iterator& i, distance_type delta ) noexcept
567  {
568  return sample_iterator( *i.m_image, i.m_iterator + delta, i.m_end );
569  }
570 
575  friend sample_iterator operator +( distance_type delta, const sample_iterator& i ) noexcept
576  {
577  return sample_iterator( *i.m_image, i.m_iterator + delta, i.m_end );
578  }
579 
584  friend sample_iterator operator -( const sample_iterator& i, distance_type delta ) noexcept
585  {
586  return sample_iterator( *i.m_image, i.m_iterator - delta, i.m_end );
587  }
588 
593  friend distance_type operator -( const sample_iterator& i, const sample_iterator& j ) noexcept
594  {
595  return i.m_iterator - j.m_iterator;
596  }
597 
602  friend distance_type operator -( const sample_iterator& i, const sample* j ) noexcept
603  {
604  return i.m_iterator - j;
605  }
606 
611  friend distance_type operator -( const sample* i, const sample_iterator& j ) noexcept
612  {
613  return i - j.m_iterator;
614  }
615 
620  friend bool operator ==( const sample_iterator& i, const sample_iterator& j ) noexcept
621  {
622  return i.m_iterator == j.m_iterator;
623  }
624 
629  friend bool operator ==( const sample_iterator& i, const sample* j ) noexcept
630  {
631  return i.m_iterator == j;
632  }
633 
638  friend bool operator ==( const sample* i, const sample_iterator& j ) noexcept
639  {
640  return i == j.m_iterator;
641  }
642 
647  friend bool operator <( const sample_iterator& i, const sample_iterator& j ) noexcept
648  {
649  return i.m_iterator < j.m_iterator;
650  }
651 
655  friend bool operator <( const sample_iterator& i, const sample* j ) noexcept
656  {
657  return i.m_iterator < j;
658  }
659 
663  friend bool operator <( const sample* i, const sample_iterator& j ) noexcept
664  {
665  return i < j.m_iterator;
666  }
667 
668  protected:
669 
670  image_type* m_image = nullptr;
671  sample* __restrict__ m_iterator = nullptr;
672  const sample* __restrict__ m_end = nullptr;
673 
674  friend class const_sample_iterator;
675  };
676 
677  // -------------------------------------------------------------------------
678 
689  {
690  public:
691 
696 
701 
705  using sample = typename image_type::sample;
706 
711 
725  const_sample_iterator( const image_type& image, int channel = -1 )
726  : m_image( &image )
727  {
728  if ( m_image->ParseChannel( channel ) )
729  {
730  m_iterator = (*m_image)[channel];
731  m_end = m_iterator + m_image->NumberOfPixels();
732  }
733  }
734 
751  const_sample_iterator( const image_type& image, const sample* i, const sample* j )
752  : m_image( &image )
753  , m_iterator( i )
754  , m_end( j )
755  {
756  }
757 
766  : m_image( i.m_image )
767  , m_iterator( i.m_iterator )
768  , m_end( i.m_end )
769  {
770  }
771 
776 
781  const_sample_iterator& operator =( const sample_iterator& i ) noexcept
782  {
783  m_image = i.m_image;
784  m_iterator = i.m_iterator;
785  m_end = i.m_end;
786  return *this;
787  }
788 
792  const_sample_iterator& operator =( const const_sample_iterator& ) = default;
793 
798  bool IsValid() const noexcept
799  {
800  return m_image != nullptr && m_iterator != nullptr;
801  }
802 
807  const image_type& Image() const noexcept
808  {
809  return *m_image;
810  }
811 
816  const sample* Position() const noexcept
817  {
818  return m_iterator;
819  }
820 
828  operator bool() const noexcept
829  {
830  return m_iterator < m_end;
831  }
832 
837  const sample& operator *() const noexcept
838  {
839  return *m_iterator;
840  }
841 
847  const_sample_iterator& operator ++() noexcept
848  {
849  ++m_iterator;
850  return *this;
851  }
852 
858  const_sample_iterator operator ++( int ) noexcept
859  {
860  return const_sample_iterator( *m_image, m_iterator++, m_end );
861  }
862 
868  const_sample_iterator& operator --() noexcept
869  {
870  --m_iterator;
871  return *this;
872  }
873 
879  const_sample_iterator operator --( int ) noexcept
880  {
881  return const_sample_iterator( *m_image, m_iterator--, m_end );
882  }
883 
891  const_sample_iterator& operator +=( distance_type delta ) noexcept
892  {
893  m_iterator += delta;
894  return *this;
895  }
896 
904  const_sample_iterator& operator -=( distance_type delta ) noexcept
905  {
906  m_iterator -= delta;
907  return *this;
908  }
909 
917  const_sample_iterator& MoveBy( int dx, int dy ) noexcept
918  {
919  m_iterator += distance_type( dy )*m_image->Width() + distance_type( dx );
920  return *this;
921  }
922 
928  {
929  return const_sample_iterator( *i.m_image, i.m_iterator + delta, i.m_end );
930  }
931 
937  {
938  return const_sample_iterator( *i.m_image, i.m_iterator + delta, i.m_end );
939  }
940 
946  {
947  return const_sample_iterator( *i.m_image, i.m_iterator - delta, i.m_end );
948  }
949 
955  {
956  return i.m_iterator - j.m_iterator;
957  }
958 
963  friend distance_type operator -( const const_sample_iterator& i, const sample* j ) noexcept
964  {
965  return i.m_iterator - j;
966  }
967 
972  friend distance_type operator -( const sample* i, const const_sample_iterator& j ) noexcept
973  {
974  return i - j.m_iterator;
975  }
976 
981  friend bool operator ==( const const_sample_iterator& i, const const_sample_iterator& j ) noexcept
982  {
983  return i.m_iterator == j.m_iterator;
984  }
985 
990  friend bool operator ==( const const_sample_iterator& i, const sample* j ) noexcept
991  {
992  return i.m_iterator == j;
993  }
994 
999  friend bool operator ==( const sample* i, const const_sample_iterator& j ) noexcept
1000  {
1001  return i == j.m_iterator;
1002  }
1003 
1008  friend bool operator <( const const_sample_iterator& i, const const_sample_iterator& j ) noexcept
1009  {
1010  return i.m_iterator < j.m_iterator;
1011  }
1012 
1016  friend bool operator <( const const_sample_iterator& i, const sample* j ) noexcept
1017  {
1018  return i.m_iterator < j;
1019  }
1020 
1024  friend bool operator <( const sample* i, const const_sample_iterator& j ) noexcept
1025  {
1026  return i < j.m_iterator;
1027  }
1028 
1029  protected:
1030 
1031  const image_type* m_image = nullptr;
1032  const sample* __restrict__ m_iterator = nullptr;
1033  const sample* __restrict__ m_end = nullptr;
1034  };
1035 
1036  // -------------------------------------------------------------------------
1037 
1038  template <class image_type, class sample_pointer>
1039  class roi_sample_iterator_base
1040  {
1041  protected:
1042 
1043  image_type* m_image = nullptr;
1044  sample_pointer m_iterator = nullptr;
1045  sample_pointer m_rowBegin = nullptr;
1046  sample_pointer m_rowEnd = nullptr;
1047  sample_pointer m_end = nullptr;
1048 
1049  roi_sample_iterator_base() = default;
1050 
1051  roi_sample_iterator_base( image_type& image, const Rect& rect, int channel )
1052  : m_image( &image )
1053  {
1054  Rect r = rect;
1055  if ( m_image->ParseRect( r ) )
1056  {
1057  if ( m_image->ParseChannel( channel ) )
1058  {
1059  m_iterator = m_rowBegin = m_image->PixelAddress( r.x0, r.y0, channel );
1060  m_rowEnd = m_rowBegin + r.Width();
1061  m_end = m_rowEnd + ((r.Height() - 1)*m_image->Width());
1062  }
1063  }
1064  }
1065 
1066  roi_sample_iterator_base( image_type& image, sample_pointer i, sample_pointer j )
1067  : m_image( &image )
1068  {
1069  if ( j < i )
1070  pcl::Swap( i, j );
1071  m_iterator = m_rowBegin = i;
1072  m_rowEnd = m_rowBegin + (j - i)%m_image->Width();
1073  m_end = j;
1074  }
1075 
1076  roi_sample_iterator_base( const roi_sample_iterator_base& i ) = default;
1077 
1078  roi_sample_iterator_base& operator =( const roi_sample_iterator_base& ) = default;
1079 
1080  void Increment() noexcept
1081  {
1082  if ( ++m_iterator == m_rowEnd )
1083  {
1084  m_rowBegin += m_image->Width();
1085  m_rowEnd += m_image->Width();
1086  m_iterator = m_rowBegin;
1087  }
1088  }
1089 
1090  void Decrement() noexcept
1091  {
1092  if ( m_iterator == m_rowBegin )
1093  {
1094  m_rowBegin -= m_image->Width();
1095  m_rowEnd -= m_image->Width();
1096  m_iterator = m_rowEnd;
1097  }
1098  --m_iterator;
1099  }
1100 
1101  void MoveBy( int cols, int rows ) noexcept
1102  {
1103  cols += m_iterator - m_rowBegin;
1104  if ( cols != 0 )
1105  {
1106  int w = m_rowEnd - m_rowBegin;
1107  if ( pcl::Abs( cols ) >= w )
1108  {
1109  rows += cols/w;
1110  cols %= w;
1111  }
1112  }
1113  int dy = rows * m_image->Width();
1114  m_rowBegin += dy;
1115  m_rowEnd += dy;
1116  m_iterator = m_rowBegin + cols;
1117  }
1118  };
1119 
1120  // -------------------------------------------------------------------------
1121 
1132  class roi_sample_iterator : private roi_sample_iterator_base<GenericImage<P>, sample*>
1133  {
1134  public:
1135 
1140 
1145 
1149  using sample = typename image_type::sample;
1150 
1151  using iterator_base = roi_sample_iterator_base<GenericImage<P>, sample*>;
1152 
1156  roi_sample_iterator() = default;
1157 
1180  roi_sample_iterator( image_type& image, const Rect& rect = Rect( 0 ), int channel = -1 )
1181  : iterator_base( image.EnsureUnique(), rect, channel )
1182  {
1183  }
1184 
1202  : iterator_base( image, i, j )
1203  {
1204  }
1205 
1210 
1214  roi_sample_iterator& operator =( const roi_sample_iterator& ) = default;
1215 
1220  bool IsValid() const noexcept
1221  {
1222  return this->m_image != nullptr && this->m_iterator != nullptr;
1223  }
1224 
1228  image_type& Image() const noexcept
1229  {
1230  return *this->m_image;
1231  }
1232 
1236  sample* Position() const noexcept
1237  {
1238  return this->m_iterator;
1239  }
1240 
1247  operator bool() const noexcept
1248  {
1249  return this->m_iterator < this->m_end;
1250  }
1251 
1256  sample& operator *() const noexcept
1257  {
1258  return *this->m_iterator;
1259  }
1260 
1266  roi_sample_iterator& operator ++() noexcept
1267  {
1268  this->Increment();
1269  return *this;
1270  }
1271 
1277  roi_sample_iterator operator ++( int ) noexcept
1278  {
1279  roi_sample_iterator i0( *this );
1280  this->Increment();
1281  return i0;
1282  }
1283 
1289  roi_sample_iterator& operator --() noexcept
1290  {
1291  this->Decrement();
1292  return *this;
1293  }
1294 
1300  roi_sample_iterator operator --( int ) noexcept
1301  {
1302  roi_sample_iterator i0( *this );
1303  this->Decrement();
1304  return i0;
1305  }
1306 
1316  roi_sample_iterator& operator +=( distance_type delta ) noexcept
1317  {
1318  int w = this->m_rowEnd - this->m_rowBegin;
1319  iterator_base::MoveBy( delta%w, delta/w );
1320  return *this;
1321  }
1322 
1332  roi_sample_iterator& operator -=( distance_type delta ) noexcept
1333  {
1334  int w = this->m_rowEnd - this->m_rowBegin;
1335  iterator_base::MoveBy( -delta%w, -delta/w );
1336  return *this;
1337  }
1338 
1347  roi_sample_iterator& MoveBy( int dx, int dy ) noexcept
1348  {
1349  iterator_base::MoveBy( dx, dy );
1350  return *this;
1351  }
1352 
1358  {
1359  roi_sample_iterator j( i );
1360  j += delta;
1361  return j;
1362  }
1363 
1369  {
1370  roi_sample_iterator j( i );
1371  j += delta;
1372  return j;
1373  }
1374 
1380  {
1381  roi_sample_iterator j( i );
1382  j -= delta;
1383  return j;
1384  }
1385 
1390  friend bool operator ==( const roi_sample_iterator& i, const roi_sample_iterator& j ) noexcept
1391  {
1392  return i.m_iterator == j.m_iterator;
1393  }
1394 
1399  friend bool operator ==( const roi_sample_iterator& i, const sample* j ) noexcept
1400  {
1401  return i.m_iterator == j;
1402  }
1403 
1408  friend bool operator ==( const sample* i, const roi_sample_iterator& j ) noexcept
1409  {
1410  return i == j.m_iterator;
1411  }
1412 
1417  friend bool operator <( const roi_sample_iterator& i, const roi_sample_iterator& j ) noexcept
1418  {
1419  return i.m_iterator < j.m_iterator;
1420  }
1421 
1425  friend bool operator <( const roi_sample_iterator& i, const sample* j ) noexcept
1426  {
1427  return i.m_iterator < j;
1428  }
1429 
1433  friend bool operator <( const sample* i, const roi_sample_iterator& j ) noexcept
1434  {
1435  return i < j.m_iterator;
1436  }
1437 
1438  friend class const_roi_pixel_iterator;
1439  };
1440 
1441  // -------------------------------------------------------------------------
1442 
1453  class const_roi_sample_iterator : private roi_sample_iterator_base<const GenericImage<P>, const sample*>
1454  {
1455  public:
1456 
1461 
1466 
1470  using sample = typename image_type::sample;
1471 
1472  using iterator_base = roi_sample_iterator_base<const GenericImage<P>, const sample*>;
1473 
1478 
1501  const_roi_sample_iterator( const image_type& image, const Rect& rect = Rect( 0 ), int channel = -1 )
1502  : iterator_base( image, rect, channel )
1503  {
1504  }
1505 
1522  const_roi_sample_iterator( const image_type& image, const sample* i, const sample* j )
1523  : iterator_base( image, i, j )
1524  {
1525  }
1526 
1536  : iterator_base( i.m_image, i.m_rowBegin, i.m_end )
1537  {
1538  }
1539 
1544 
1549  const_roi_sample_iterator& operator =( const roi_sample_iterator& i ) noexcept
1550  {
1551  this->m_image = i.m_image;
1552  this->m_iterator = i.m_iterator;
1553  this->m_rowBegin = i.m_rowBegin;
1554  this->m_rowEnd = i.m_rowEnd;
1555  this->m_end = i.m_end;
1556  return *this;
1557  }
1558 
1562  const_roi_sample_iterator& operator =( const const_roi_sample_iterator& ) = default;
1563 
1568  bool IsValid() const noexcept
1569  {
1570  return this->m_image != nullptr && this->m_iterator != nullptr;
1571  }
1572 
1577  const image_type& Image() const noexcept
1578  {
1579  return *this->m_image;
1580  }
1581 
1586  const sample* Position() const noexcept
1587  {
1588  return this->m_iterator;
1589  }
1590 
1597  operator bool() const noexcept
1598  {
1599  return this->m_iterator < this->m_end;
1600  }
1601 
1606  const sample& operator *() const noexcept
1607  {
1608  return *this->m_iterator;
1609  }
1610 
1616  const_roi_sample_iterator& operator ++() noexcept
1617  {
1618  this->Increment();
1619  return *this;
1620  }
1621 
1627  const_roi_sample_iterator operator ++( int ) noexcept
1628  {
1629  const_roi_sample_iterator i0( *this );
1630  this->Increment();
1631  return i0;
1632  }
1633 
1639  const_roi_sample_iterator& operator --() noexcept
1640  {
1641  this->Decrement();
1642  return *this;
1643  }
1644 
1650  const_roi_sample_iterator operator --( int ) noexcept
1651  {
1652  const_roi_sample_iterator i0( *this );
1653  this->Decrement();
1654  return i0;
1655  }
1656 
1666  const_roi_sample_iterator& operator +=( distance_type delta ) noexcept
1667  {
1668  int w = this->m_rowEnd - this->m_rowBegin;
1669  iterator_base::MoveBy( delta%w, delta/w );
1670  return *this;
1671  }
1672 
1682  const_roi_sample_iterator& operator -=( distance_type delta ) noexcept
1683  {
1684  int w = this->m_rowEnd - this->m_rowBegin;
1685  iterator_base::MoveBy( -delta%w, -delta/w );
1686  return *this;
1687  }
1688 
1697  const_roi_sample_iterator& MoveBy( int dx, int dy ) noexcept
1698  {
1699  iterator_base::MoveBy( dx, dy );
1700  return *this;
1701  }
1702 
1708  {
1710  j += delta;
1711  return j;
1712  }
1713 
1719  {
1721  j += delta;
1722  return j;
1723  }
1724 
1730  {
1732  j -= delta;
1733  return j;
1734  }
1735 
1740  friend bool operator ==( const const_roi_sample_iterator& i, const const_roi_sample_iterator& j ) noexcept
1741  {
1742  return i.m_iterator == j.m_iterator;
1743  }
1744 
1749  friend bool operator ==( const const_roi_sample_iterator& i, const sample* j ) noexcept
1750  {
1751  return i.m_iterator == j;
1752  }
1753 
1758  friend bool operator ==( const sample* i, const const_roi_sample_iterator& j ) noexcept
1759  {
1760  return i == j.m_iterator;
1761  }
1762 
1767  friend bool operator <( const const_roi_sample_iterator& i, const const_roi_sample_iterator& j ) noexcept
1768  {
1769  return i.m_iterator < j.m_iterator;
1770  }
1771 
1775  friend bool operator <( const const_roi_sample_iterator& i, const sample* j ) noexcept
1776  {
1777  return i.m_iterator < j;
1778  }
1779 
1783  friend bool operator <( const sample* i, const const_roi_sample_iterator& j ) noexcept
1784  {
1785  return i < j.m_iterator;
1786  }
1787  };
1788 
1789  // -------------------------------------------------------------------------
1790 
1791  template <class image_type, class iterator_base, class sample_pointer, class filter_type>
1792  class filter_sample_iterator_base : public iterator_base
1793  {
1794  protected:
1795 
1796  filter_type m_filter;
1797  sample_pointer m_begin = nullptr;
1798 
1799  filter_sample_iterator_base() = default;
1800 
1801  filter_sample_iterator_base( image_type& image, const filter_type& filter, int channel )
1802  : iterator_base( image, channel )
1803  , m_filter( filter )
1804  , m_begin( iterator_base::m_iterator )
1805  {
1806  JumpToNextValidSample();
1807  }
1808 
1809  filter_sample_iterator_base( image_type& image, const filter_type& filter, sample_pointer i, sample_pointer j )
1810  : iterator_base( image, i, j )
1811  , m_filter( filter )
1812  , m_begin( iterator_base::m_iterator )
1813  {
1814  JumpToNextValidSample();
1815  }
1816 
1817  filter_sample_iterator_base( const iterator_base& i, const filter_type& filter )
1818  : iterator_base( i )
1819  , m_filter( filter )
1820  , m_begin( iterator_base::m_iterator )
1821  {
1822  JumpToNextValidSample();
1823  }
1824 
1825  filter_sample_iterator_base( const filter_sample_iterator_base& ) = default;
1826 
1827  filter_sample_iterator_base& operator =( const filter_sample_iterator_base& ) = default;
1828 
1829  filter_sample_iterator_base& operator =( const iterator_base& i ) noexcept
1830  {
1831  (void)iterator_base::operator =( i );
1832  JumpToNextValidSample();
1833  return *this;
1834  }
1835 
1836  void JumpToNextValidSample() noexcept
1837  {
1838  while ( this->m_iterator < this->m_end && !this->m_filter( *this->m_iterator ) )
1839  ++this->m_iterator;
1840  }
1841 
1842  void JumpToPrevValidSample() noexcept
1843  {
1844  while ( this->m_iterator > this->m_begin && !this->m_filter( *this->m_iterator ) )
1845  --this->m_iterator;
1846  }
1847  };
1848 
1849  // -------------------------------------------------------------------------
1850 
1884  template <class F>
1886  public filter_sample_iterator_base<GenericImage<P>, sample_iterator, sample*, F>
1887  {
1888  public:
1889 
1894 
1899 
1903  using sample = typename image_type::sample;
1904 
1909  using filter_type = F;
1910 
1911  using iterator_base = filter_sample_iterator_base<GenericImage<P>, sample_iterator, sample*, F>;
1912 
1917 
1934  filter_sample_iterator( image_type& image, const F& filter, int channel = -1 )
1935  : iterator_base( image.EnsureUnique(), filter, channel )
1936  {
1937  }
1938 
1957  filter_sample_iterator( image_type& image, const F& filter, sample* i, sample* j )
1958  : iterator_base( image, filter, i, j )
1959  {
1960  }
1961 
1966  filter_sample_iterator( const sample_iterator& i, const F& filter )
1967  : iterator_base( i, filter )
1968  {
1969  }
1970 
1975 
1979  filter_sample_iterator& operator =( const filter_sample_iterator& ) = default;
1980 
1985  filter_sample_iterator& operator =( const sample_iterator& i ) noexcept
1986  {
1987  (void)iterator_base::operator =( i );
1988  return *this;
1989  }
1990 
1995  bool IsValid() const noexcept
1996  {
1997  return this->m_image != nullptr && this->m_iterator != nullptr;
1998  }
1999 
2003  image_type& Image() const noexcept
2004  {
2005  return *this->m_image;
2006  }
2007 
2012  const filter_type& Filter() const noexcept
2013  {
2014  return this->m_filter;
2015  }
2016 
2021  filter_type& Filter() noexcept
2022  {
2023  return this->m_filter;
2024  }
2025 
2029  sample* Position() const noexcept
2030  {
2031  return this->m_iterator;
2032  }
2033 
2041  operator bool() const noexcept
2042  {
2043  return this->m_iterator < this->m_end;
2044  }
2045 
2050  sample& operator *() const noexcept
2051  {
2052  return *this->m_iterator;
2053  }
2054 
2060  filter_sample_iterator& operator ++() noexcept
2061  {
2062  ++this->m_iterator;
2063  this->JumpToNextValidSample();
2064  return *this;
2065  }
2066 
2072  filter_sample_iterator operator ++( int ) noexcept
2073  {
2074  sample* __restrict__ i0 = this->m_iterator++;
2075  this->JumpToNextValidSample();
2076  return filter_sample_iterator( *this->m_image, this->m_filter, i0, this->m_end );
2077  }
2078 
2084  filter_sample_iterator& operator --() noexcept
2085  {
2086  --this->m_iterator;
2087  this->JumpToPrevValidSample();
2088  return *this;
2089  }
2090 
2096  filter_sample_iterator operator --( int ) noexcept
2097  {
2098  sample* __restrict__ i0 = this->m_iterator--;
2099  this->JumpToPrevValidSample();
2100  return filter_sample_iterator( *this->m_image, this->m_filter, i0, this->m_end );
2101  }
2102 
2110  filter_sample_iterator& operator +=( distance_type delta ) noexcept
2111  {
2112  this->m_iterator += delta;
2113  this->JumpToNextValidSample();
2114  return *this;
2115  }
2116 
2124  filter_sample_iterator& operator -=( distance_type delta ) noexcept
2125  {
2126  this->m_iterator -= delta;
2127  this->JumpToPrevValidSample();
2128  return *this;
2129  }
2130 
2138  filter_sample_iterator& MoveBy( int dx, int dy ) noexcept
2139  {
2140  distance_type d = distance_type( dy )*this->m_image->Width() + distance_type( dx );
2141  this->m_iterator += d;
2142  if ( d >= 0 )
2143  this->JumpToNextValidSample();
2144  else
2145  this->JumpToPrevValidSample();
2146  return *this;
2147  }
2148 
2154  {
2155  return filter_sample_iterator( *i.m_image, i.m_iterator + delta, i.m_end );
2156  }
2157 
2163  {
2164  return filter_sample_iterator( *i.m_image, i.m_iterator + delta, i.m_end );
2165  }
2166 
2172  {
2173  return filter_sample_iterator( *i.m_image, i.m_iterator - delta, i.m_end );
2174  }
2175 
2181  {
2182  return i.m_iterator - j.m_iterator;
2183  }
2184 
2189  friend bool operator ==( const filter_sample_iterator& i, const filter_sample_iterator& j ) noexcept
2190  {
2191  return i.m_iterator == j.m_iterator;
2192  }
2193 
2198  friend bool operator ==( const filter_sample_iterator& i, const sample* j ) noexcept
2199  {
2200  return i.m_iterator == j;
2201  }
2202 
2207  friend bool operator ==( const sample* i, const filter_sample_iterator& j ) noexcept
2208  {
2209  return i == j.m_iterator;
2210  }
2211 
2216  friend bool operator <( const filter_sample_iterator& i, const filter_sample_iterator& j ) noexcept
2217  {
2218  return i.m_iterator < j.m_iterator;
2219  }
2220 
2224  friend bool operator <( const filter_sample_iterator& i, const sample* j ) noexcept
2225  {
2226  return i.m_iterator < j;
2227  }
2228 
2232  friend bool operator <( const sample* i, const filter_sample_iterator& j ) noexcept
2233  {
2234  return i < j.m_iterator;
2235  }
2236  };
2237 
2238  // -------------------------------------------------------------------------
2239 
2273  template <class F>
2275  public filter_sample_iterator_base<const GenericImage<P>, const_sample_iterator, const sample*, F>
2276  {
2277  public:
2278 
2283 
2288 
2292  using sample = typename image_type::sample;
2293 
2298  using filter_type = F;
2299 
2300  using iterator_base = filter_sample_iterator_base<const GenericImage<P>, const_sample_iterator, const sample*, F>;
2301 
2306 
2323  const_filter_sample_iterator( const image_type& image, const F& filter, int channel = -1 )
2324  : iterator_base( image, filter, channel )
2325  {
2326  }
2327 
2346  const_filter_sample_iterator( const image_type& image, const F& filter, const sample* i, const sample* j )
2347  : iterator_base( image, filter, i, j )
2348  {
2349  }
2350 
2355  const_filter_sample_iterator( const sample_iterator& i, const F& filter )
2356  : iterator_base( i.m_image, filter, i.m_iterator, i.m_end )
2357  {
2358  }
2359 
2365  : iterator_base( i, filter )
2366  {
2367  }
2368 
2374  : iterator_base( i )
2375  {
2376  }
2377 
2382 
2387 
2392  const_filter_sample_iterator& operator =( const sample_iterator& i ) noexcept
2393  {
2394  (void)const_sample_iterator::operator =( i );
2395  this->JumpToNextValidSample();
2396  return *this;
2397  }
2398 
2403  const_filter_sample_iterator& operator =( const const_sample_iterator& i ) noexcept
2404  {
2405  (void)iterator_base::operator =( i );
2406  return *this;
2407  }
2408 
2413  bool IsValid() const noexcept
2414  {
2415  return this->m_image != nullptr && this->m_iterator != nullptr;
2416  }
2417 
2422  const image_type& Image() const noexcept
2423  {
2424  return *this->m_image;
2425  }
2426 
2431  const filter_type& Filter() const noexcept
2432  {
2433  return this->m_filter;
2434  }
2435 
2440  filter_type& Filter() noexcept
2441  {
2442  return this->m_filter;
2443  }
2444 
2449  const sample* Position() const noexcept
2450  {
2451  return this->m_iterator;
2452  }
2453 
2461  operator bool() const noexcept
2462  {
2463  return this->m_iterator < this->m_end;
2464  }
2465 
2470  const sample& operator *() const noexcept
2471  {
2472  return *this->m_iterator;
2473  }
2474 
2480  const_filter_sample_iterator& operator ++() noexcept
2481  {
2482  ++this->m_iterator;
2483  this->JumpToNextValidSample();
2484  return *this;
2485  }
2486 
2492  const_filter_sample_iterator operator ++( int ) noexcept
2493  {
2494  sample* __restrict__ i0 = this->m_iterator++;
2495  this->JumpToNextValidSample();
2496  return const_filter_sample_iterator( *this->m_image, this->m_filter, i0, this->m_end );
2497  }
2498 
2504  const_filter_sample_iterator& operator --() noexcept
2505  {
2506  --this->m_iterator;
2507  this->JumpToPrevValidSample();
2508  return *this;
2509  }
2510 
2516  const_filter_sample_iterator operator --( int ) noexcept
2517  {
2518  sample* __restrict__ i0 = this->m_iterator--;
2519  this->JumpToPrevValidSample();
2520  return const_filter_sample_iterator( *this->m_image, this->m_filter, i0, this->m_end );
2521  }
2522 
2530  const_filter_sample_iterator& operator +=( distance_type delta ) noexcept
2531  {
2532  this->m_iterator += delta;
2533  this->JumpToNextValidSample();
2534  return *this;
2535  }
2536 
2544  const_filter_sample_iterator& operator -=( distance_type delta ) noexcept
2545  {
2546  this->m_iterator -= delta;
2547  this->JumpToPrevValidSample();
2548  return *this;
2549  }
2550 
2558  const_filter_sample_iterator& MoveBy( int dx, int dy ) noexcept
2559  {
2560  distance_type d = distance_type( dy )*this->m_image->Width() + distance_type( dx );
2561  this->m_iterator += d;
2562  if ( d >= 0 )
2563  this->JumpToNextValidSample();
2564  else
2565  this->JumpToPrevValidSample();
2566  return *this;
2567  }
2568 
2574  {
2575  return const_filter_sample_iterator( *i.m_image, i.m_iterator + delta, i.m_end );
2576  }
2577 
2583  {
2584  return const_filter_sample_iterator( *i.m_image, i.m_iterator + delta, i.m_end );
2585  }
2586 
2592  {
2593  return const_filter_sample_iterator( *i.m_image, i.m_iterator - delta, i.m_end );
2594  }
2595 
2601  {
2602  return i.m_iterator - j.m_iterator;
2603  }
2604 
2609  friend bool operator ==( const const_filter_sample_iterator& i, const const_filter_sample_iterator& j ) noexcept
2610  {
2611  return i.m_iterator == j.m_iterator;
2612  }
2613 
2618  friend bool operator ==( const const_filter_sample_iterator& i, const sample* j ) noexcept
2619  {
2620  return i.m_iterator == j;
2621  }
2622 
2627  friend bool operator ==( const sample* i, const const_filter_sample_iterator& j ) noexcept
2628  {
2629  return i == j.m_iterator;
2630  }
2631 
2636  friend bool operator <( const const_filter_sample_iterator& i, const const_filter_sample_iterator& j ) noexcept
2637  {
2638  return i.m_iterator < j.m_iterator;
2639  }
2640 
2644  friend bool operator <( const const_filter_sample_iterator& i, const sample* j ) noexcept
2645  {
2646  return i.m_iterator < j;
2647  }
2648 
2652  friend bool operator <( const sample* i, const const_filter_sample_iterator& j ) noexcept
2653  {
2654  return i < j.m_iterator;
2655  }
2656  };
2657 
2658  // -------------------------------------------------------------------------
2659 
2660  template <class image_type, class sample_pointer, class filter_type>
2661  class roi_filter_sample_iterator_base : public roi_sample_iterator_base<image_type, sample_pointer>
2662  {
2663  protected:
2664 
2665  using roi_iterator_base = roi_sample_iterator_base<image_type, sample_pointer>;
2666 
2667  filter_type m_filter;
2668  sample_pointer m_begin = nullptr;
2669 
2670  roi_filter_sample_iterator_base() = default;
2671 
2672  roi_filter_sample_iterator_base( image_type& image, const filter_type& filter, const Rect& rect, int channel )
2673  : roi_iterator_base( image, rect, channel )
2674  , m_filter( filter )
2675  , m_begin( roi_iterator_base::m_iterator )
2676  {
2677  JumpToNextValidSample();
2678  }
2679 
2680  roi_filter_sample_iterator_base( image_type& image, const filter_type& filter, sample_pointer i, sample_pointer j )
2681  : roi_iterator_base( image, i, j )
2682  , m_filter( filter )
2683  , m_begin( roi_iterator_base::m_iterator )
2684  {
2685  JumpToNextValidSample();
2686  }
2687 
2688  roi_filter_sample_iterator_base( const roi_iterator_base& i, const filter_type& filter )
2689  : roi_iterator_base( i )
2690  , m_filter( filter )
2691  , m_begin( roi_iterator_base::m_iterator )
2692  {
2693  JumpToNextValidSample();
2694  }
2695 
2696  roi_filter_sample_iterator_base( const roi_filter_sample_iterator_base& ) = default;
2697 
2698  roi_filter_sample_iterator_base& operator =( const roi_filter_sample_iterator_base& i ) = default;
2699 
2700  roi_filter_sample_iterator_base& operator =( const roi_iterator_base& i ) noexcept
2701  {
2702  (void)roi_iterator_base::operator =( i );
2703  JumpToNextValidSample();
2704  return *this;
2705  }
2706 
2707  void JumpToNextValidSample() noexcept
2708  {
2709  while ( this->m_iterator < this->m_end && !this->m_filter( *this->m_iterator ) )
2710  roi_iterator_base::Increment();
2711  }
2712 
2713  void JumpToPrevValidSample() noexcept
2714  {
2715  while ( this->m_iterator > this->m_begin && !this->m_filter( *this->m_iterator ) )
2716  roi_iterator_base::Decrement();
2717  }
2718  };
2719 
2720  // -------------------------------------------------------------------------
2721 
2732  template <class F>
2733  class roi_filter_sample_iterator : public roi_filter_sample_iterator_base<GenericImage<P>, sample*, F>
2734  {
2735  public:
2736 
2741 
2746 
2750  using sample = typename image_type::sample;
2751 
2756  using filter_type = F;
2757 
2758  using iterator_base = roi_filter_sample_iterator_base<GenericImage<P>, sample*, F>;
2759 
2764 
2790  roi_filter_sample_iterator( image_type& image, const F& filter, const Rect& rect = Rect( 0 ), int channel = -1 )
2791  : iterator_base( image.EnsureUnique(), filter, rect, channel )
2792  {
2793  }
2794 
2813  roi_filter_sample_iterator( image_type& image, const F& filter, sample* i, sample* j )
2814  : iterator_base( image, filter, i, j )
2815  {
2816  }
2817 
2823  : iterator_base( i, filter )
2824  {
2825  }
2826 
2831 
2836 
2841  roi_filter_sample_iterator& operator =( const roi_sample_iterator& i ) noexcept
2842  {
2843  (void)iterator_base::operator =( i );
2844  return *this;
2845  }
2846 
2851  bool IsValid() const noexcept
2852  {
2853  return this->m_image != nullptr && this->m_iterator != nullptr;
2854  }
2855 
2859  image_type& Image() const noexcept
2860  {
2861  return *this->m_image;
2862  }
2863 
2868  const filter_type& Filter() const noexcept
2869  {
2870  return this->m_filter;
2871  }
2872 
2877  filter_type& Filter() noexcept
2878  {
2879  return this->m_filter;
2880  }
2881 
2885  sample* Position() const noexcept
2886  {
2887  return this->m_iterator;
2888  }
2889 
2896  operator bool() const noexcept
2897  {
2898  return this->m_iterator < this->m_end;
2899  }
2900 
2905  sample& operator *() const noexcept
2906  {
2907  return *this->m_iterator;
2908  }
2909 
2915  roi_filter_sample_iterator& operator ++() noexcept
2916  {
2917  this->Increment();
2918  this->JumpToNextValidSample();
2919  return *this;
2920  }
2921 
2927  roi_filter_sample_iterator operator ++( int ) noexcept
2928  {
2929  roi_filter_sample_iterator i0( *this );
2930  this->Increment();
2931  this->JumpToNextValidSample();
2932  return i0;
2933  }
2934 
2940  roi_filter_sample_iterator& operator --() noexcept
2941  {
2942  this->Decrement();
2943  this->JumpToPrevValidSample();
2944  return *this;
2945  }
2946 
2952  roi_filter_sample_iterator operator --( int ) noexcept
2953  {
2954  roi_filter_sample_iterator i0( *this );
2955  this->Decrement();
2956  this->JumpToPrevValidSample();
2957  return i0;
2958  }
2959 
2969  roi_filter_sample_iterator& operator +=( distance_type delta ) noexcept
2970  {
2971  int w = this->m_rowEnd - this->m_rowBegin;
2972  return MoveBy( delta%w, delta/w );
2973  }
2974 
2984  roi_filter_sample_iterator& operator -=( distance_type delta ) noexcept
2985  {
2986  int w = this->m_rowEnd - this->m_rowBegin;
2987  return MoveBy( -delta%w, -delta/w );
2988  }
2989 
2998  roi_filter_sample_iterator& MoveBy( int dx, int dy ) noexcept
2999  {
3000  sample* __restrict__ i0 = this->m_iterator;
3001  iterator_base::MoveBy( dx, dy );
3002  if ( this->m_iterator >= i0 )
3003  this->JumpToNextValidSample();
3004  else
3005  this->JumpToPrevValidSample();
3006  return *this;
3007  }
3008 
3014  {
3016  j += delta;
3017  return j;
3018  }
3019 
3025  {
3027  j += delta;
3028  return j;
3029  }
3030 
3036  {
3038  j -= delta;
3039  return j;
3040  }
3041 
3046  friend bool operator ==( const roi_filter_sample_iterator& i, const roi_filter_sample_iterator& j ) noexcept
3047  {
3048  return i.m_iterator == j.m_iterator;
3049  }
3050 
3055  friend bool operator ==( const roi_filter_sample_iterator& i, const sample* j ) noexcept
3056  {
3057  return i.m_iterator == j;
3058  }
3059 
3064  friend bool operator ==( const sample* i, const roi_filter_sample_iterator& j ) noexcept
3065  {
3066  return i == j.m_iterator;
3067  }
3068 
3073  friend bool operator <( const roi_filter_sample_iterator& i, const roi_filter_sample_iterator& j ) noexcept
3074  {
3075  return i.m_iterator < j.m_iterator;
3076  }
3077 
3081  friend bool operator <( const roi_filter_sample_iterator& i, const sample* j ) noexcept
3082  {
3083  return i.m_iterator < j;
3084  }
3085 
3089  friend bool operator <( const sample* i, const roi_filter_sample_iterator& j ) noexcept
3090  {
3091  return i < j.m_iterator;
3092  }
3093  };
3094 
3095  // -------------------------------------------------------------------------
3096 
3107  template <class F>
3108  class const_roi_filter_sample_iterator : public roi_filter_sample_iterator_base<const GenericImage<P>, const sample*, F>
3109  {
3110  public:
3111 
3116 
3121 
3125  using sample = typename image_type::sample;
3126 
3131  using filter_type = F;
3132 
3133  using iterator_base = roi_filter_sample_iterator_base<const GenericImage<P>, const sample*, F>;
3134 
3139 
3165  const_roi_filter_sample_iterator( const image_type& image, const F& filter, const Rect& rect = Rect( 0 ), int channel = -1 )
3166  : iterator_base( image, filter, rect, channel )
3167  {
3168  }
3169 
3188  const_roi_filter_sample_iterator( const image_type& image, const F& filter, const sample* i, const sample* j )
3189  : iterator_base( image, filter, i, j )
3190  {
3191  }
3192 
3198  : iterator_base( i, filter )
3199  {
3200  }
3201 
3206 
3211 
3217  {
3218  (void)iterator_base::operator =( i );
3219  return *this;
3220  }
3221 
3226  bool IsValid() const noexcept
3227  {
3228  return this->m_image != nullptr && this->m_iterator != nullptr;
3229  }
3230 
3235  const image_type& Image() const noexcept
3236  {
3237  return *this->m_image;
3238  }
3239 
3244  const filter_type& Filter() const noexcept
3245  {
3246  return this->m_filter;
3247  }
3248 
3253  filter_type& Filter() noexcept
3254  {
3255  return this->m_filter;
3256  }
3257 
3262  const sample* Position() const noexcept
3263  {
3264  return this->m_iterator;
3265  }
3266 
3273  operator bool() const noexcept
3274  {
3275  return this->m_iterator < this->m_end;
3276  }
3277 
3282  const sample& operator *() const noexcept
3283  {
3284  return *this->m_iterator;
3285  }
3286 
3292  const_roi_filter_sample_iterator& operator ++() noexcept
3293  {
3294  this->Increment();
3295  this->JumpToNextValidSample();
3296  return *this;
3297  }
3298 
3304  const_roi_filter_sample_iterator operator ++( int ) noexcept
3305  {
3307  this->Increment();
3308  this->JumpToNextValidSample();
3309  return i0;
3310  }
3311 
3317  const_roi_filter_sample_iterator& operator --() noexcept
3318  {
3319  this->Decrement();
3320  this->JumpToPrevValidSample();
3321  return *this;
3322  }
3323 
3329  const_roi_filter_sample_iterator operator --( int ) noexcept
3330  {
3332  this->Decrement();
3333  this->JumpToPrevValidSample();
3334  return i0;
3335  }
3336 
3346  const_roi_filter_sample_iterator& operator +=( distance_type delta ) noexcept
3347  {
3348  int w = this->m_rowEnd - this->m_rowBegin;
3349  return MoveBy( delta%w, delta/w );
3350  }
3351 
3361  const_roi_filter_sample_iterator& operator -=( distance_type delta ) noexcept
3362  {
3363  int w = this->m_rowEnd - this->m_rowBegin;
3364  return MoveBy( -delta%w, -delta/w );
3365  }
3366 
3375  const_roi_filter_sample_iterator& MoveBy( int dx, int dy ) noexcept
3376  {
3377  const sample* __restrict__ i0 = this->m_iterator;
3378  iterator_base::MoveBy( dx, dy );
3379  if ( this->m_iterator >= i0 )
3380  this->JumpToNextValidSample();
3381  else
3382  this->JumpToPrevValidSample();
3383  return *this;
3384  }
3385 
3391  {
3393  j += delta;
3394  return j;
3395  }
3396 
3402  {
3404  j += delta;
3405  return j;
3406  }
3407 
3413  {
3415  j -= delta;
3416  return j;
3417  }
3418 
3424  {
3425  return i.m_iterator == j.m_iterator;
3426  }
3427 
3432  friend bool operator ==( const const_roi_filter_sample_iterator& i, const sample* j ) noexcept
3433  {
3434  return i.m_iterator == j;
3435  }
3436 
3441  friend bool operator ==( const sample* i, const const_roi_filter_sample_iterator& j ) noexcept
3442  {
3443  return i == j.m_iterator;
3444  }
3445 
3451  {
3452  return i.m_iterator < j.m_iterator;
3453  }
3454 
3458  friend bool operator <( const const_roi_filter_sample_iterator& i, const sample* j ) noexcept
3459  {
3460  return i.m_iterator < j;
3461  }
3462 
3466  friend bool operator <( const sample* i, const const_roi_filter_sample_iterator& j ) noexcept
3467  {
3468  return i < j.m_iterator;
3469  }
3470  };
3471 
3472  // -------------------------------------------------------------------------
3473 
3484  {
3485  public:
3486 
3491 
3496 
3500  using sample = typename image_type::sample;
3501 
3503 
3507  pixel_iterator() = default;
3508 
3513  : m_image( &image )
3514  {
3515  m_image->EnsureUnique();
3516  if ( !m_image->IsEmpty() )
3517  {
3518  m_iterator = iterator_type( m_image->NumberOfChannels() );
3519  for ( int i = 0; i < m_iterator.Length(); ++i )
3520  m_iterator[i] = (*m_image)[i];
3521  m_end = m_iterator[0] + m_image->NumberOfPixels();
3522  }
3523  }
3524 
3528  pixel_iterator( const pixel_iterator& ) = default;
3529 
3533  pixel_iterator& operator =( const pixel_iterator& ) = default;
3534 
3539  bool IsValid() const noexcept
3540  {
3541  return m_image != nullptr && !m_iterator.IsEmpty();
3542  }
3543 
3547  image_type& Image() const noexcept
3548  {
3549  return *m_image;
3550  }
3551 
3556  sample* Position( int channel ) const noexcept
3557  {
3558  return m_iterator[channel];
3559  }
3560 
3566  operator bool() const noexcept
3567  {
3568  return m_iterator[0] < m_end;
3569  }
3570 
3575  sample& operator []( int channel ) const noexcept
3576  {
3577  return *m_iterator[channel];
3578  }
3579 
3584  pixel_iterator& operator ++() noexcept
3585  {
3586  for ( int i = 0; i < m_iterator.Length(); ++i )
3587  ++m_iterator[i];
3588  return *this;
3589  }
3590 
3596  pixel_iterator operator ++( int ) noexcept
3597  {
3598  pixel_iterator i0( *this );
3599  for ( int i = 0; i < m_iterator.Length(); ++i )
3600  ++m_iterator[i];
3601  return i0;
3602  }
3603 
3608  pixel_iterator& operator --() noexcept
3609  {
3610  for ( int i = 0; i < m_iterator.Length(); ++i )
3611  --m_iterator[i];
3612  return *this;
3613  }
3614 
3620  pixel_iterator operator --( int ) noexcept
3621  {
3622  pixel_iterator i0( *this );
3623  for ( int i = 0; i < m_iterator.Length(); ++i )
3624  --m_iterator[i];
3625  return i0;
3626  }
3627 
3635  pixel_iterator& operator +=( distance_type delta ) noexcept
3636  {
3637  for ( int i = 0; i < m_iterator.Length(); ++i )
3638  m_iterator[i] += delta;
3639  return *this;
3640  }
3641 
3649  pixel_iterator& operator -=( distance_type delta ) noexcept
3650  {
3651  for ( int i = 0; i < m_iterator.Length(); ++i )
3652  m_iterator[i] -= delta;
3653  return *this;
3654  }
3655 
3663  pixel_iterator& MoveBy( int dx, int dy ) noexcept
3664  {
3665  return operator +=( distance_type( dy )*m_image->Width() + distance_type( dx ) );
3666  }
3667 
3672  friend pixel_iterator operator +( const pixel_iterator& i, distance_type delta ) noexcept
3673  {
3674  pixel_iterator j( i );
3675  j += delta;
3676  return j;
3677  }
3678 
3683  friend pixel_iterator operator +( distance_type delta, const pixel_iterator& i ) noexcept
3684  {
3685  pixel_iterator j( i );
3686  j += delta;
3687  return j;
3688  }
3689 
3694  friend pixel_iterator operator -( const pixel_iterator& i, distance_type delta ) noexcept
3695  {
3696  pixel_iterator j( i );
3697  j -= delta;
3698  return j;
3699  }
3700 
3705  friend distance_type operator -( const pixel_iterator& i, const pixel_iterator& j ) noexcept
3706  {
3707  return i.m_iterator[0] - j.m_iterator[0];
3708  }
3709 
3714  friend bool operator ==( const pixel_iterator& i, const pixel_iterator& j ) noexcept
3715  {
3716  return i.m_iterator[0] == j.m_iterator[0];
3717  }
3718 
3723  friend bool operator <( const pixel_iterator& i, const pixel_iterator& j ) noexcept
3724  {
3725  return i.m_iterator[0] < j.m_iterator[0];
3726  }
3727 
3728  protected:
3729 
3730  image_type* m_image = nullptr;
3731  iterator_type m_iterator;
3732  const sample* __restrict__ m_end = nullptr;
3733  };
3734 
3735  // -------------------------------------------------------------------------
3736 
3747  {
3748  public:
3749 
3754 
3759 
3763  using sample = typename image_type::sample;
3764 
3766 
3771 
3776  : m_image( &image )
3777  {
3778  if ( !m_image->IsEmpty() )
3779  {
3780  m_iterator = iterator_type( m_image->NumberOfChannels() );
3781  for ( int i = 0; i < m_iterator.Length(); ++i )
3782  m_iterator[i] = (*m_image)[i];
3783  m_end = m_iterator[0] + m_image->NumberOfPixels();
3784  }
3785  }
3786 
3791 
3795  const_pixel_iterator& operator =( const const_pixel_iterator& ) = default;
3796 
3801  bool IsValid() const noexcept
3802  {
3803  return m_image != nullptr && !m_iterator.IsEmpty();
3804  }
3805 
3810  const image_type& Image() const noexcept
3811  {
3812  return *m_image;
3813  }
3814 
3819  const sample* Position( int channel ) const noexcept
3820  {
3821  return m_iterator[channel];
3822  }
3823 
3829  operator bool() const noexcept
3830  {
3831  return m_iterator[0] < m_end;
3832  }
3833 
3838  const sample& operator []( int channel ) const noexcept
3839  {
3840  return *m_iterator[channel];
3841  }
3842 
3847  const_pixel_iterator& operator ++() noexcept
3848  {
3849  for ( int i = 0; i < m_iterator.Length(); ++i )
3850  ++m_iterator[i];
3851  return *this;
3852  }
3853 
3859  const_pixel_iterator operator ++( int ) noexcept
3860  {
3861  const_pixel_iterator i0( *this );
3862  for ( int i = 0; i < m_iterator.Length(); ++i )
3863  ++m_iterator[i];
3864  return i0;
3865  }
3866 
3871  const_pixel_iterator& operator --() noexcept
3872  {
3873  for ( int i = 0; i < m_iterator.Length(); ++i )
3874  --m_iterator[i];
3875  return *this;
3876  }
3877 
3883  const_pixel_iterator operator --( int ) noexcept
3884  {
3885  const_pixel_iterator i0( *this );
3886  for ( int i = 0; i < m_iterator.Length(); ++i )
3887  --m_iterator[i];
3888  return i0;
3889  }
3890 
3898  const_pixel_iterator& operator +=( distance_type delta ) noexcept
3899  {
3900  for ( int i = 0; i < m_iterator.Length(); ++i )
3901  m_iterator[i] += delta;
3902  return *this;
3903  }
3904 
3912  const_pixel_iterator& operator -=( distance_type delta ) noexcept
3913  {
3914  for ( int i = 0; i < m_iterator.Length(); ++i )
3915  m_iterator[i] -= delta;
3916  return *this;
3917  }
3918 
3926  const_pixel_iterator& MoveBy( int dx, int dy ) noexcept
3927  {
3928  return operator +=( distance_type( dy )*m_image->Width() + distance_type( dx ) );
3929  }
3930 
3936  {
3937  const_pixel_iterator j( i );
3938  j += delta;
3939  return j;
3940  }
3941 
3947  {
3948  const_pixel_iterator j( i );
3949  j += delta;
3950  return j;
3951  }
3952 
3958  {
3959  const_pixel_iterator j( i );
3960  j -= delta;
3961  return j;
3962  }
3963 
3969  {
3970  return i.m_iterator[0] - j.m_iterator[0];
3971  }
3972 
3977  friend bool operator ==( const const_pixel_iterator& i, const const_pixel_iterator& j ) noexcept
3978  {
3979  return i.m_iterator[0] == j.m_iterator[0];
3980  }
3981 
3986  friend bool operator <( const const_pixel_iterator& i, const const_pixel_iterator& j ) noexcept
3987  {
3988  return i.m_iterator[0] < j.m_iterator[0];
3989  }
3990 
3991  protected:
3992 
3993  const image_type* m_image = nullptr;
3994  iterator_type m_iterator;
3995  const sample* __restrict__ m_end = nullptr;
3996  };
3997 
3998  // -------------------------------------------------------------------------
3999 
4000  template <class image_type, class sample_pointer>
4001  class roi_pixel_iterator_base
4002  {
4003  protected:
4004 
4005  using iterator_type = GenericVector<sample_pointer>;
4006 
4007  image_type* m_image = nullptr;
4008  iterator_type m_iterator;
4009  sample_pointer m_rowBegin = nullptr;
4010  sample_pointer m_rowEnd = nullptr;
4011  sample_pointer m_end = nullptr;
4012 
4013  roi_pixel_iterator_base() = default;
4014 
4015  roi_pixel_iterator_base( image_type& image, const Rect& rect )
4016  : m_image( &image )
4017  {
4018  Rect r = rect;
4019  if ( m_image->ParseRect( r ) )
4020  {
4021  m_iterator = iterator_type( m_image->NumberOfChannels() );
4022  for ( int i = 0; i < m_iterator.Length(); ++i )
4023  m_iterator[i] = m_image->PixelAddress( r.x0, r.y0, i );
4024  m_rowBegin = m_iterator[0];
4025  m_rowEnd = m_rowBegin + r.Width();
4026  m_end = m_rowEnd + ((r.Height() - 1)*m_image->Width());
4027  }
4028  }
4029 
4030  roi_pixel_iterator_base( const roi_pixel_iterator_base& ) = default;
4031 
4032  roi_pixel_iterator_base& operator =( const roi_pixel_iterator_base& ) = default;
4033 
4034  void Increment() noexcept
4035  {
4036  for ( int i = 0; i < m_iterator.Length(); ++i )
4037  ++m_iterator[i];
4038  if ( m_iterator[0] == m_rowEnd )
4039  {
4040  int w = m_rowEnd - m_rowBegin;
4041  for ( int i = 0; i < m_iterator.Length(); ++i )
4042  m_iterator[i] += m_image->Width() - w;
4043  m_rowBegin += m_image->Width();
4044  m_rowEnd += m_image->Width();
4045  }
4046  }
4047 
4048  void Decrement() noexcept
4049  {
4050  if ( m_iterator[0] == m_rowBegin )
4051  {
4052  int w = m_rowEnd - m_rowBegin;
4053  for ( int i = 0; i < m_iterator.Length(); ++i )
4054  m_iterator[i] -= m_image->Width() - w;
4055  m_rowBegin -= m_image->Width();
4056  m_rowEnd -= m_image->Width();
4057  }
4058  for ( int i = 0; i < m_iterator.Length(); ++i )
4059  --m_iterator[i];
4060  }
4061 
4062  void MoveBy( int cols, int rows ) noexcept
4063  {
4064  int dx = m_iterator[0] - m_rowBegin;
4065  for ( int i = 0; i < m_iterator.Length(); ++i )
4066  m_iterator[i] -= dx;
4067  cols += dx;
4068  if ( cols != 0 )
4069  {
4070  int w = m_rowEnd - m_rowBegin;
4071  if ( pcl::Abs( cols ) >= w )
4072  {
4073  rows += cols/w;
4074  cols %= w;
4075  }
4076  }
4077  int dy = rows * m_image->Width();
4078  for ( int i = 0; i < m_iterator.Length(); ++i )
4079  m_iterator[i] += dy + cols;
4080  m_rowBegin += dy;
4081  m_rowEnd += dy;
4082  }
4083  };
4084 
4085  // -------------------------------------------------------------------------
4086 
4096  class roi_pixel_iterator : private roi_pixel_iterator_base<GenericImage<P>, sample*>
4097  {
4098  public:
4099 
4104 
4109 
4113  using sample = typename image_type::sample;
4114 
4115  using iterator_base = roi_pixel_iterator_base<GenericImage<P>, sample*>;
4116 
4120  roi_pixel_iterator() = default;
4121 
4136  roi_pixel_iterator( image_type& image, const Rect& rect = Rect( 0 ) )
4137  : iterator_base( image.EnsureUnique(), rect )
4138  {
4139  }
4140 
4145 
4149  roi_pixel_iterator& operator =( const roi_pixel_iterator& ) = default;
4150 
4155  bool IsValid() const noexcept
4156  {
4157  return this->m_image != nullptr && !this->m_iterator.IsEmpty();
4158  }
4159 
4163  image_type& Image() const noexcept
4164  {
4165  return *this->m_image;
4166  }
4167 
4172  sample* Position( int channel ) const noexcept
4173  {
4174  return this->m_iterator[channel];
4175  }
4176 
4183  operator bool() const noexcept
4184  {
4185  return this->m_iterator[0] < this->m_end;
4186  }
4187 
4192  sample& operator []( int channel ) const noexcept
4193  {
4194  return *this->m_iterator[channel];
4195  }
4196 
4202  roi_pixel_iterator& operator ++() noexcept
4203  {
4204  this->Increment();
4205  return *this;
4206  }
4207 
4213  roi_pixel_iterator operator ++( int ) noexcept
4214  {
4215  roi_pixel_iterator i0( *this );
4216  this->Increment();
4217  return i0;
4218  }
4219 
4225  roi_pixel_iterator& operator --() noexcept
4226  {
4227  this->Decrement();
4228  return *this;
4229  }
4230 
4236  roi_pixel_iterator operator --( int ) noexcept
4237  {
4238  roi_pixel_iterator i0( *this );
4239  this->Decrement();
4240  return i0;
4241  }
4242 
4252  roi_pixel_iterator& operator +=( distance_type delta ) noexcept
4253  {
4254  int w = this->m_rowEnd - this->m_rowBegin;
4255  iterator_base::MoveBy( delta%w, delta/w );
4256  return *this;
4257  }
4258 
4268  roi_pixel_iterator& operator -=( distance_type delta ) noexcept
4269  {
4270  int w = this->m_rowEnd - this->m_rowBegin;
4271  iterator_base::MoveBy( -delta%w, -delta/w );
4272  return *this;
4273  }
4274 
4283  roi_pixel_iterator& MoveBy( int dx, int dy ) noexcept
4284  {
4285  iterator_base::MoveBy( dx, dy );
4286  return *this;
4287  }
4288 
4294  {
4295  roi_pixel_iterator j( i );
4296  j += delta;
4297  return j;
4298  }
4299 
4305  {
4306  roi_pixel_iterator j( i );
4307  j += delta;
4308  return j;
4309  }
4310 
4316  {
4317  roi_pixel_iterator j( i );
4318  j -= delta;
4319  return j;
4320  }
4321 
4326  friend bool operator ==( const roi_pixel_iterator& i, const roi_pixel_iterator& j ) noexcept
4327  {
4328  return i.m_iterator[0] == j.m_iterator[0];
4329  }
4330 
4335  friend bool operator <( const roi_pixel_iterator& i, const roi_pixel_iterator& j ) noexcept
4336  {
4337  return i.m_iterator[0] < j.m_iterator[0];
4338  }
4339  };
4340 
4341  // -------------------------------------------------------------------------
4342 
4352  class const_roi_pixel_iterator : private roi_pixel_iterator_base<const GenericImage<P>, const sample*>
4353  {
4354  public:
4355 
4360 
4365 
4369  using sample = typename image_type::sample;
4370 
4371  using iterator_base = roi_pixel_iterator_base<const GenericImage<P>, const sample*>;
4372 
4377 
4392  const_roi_pixel_iterator( const image_type& image, const Rect& rect = Rect( 0 ) )
4393  : iterator_base( image, rect )
4394  {
4395  }
4396 
4401 
4405  const_roi_pixel_iterator& operator =( const const_roi_pixel_iterator& ) = default;
4406 
4411  bool IsValid() const noexcept
4412  {
4413  return this->m_image != nullptr && !this->m_iterator.IsEmpty();
4414  }
4415 
4420  const image_type& Image() const noexcept
4421  {
4422  return *this->m_image;
4423  }
4424 
4429  const sample* Position( int channel ) const noexcept
4430  {
4431  return this->m_iterator[channel];
4432  }
4433 
4440  operator bool() const noexcept
4441  {
4442  return this->m_iterator[0] < this->m_end;
4443  }
4444 
4449  const sample& operator []( int channel ) const noexcept
4450  {
4451  return *this->m_iterator[channel];
4452  }
4453 
4459  const_roi_pixel_iterator& operator ++() noexcept
4460  {
4461  this->Increment();
4462  return *this;
4463  }
4464 
4470  const_roi_pixel_iterator operator ++( int ) noexcept
4471  {
4472  const_roi_pixel_iterator i0( *this );
4473  this->Increment();
4474  return i0;
4475  }
4476 
4482  const_roi_pixel_iterator& operator --() noexcept
4483  {
4484  this->Decrement();
4485  return *this;
4486  }
4487 
4493  const_roi_pixel_iterator operator --( int ) noexcept
4494  {
4495  const_roi_pixel_iterator i0( *this );
4496  this->Decrement();
4497  return i0;
4498  }
4499 
4509  const_roi_pixel_iterator& operator +=( distance_type delta ) noexcept
4510  {
4511  int w = this->m_rowEnd - this->m_rowBegin;
4512  iterator_base::MoveBy( delta%w, delta/w );
4513  return *this;
4514  }
4515 
4525  const_roi_pixel_iterator& operator -=( distance_type delta ) noexcept
4526  {
4527  int w = this->m_rowEnd - this->m_rowBegin;
4528  iterator_base::MoveBy( -delta%w, -delta/w );
4529  return *this;
4530  }
4531 
4540  const_roi_pixel_iterator& MoveBy( int dx, int dy ) noexcept
4541  {
4542  iterator_base::MoveBy( dx, dy );
4543  return *this;
4544  }
4545 
4551  {
4552  const_roi_pixel_iterator j( i );
4553  j += delta;
4554  return j;
4555  }
4556 
4562  {
4563  const_roi_pixel_iterator j( i );
4564  j += delta;
4565  return j;
4566  }
4567 
4573  {
4574  const_roi_pixel_iterator j( i );
4575  j -= delta;
4576  return j;
4577  }
4578 
4583  friend bool operator ==( const const_roi_pixel_iterator& i, const const_roi_pixel_iterator& j ) noexcept
4584  {
4585  return i.m_iterator[0] == j.m_iterator[0];
4586  }
4587 
4592  friend bool operator <( const const_roi_pixel_iterator& i, const const_roi_pixel_iterator& j ) noexcept
4593  {
4594  return i.m_iterator[0] < j.m_iterator[0];
4595  }
4596  };
4597 
4598  // -------------------------------------------------------------------------
4599 
4600  template <class image_type, class iterator_base, class sample_pointer, class filter_type>
4601  class filter_pixel_iterator_base : public iterator_base
4602  {
4603  protected:
4604 
4605  filter_type m_filter;
4606  sample_pointer m_begin = nullptr;
4607 
4608  filter_pixel_iterator_base() = default;
4609 
4610  filter_pixel_iterator_base( image_type& image, const filter_type& filter )
4611  : iterator_base( image )
4612  , m_filter( filter )
4613  , m_begin( iterator_base::m_iterator )
4614  {
4615  JumpToNextValidSample();
4616  }
4617 
4618  filter_pixel_iterator_base( const iterator_base& i, const filter_type& filter )
4619  : iterator_base( i )
4620  , m_filter( filter )
4621  , m_begin( iterator_base::m_iterator )
4622  {
4623  JumpToNextValidSample();
4624  }
4625 
4626  filter_pixel_iterator_base( const filter_pixel_iterator_base& ) = default;
4627 
4628  filter_pixel_iterator_base& operator =( const filter_pixel_iterator_base& ) = default;
4629 
4630  filter_pixel_iterator_base& operator =( const iterator_base& i ) noexcept
4631  {
4632  (void)iterator_base::operator =( i );
4633  JumpToNextValidSample();
4634  }
4635 
4636  void JumpToNextValidSample() noexcept
4637  {
4638  while ( this->m_iterator[0] < this->m_end && !this->m_filter( this->m_iterator ) )
4639  for ( int i = 0; i < this->m_iterator.Length(); ++i )
4640  ++this->m_iterator[i];
4641  }
4642 
4643  void JumpToPrevValidSample() noexcept
4644  {
4645  while ( this->m_iterator[0] > this->m_begin && !this->m_filter( this->m_iterator ) )
4646  for ( int i = 0; i < this->m_iterator.Length(); ++i )
4647  --this->m_iterator[i];
4648  }
4649  };
4650 
4651  // -------------------------------------------------------------------------
4652 
4688  template <class F>
4690  public filter_pixel_iterator_base<GenericImage<P>, pixel_iterator, sample*, F>
4691  {
4692  public:
4693 
4698 
4703 
4707  using sample = typename image_type::sample;
4708 
4713  using filter_type = F;
4714 
4715  using iterator_base = filter_pixel_iterator_base<GenericImage<P>, pixel_iterator, sample*, F>;
4716 
4721 
4730  filter_pixel_iterator( image_type& image, const F& filter )
4731  : iterator_base( image.EnsureUnique(), filter )
4732  {
4733  }
4734 
4739  filter_pixel_iterator( const pixel_iterator& i, const F& filter )
4740  : iterator_base( i, filter )
4741  {
4742  }
4743 
4748 
4752  filter_pixel_iterator& operator =( const filter_pixel_iterator& ) = default;
4753 
4758  filter_pixel_iterator& operator =( const pixel_iterator& i ) noexcept
4759  {
4760  (void)iterator_base::operator =( i );
4761  return *this;
4762  }
4763 
4768  bool IsValid() const noexcept
4769  {
4770  return this->m_image != nullptr && !this->m_iterator.IsEmpty();
4771  }
4772 
4776  image_type& Image() const noexcept
4777  {
4778  return *this->m_image;
4779  }
4780 
4785  const filter_type& Filter() const noexcept
4786  {
4787  return this->m_filter;
4788  }
4789 
4794  filter_type& Filter() noexcept
4795  {
4796  return this->m_filter;
4797  }
4798 
4803  sample* Position( int channel ) const noexcept
4804  {
4805  return this->m_iterator[channel];
4806  }
4807 
4813  operator bool() const noexcept
4814  {
4815  return this->m_iterator[0] < this->m_end;
4816  }
4817 
4822  sample& operator []( int channel ) const noexcept
4823  {
4824  return *this->m_iterator[channel];
4825  }
4826 
4832  filter_pixel_iterator& operator ++() noexcept
4833  {
4834  for ( int i = 0; i < this->m_iterator.Length(); ++i )
4835  ++this->m_iterator[i];
4836  this->JumpToNextValidSample();
4837  return *this;
4838  }
4839 
4845  filter_pixel_iterator operator ++( int ) noexcept
4846  {
4847  filter_pixel_iterator i0( *this );
4848  for ( int i = 0; i < this->m_iterator.Length(); ++i )
4849  ++this->m_iterator[i];
4850  this->JumpToNextValidSample();
4851  return i0;
4852  }
4853 
4859  filter_pixel_iterator& operator --() noexcept
4860  {
4861  for ( int i = 0; i < this->m_iterator.Length(); ++i )
4862  --this->m_iterator[i];
4863  this->JumpToPrevValidSample();
4864  return *this;
4865  }
4866 
4872  filter_pixel_iterator operator --( int ) noexcept
4873  {
4874  filter_pixel_iterator i0( *this );
4875  for ( int i = 0; i < this->m_iterator.Length(); ++i )
4876  --this->m_iterator[i];
4877  this->JumpToPrevValidSample();
4878  return i0;
4879  }
4880 
4888  filter_pixel_iterator& operator +=( distance_type delta ) noexcept
4889  {
4890  for ( int i = 0; i < this->m_iterator.Length(); ++i )
4891  this->m_iterator[i] += delta;
4892  this->JumpToNextValidSample();
4893  return *this;
4894  }
4895 
4903  filter_pixel_iterator& operator -=( distance_type delta ) noexcept
4904  {
4905  for ( int i = 0; i < this->m_iterator.Length(); ++i )
4906  this->m_iterator[i] -= delta;
4907  this->JumpToPrevValidSample();
4908  return *this;
4909  }
4910 
4918  filter_pixel_iterator& MoveBy( int dx, int dy ) noexcept
4919  {
4920  distance_type d = distance_type( dy )*this->m_image->Width() + distance_type( dx );
4921  for ( int i = 0; i < this->m_iterator.Length(); ++i )
4922  this->m_iterator[i] += d;
4923  if ( d >= 0 )
4924  this->JumpToNextValidSample();
4925  else
4926  this->JumpToPrevValidSample();
4927  return *this;
4928  }
4929 
4935  {
4936  filter_pixel_iterator j( i );
4937  j += delta;
4938  return j;
4939  }
4940 
4946  {
4947  filter_pixel_iterator j( i );
4948  j += delta;
4949  return j;
4950  }
4951 
4957  {
4958  filter_pixel_iterator j( i );
4959  j -= delta;
4960  return j;
4961  }
4962 
4967  friend bool operator ==( const filter_pixel_iterator& i, const filter_pixel_iterator& j ) noexcept
4968  {
4969  return i.m_iterator[0] == j.m_iterator[0];
4970  }
4971 
4976  friend bool operator <( const filter_pixel_iterator& i, const filter_pixel_iterator& j ) noexcept
4977  {
4978  return i.m_iterator[0] < j.m_iterator[0];
4979  }
4980  };
4981 
4982  // -------------------------------------------------------------------------
4983 
5019  template <class F>
5021  public filter_pixel_iterator_base<const GenericImage<P>, const_pixel_iterator, const sample*, F>
5022  {
5023  public:
5024 
5029 
5034 
5038  using sample = typename image_type::sample;
5039 
5044  using filter_type = F;
5045 
5046  using iterator_base = filter_pixel_iterator_base<const GenericImage<P>, const_pixel_iterator, const sample*, F>;
5047 
5052 
5061  const_filter_pixel_iterator( const image_type& image, const F& filter )
5062  : iterator_base( image, filter )
5063  {
5064  }
5065 
5071  : iterator_base( i, filter )
5072  {
5073  }
5074 
5079 
5084 
5089  const_filter_pixel_iterator& operator =( const const_pixel_iterator& i ) noexcept
5090  {
5091  (void)iterator_base::operator =( i );
5092  return *this;
5093  }
5094 
5099  bool IsValid() const noexcept
5100  {
5101  return this->m_image != nullptr && !this->m_iterator.IsEmpty();
5102  }
5103 
5108  const image_type& Image() const noexcept
5109  {
5110  return *this->m_image;
5111  }
5112 
5117  const filter_type& Filter() const noexcept
5118  {
5119  return this->m_filter;
5120  }
5121 
5126  filter_type& Filter() noexcept
5127  {
5128  return this->m_filter;
5129  }
5130 
5135  const sample* Position( int channel ) const noexcept
5136  {
5137  return this->m_iterator[channel];
5138  }
5139 
5145  operator bool() const noexcept
5146  {
5147  return this->m_iterator[0] < this->m_end;
5148  }
5149 
5154  const sample& operator []( int channel ) const noexcept
5155  {
5156  return *this->m_iterator[channel];
5157  }
5158 
5164  const_filter_pixel_iterator& operator ++() noexcept
5165  {
5166  for ( int i = 0; i < this->m_iterator.Length(); ++i )
5167  ++this->m_iterator[i];
5168  this->JumpToNextValidSample();
5169  return *this;
5170  }
5171 
5177  const_filter_pixel_iterator operator ++( int ) noexcept
5178  {
5179  const_filter_pixel_iterator i0( *this );
5180  for ( int i = 0; i < this->m_iterator.Length(); ++i )
5181  ++this->m_iterator[i];
5182  this->JumpToNextValidSample();
5183  return i0;
5184  }
5185 
5191  const_filter_pixel_iterator& operator --() noexcept
5192  {
5193  for ( int i = 0; i < this->m_iterator.Length(); ++i )
5194  --this->m_iterator[i];
5195  this->JumpToPrevValidSample();
5196  return *this;
5197  }
5198 
5204  const_filter_pixel_iterator operator --( int ) noexcept
5205  {
5206  const_filter_pixel_iterator i0( *this );
5207  for ( int i = 0; i < this->m_iterator.Length(); ++i )
5208  --this->m_iterator[i];
5209  this->JumpToPrevValidSample();
5210  return i0;
5211  }
5212 
5220  const_filter_pixel_iterator& operator +=( distance_type delta ) noexcept
5221  {
5222  for ( int i = 0; i < this->m_iterator.Length(); ++i )
5223  this->m_iterator[i] += delta;
5224  this->JumpToNextValidSample();
5225  return *this;
5226  }
5227 
5235  const_filter_pixel_iterator& operator -=( distance_type delta ) noexcept
5236  {
5237  for ( int i = 0; i < this->m_iterator.Length(); ++i )
5238  this->m_iterator[i] -= delta;
5239  this->JumpToPrevValidSample();
5240  return *this;
5241  }
5242 
5250  const_filter_pixel_iterator& MoveBy( int dx, int dy ) noexcept
5251  {
5252  distance_type d = distance_type( dy )*this->m_image->Width() + distance_type( dx );
5253  for ( int i = 0; i < this->m_iterator.Length(); ++i )
5254  this->m_iterator[i] += d;
5255  if ( d >= 0 )
5256  this->JumpToNextValidSample();
5257  else
5258  this->JumpToPrevValidSample();
5259  return *this;
5260  }
5261 
5267  {
5269  j += delta;
5270  return j;
5271  }
5272 
5278  {
5280  j += delta;
5281  return j;
5282  }
5283 
5289  {
5291  j -= delta;
5292  return j;
5293  }
5294 
5299  friend bool operator ==( const const_filter_pixel_iterator& i, const const_filter_pixel_iterator& j ) noexcept
5300  {
5301  return i.m_iterator[0] == j.m_iterator[0];
5302  }
5303 
5308  friend bool operator <( const const_filter_pixel_iterator& i, const const_filter_pixel_iterator& j ) noexcept
5309  {
5310  return i.m_iterator[0] < j.m_iterator[0];
5311  }
5312  };
5313 
5314  // -------------------------------------------------------------------------
5315 
5316  template <class image_type, class sample_pointer, class filter_type>
5317  class roi_filter_pixel_iterator_base : public roi_pixel_iterator_base<image_type, sample_pointer>
5318  {
5319  protected:
5320 
5321  using roi_iterator_base = roi_pixel_iterator_base<image_type, sample_pointer>;
5322 
5323  filter_type m_filter;
5324  sample_pointer m_begin = nullptr;
5325 
5326  roi_filter_pixel_iterator_base() = default;
5327 
5328  roi_filter_pixel_iterator_base( image_type& image, const filter_type& filter, const Rect& rect )
5329  : roi_iterator_base( image, rect )
5330  , m_filter( filter )
5331  , m_begin( roi_iterator_base::m_iterator )
5332  {
5333  JumpToNextValidSample();
5334  }
5335 
5336  roi_filter_pixel_iterator_base( const roi_iterator_base& i, const filter_type& filter )
5337  : roi_iterator_base( i )
5338  , m_filter( filter )
5339  , m_begin( roi_iterator_base::m_iterator )
5340  {
5341  JumpToNextValidSample();
5342  }
5343 
5344  roi_filter_pixel_iterator_base( const roi_filter_pixel_iterator_base& ) = default;
5345 
5346  roi_filter_pixel_iterator_base& operator =( const roi_filter_pixel_iterator_base& ) = default;
5347 
5348  roi_filter_pixel_iterator_base& operator =( const roi_iterator_base& i ) noexcept
5349  {
5350  (void)roi_iterator_base::operator =( i );
5351  JumpToNextValidSample();
5352  return *this;
5353  }
5354 
5355  void JumpToNextValidSample() noexcept
5356  {
5357  while ( this->m_iterator[0] < this->m_end && !this->m_filter( this->m_iterator ) )
5358  roi_iterator_base::Increment();
5359  }
5360 
5361  void JumpToPrevValidSample() noexcept
5362  {
5363  while ( this->m_iterator[0] > this->m_begin && !this->m_filter( this->m_iterator ) )
5364  roi_iterator_base::Decrement();
5365  }
5366  };
5367 
5368  // -------------------------------------------------------------------------
5369 
5380  template <class F>
5381  class roi_filter_pixel_iterator : public roi_filter_pixel_iterator_base<GenericImage<P>, sample*, F>
5382  {
5383  public:
5384 
5389 
5394 
5398  using sample = typename image_type::sample;
5399 
5404  using filter_type = F;
5405 
5406  using iterator_base = roi_filter_pixel_iterator_base<GenericImage<P>, sample*, F>;
5407 
5412 
5430  roi_filter_pixel_iterator( image_type& image, const F& filter, const Rect& rect = Rect( 0 ) )
5431  : iterator_base( image.EnsureUnique(), filter, rect )
5432  {
5433  }
5434 
5439  roi_filter_pixel_iterator( const roi_pixel_iterator& i, const F& filter )
5440  : iterator_base( i, filter )
5441  {
5442  }
5443 
5448 
5452  roi_filter_pixel_iterator& operator =( const roi_filter_pixel_iterator& ) = default;
5453 
5457  roi_filter_pixel_iterator& operator =( const roi_pixel_iterator& i ) noexcept
5458  {
5459  (void)iterator_base::operator =( i );
5460  return *this;
5461  }
5462 
5467  bool IsValid() const noexcept
5468  {
5469  return this->m_image != nullptr && !this->m_iterator.IsEmpty();
5470  }
5471 
5475  image_type& Image() const noexcept
5476  {
5477  return *this->m_image;
5478  }
5479 
5484  const filter_type& Filter() const noexcept
5485  {
5486  return this->m_filter;
5487  }
5488 
5493  filter_type& Filter() noexcept
5494  {
5495  return this->m_filter;
5496  }
5497 
5502  sample* Position( int channel ) const noexcept
5503  {
5504  return this->m_iterator[channel];
5505  }
5506 
5513  operator bool() const noexcept
5514  {
5515  return this->m_iterator[0] < this->m_end;
5516  }
5517 
5522  sample& operator []( int channel ) const noexcept
5523  {
5524  return *this->m_iterator[channel];
5525  }
5526 
5532  roi_filter_pixel_iterator& operator ++() noexcept
5533  {
5534  this->Increment();
5535  this->JumpToNextValidSample();
5536  return *this;
5537  }
5538 
5544  roi_filter_pixel_iterator operator ++( int ) noexcept
5545  {
5546  roi_filter_pixel_iterator i0( *this );
5547  this->Increment();
5548  this->JumpToNextValidSample();
5549  return i0;
5550  }
5551 
5557  roi_filter_pixel_iterator& operator --() noexcept
5558  {
5559  this->Decrement();
5560  this->JumpToPrevValidSample();
5561  return *this;
5562  }
5563 
5569  roi_filter_pixel_iterator operator --( int ) noexcept
5570  {
5571  roi_filter_pixel_iterator i0( *this );
5572  this->Decrement();
5573  this->JumpToPrevValidSample();
5574  return i0;
5575  }
5576 
5586  roi_filter_pixel_iterator& operator +=( distance_type delta ) noexcept
5587  {
5588  int w = this->m_rowEnd - this->m_rowBegin;
5589  return MoveBy( delta%w, delta/w );
5590  }
5591 
5601  roi_filter_pixel_iterator& operator -=( distance_type delta ) noexcept
5602  {
5603  int w = this->m_rowEnd - this->m_rowBegin;
5604  return MoveBy( -delta%w, -delta/w );
5605  }
5606 
5615  roi_filter_pixel_iterator& MoveBy( int dx, int dy ) noexcept
5616  {
5617  sample* __restrict__ i0 = this->m_iterator[0];
5618  iterator_base::MoveBy( dx, dy );
5619  if ( this->m_iterator[0] >= i0 )
5620  this->JumpToNextValidSample();
5621  else
5622  this->JumpToPrevValidSample();
5623  return *this;
5624  }
5625 
5631  {
5633  j += delta;
5634  return j;
5635  }
5636 
5642  {
5644  j += delta;
5645  return j;
5646  }
5647 
5653  {
5655  j -= delta;
5656  return j;
5657  }
5658 
5663  friend bool operator ==( const roi_filter_pixel_iterator& i, const roi_filter_pixel_iterator& j ) noexcept
5664  {
5665  return i.m_iterator[0] == j.m_iterator[0];
5666  }
5667 
5672  friend bool operator <( const roi_filter_pixel_iterator& i, const roi_filter_pixel_iterator& j ) noexcept
5673  {
5674  return i.m_iterator[0] < j.m_iterator[0];
5675  }
5676  };
5677 
5678  // -------------------------------------------------------------------------
5679 
5690  template <class F>
5691  class const_roi_filter_pixel_iterator : public roi_filter_pixel_iterator_base<const GenericImage<P>, const sample*, F>
5692  {
5693  public:
5694 
5699 
5704 
5708  using sample = typename image_type::sample;
5709 
5714  using filter_type = F;
5715 
5716  using iterator_base = roi_filter_pixel_iterator_base<const GenericImage<P>, const sample*, F>;
5717 
5722 
5741  const_roi_filter_pixel_iterator( const image_type& image, const F& filter, const Rect& rect = Rect( 0 ) )
5742  : iterator_base( image, filter, rect )
5743  {
5744  }
5745 
5751  : iterator_base( i, filter )
5752  {
5753  }
5754 
5759 
5760 
5765 
5770  {
5771  (void)iterator_base::operator =( i );
5772  return *this;
5773  }
5774 
5779  bool IsValid() const noexcept
5780  {
5781  return this->m_image != nullptr && !this->m_iterator.IsEmpty();
5782  }
5783 
5788  const image_type& Image() const noexcept
5789  {
5790  return *this->m_image;
5791  }
5792 
5797  const filter_type& Filter() const noexcept
5798  {
5799  return this->m_filter;
5800  }
5801 
5806  filter_type& Filter() noexcept
5807  {
5808  return this->m_filter;
5809  }
5810 
5815  const sample* Position( int channel ) const noexcept
5816  {
5817  return this->m_iterator[channel];
5818  }
5819 
5826  operator bool() const noexcept
5827  {
5828  return this->m_iterator[0] < this->m_end;
5829  }
5830 
5835  const sample& operator []( int channel ) const noexcept
5836  {
5837  return *this->m_iterator[channel];
5838  }
5839 
5845  const_roi_filter_pixel_iterator& operator ++() noexcept
5846  {
5847  this->Increment();
5848  this->JumpToNextValidSample();
5849  return *this;
5850  }
5851 
5857  const_roi_filter_pixel_iterator operator ++( int ) noexcept
5858  {
5859  const_roi_filter_pixel_iterator i0( *this );
5860  this->Increment();
5861  this->JumpToNextValidSample();
5862  return i0;
5863  }
5864 
5870  const_roi_filter_pixel_iterator& operator --() noexcept
5871  {
5872  this->Decrement();
5873  this->JumpToPrevValidSample();
5874  return *this;
5875  }
5876 
5882  const_roi_filter_pixel_iterator operator --( int ) noexcept
5883  {
5884  const_roi_filter_pixel_iterator i0( *this );
5885  this->Decrement();
5886  this->JumpToPrevValidSample();
5887  return i0;
5888  }
5889 
5899  const_roi_filter_pixel_iterator& operator +=( distance_type delta ) noexcept
5900  {
5901  int w = this->m_rowEnd - this->m_rowBegin;
5902  return MoveBy( delta%w, delta/w );
5903  }
5904 
5914  const_roi_filter_pixel_iterator& operator -=( distance_type delta ) noexcept
5915  {
5916  int w = this->m_rowEnd - this->m_rowBegin;
5917  return MoveBy( -delta%w, -delta/w );
5918  }
5919 
5928  const_roi_filter_pixel_iterator& MoveBy( int dx, int dy ) noexcept
5929  {
5930  const sample* __restrict__ i0 = this->m_iterator[0];
5931  iterator_base::MoveBy( dx, dy );
5932  if ( this->m_iterator[0] >= i0 )
5933  this->JumpToNextValidSample();
5934  else
5935  this->JumpToPrevValidSample();
5936  return *this;
5937  }
5938 
5944  {
5946  j += delta;
5947  return j;
5948  }
5949 
5955  {
5957  j += delta;
5958  return j;
5959  }
5960 
5966  {
5968  j -= delta;
5969  return j;
5970  }
5971 
5977  {
5978  return i.m_iterator[0] == j.m_iterator[0];
5979  }
5980 
5986  {
5987  return i.m_iterator[0] < j.m_iterator[0];
5988  }
5989  };
5990 
5991  // -------------------------------------------------------------------------
5992 
5997  static bool IsFloatSample() noexcept
5998  {
5999  return pixel_traits::IsFloatSample();
6000  }
6001 
6006  static bool IsComplexSample() noexcept
6007  {
6008  return pixel_traits::IsComplexSample();
6009  }
6010 
6015  static int BytesPerSample() noexcept
6016  {
6017  return P::BytesPerSample();
6018  }
6019 
6024  static int BitsPerSample() noexcept
6025  {
6026  return P::BitsPerSample();
6027  }
6028 
6036  template <class P1>
6037  bool SameSampleType( const GenericImage<P1>& image ) const noexcept
6038  {
6039  return image.BitsPerSample() == BitsPerSample() &&
6040  image.IsFloatSample() == IsFloatSample() &&
6041  image.IsComplexSample() == IsComplexSample();
6042  }
6043 
6049  bool SameSampleType( const GenericImage& image ) const noexcept
6050  {
6051  return true;
6052  }
6053 
6054  // -------------------------------------------------------------------------
6055 
6061  {
6062  m_data = new Data( this );
6063  }
6064 
6094  GenericImage( const GenericImage& image )
6095  {
6096  if ( !image.IsShared() )
6097  if ( image.IsCompletelySelected() )
6098  {
6099  image.m_data->Attach( this );
6100  m_data = image.m_data;
6101  m_status = image.m_status;
6102  ResetSelections();
6103  return;
6104  }
6105 
6106  m_data = new Data( this );
6107  (void)Assign( image );
6108  }
6109 
6114  : AbstractImage( image )
6115  , m_data( image.m_data )
6116  {
6117  image.m_data = nullptr;
6118  }
6119 
6132  template <class P1>
6134  {
6135  m_data = new Data( this );
6136  (void)Assign( image );
6137  }
6138 
6179  template <class P1>
6180  GenericImage( const GenericImage<P1>& image, const Rect& rect, int firstChannel = -1, int lastChannel = -1 )
6181  {
6182  m_data = new Data( this );
6183  (void)Assign( image, rect, firstChannel, lastChannel );
6184  }
6185 
6204  GenericImage( int width, int height, color_space colorSpace = ColorSpace::Gray )
6205  {
6206  m_data = new Data( this );
6207  m_data->Allocate( width, height, ColorSpace::NumberOfNominalChannels( colorSpace ), colorSpace );
6208  ResetSelections();
6209  }
6210 
6225  explicit GenericImage( File& stream )
6226  {
6227  m_data = new Data( this );
6228  Read( stream );
6229  }
6230 
6250  explicit GenericImage( void* handle )
6251  {
6252  m_data = new Data( this, handle );
6253  ResetSelections();
6254  }
6255 
6283  GenericImage( void*, int width, int height, color_space colorSpace = ColorSpace::Gray )
6284  {
6285  m_data = new Data( this, width, height, ColorSpace::NumberOfNominalChannels( colorSpace ), colorSpace );
6286  ResetSelections();
6287  }
6288 
6306  ~GenericImage() override
6307  {
6308  if ( m_data != nullptr )
6309  {
6310  DetachFromData();
6311  m_data = nullptr;
6312  }
6313  }
6314 
6315  // -------------------------------------------------------------------------
6316 
6333  bool IsShared() const noexcept
6334  {
6335  return m_data->IsShared();
6336  }
6337 
6348  bool IsUnique() const noexcept
6349  {
6350  return m_data->IsUnique();
6351  }
6352 
6366  {
6367  if ( m_data->IsShared() )
6368  {
6369  GenericImage local_;
6370  GenericImage* local = &local_; // ### Workaround to GCC 7's 'dereferencing type-punned pointer' bugs
6371  local->m_data->Allocate( m_width, m_height, m_numberOfChannels, m_colorSpace );
6372  local->m_RGBWS = m_RGBWS;
6373  local->m_selected = m_selected;
6374  local->m_savedSelections = m_savedSelections;
6375  local->m_status = m_status;
6376  for ( int c = 0; c < m_numberOfChannels; ++c )
6377  P::Copy( (*local)[c], m_channelData( c ), NumberOfPixels() );
6378 
6379  local->m_data->Attach( this );
6380  DetachFromData();
6381  m_data = local->m_data;
6382  }
6383  return *this;
6384  }
6385 
6402  {
6403  if ( !m_data->IsUnique() )
6404  {
6405  Data* newData = m_data->Clone( this );
6406  DetachFromData();
6407  m_data = newData;
6408  }
6409  return *this;
6410  }
6411 
6434  pixel_allocator& Allocator() const noexcept
6435  {
6436  return m_allocator;
6437  }
6438 
6444  void Synchronize()
6445  {
6446  m_data->SynchronizeWithSharedImage();
6447  ResetSelections();
6448  }
6449 
6484  GenericImage& AllocateData( int width, int height,
6485  int numberOfChannels = 1,
6486  color_space colorSpace = ColorSpace::Gray )
6487  {
6488  if ( !m_data->IsUnique() )
6489  {
6490  Data* newData = new Data( this );
6491  DetachFromData();
6492  m_data = newData;
6493  }
6494  m_data->Allocate( width, height, numberOfChannels, colorSpace );
6495  ResetSelections();
6496  return *this;
6497  }
6498 
6510  int numberOfChannels = 1,
6511  color_space colorSpace = ColorSpace::Gray )
6512  {
6513  return AllocateData( rect.Width(), rect.Height(), numberOfChannels, colorSpace );
6514  }
6515 
6528  {
6529  if ( !m_data->IsEmpty() )
6530  if ( m_data->IsUnique() )
6531  m_data->Deallocate();
6532  else
6533  {
6534  Data* newData = new Data( this );
6535  DetachFromData();
6536  m_data = newData;
6537  }
6538  ResetSelections();
6539  return *this;
6540  }
6541 
6597  GenericImage& ImportData( sample** data, int width, int height,
6598  int numberOfChannels = 1, color_space colorSpace = ColorSpace::Gray )
6599  {
6600  if ( !m_data->IsUnique() )
6601  {
6602  if ( m_data->IsShared() )
6603  throw Error( "GenericImage::ImportData(): Invalid operation for an aliased shared image" );
6604  Data* newData = new Data( this );
6605  DetachFromData();
6606  m_data = newData;
6607  }
6608  m_data->Import( data, width, height, numberOfChannels, colorSpace );
6609  ResetSelections();
6610  return *this;
6611  }
6612 
6648  {
6649  if ( !m_data->IsUnique() )
6650  throw Error( "GenericImage::ReleaseData(): Invalid operation for an aliased image" );
6651  sample** data = m_data->Release();
6652  ResetSelections();
6653  return data;
6654  }
6655 
6656  // -------------------------------------------------------------------------
6657 
6662  size_type LineSize() const noexcept
6663  {
6664  return BytesPerSample() * size_type( m_width );
6665  }
6666 
6671  size_type ChannelSize() const noexcept
6672  {
6673  return BytesPerSample() * NumberOfPixels();
6674  }
6675 
6682  size_type ImageSize() const noexcept
6683  {
6684  return ChannelSize() * size_type( m_numberOfChannels );
6685  }
6686 
6692  size_type NominalSize() const noexcept
6693  {
6694  return ChannelSize() * NumberOfNominalChannels();
6695  }
6696 
6702  size_type AlphaSize() const noexcept
6703  {
6704  return ChannelSize() * NumberOfAlphaChannels();
6705  }
6706 
6721  sample* PixelData( int channel = 0 )
6722  {
6723  PCL_PRECONDITION( 0 <= channel && channel < m_numberOfChannels )
6724  EnsureUnique();
6725  return m_channelData( channel );
6726  }
6727 
6734  const sample* PixelData( int channel = 0 ) const noexcept
6735  {
6736  PCL_PRECONDITION( 0 <= channel && channel < m_numberOfChannels )
6737  return m_channelData( channel );
6738  }
6739 
6743  operator bool() const noexcept
6744  {
6745  return m_data != nullptr && !m_data->IsEmpty();
6746  }
6747 
6758  sample* operator []( int channel )
6759  {
6760  PCL_PRECONDITION( 0 <= channel && channel < m_numberOfChannels )
6761  return PixelData( channel );
6762  }
6763 
6770  const sample* operator []( int channel ) const noexcept
6771  {
6772  PCL_PRECONDITION( 0 <= channel && channel < m_numberOfChannels )
6773  return PixelData( channel );
6774  }
6775 
6784  {
6785  PCL_PRECONDITION( 0 < m_numberOfChannels )
6786  return PixelData( 0 );
6787  }
6788 
6795  const sample* operator *() const noexcept
6796  {
6797  PCL_PRECONDITION( 0 < m_numberOfChannels )
6798  return PixelData( 0 );
6799  }
6800 
6820  sample* ScanLine( int y, int channel = 0 )
6821  {
6822  PCL_PRECONDITION( 0 <= channel && channel < m_numberOfChannels )
6823  PCL_PRECONDITION( 0 <= y && y < m_height )
6824  EnsureUnique();
6825  return m_channelData( channel ) + RowOffset( y );
6826  }
6827 
6834  const sample* ScanLine( int y, int channel = 0 ) const noexcept
6835  {
6836  PCL_PRECONDITION( 0 <= channel && channel < m_numberOfChannels )
6837  PCL_PRECONDITION( 0 <= y && y < m_height )
6838  return m_channelData( channel ) + RowOffset( y );
6839  }
6840 
6864  sample* PixelAddress( int x, int y, int channel = 0 )
6865  {
6866  PCL_PRECONDITION( 0 <= channel && channel < m_numberOfChannels )
6867  PCL_PRECONDITION( 0 <= x && x < m_width )
6868  PCL_PRECONDITION( 0 <= y && y < m_height )
6869  EnsureUnique();
6870  return m_channelData( channel ) + PixelOffset( x, y );
6871  }
6872 
6879  const sample* PixelAddress( int x, int y, int channel = 0 ) const noexcept
6880  {
6881  PCL_PRECONDITION( 0 <= channel && channel < m_numberOfChannels )
6882  PCL_PRECONDITION( 0 <= x && x < m_width )
6883  PCL_PRECONDITION( 0 <= y && y < m_height )
6884  return m_channelData( channel ) + PixelOffset( x, y );
6885  }
6886 
6907  sample* PixelAddress( const Point& p, int channel = 0 )
6908  {
6909  return PixelAddress( p.x, p.y, channel );
6910  }
6911 
6919  const sample* PixelAddress( const Point& p, int channel = 0 ) const noexcept
6920  {
6921  return PixelAddress( p.x, p.y, channel );
6922  }
6923 
6944  sample& operator ()( int x, int y, int channel = 0 )
6945  {
6946  return *PixelAddress( x, y, channel );
6947  }
6948 
6965  sample operator ()( int x, int y, int channel = 0 ) const noexcept
6966  {
6967  return *PixelAddress( x, y, channel );
6968  }
6969 
6987  sample& operator ()( const Point& p, int channel = 0 )
6988  {
6989  return *PixelAddress( p, channel );
6990  }
6991 
7005  sample operator ()( const Point& p, int channel = 0 ) const noexcept
7006  {
7007  return *PixelAddress( p, channel );
7008  }
7009 
7017  sample& Pixel( int x, int y, int channel = 0 )
7018  {
7019  return operator()( x, y, channel );
7020  }
7021 
7029  sample Pixel( int x, int y, int channel = 0 ) const noexcept
7030  {
7031  return operator()( x, y, channel );
7032  }
7033 
7041  sample& Pixel( const Point& p, int channel = 0 )
7042  {
7043  return operator()( p, channel );
7044  }
7045 
7054  sample Pixel( const Point& p, int channel = 0 ) const noexcept
7055  {
7056  return operator()( p, channel );
7057  }
7058 
7059  // -------------------------------------------------------------------------
7060 
7068  void SetRGBWorkingSpace( const RGBColorSystem& RGBWS ) override
7069  {
7070  if ( !IsShared() )
7071  {
7072  EnsureUnique();
7073  m_RGBWS = RGBWS;
7074  }
7075  }
7076 
7077  // -------------------------------------------------------------------------
7078 
7149  template <class P1>
7151  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
7152  {
7153  m_status = image.Status();
7154 
7155  Rect r = rect;
7156  if ( !image.ParseSelection( r, firstChannel, lastChannel ) )
7157  {
7158  FreeData();
7159  return *this;
7160  }
7161 
7162  int n = 1 + lastChannel - firstChannel;
7163  AllocateData( r, n, (firstChannel == 0 && n >= ColorSpace::NumberOfNominalChannels( image.ColorSpace() )) ?
7164  image.ColorSpace() : ColorSpace::Gray );
7165 
7166  if ( !IsShared() ) // ### cannot modify a shared image's RGBWS
7167  m_RGBWS = image.RGBWorkingSpace();
7168 
7169  ResetSelections();
7170 
7171  if ( r == image.Bounds() )
7172  for ( int c = 0; firstChannel <= lastChannel; ++c, ++firstChannel )
7173  P::Copy( m_channelData( c ), image[firstChannel], NumberOfPixels() );
7174  else
7175  for ( int c = 0; firstChannel <= lastChannel; ++c, ++firstChannel )
7176  {
7177  sample* f = m_channelData( c );
7178  const typename P1::sample* g = image.PixelAddress( r.LeftTop(), firstChannel );
7179  for ( int y = 0; y < m_height; ++y, f += m_width, g += image.Width() )
7180  P::Copy( f, g, m_width );
7181  }
7182 
7183  return *this;
7184  }
7185 
7186  GenericImage& Assign( const GenericImage& image,
7187  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
7188  {
7189  m_status = image.Status();
7190 
7191  Rect r = rect;
7192  if ( !image.ParseSelection( r, firstChannel, lastChannel ) )
7193  {
7194  FreeData();
7195  return *this;
7196  }
7197 
7198 #define completeSelection (firstChannel == 0 && lastChannel == image.m_numberOfChannels-1 && r == image.Bounds())
7199 
7200  if ( m_data == image.m_data )
7201  {
7202  // Self-assignment
7203  if ( !completeSelection )
7204  {
7205  GenericImage result( image, r, firstChannel, lastChannel ); // ### implicit recursion
7206  result.m_data->Attach( this );
7207  DetachFromData();
7208  m_data = result.m_data;
7209  }
7210  ResetSelections();
7211  return *this;
7212  }
7213 
7214  if ( !IsShared() )
7215  if ( !image.IsShared() )
7216  if ( completeSelection )
7217  {
7218  image.m_data->Attach( this );
7219  DetachFromData();
7220  m_data = image.m_data;
7221  ResetSelections();
7222  return *this;
7223  }
7224 
7225 #undef completeSelection
7226 
7227  int n = 1 + lastChannel - firstChannel;
7228  AllocateData( r, n, (firstChannel == 0 && n >= ColorSpace::NumberOfNominalChannels( image.ColorSpace() )) ?
7229  image.ColorSpace() : ColorSpace::Gray );
7230 
7231  if ( !IsShared() ) // ### cannot modify a shared image's RGBWS
7232  m_RGBWS = image.m_RGBWS;
7233 
7234  ResetSelections();
7235 
7236  if ( r == image.Bounds() )
7237  for ( int c = 0; firstChannel <= lastChannel; ++c, ++firstChannel )
7238  P::Copy( m_channelData( c ), image[firstChannel], NumberOfPixels() );
7239  else
7240  for ( int c = 0; firstChannel <= lastChannel; ++c, ++firstChannel )
7241  {
7242  sample* f = m_channelData( c );
7243  const sample* g = image.PixelAddress( r.LeftTop(), firstChannel );
7244  for ( int y = 0; y < m_height; ++y, f += m_width, g += image.m_width )
7245  P::Copy( f, g, m_width );
7246  }
7247 
7248  return *this;
7249  }
7250 
7257  template <class P1>
7258  GenericImage& operator =( const GenericImage<P1>& image )
7259  {
7260  return Assign( image );
7261  }
7262 
7273  GenericImage& operator =( const GenericImage& image )
7274  {
7275  return Assign( image );
7276  }
7277 
7278 #define TRANSFER_BODY() \
7279  if ( m_data != image.m_data ) \
7280  { \
7281  DetachFromData(); \
7282  m_data = image.m_data; \
7283  (void)AbstractImage::operator =( image ); \
7284  image.m_data = nullptr; \
7285  } \
7286  return *this
7287 
7300  {
7301  TRANSFER_BODY();
7302  }
7303 
7316  {
7317  TRANSFER_BODY();
7318  }
7319 
7320 #undef TRANSFER_BODY
7321 
7327  GenericImage& operator =( GenericImage&& image )
7328  {
7329  return Transfer( image );
7330  }
7331 
7338  GenericImage& operator =( sample scalar )
7339  {
7340  return Fill( scalar );
7341  }
7342 
7346  friend void Swap( GenericImage& x1, GenericImage& x2 ) noexcept
7347  {
7348  x1.AbstractImage::Swap( x2 );
7349  pcl::Swap( x1.m_data, x2.m_data );
7350  }
7351 
7352  // -------------------------------------------------------------------------
7353 
7354 #ifndef __PCL_NO_VECTOR_IMAGE_CONVERSION
7355 
7374  sample_vector RowVector( int y, int channel = -1 ) const
7375  {
7376  if ( channel < 0 )
7377  channel = m_channel;
7378  if ( y < 0 || y >= m_height || channel < 0 || channel >= m_numberOfChannels )
7379  return sample_vector();
7380  sample_vector row( m_width );
7381  P::Get( row.Begin(), ScanLine( y, channel ), m_width );
7382  return row;
7383  }
7384 
7403  sample_vector ColumnVector( int x, int channel = -1 ) const
7404  {
7405  if ( channel < 0 )
7406  channel = m_channel;
7407  if ( x < 0 || x >= m_width || channel < 0 || channel >= m_numberOfChannels )
7408  return sample_vector();
7409  sample_vector col( m_height );
7410  const sample* v = PixelAddress( x, 0, channel );
7411  for ( int y = 0; y < m_height; ++y, v += m_width )
7412  col[y] = *v;
7413  return col;
7414  }
7415 
7419  sample_vector ColVector( int x, int channel = 0 ) const
7420  {
7421  return ColumnVector( x, channel );
7422  }
7423 
7424 #endif // !__PCL_NO_VECTOR_IMAGE_CONVERSION
7425 
7447  template <typename T>
7448  void GetRow( T* buffer, int y, int channel = -1 ) const
7449  {
7450  PCL_PRECONDITION( buffer != nullptr )
7451  if ( channel < 0 )
7452  channel = m_channel;
7453  if ( y >= 0 && y < m_height && channel >= 0 && channel < m_numberOfChannels )
7454  P::Get( buffer, ScanLine( y, channel ), m_width );
7455  }
7456 
7478  template <typename T>
7479  void GetColumn( T* buffer, int x, int channel = -1 ) const
7480  {
7481  PCL_PRECONDITION( buffer != nullptr )
7482  if ( channel < 0 )
7483  channel = m_channel;
7484  if ( x >= 0 && x < m_width && channel >= 0 && channel < m_numberOfChannels )
7485  {
7486  const sample* v = PixelAddress( x, 0, channel );
7487  for ( int y = 0; y < m_height; ++y, ++buffer, v += m_width )
7488  P::FromSample( *buffer, *v );
7489  }
7490  }
7491 
7513  template <typename T>
7514  GenericImage& SetRow( const T* buffer, int y, int channel = -1 )
7515  {
7516  PCL_PRECONDITION( buffer != nullptr )
7517  if ( channel < 0 )
7518  channel = m_channel;
7519  if ( y >= 0 && y < m_height && channel >= 0 && channel < m_numberOfChannels )
7520  P::Copy( ScanLine( y, channel ), buffer, m_width );
7521  return *this;
7522  }
7523 
7545  template <typename T>
7546  GenericImage& SetColumn( const T* buffer, int x, int channel = -1 )
7547  {
7548  PCL_PRECONDITION( buffer != nullptr )
7549  if ( channel < 0 )
7550  channel = m_channel;
7551  if ( x >= 0 && x < m_width && channel >= 0 && channel < m_numberOfChannels )
7552  {
7553  sample* v = PixelAddress( x, 0, channel );
7554  for ( int y = 0; y < m_height; ++y, ++buffer, v += m_width )
7555  *v = P::ToSample( *buffer );
7556  }
7557  return *this;
7558  }
7559 
7560  // -------------------------------------------------------------------------
7561 
7572  {
7573  if ( n > 0 && m_numberOfChannels > 0 )
7574  {
7575  EnsureUnique();
7576  sample** oldData = m_pixelData;
7577  sample** newData = nullptr;
7578  try
7579  {
7580  newData = m_allocator.AllocateChannelSlots( m_numberOfChannels+n );
7581  for ( int i = 0; i < m_numberOfChannels; ++i )
7582  newData[i] = oldData[i];
7583  for ( int i = 0; i < n; ++i )
7584  newData[m_numberOfChannels+i] = m_allocator.AllocatePixels( m_width, m_height );
7585  }
7586  catch ( ... )
7587  {
7588  if ( newData != nullptr )
7589  {
7590  for ( int i = 0; i < n; ++i )
7591  if ( newData[m_numberOfChannels+i] != nullptr )
7592  m_allocator.Deallocate( newData[m_numberOfChannels+i] );
7593  m_allocator.Deallocate( newData );
7594  }
7595  throw;
7596  }
7597 
7598  m_allocator.SetSharedData( m_pixelData = newData );
7599  m_allocator.SetSharedGeometry( m_width, m_height, m_numberOfChannels += n );
7600  m_allocator.Deallocate( oldData );
7601  }
7602 
7603  return *this;
7604  }
7605 
7621  {
7622  // $$$ WARNING $$$
7623  // * If this is a shared image, data must either be null or point to a
7624  // shared memory block.
7625  // * If this is a local image, data must either be null or point to a
7626  // block allocated in the local heap.
7627 
7628  if ( data == nullptr )
7629  CreateAlphaChannels( 1 );
7630  else if ( m_numberOfChannels > 0 )
7631  {
7632  EnsureUnique();
7633  sample** oldData = m_pixelData;
7634  sample** newData = nullptr;
7635  try
7636  {
7637  newData = m_allocator.AllocateChannelSlots( m_numberOfChannels+1 );
7638  for ( int i = 0; i < m_numberOfChannels; ++i )
7639  newData[i] = oldData[i];
7640  newData[m_numberOfChannels] = data;
7641  }
7642  catch ( ... )
7643  {
7644  if ( newData != nullptr )
7645  m_allocator.Deallocate( newData );
7646  throw;
7647  }
7648 
7649  m_allocator.SetSharedData( m_pixelData = newData );
7650  m_allocator.SetSharedGeometry( m_width, m_height, ++m_numberOfChannels );
7651  m_allocator.Deallocate( oldData );
7652  }
7653 
7654  return *this;
7655  }
7656 
7680  {
7681  if ( IsShared() != image.IsShared() )
7682  throw Error( "GenericImage::ReleaseAlphaChannel(): Cannot release pixel data between local and shared images" );
7683 
7684  if ( channel < 0 || channel >= NumberOfAlphaChannels() )
7685  {
7686  image.FreeData();
7687  return *this;
7688  }
7689 
7690  int c = NumberOfNominalChannels() + channel;
7691 
7692  sample** newData = nullptr;
7693  try
7694  {
7695  newData = image.m_allocator.AllocateChannelSlots( 1 );
7696  *newData = m_pixelData[c];
7697  }
7698  catch ( ... )
7699  {
7700  if ( newData != nullptr )
7701  image.m_allocator.Deallocate( newData );
7702  throw;
7703  }
7704 
7705  image.FreeData();
7706 
7707  image.m_pixelData = newData;
7708  image.m_width = m_width;
7709  image.m_height = m_height;
7710  image.m_numberOfChannels = 1;
7711  image.m_colorSpace = ColorSpace::Gray;
7712  image.m_data->UpdateSharedImage();
7713  image.ResetSelections();
7714 
7715  m_pixelData[c] = nullptr;
7716  ForgetAlphaChannel( channel );
7717 
7718  return *this;
7719  }
7720 
7733  {
7734  if ( channel >= 0 && channel < NumberOfAlphaChannels() )
7735  {
7736  EnsureUnique();
7737  int c = NumberOfNominalChannels() + channel;
7738  m_allocator.Deallocate( m_pixelData[c] );
7739  m_pixelData[c] = nullptr;
7740  ForgetAlphaChannel( channel );
7741  }
7742 
7743  return *this;
7744  }
7745 
7763  {
7764  if ( channel >= 0 && channel < NumberOfAlphaChannels() )
7765  {
7766  EnsureUnique();
7767  sample** oldData = m_pixelData;
7768  sample** newData = m_allocator.AllocateChannelSlots( m_numberOfChannels-1 );
7769 
7770  int n0 = NumberOfNominalChannels();
7771  int c = n0 + channel;
7772 
7773  for ( int i = 0; i < c; ++i )
7774  newData[i] = oldData[i];
7775  for ( int i = c, j = c; ++j < m_numberOfChannels; ++i )
7776  newData[i] = oldData[j];
7777 
7778  m_allocator.SetSharedData( m_pixelData = newData );
7779  m_allocator.SetSharedGeometry( m_width, m_height, --m_numberOfChannels );
7780 
7781  if ( m_channel >= n0 || m_lastChannel >= n0 )
7782  ResetChannelRange();
7783 
7784  m_allocator.Deallocate( oldData );
7785  }
7786 
7787  return *this;
7788  }
7789 
7795  {
7796  int n0 = NumberOfNominalChannels();
7797  int n = m_numberOfChannels;
7798  if ( n > n0 )
7799  {
7800  EnsureUnique();
7801  do
7802  m_allocator.Deallocate( m_pixelData[--n] ), m_pixelData[n] = nullptr;
7803  while ( n > n0 );
7804  ForgetAlphaChannels();
7805  }
7806 
7807  return *this;
7808  }
7809 
7819  {
7820  int n0 = NumberOfNominalChannels();
7821  if ( m_numberOfChannels > n0 )
7822  {
7823  EnsureUnique();
7824  sample** oldData = m_pixelData;
7825  sample** newData = m_allocator.AllocateChannelSlots( n0 );
7826 
7827  for ( int i = 0; i < n0; ++i )
7828  newData[i] = oldData[i];
7829 
7830  m_allocator.SetSharedData( m_pixelData = newData );
7831  m_allocator.SetSharedGeometry( m_width, m_height, m_numberOfChannels = n0 );
7832 
7833  if ( m_channel >= n0 || m_lastChannel >= n0 )
7834  ResetChannelRange();
7835 
7836  m_allocator.Deallocate( oldData );
7837  }
7838 
7839  return *this;
7840  }
7841 
7842  // -------------------------------------------------------------------------
7843 
7880  template <typename T>
7881  GenericImage& Fill( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
7882  {
7883  Rect r = rect;
7884  if ( !ParseSelection( r, firstChannel, lastChannel ) )
7885  return *this;
7886 
7887  EnsureUnique();
7888 
7889  size_type N = r.IntegerArea();
7890  if ( m_status.IsInitializationEnabled() )
7891  m_status.Initialize( "Filling image", N*(1 + lastChannel - firstChannel) );
7892 
7893  sample v = P::ToSample( scalar );
7894 
7895  if ( r == Bounds() )
7896  for ( int i = firstChannel; i <= lastChannel; ++i, m_status += N )
7897  P::Fill( m_pixelData[i], v, N );
7898  else
7899  for ( int i = firstChannel, w = r.Width(), h = r.Height(); i <= lastChannel; ++i, m_status += N )
7900  {
7901  sample* __restrict__ f = PixelAddress( r.LeftTop(), i );
7902  PCL_IVDEP
7903  for ( int j = 0; j < h; ++j, f += m_width )
7904  P::Fill( f, v, w );
7905  }
7906 
7907  return *this;
7908  }
7909 
7927  template <typename T>
7929  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
7930  {
7931  Rect r = rect;
7932  if ( !ParseSelection( r, firstChannel, lastChannel ) )
7933  return *this;
7934 
7935  EnsureUnique();
7936 
7937  size_type N = r.IntegerArea();
7938  if ( m_status.IsInitializationEnabled() )
7939  m_status.Initialize( "Filling image", N*(1 + lastChannel - firstChannel) );
7940 
7941  if ( r == Bounds() )
7942  for ( int i = firstChannel, c = 0; i <= lastChannel; ++i, ++c, m_status += N )
7943  {
7944  sample v = (c < values.Length()) ? P::ToSample( values[c] ) : P::MinSampleValue();
7945  P::Fill( m_pixelData[i], v, N );
7946  }
7947  else
7948  for ( int i = firstChannel, c = 0, w = r.Width(), h = r.Height(); i <= lastChannel; ++i, ++c, m_status += N )
7949  {
7950  sample* __restrict__ f = PixelAddress( r.LeftTop(), i );
7951  sample v = (c < values.Length()) ? P::ToSample( values[c] ) : P::MinSampleValue();
7952  PCL_IVDEP
7953  for ( int j = 0; j < h; ++j, f += m_width )
7954  P::Fill( f, v, w );
7955  }
7956 
7957  return *this;
7958  }
7959 
7970  template <typename T>
7971  GenericImage Filled( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) const
7972  {
7973  GenericImage result( *this, rect, firstChannel, lastChannel );
7974  (void)result.Fill( scalar );
7975  return result;
7976  }
7977 
7989  template <typename T>
7991  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) const
7992  {
7993  GenericImage result( *this, rect, firstChannel, lastChannel );
7994  (void)result.Fill( values );
7995  return result;
7996  }
7997 
8012  GenericImage& Zero( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
8013  {
8014  return Fill( P::ToSample( 0.0 ), rect, firstChannel, lastChannel );
8015  }
8016 
8031  GenericImage& One( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
8032  {
8033  return Fill( P::ToSample( 1.0 ), rect, firstChannel, lastChannel );
8034  }
8035 
8049  GenericImage& Black( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
8050  {
8051  return Fill( P::MinSampleValue(), rect, firstChannel, lastChannel );
8052  }
8053 
8067  GenericImage& White( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
8068  {
8069  return Fill( P::MaxSampleValue(), rect, firstChannel, lastChannel );
8070  }
8071 
8072  // -------------------------------------------------------------------------
8073 
8088  template <typename T>
8089  GenericImage& Invert( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
8090  {
8091  Rect r = rect;
8092  if ( !ParseSelection( r, firstChannel, lastChannel ) )
8093  return *this;
8094 
8095  EnsureUnique();
8096 
8097  size_type N = r.IntegerArea();
8098  if ( m_status.IsInitializationEnabled() )
8099  m_status.Initialize( "Inverting pixel samples", N*(1 + lastChannel - firstChannel) );
8100 
8101  sample v = P::ToSample( scalar );
8102  if ( r == Bounds() )
8103  for ( int i = firstChannel; i <= lastChannel; ++i, m_status += N )
8104  {
8105  sample* __restrict__ f = m_pixelData[i];
8106  PCL_IVDEP
8107  for ( size_type j = 0; j < N; ++j, ++f )
8108  *f = v - *f;
8109  }
8110  else
8111  for ( int i = firstChannel, w = r.Width(), h = r.Height(); i <= lastChannel; ++i, m_status += N )
8112  {
8113  sample* __restrict__ f = PixelAddress( r.LeftTop(), i );
8114  PCL_IVDEP
8115  for ( int j = 0; j < h; ++j, f += m_width-w )
8116  {
8117  PCL_IVDEP
8118  for ( int k = 0; k < w; ++k, ++f )
8119  *f = v - *f;
8120  }
8121  }
8122 
8123  return *this;
8124  }
8125 
8136  template <typename T>
8137  GenericImage Inverted( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) const
8138  {
8139  GenericImage result( *this, rect, firstChannel, lastChannel );
8140  (void)result.Invert( scalar );
8141  return result;
8142  }
8143 
8161  GenericImage& Invert( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
8162  {
8163  return Invert( P::MaxSampleValue(), rect, firstChannel, lastChannel );
8164  }
8165 
8177  GenericImage Inverted( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) const
8178  {
8179  GenericImage result( *this, rect, firstChannel, lastChannel );
8180  (void)result.Invert();
8181  return result;
8182  }
8183 
8192  GenericImage operator ~() const
8193  {
8194  GenericImage result( *this );
8195  (void)result.Invert();
8196  return result;
8197  }
8198 
8209  GenericImage& Not( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
8210  {
8211  Rect r = rect;
8212  if ( !ParseSelection( r, firstChannel, lastChannel ) )
8213  return *this;
8214 
8215  EnsureUnique();
8216 
8217  size_type N = r.IntegerArea();
8218  if ( m_status.IsInitializationEnabled() )
8219  m_status.Initialize( "Bitwise Not", N*(1 + lastChannel - firstChannel) );
8220 
8221  if ( r == Bounds() )
8222  for ( int i = firstChannel; i <= lastChannel; ++i, m_status += N )
8223  {
8224  sample* __restrict__ f = m_pixelData[i];
8225  PCL_IVDEP
8226  for ( size_type j = 0; j < N; ++j, ++f )
8227  P::Not( *f );
8228  }
8229  else
8230  for ( int i = firstChannel, w = r.Width(), h = r.Height(); i <= lastChannel; ++i, m_status += N )
8231  {
8232  sample* __restrict__ f = PixelAddress( r.LeftTop(), i );
8233  PCL_IVDEP
8234  for ( int j = 0; j < h; ++j, f += m_width-w )
8235  {
8236  PCL_IVDEP
8237  for ( int k = 0; k < w; ++k, ++f )
8238  P::Not( *f );
8239  }
8240  }
8241 
8242  return *this;
8243  }
8244 
8261  template <typename T>
8262  GenericImage& Truncate( T lowerBound, T upperBound,
8263  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
8264  {
8265  Rect r = rect;
8266  if ( !ParseSelection( r, firstChannel, lastChannel ) )
8267  return *this;
8268 
8269  EnsureUnique();
8270 
8271  size_type N = r.IntegerArea();
8272  if ( m_status.IsInitializationEnabled() )
8273  m_status.Initialize( "Truncating pixel samples", N*(1 + lastChannel - firstChannel) );
8274 
8275  sample b0 = P::ToSample( lowerBound );
8276  sample b1 = P::ToSample( upperBound );
8277  if ( b1 < b0 )
8278  pcl::Swap( b0, b1 );
8279 
8280  if ( r == Bounds() )
8281  for ( int i = firstChannel; i <= lastChannel; ++i, m_status += N )
8282  {
8283  sample* __restrict__ f = m_pixelData[i];
8284  PCL_IVDEP
8285  for ( size_type j = 0; j < N; ++j, ++f )
8286  if ( *f < b0 )
8287  *f = b0;
8288  else if ( b1 < *f )
8289  *f = b1;
8290  }
8291  else
8292  for ( int i = firstChannel, w = r.Width(), h = r.Height(); i <= lastChannel; ++i, m_status += N )
8293  {
8294  sample* __restrict__ f = PixelAddress( r.LeftTop(), i );
8295  PCL_IVDEP
8296  for ( int j = 0; j < h; ++j, f += m_width-w )
8297  {
8298  PCL_IVDEP
8299  for ( int k = 0; k < w; ++k, ++f )
8300  if ( *f < b0 )
8301  *f = b0;
8302  else if ( b1 < *f )
8303  *f = b1;
8304  }
8305  }
8306 
8307  return *this;
8308  }
8309 
8323  template <typename T>
8324  GenericImage Truncated( T lowerBound, T upperBound,
8325  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) const
8326  {
8327  GenericImage result( *this, rect, firstChannel, lastChannel );
8328  (void)result.Truncate( lowerBound, upperBound );
8329  return result;
8330  }
8331 
8353  GenericImage& Truncate( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
8354  {
8355  return Truncate( P::MinSampleValue(), P::MaxSampleValue(), rect, firstChannel, lastChannel );
8356  }
8357 
8368  GenericImage Truncated( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) const
8369  {
8370  GenericImage result( *this, rect, firstChannel, lastChannel );
8371  (void)result.Truncate();
8372  return result;
8373  }
8374 
8403  template <typename T>
8404  GenericImage& Rescale( T lowerBound, T upperBound,
8405  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
8406  {
8407  Rect r = rect;
8408  if ( !ParseSelection( r, firstChannel, lastChannel ) )
8409  return *this;
8410 
8411  size_type N = r.IntegerArea();
8412  size_type Ns = N*(1 + lastChannel - firstChannel);
8413  if ( m_status.IsInitializationEnabled() )
8414  m_status.Initialize( "Rescaling pixel samples", Ns );
8415 
8416  sample b0 = P::ToSample( lowerBound );
8417  sample b1 = P::ToSample( upperBound );
8418  if ( b1 < b0 )
8419  pcl::Swap( b0, b1 );
8420 
8421  sample v0, v1;
8422  GetExtremePixelValues( v0, v1, r, firstChannel, lastChannel );
8423  if ( v0 == b0 && v1 == b1 )
8424  {
8425  m_status += Ns;
8426  return *this;
8427  }
8428 
8429  EnsureUnique();
8430 
8431  double d = 0;
8432  if ( b0 != b1 )
8433  if ( v0 != v1 )
8434  d = (double( b1 ) - double( b0 ))/(double( v1 ) - double( v0 ));
8435 
8436  if ( r == Bounds() )
8437  for ( int i = firstChannel; i <= lastChannel; ++i, m_status += N )
8438  {
8439  sample* __restrict__ f = m_pixelData[i];
8440 
8441  if ( v0 != v1 )
8442  {
8443  if ( b0 != b1 )
8444  {
8445  if ( b0 == sample( 0 ) )
8446  {
8447  PCL_IVDEP
8448  for ( size_type j = 0; j < N; ++j, ++f )
8449  *f = P::FloatToSample( d*(*f - v0) );
8450  }
8451  else
8452  {
8453  PCL_IVDEP
8454  for ( size_type j = 0; j < N; ++j, ++f )
8455  *f = P::FloatToSample( d*(*f - v0) + b0 );
8456  }
8457  }
8458  else
8459  P::Fill( f, b0, N );
8460  }
8461  else
8462  P::Fill( f, pcl::Range( v0, b0, b1 ), N );
8463  }
8464  else
8465  for ( int i = firstChannel, w = r.Width(), h = r.Height(); i <= lastChannel; ++i, m_status += N )
8466  {
8467  sample* __restrict__ f = PixelAddress( r.LeftTop(), i );
8468 
8469  if ( v0 != v1 )
8470  {
8471  if ( b0 != b1 )
8472  {
8473  if ( b0 == sample( 0 ) )
8474  {
8475  PCL_IVDEP
8476  for ( int j = 0; j < h; ++j, f += m_width-w )
8477  {
8478  PCL_IVDEP
8479  for ( int k = 0; k < w; ++k, ++f )
8480  *f = P::FloatToSample( d*(*f - v0) );
8481  }
8482  }
8483  else
8484  {
8485  PCL_IVDEP
8486  for ( int j = 0; j < h; ++j, f += m_width-w )
8487  {
8488  PCL_IVDEP
8489  for ( int k = 0; k < w; ++k, ++f )
8490  *f = P::FloatToSample( d*(*f - v0) + b0 );
8491  }
8492  }
8493  }
8494  else
8495  {
8496  PCL_IVDEP
8497  for ( int j = 0; j < h; ++j, f += m_width )
8498  P::Fill( f, b0, w );
8499  }
8500  }
8501  else
8502  {
8503  sample v = pcl::Range( v0, b0, b1 );
8504  PCL_IVDEP
8505  for ( int j = 0; j < h; ++j, f += m_width )
8506  P::Fill( f, v, w );
8507  }
8508  }
8509 
8510  return *this;
8511  }
8512 
8526  template <typename T>
8527  GenericImage Rescaled( T lowerBound, T upperBound,
8528  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) const
8529  {
8530  GenericImage result( *this, rect, firstChannel, lastChannel );
8531  (void)result.Rescale( lowerBound, upperBound );
8532  return result;
8533  }
8534 
8551  GenericImage& Rescale( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
8552  {
8553  return Rescale( P::MinSampleValue(), P::MaxSampleValue(), rect, firstChannel, lastChannel );
8554  }
8555 
8566  GenericImage Rescaled( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) const
8567  {
8568  GenericImage result( *this, rect, firstChannel, lastChannel );
8569  (void)result.Rescale();
8570  return result;
8571  }
8572 
8607  template <typename T>
8608  GenericImage& Normalize( T lowerBound, T upperBound,
8609  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
8610  {
8611  Rect r = rect;
8612  if ( !ParseSelection( r, firstChannel, lastChannel ) )
8613  return *this;
8614 
8615  size_type N = r.IntegerArea();
8616  size_type Ns = N*(1 + lastChannel - firstChannel);
8617  if ( m_status.IsInitializationEnabled() )
8618  m_status.Initialize( "Normalizing pixel samples", Ns );
8619 
8620  sample b0 = P::ToSample( lowerBound );
8621  sample b1 = P::ToSample( upperBound );
8622  if ( b1 < b0 )
8623  pcl::Swap( b0, b1 );
8624 
8625  sample v0, v1;
8626  GetExtremePixelValues( v0, v1, r, firstChannel, lastChannel );
8627 
8628  if ( v0 >= b0 && v1 <= b1 )
8629  {
8630  m_status += Ns;
8631  return *this;
8632  }
8633 
8634  EnsureUnique();
8635 
8636  double d = 0;
8637  if ( b0 != b1 )
8638  if ( v0 != v1 )
8639  d = (double( b1 ) - double( b0 ))/(double( v1 ) - double( v0 ));
8640 
8641  if ( r == Bounds() )
8642  for ( int i = firstChannel; i <= lastChannel; ++i, m_status += N )
8643  {
8644  sample* __restrict__ f = m_pixelData[i];
8645 
8646  if ( v0 != v1 )
8647  {
8648  if ( b0 != b1 )
8649  {
8650  if ( b0 == sample( 0 ) )
8651  {
8652  PCL_IVDEP
8653  for ( size_type j = 0; j < N; ++j, ++f )
8654  *f = P::FloatToSample( d*(*f - v0) );
8655  }
8656  else
8657  {
8658  PCL_IVDEP
8659  for ( size_type j = 0; j < N; ++j, ++f )
8660  *f = P::FloatToSample( d*(*f - v0) + b0 );
8661  }
8662  }
8663  else
8664  P::Fill( f, b0, N );
8665  }
8666  else
8667  P::Fill( f, pcl::Range( v0, b0, b1 ), N );
8668  }
8669  else
8670  for ( int i = firstChannel, w = r.Width(), h = r.Height(); i <= lastChannel; ++i, m_status += N )
8671  {
8672  sample* __restrict__ f = PixelAddress( r.LeftTop(), i );
8673 
8674  if ( v0 != v1 )
8675  {
8676  if ( b0 != b1 )
8677  {
8678  if ( b0 == sample( 0 ) )
8679  {
8680  PCL_IVDEP
8681  for ( int j = 0; j < h; ++j, f += m_width-w )
8682  {
8683  PCL_IVDEP
8684  for ( int k = 0; k < w; ++k, ++f )
8685  *f = P::FloatToSample( d*(*f - v0) );
8686  }
8687  }
8688  else
8689  {
8690  PCL_IVDEP
8691  for ( int j = 0; j < h; ++j, f += m_width-w )
8692  {
8693  PCL_IVDEP
8694  for ( int k = 0; k < w; ++k, ++f )
8695  *f = P::FloatToSample( d*(*f - v0) + b0 );
8696  }
8697  }
8698  }
8699  else
8700  {
8701  PCL_IVDEP
8702  for ( int j = 0; j < h; ++j, f += m_width )
8703  P::Fill( f, b0, w );
8704  }
8705  }
8706  else
8707  {
8708  sample v = pcl::Range( v0, b0, b1 );
8709  PCL_IVDEP
8710  for ( int j = 0; j < h; ++j, f += m_width )
8711  P::Fill( f, v, w );
8712  }
8713  }
8714 
8715  return *this;
8716  }
8717 
8731  template <typename T>
8732  GenericImage Normalized( T lowerBound, T upperBound,
8733  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) const
8734  {
8735  GenericImage result( *this, rect, firstChannel, lastChannel );
8736  (void)result.Normalize( lowerBound, upperBound );
8737  return result;
8738  }
8739 
8756  GenericImage& Normalize( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
8757  {
8758  return Normalize( P::MinSampleValue(), P::MaxSampleValue(), rect, firstChannel, lastChannel );
8759  }
8760 
8771  GenericImage Normalized( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) const
8772  {
8773  GenericImage result( *this, rect, firstChannel, lastChannel );
8774  (void)result.Normalize();
8775  return result;
8776  }
8777 
8807  template <typename T>
8808  GenericImage& Binarize( T threshold,
8809  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
8810  {
8811  Rect r = rect;
8812  if ( !ParseSelection( r, firstChannel, lastChannel ) )
8813  return *this;
8814 
8815  EnsureUnique();
8816 
8817  size_type N = r.IntegerArea();
8818  if ( m_status.IsInitializationEnabled() )
8819  m_status.Initialize( "Binarizing pixel samples", N*(1 + lastChannel - firstChannel) );
8820 
8821  sample t = P::ToSample( threshold );
8822 
8823  if ( r == Bounds() )
8824  for ( int i = firstChannel; i <= lastChannel; ++i, m_status += N )
8825  {
8826  sample* __restrict__ f = m_pixelData[i];
8827  PCL_IVDEP
8828  for ( size_type j = 0; j < N; ++j, ++f )
8829  *f = (*f < t) ? P::MinSampleValue() : P::MaxSampleValue();
8830  }
8831  else
8832  for ( int c = firstChannel, w = r.Width(), h = r.Height(); c <= lastChannel; ++c, m_status += N )
8833  {
8834  sample* __restrict__ f = PixelAddress( r.LeftTop(), c );
8835  PCL_IVDEP
8836  for ( int j = 0; j < h; ++j, f += m_width-w )
8837  {
8838  PCL_IVDEP
8839  for ( int k = 0; k < w; ++k, ++f )
8840  *f = (*f < t) ? P::MinSampleValue() : P::MaxSampleValue();
8841  }
8842  }
8843 
8844  return *this;
8845  }
8846 
8857  template <typename T>
8858  GenericImage Binarized( T threshold,
8859  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) const
8860  {
8861  GenericImage result( *this, rect, firstChannel, lastChannel );
8862  (void)result.Binarize( threshold );
8863  return result;
8864  }
8865 
8882  GenericImage& Binarize( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
8883  {
8884  return Binarize( (P::MinSampleValue() + P::MaxSampleValue())/2, rect, firstChannel, lastChannel );
8885  }
8886 
8898  GenericImage Binarized( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) const
8899  {
8900  GenericImage result( *this, rect, firstChannel, lastChannel );
8901  (void)result.Binarize();
8902  return result;
8903  }
8904 
8918  GenericImage& SetAbsoluteValue( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
8919  {
8920  Rect r = rect;
8921  if ( !ParseSelection( r, firstChannel, lastChannel ) )
8922  return *this;
8923 
8924  EnsureUnique();
8925 
8926  size_type N = r.IntegerArea();
8927  if ( m_status.IsInitializationEnabled() )
8928  m_status.Initialize( "Computing absolute value", N*(1 + lastChannel - firstChannel) );
8929 
8930  if ( r == Bounds() )
8931  for ( int i = firstChannel; i <= lastChannel; ++i, m_status += N )
8932  {
8933  sample* __restrict__ f = m_pixelData[i];
8934  PCL_IVDEP
8935  for ( size_type j = 0; j < N; ++j, ++f )
8936  *f = pcl::Abs( *f );
8937  }
8938  else
8939  for ( int i = firstChannel, w = r.Width(), h = r.Height(); i <= lastChannel; ++i, m_status += N )
8940  {
8941  sample* __restrict__ f = PixelAddress( r.LeftTop(), i );
8942  PCL_IVDEP
8943  for ( int j = 0; j < h; ++j, f += m_width-w )
8944  {
8945  PCL_IVDEP
8946  for ( int k = 0; k < w; ++k, ++f )
8947  *f = pcl::Abs( *f );
8948  }
8949  }
8950 
8951  return *this;
8952  }
8953 
8957  GenericImage& Abs( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
8958  {
8959  return SetAbsoluteValue( rect, firstChannel, lastChannel );
8960  }
8961 
8972  GenericImage AbsoluteValue( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) const
8973  {
8974  GenericImage result( *this, rect, firstChannel, lastChannel );
8975  (void)result.SetAbsoluteValue();
8976  return result;
8977  }
8978 
8979  // -------------------------------------------------------------------------
8980 
8981  /*
8982  * Implementation of Apply( scalar ) member functions. We have to have a
8983  * separate implementation function to prevent infinite recursion in several
8984  * template specializations of the Apply() member function.
8985  */
8986  template <typename T>
8987  GenericImage& ApplyScalar( T scalar, image_op op = ImageOp::Mov,
8988  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
8989  {
8990  if ( op == ImageOp::Div )
8991  if ( 1 + scalar == 1 )
8992  throw Error( "Division by zero or insignificant scalar" );
8993 
8994  Rect r = rect;
8995  if ( !ParseSelection( r, firstChannel, lastChannel ) )
8996  return *this;
8997 
8998  EnsureUnique();
8999 
9000  size_type N = r.IntegerArea();
9001  if ( m_status.IsInitializationEnabled() )
9002  m_status.Initialize( "Applying scalar: "
9003  + ImageOp::Id( op )
9004  + ' ' + String( scalar ), N*(1 + lastChannel - firstChannel) );
9005 
9006  if ( r == Bounds() )
9007  for ( int i = firstChannel; i <= lastChannel; ++i, m_status += N )
9008  {
9009  sample* __restrict__ f = m_pixelData[i];
9010 #define ITERATE( Op ) \
9011  PCL_IVDEP \
9012  for ( size_type j = 0; j < N; ++j, ++f ) \
9013  P::Op( *f, scalar )
9014  switch ( op )
9015  {
9016  case ImageOp::Mov: ITERATE( Mov ); break;
9017  case ImageOp::Add: ITERATE( Add ); break;
9018  case ImageOp::Sub: ITERATE( Sub ); break;
9019  case ImageOp::Mul: ITERATE( Mul ); break;
9020  case ImageOp::Div: ITERATE( Div ); break;
9021  case ImageOp::Pow: ITERATE( Pow ); break;
9022  case ImageOp::Dif: ITERATE( Dif ); break;
9023  case ImageOp::Min: ITERATE( Min ); break;
9024  case ImageOp::Max: ITERATE( Max ); break;
9025  case ImageOp::Not: ITERATE( Not ); break;
9026  case ImageOp::Or: ITERATE( Or ); break;
9027  case ImageOp::Nor: ITERATE( Nor ); break;
9028  case ImageOp::And: ITERATE( And ); break;
9029  case ImageOp::Nand: ITERATE( Nand ); break;
9030  case ImageOp::Xor: ITERATE( Xor ); break;
9031  case ImageOp::Xnor: ITERATE( Xnor ); break;
9032  case ImageOp::ColorBurn: ITERATE( ColorBurn ); break;
9033  case ImageOp::LinearBurn: ITERATE( LinearBurn ); break;
9034  case ImageOp::Screen: ITERATE( Screen ); break;
9035  case ImageOp::ColorDodge: ITERATE( ColorDodge ); break;
9036  case ImageOp::Overlay: ITERATE( Overlay ); break;
9037  case ImageOp::SoftLight: ITERATE( SoftLight ); break;
9038  case ImageOp::HardLight: ITERATE( HardLight ); break;
9039  case ImageOp::VividLight: ITERATE( VividLight ); break;
9040  case ImageOp::LinearLight: ITERATE( LinearLight ); break;
9041  case ImageOp::PinLight: ITERATE( PinLight ); break;
9042  case ImageOp::Exclusion: ITERATE( Exclusion ); break;
9043  default: break;
9044  }
9045 #undef ITERATE
9046  }
9047  else
9048  for ( int i = firstChannel, w = r.Width(), h = r.Height(); i <= lastChannel; ++i, m_status += N )
9049  {
9050  sample* __restrict__ f = PixelAddress( r.LeftTop(), i );
9051 #define ITERATE( Op ) \
9052  PCL_IVDEP \
9053  for ( int j = 0; j < h; ++j, f += m_width-w ) \
9054  { \
9055  PCL_IVDEP \
9056  for ( int k = 0; k < w; ++k, ++f ) \
9057  P::Op( *f, scalar ); \
9058  }
9059  switch ( op )
9060  {
9061  case ImageOp::Mov: ITERATE( Mov ); break;
9062  case ImageOp::Add: ITERATE( Add ); break;
9063  case ImageOp::Sub: ITERATE( Sub ); break;
9064  case ImageOp::Mul: ITERATE( Mul ); break;
9065  case ImageOp::Div: ITERATE( Div ); break;
9066  case ImageOp::Pow: ITERATE( Pow ); break;
9067  case ImageOp::Dif: ITERATE( Dif ); break;
9068  case ImageOp::Min: ITERATE( Min ); break;
9069  case ImageOp::Max: ITERATE( Max ); break;
9070  case ImageOp::Not: ITERATE( Not ); break;
9071  case ImageOp::Or: ITERATE( Or ); break;
9072  case ImageOp::Nor: ITERATE( Nor ); break;
9073  case ImageOp::And: ITERATE( And ); break;
9074  case ImageOp::Nand: ITERATE( Nand ); break;
9075  case ImageOp::Xor: ITERATE( Xor ); break;
9076  case ImageOp::Xnor: ITERATE( Xnor ); break;
9077  case ImageOp::ColorBurn: ITERATE( ColorBurn ); break;
9078  case ImageOp::LinearBurn: ITERATE( LinearBurn ); break;
9079  case ImageOp::Screen: ITERATE( Screen ); break;
9080  case ImageOp::ColorDodge: ITERATE( ColorDodge ); break;
9081  case ImageOp::Overlay: ITERATE( Overlay ); break;
9082  case ImageOp::SoftLight: ITERATE( SoftLight ); break;
9083  case ImageOp::HardLight: ITERATE( HardLight ); break;
9084  case ImageOp::VividLight: ITERATE( VividLight ); break;
9085  case ImageOp::LinearLight: ITERATE( LinearLight ); break;
9086  case ImageOp::PinLight: ITERATE( PinLight ); break;
9087  case ImageOp::Exclusion: ITERATE( Exclusion ); break;
9088  default: break;
9089  }
9090 #undef ITERATE
9091  }
9092 
9093  return *this;
9094  }
9095 
9118  template <typename T>
9119  GenericImage& Apply( T scalar, image_op op = ImageOp::Mov,
9120  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
9121  {
9122  return ApplyScalar( scalar, op, rect, firstChannel, lastChannel );
9123  }
9124 
9125  GenericImage& Apply( float scalar, image_op op = ImageOp::Mov,
9126  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
9127  {
9129  return ApplyScalar( scalar, op, rect, firstChannel, lastChannel );
9130  return ApplyScalar( P::ToSample( scalar ), op, rect, firstChannel, lastChannel );
9131  }
9132 
9133  GenericImage& Apply( double scalar, image_op op = ImageOp::Mov,
9134  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
9135  {
9137  return ApplyScalar( scalar, op, rect, firstChannel, lastChannel );
9138  return ApplyScalar( P::ToSample( scalar ), op, rect, firstChannel, lastChannel );
9139  }
9140 
9141  GenericImage& Apply( pcl::Complex<float> scalar, image_op op = ImageOp::Mov,
9142  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
9143  {
9145  return ApplyScalar( scalar, op, rect, firstChannel, lastChannel );
9146  return ApplyScalar( P::ToSample( scalar ), op, rect, firstChannel, lastChannel );
9147  }
9148 
9149  GenericImage& Apply( pcl::Complex<double> scalar, image_op op = ImageOp::Mov,
9150  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
9151  {
9153  return ApplyScalar( scalar, op, rect, firstChannel, lastChannel );
9154  return ApplyScalar( P::ToSample( scalar ), op, rect, firstChannel, lastChannel );
9155  }
9156 
9175  template <typename T>
9176  GenericImage Applied( T scalar, image_op op = ImageOp::Mov,
9177  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) const
9178  {
9179  GenericImage result( *this, rect, firstChannel, lastChannel );
9180  (void)result.Apply( scalar, op );
9181  return result;
9182  }
9183 
9184  // -------------------------------------------------------------------------
9185 
9230  template <class P1>
9231  GenericImage& Apply( const GenericImage<P1>& image, image_op op = ImageOp::Mov,
9232  const Point& point = Point( int_max ), int channel = -1,
9233  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
9234  {
9235  Rect r = rect;
9236  if ( !image.ParseSelection( r, firstChannel, lastChannel ) )
9237  return *this;
9238 
9239  if ( !ParseChannel( channel ) )
9240  return *this;
9241 
9242  Point p = point;
9243  if ( p.x == int_max || p.y == int_max )
9244  p = m_point;
9245 
9246  if ( p.x < 0 )
9247  {
9248  if ( (r.x0 -= p.x) >= r.x1 )
9249  return *this;
9250  p.x = 0;
9251  }
9252  else if ( p.x >= m_width )
9253  return *this;
9254 
9255  if ( p.y < 0 )
9256  {
9257  if ( (r.y0 -= p.y) >= r.y1 )
9258  return *this;
9259  p.y = 0;
9260  }
9261  else if ( p.y >= m_height )
9262  return *this;
9263 
9264  r.ResizeTo( pcl::Min( m_width - p.x, r.Width() ),
9265  pcl::Min( m_height - p.y, r.Height() ) );
9266 
9267  lastChannel = pcl::Min( lastChannel, firstChannel + m_numberOfChannels - channel - 1 );
9268 
9269  EnsureUnique();
9270 
9271  size_type N = r.IntegerArea();
9272  if ( m_status.IsInitializationEnabled() )
9273  m_status.Initialize( "Applying image, op=" + ImageOp::Id( op ), N*(1 + lastChannel - firstChannel) );
9274 
9275  if ( r == Bounds() && r == image.Bounds() )
9276  for ( int i = channel, j = firstChannel; j <= lastChannel; ++i, ++j, m_status += N )
9277  {
9278  sample* __restrict__ f = m_pixelData[i];
9279  const typename P1::sample* __restrict__ g = image[j];
9280 
9281 #define ITERATE( Op ) \
9282  PCL_IVDEP \
9283  for ( size_type k = 0; k < N; ++k, ++f, ++g ) \
9284  P::Op( *f, *g )
9285  switch ( op )
9286  {
9287  case ImageOp::Mov:
9288  P::Copy( f, g, N );
9289  break;
9290  case ImageOp::Min:
9291  P::CopyMin( f, g, N );
9292  break;
9293  case ImageOp::Max:
9294  P::CopyMax( f, g, N );
9295  break;
9296  default:
9297  switch ( op )
9298  {
9299  case ImageOp::Add: ITERATE( Add ); break;
9300  case ImageOp::Sub: ITERATE( Sub ); break;
9301  case ImageOp::Mul: ITERATE( Mul ); break;
9302  case ImageOp::Div:
9303  for ( size_type j = 0; j < N; ++j, ++f, ++g )
9304  if ( *g != 0 )
9305  P::Div( *f, *g );
9306  else
9307  *f = P::MaxSampleValue();
9308  break;
9309  case ImageOp::Pow: ITERATE( Pow ); break;
9310  case ImageOp::Dif: ITERATE( Dif ); break;
9311  case ImageOp::Not: ITERATE( Not ); break;
9312  case ImageOp::Or: ITERATE( Or ); break;
9313  case ImageOp::Nor: ITERATE( Nor ); break;
9314  case ImageOp::And: ITERATE( And ); break;
9315  case ImageOp::Nand: ITERATE( Nand ); break;
9316  case ImageOp::Xor: ITERATE( Xor ); break;
9317  case ImageOp::Xnor: ITERATE( Xnor ); break;
9318  case ImageOp::ColorBurn: ITERATE( ColorBurn ); break;
9319  case ImageOp::LinearBurn: ITERATE( LinearBurn ); break;
9320  case ImageOp::Screen: ITERATE( Screen ); break;
9321  case ImageOp::ColorDodge: ITERATE( ColorDodge ); break;
9322  case ImageOp::Overlay: ITERATE( Overlay ); break;
9323  case ImageOp::SoftLight: ITERATE( SoftLight ); break;
9324  case ImageOp::HardLight: ITERATE( HardLight ); break;
9325  case ImageOp::VividLight: ITERATE( VividLight ); break;
9326  case ImageOp::LinearLight: ITERATE( LinearLight ); break;
9327  case ImageOp::PinLight: ITERATE( PinLight ); break;
9328  case ImageOp::Exclusion: ITERATE( Exclusion ); break;
9329  default: break;
9330  }
9331 #undef ITERATE
9332  break;
9333  }
9334  }
9335  else
9336  for ( int i = channel, j = firstChannel, w = r.Width(), h = r.Height(); j <= lastChannel; ++i, ++j, m_status += N )
9337  {
9338  sample* __restrict__ f = PixelAddress( p, i );
9339  const typename P1::sample* __restrict__ g = image.PixelAddress( r.LeftTop(), j );
9340 
9341 #define ITERATE( Op ) \
9342  PCL_IVDEP \
9343  for ( int k = 0; k < h; ++k, f += m_width-w, g += image.Width()-w ) \
9344  { \
9345  PCL_IVDEP \
9346  for ( int l = 0; l < w; ++l, ++f, ++g ) \
9347  P::Op( *f, *g ); \
9348  }
9349  switch ( op )
9350  {
9351  case ImageOp::Mov:
9352  for ( int k = 0; k < h; ++k, f += m_width, g += image.Width() )
9353  P::Copy( f, g, w );
9354  break;
9355  case ImageOp::Min:
9356  for ( int k = 0; k < h; ++k, f += m_width, g += image.Width() )
9357  P::CopyMin( f, g, w );
9358  break;
9359  case ImageOp::Max:
9360  for ( int k = 0; k < h; ++k, f += m_width, g += image.Width() )
9361  P::CopyMax( f, g, w );
9362  break;
9363  case ImageOp::Add: ITERATE( Add ); break;
9364  case ImageOp::Sub: ITERATE( Sub ); break;
9365  case ImageOp::Mul: ITERATE( Mul ); break;
9366  case ImageOp::Div:
9367  PCL_IVDEP
9368  for ( int k = 0; k < h; ++k, f += m_width-w, g += image.Width()-w )
9369  {
9370  PCL_IVDEP
9371  for ( int l = 0; l < w; ++l, ++f, ++g )
9372  if ( *g != 0 )
9373  P::Div( *f, *g );
9374  else
9375  *f = P::MaxSampleValue();
9376  }
9377  break;
9378  case ImageOp::Pow: ITERATE( Pow ); break;
9379  case ImageOp::Dif: ITERATE( Dif ); break;
9380  case ImageOp::Not: ITERATE( Not ); break;
9381  case ImageOp::Or: ITERATE( Or ); break;
9382  case ImageOp::Nor: ITERATE( Nor ); break;
9383  case ImageOp::And: ITERATE( And ); break;
9384  case ImageOp::Nand: ITERATE( Nand ); break;
9385  case ImageOp::Xor: ITERATE( Xor ); break;
9386  case ImageOp::Xnor: ITERATE( Xnor ); break;
9387  case ImageOp::ColorBurn: ITERATE( ColorBurn ); break;
9388  case ImageOp::LinearBurn: ITERATE( LinearBurn ); break;
9389  case ImageOp::Screen: ITERATE( Screen ); break;
9390  case ImageOp::ColorDodge: ITERATE( ColorDodge ); break;
9391  case ImageOp::Overlay: ITERATE( Overlay ); break;
9392  case ImageOp::SoftLight: ITERATE( SoftLight ); break;
9393  case ImageOp::HardLight: ITERATE( HardLight ); break;
9394  case ImageOp::VividLight: ITERATE( VividLight ); break;
9395  case ImageOp::LinearLight: ITERATE( LinearLight ); break;
9396  case ImageOp::PinLight: ITERATE( PinLight ); break;
9397  case ImageOp::Exclusion: ITERATE( Exclusion ); break;
9398  break;
9399  default:
9400  break;
9401  }
9402 #undef ITERATE
9403  }
9404 
9405  return *this;
9406  }
9407 
9419  template <class P1>
9420  GenericImage Applied( const GenericImage<P1>& image, image_op op = ImageOp::Mov,
9421  const Point& point = Point( int_max ), int channel = -1,
9422  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) const
9423  {
9424  int c0 = (channel < 0) ? m_channel : channel;
9425  int c1 = c0 + ((firstChannel < 0) ? image.FirstSelectedChannel() : firstChannel);
9426  int c2 = c0 + ((lastChannel < 0) ? image.LastSelectedChannel() : lastChannel);
9427  GenericImage result( *this, rect.MovedTo( point ), c1, c2 );
9428  (void)result.Apply( image, op, Point( 0 ), 0, rect, firstChannel, lastChannel );
9429  return result;
9430  }
9431 
9432  // -------------------------------------------------------------------------
9433 
9467  GenericImage& Apply( const ImageTransformation& transformation,
9468  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 );
9469  // ### Implemented in pcl/ImageTransformation.h (because of mutual dependencies)
9470 
9489  GenericImage Applied( const ImageTransformation& transformation,
9490  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) const
9491  {
9492  GenericImage result( *this, rect, firstChannel, lastChannel );
9493  (void)result.Apply( transformation );
9494  return result;
9495  }
9496 
9518  void Transform( BidirectionalImageTransformation& transform,
9519  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) const;
9520  // ### Implemented in pcl/ImageTransformation.h (because of mutual dependencies)
9521 
9522  // -------------------------------------------------------------------------
9523 
9524 #ifndef __PCL_IMAGE_NO_BITMAP
9525 
9567  GenericImage& Blend( const Bitmap& bitmap,
9568  const Point& point = Point( int_max ), const Rect& rect = Rect( 0 ) )
9569  {
9570  Rect r = rect;
9571  if ( r.IsRect() )
9572  r = bitmap.Bounds().Intersection( r );
9573  else
9574  r = bitmap.Bounds();
9575  if ( !r.IsRect() )
9576  return *this;
9577 
9578  Point p = point;
9579  if ( p.x == int_max || p.y == int_max )
9580  p = m_point;
9581 
9582  if ( p.x < 0 )
9583  {
9584  if ( (r.x0 -= p.x) >= r.x1 )
9585  return *this;
9586  p.x = 0;
9587  }
9588  else if ( p.x >= m_width )
9589  return *this;
9590 
9591  if ( p.y < 0 )
9592  {
9593  if ( (r.y0 -= p.y) >= r.y1 )
9594  return *this;
9595  p.y = 0;
9596  }
9597  else if ( p.y >= m_height )
9598  return *this;
9599 
9600  r.ResizeTo( pcl::Min( m_width - p.x, r.Width() ),
9601  pcl::Min( m_height - p.y, r.Height() ) );
9602 
9603  bool hasAlpha = HasAlphaChannels();
9604  int w = r.Width();
9605  int h = r.Height();
9606 
9607  EnsureUnique();
9608 
9609  if ( IsColor() )
9610  {
9611  if ( m_status.IsInitializationEnabled() )
9612  m_status.Initialize( "Blending RGBA bitmap", size_type( w )*size_type( h ) );
9613 
9614  sample* __restrict__ fR = nullptr;
9615  sample* __restrict__ fG = nullptr;
9616  sample* __restrict__ fB = nullptr;
9617  sample* __restrict__ fA = nullptr;
9618 
9619  for ( int i = 0; i < h; ++i, ++p.y, m_status += w )
9620  {
9621  fR = PixelAddress( p, 0 );
9622  fG = PixelAddress( p, 1 );
9623  fB = PixelAddress( p, 2 );
9624  if ( hasAlpha )
9625  fA = PixelAddress( p, 3 );
9626 
9627  const RGBA* __restrict__ g = bitmap.ScanLine( r.y0 + i ) + r.x0;
9628 
9629  for ( int j = 0; j < w; ++j, ++fR, ++fG, ++fB, ++fA, ++g )
9630  {
9631  RGBA rgba = *g;
9632 
9633  uint8 A = Alpha( rgba );
9634 
9635  if ( hasAlpha )
9636  {
9637  P::Mov( *fR, Red( rgba ) );
9638  P::Mov( *fG, Green( rgba ) );
9639  P::Mov( *fB, Blue( rgba ) );
9640  P::Mov( *fA, A );
9641  }
9642  else if ( A != 0 )
9643  {
9644  if ( A == 0xFF )
9645  {
9646  P::Mov( *fR, Red( rgba ) );
9647  P::Mov( *fG, Green( rgba ) );
9648  P::Mov( *fB, Blue( rgba ) );
9649  }
9650  else
9651  {
9652  double k = PTLUT->pDLUTA[A];
9653  double k1 = PTLUT->p1DLUT8[A];
9654  double v;
9655  P::FromSample( v, *fR ), P::Mov( *fR, k*Red( rgba ) + k1*v );
9656  P::FromSample( v, *fG ), P::Mov( *fG, k*Green( rgba ) + k1*v );
9657  P::FromSample( v, *fB ), P::Mov( *fB, k*Blue( rgba ) + k1*v );
9658  }
9659  }
9660  }
9661  }
9662  }
9663  else
9664  {
9665  if ( m_status.IsInitializationEnabled() )
9666  m_status.Initialize( "Blending grayscale bitmap", size_type( w )*size_type( h ) );
9667 
9668  sample* __restrict__ fK = nullptr;
9669  sample* __restrict__ fA = nullptr;
9670 
9671  for ( int i = 0; i < h; ++i, ++p.y, m_status += w )
9672  {
9673  fK = PixelAddress( p, 0 );
9674  if ( hasAlpha )
9675  fA = PixelAddress( p, 1 );
9676 
9677  const RGBA* __restrict__ g = bitmap.ScanLine( r.y0 + i ) + r.x0;
9678 
9679  for ( int j = 0; j < w; ++j, ++fK, ++fA, ++g )
9680  {
9681  RGBA rgba = *g;
9682  uint8 R = Red( rgba );
9683  uint8 G = Green( rgba );
9684  uint8 B = Blue( rgba );
9685  uint8 A = Alpha( rgba );
9686 
9687  double K = 0.5*(pcl::Min( pcl::Min( R, G ), B )
9688  + pcl::Max( pcl::Max( R, G ), B ));
9689 
9690  if ( hasAlpha )
9691  {
9692  P::Mov( *fK, K/255 );
9693  P::Mov( *fA, A );
9694  }
9695  else if ( A != 0 )
9696  {
9697  if ( A == 0xFF )
9698  P::Mov( *fK, K/255 );
9699  else
9700  {
9701  double v;
9702  P::FromSample( v, *fK );
9703  P::Mov( *fK, PTLUT->pDLUTA[A]*K + PTLUT->p1DLUT8[A]*v );
9704  }
9705  }
9706  }
9707  }
9708  }
9709 
9710  return *this;
9711  }
9712 
9724  GenericImage Blended( const Bitmap& bitmap,
9725  const Point& point = Point( int_max ), const Rect& rect = Rect( 0 ) ) const
9726  {
9727  GenericImage result( *this, Bounds(), 0, m_numberOfChannels-1 );
9728  (void)result.Blend( bitmap, point, rect );
9729  return result;
9730  }
9731 
9732 #endif // !__PCL_IMAGE_NO_BITMAP
9733 
9734  // -------------------------------------------------------------------------
9735 
9746  template <typename T>
9747  GenericImage& Move( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
9748  {
9749  return Apply( scalar, ImageOp::Mov, rect, firstChannel, lastChannel );
9750  }
9751 
9755  template <typename T>
9756  GenericImage& Mov( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
9757  {
9758  return Move( scalar, rect, firstChannel, lastChannel );
9759  }
9760 
9771  template <typename T>
9772  GenericImage& Add( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
9773  {
9774  return Apply( scalar, ImageOp::Add, rect, firstChannel, lastChannel );
9775  }
9776 
9780  template <typename T>
9781  GenericImage& operator +=( T scalar )
9782  {
9783  return Add( scalar );
9784  }
9785 
9796  template <typename T>
9797  GenericImage Added( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) const
9798  {
9799  GenericImage result( *this, rect, firstChannel, lastChannel );
9800  (void)result.Add( scalar );
9801  return result;
9802  }
9803 
9814  template <typename T>
9815  GenericImage& Subtract( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
9816  {
9817  return Apply( scalar, ImageOp::Sub, rect, firstChannel, lastChannel );
9818  }
9819 
9823  template <typename T>
9824  GenericImage& Sub( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
9825  {
9826  return Subtract( scalar, rect, firstChannel, lastChannel );
9827  }
9828 
9832  template <typename T>
9833  GenericImage& operator -=( T scalar )
9834  {
9835  return Subtract( scalar );
9836  }
9837 
9848  template <typename T>
9849  GenericImage Subtracted( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) const
9850  {
9851  GenericImage result( *this, rect, firstChannel, lastChannel );
9852  (void)result.Subtract( scalar );
9853  return result;
9854  }
9855 
9866  template <typename T>
9867  GenericImage& Multiply( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
9868  {
9869  return Apply( scalar, ImageOp::Mul, rect, firstChannel, lastChannel );
9870  }
9871 
9875  template <typename T>
9876  GenericImage& Mul( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
9877  {
9878  return Multiply( scalar, rect, firstChannel, lastChannel );
9879  }
9880 
9884  template <typename T>
9885  GenericImage& operator *=( T scalar )
9886  {
9887  return Multiply( scalar );
9888  }
9889 
9900  template <typename T>
9901  GenericImage Multiplied( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) const
9902  {
9903  GenericImage result( *this, rect, firstChannel, lastChannel );
9904  (void)result.Multiply( scalar );
9905  return result;
9906  }
9907 
9921  template <typename T>
9922  GenericImage& Divide( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
9923  {
9924  return Apply( scalar, ImageOp::Div, rect, firstChannel, lastChannel );
9925  }
9926 
9930  template <typename T>
9931  GenericImage& Div( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
9932  {
9933  return Divide( scalar, rect, firstChannel, lastChannel );
9934  }
9935 
9939  template <typename T>
9940  GenericImage& operator /=( T scalar )
9941  {
9942  return Divide( scalar );
9943  }
9944 
9958  template <typename T>
9959  GenericImage Divided( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) const
9960  {
9961  GenericImage result( *this, rect, firstChannel, lastChannel );
9962  (void)result.Divide( scalar );
9963  return result;
9964  }
9965 
9976  template <typename T>
9977  GenericImage& Raise( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
9978  {
9979  return Apply( scalar, ImageOp::Pow, rect, firstChannel, lastChannel );
9980  }
9981 
9985  template <typename T>
9986  GenericImage& Pow( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
9987  {
9988  return Raise( scalar, rect, firstChannel, lastChannel );
9989  }
9990 
9994  template <typename T>
9995  GenericImage& operator ^=( T scalar )
9996  {
9997  return Raise( scalar );
9998  }
9999 
10010  template <typename T>
10011  GenericImage Raised( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) const
10012  {
10013  GenericImage result( *this, rect, firstChannel, lastChannel );
10014  (void)result.Raise( scalar );
10015  return result;
10016  }
10017 
10029  template <typename T>
10030  GenericImage& SetAbsoluteDifference( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
10031  {
10032  return Apply( scalar, ImageOp::Dif, rect, firstChannel, lastChannel );
10033  }
10034 
10038  template <typename T>
10039  GenericImage& Dif( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
10040  {
10041  return SetAbsoluteDifference( scalar, rect, firstChannel, lastChannel );
10042  }
10043 
10055  template <typename T>
10056  GenericImage AbsoluteDifference( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) const
10057  {
10058  GenericImage result( *this, rect, firstChannel, lastChannel );
10059  (void)result.SetAbsoluteDifference( scalar );
10060  return result;
10061  }
10062 
10073  template <typename T>
10074  GenericImage& SetMinimum( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
10075  {
10076  return Apply( scalar, ImageOp::Min, rect, firstChannel, lastChannel );
10077  }
10078 
10082  template <typename T>
10083  GenericImage& Min( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
10084  {
10085  return SetMinimum( scalar, rect, firstChannel, lastChannel );
10086  }
10087 
10099  template <typename T>
10100  GenericImage Minimum( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) const
10101  {
10102  GenericImage result( *this, rect, firstChannel, lastChannel );
10103  (void)result.SetMinimum( scalar );
10104  return result;
10105  }
10106 
10117  template <typename T>
10118  GenericImage& SetMaximum( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
10119  {
10120  return Apply( scalar, ImageOp::Max, rect, firstChannel, lastChannel );
10121  }
10122 
10126  template <typename T>
10127  GenericImage& Max( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
10128  {
10129  return SetMaximum( scalar, rect, firstChannel, lastChannel );
10130  }
10131 
10143  template <typename T>
10144  GenericImage Maximum( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) const
10145  {
10146  GenericImage result( *this, rect, firstChannel, lastChannel );
10147  (void)result.SetMaximum( scalar );
10148  return result;
10149  }
10150 
10162  template <typename T>
10163  GenericImage& Or( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
10164  {
10165  return Apply( scalar, ImageOp::Or, rect, firstChannel, lastChannel );
10166  }
10167 
10179  template <typename T>
10180  GenericImage& And( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
10181  {
10182  return Apply( scalar, ImageOp::And, rect, firstChannel, lastChannel );
10183  }
10184 
10196  template <typename T>
10197  GenericImage& Xor( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
10198  {
10199  return Apply( scalar, ImageOp::Xor, rect, firstChannel, lastChannel );
10200  }
10201 
10213  template <typename T>
10214  GenericImage& Nor( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
10215  {
10216  return Apply( scalar, ImageOp::Nor, rect, firstChannel, lastChannel );
10217  }
10218 
10230  template <typename T>
10231  GenericImage& Nand( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
10232  {
10233  return Apply( scalar, ImageOp::Nand, rect, firstChannel, lastChannel );
10234  }
10235 
10247  template <typename T>
10248  GenericImage& Xnor( T scalar, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
10249  {
10250  return Apply( scalar, ImageOp::Xnor, rect, firstChannel, lastChannel );
10251  }
10252 
10253  // -------------------------------------------------------------------------
10254 
10265  template <class P1>
10267  const Point& point = Point( int_max ), int channel = -1,
10268  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
10269  {
10270  return Apply( image, ImageOp::Mov, point, channel, rect, firstChannel, lastChannel );
10271  }
10272 
10276  template <class P1>
10278  const Point& point = Point( int_max ), int channel = -1,
10279  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
10280  {
10281  return Move( image, point, channel, rect, firstChannel, lastChannel );
10282  }
10283 
10294  template <class P1>
10296  const Point& point = Point( int_max ), int channel = -1,
10297  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
10298  {
10299  return Apply( image, ImageOp::Add, point, channel, rect, firstChannel, lastChannel );
10300  }
10301 
10305  template <class P1>
10306  GenericImage& operator +=( const GenericImage<P1>& image )
10307  {
10308  return Add( image );
10309  }
10310 
10321  template <class P1>
10323  const Point& point = Point( int_max ), int channel = -1,
10324  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
10325  {
10326  return Apply( image, ImageOp::Sub, point, channel, rect, firstChannel, lastChannel );
10327  }
10328 
10332  template <class P1>
10334  const Point& point = Point( int_max ), int channel = -1,
10335  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
10336  {
10337  return Subtract( image, point, channel, rect, firstChannel, lastChannel );
10338  }
10339 
10343  template <class P1>
10344  GenericImage& operator -=( const GenericImage<P1>& image )
10345  {
10346  return Subtract( image );
10347  }
10348 
10359  template <class P1>
10361  const Point& point = Point( int_max ), int channel = -1,
10362  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
10363  {
10364  return Apply( image, ImageOp::Mul, point, channel, rect, firstChannel, lastChannel );
10365  }
10366 
10370  template <class P1>
10372  const Point& point = Point( int_max ), int channel = -1,
10373  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
10374  {
10375  return Multiply( image, point, channel, rect, firstChannel, lastChannel );
10376  }
10377 
10381  template <class P1>
10382  GenericImage& operator *=( const GenericImage<P1>& image )
10383  {
10384  return Multiply( image );
10385  }
10386 
10401  template <class P1>
10403  const Point& point = Point( int_max ), int channel = -1,
10404  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
10405  {
10406  return Apply( image, ImageOp::Div, point, channel, rect, firstChannel, lastChannel );
10407  }
10408 
10412  template <class P1>
10414  const Point& point = Point( int_max ), int channel = -1,
10415  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
10416  {
10417  return Divide( image, point, channel, rect, firstChannel, lastChannel );
10418  }
10419 
10423  template <class P1>
10424  GenericImage& operator /=( const GenericImage<P1>& image )
10425  {
10426  return Divide( image );
10427  }
10428 
10440  template <class P1>
10442  const Point& point = Point( int_max ), int channel = -1,
10443  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
10444  {
10445  return Apply( image, ImageOp::Pow, point, channel, rect, firstChannel, lastChannel );
10446  }
10447 
10451  template <class P1>
10453  const Point& point = Point( int_max ), int channel = -1,
10454  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
10455  {
10456  return Raise( image, point, channel, rect, firstChannel, lastChannel );
10457  }
10458 
10462  template <class P1>
10463  GenericImage& operator ^=( const GenericImage<P1>& image )
10464  {
10465  return Raise( image );
10466  }
10467 
10479  template <class P1>
10481  const Point& point = Point( int_max ), int channel = -1,
10482  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
10483  {
10484  return Apply( image, ImageOp::Dif, point, channel, rect, firstChannel, lastChannel );
10485  }
10486 
10490  template <class P1>
10492  const Point& point = Point( int_max ), int channel = -1,
10493  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
10494  {
10495  return SetAbsoluteDifference( image, point, channel, rect, firstChannel, lastChannel );
10496  }
10497 
10509  template <class P1>
10511  const Point& point = Point( int_max ), int channel = -1,
10512  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
10513  {
10514  return Apply( image, ImageOp::Min, point, channel, rect, firstChannel, lastChannel );
10515  }
10516 
10520  template <class P1>
10522  const Point& point = Point( int_max ), int channel = -1,
10523  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
10524  {
10525  return SetMinimum( image, point, channel, rect, firstChannel, lastChannel );
10526  }
10527 
10539  template <class P1>
10541  const Point& point = Point( int_max ), int channel = -1,
10542  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
10543  {
10544  return Apply( image, ImageOp::Max, point, channel, rect, firstChannel, lastChannel );
10545  }
10546 
10550  template <class P1>
10552  const Point& point = Point( int_max ), int channel = -1,
10553  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
10554  {
10555  return SetMaximum( image, point, channel, rect, firstChannel, lastChannel );
10556  }
10557 
10569  template <class P1>
10571  const Point& point = Point( int_max ), int channel = -1,
10572  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
10573  {
10574  return Apply( image, ImageOp::Or, point, channel, rect, firstChannel, lastChannel );
10575  }
10576 
10588  template <class P1>
10590  const Point& point = Point( int_max ), int channel = -1,
10591  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
10592  {
10593  return Apply( image, ImageOp::And, point, channel, rect, firstChannel, lastChannel );
10594  }
10595 
10607  template <class P1>
10609  const Point& point = Point( int_max ), int channel = -1,
10610  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
10611  {
10612  return Apply( image, ImageOp::Xor, point, channel, rect, firstChannel, lastChannel );
10613  }
10614 
10626  template <class P1>
10628  const Point& point = Point( int_max ), int channel = -1,
10629  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
10630  {
10631  return Apply( image, ImageOp::Nor, point, channel, rect, firstChannel, lastChannel );
10632  }
10633 
10645  template <class P1>
10647  const Point& point = Point( int_max ), int channel = -1,
10648  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
10649  {
10650  return Apply( image, ImageOp::Nand, point, channel, rect, firstChannel, lastChannel );
10651  }
10652 
10664  template <class P1>
10666  const Point& point = Point( int_max ), int channel = -1,
10667  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
10668  {
10669  return Apply( image, ImageOp::Xnor, point, channel, rect, firstChannel, lastChannel );
10670  }
10671 
10672  // -------------------------------------------------------------------------
10673 
10711  template <class P1>
10713  const Point& point = Point( int_max ), int channel = -1,
10714  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
10715  {
10716  Rect r = rect;
10717  if ( !image.ParseSelection( r, firstChannel, lastChannel ) )
10718  return *this;
10719 
10720  if ( !ParseChannel( channel ) )
10721  return *this;
10722 
10723  Point p = point;
10724  if ( p.x == int_max || p.y == int_max )
10725  p = m_point;
10726 
10727  if ( p.x < 0 )
10728  {
10729  if ( (r.x0 -= p.x) >= r.x1 )
10730  return *this;
10731  p.x = 0;
10732  }
10733  else if ( p.x >= m_width )
10734  return *this;
10735 
10736  if ( p.y < 0 )
10737  {
10738  if ( (r.y0 -= p.y) >= r.y1 )
10739  return *this;
10740  p.y = 0;
10741  }
10742  else if ( p.y >= m_height )
10743  return *this;
10744 
10745  r.ResizeTo( pcl::Min( m_width - p.x, r.Width() ),
10746  pcl::Min( m_height - p.y, r.Height() ) );
10747 
10748  lastChannel = pcl::Min( lastChannel, firstChannel + m_numberOfChannels - channel - 1 );
10749 
10750  EnsureUnique();
10751  image.EnsureUnique();
10752 
10753  size_type N = r.IntegerArea();
10754  if ( m_status.IsInitializationEnabled() )
10755  m_status.Initialize( "Exchanging pixel samples", N*(1 + lastChannel - firstChannel) );
10756 
10757  if ( r == Bounds() && r == image.Bounds() )
10758  for ( int i = channel, j = firstChannel; j <= lastChannel; ++i, ++j, m_status += N )
10759  {
10760  sample* __restrict__ f = m_pixelData[i];
10761  typename P1::sample* __restrict__ g = image[j];
10762  PCL_IVDEP
10763  for ( size_type k = 0; k < N; ++k, ++f, ++g )
10764  {
10765  sample t = *f;
10766  P1::FromSample( *f, *g );
10767  P::FromSample( *g, t );
10768  }
10769  }
10770  else
10771  for ( int i = channel, j = firstChannel, w = r.Width(), h = r.Height(); j <= lastChannel; ++i, ++j, m_status += N )
10772  {
10773  sample* __restrict__ f = PixelAddress( p, i );
10774  typename P1::sample* __restrict__ g = image.PixelAddress( r.LeftTop(), j );
10775  PCL_IVDEP
10776  for ( int k = 0; k < h; ++k, f += m_width-w, g += image.Width()-w )
10777  {
10778  PCL_IVDEP
10779  for ( int l = 0; l < w; ++l, ++f, ++g )
10780  {
10781  sample t = *f;
10782  P1::FromSample( *f, *g );
10783  P::FromSample( *g, t );
10784  }
10785  }
10786  }
10787 
10788  return *this;
10789  }
10790 
10794  template <class P1>
10796  const Point& point = Point( int_max ), int channel = -1,
10797  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 )
10798  {
10799  return Exchange( image, point, channel, rect, firstChannel, lastChannel );
10800  }
10801 
10802  // -------------------------------------------------------------------------
10803 
10831  Array<double> PixelSamples( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1,
10832  int maxProcessors = 0, bool __performanceAnalysis__ = false )
10833  {
10834  Rect r = rect;
10835  if ( !ParseSelection( r, firstChannel, lastChannel ) )
10836  return Array<double>();
10837 
10838  size_type N = r.IntegerArea()*(1 + lastChannel - firstChannel);
10839  if ( m_status.IsInitializationEnabled() )
10840  m_status.Initialize( "Sampling pixel values", N );
10841 
10842  Array<size_type> L;
10843  if ( likely( !__performanceAnalysis__ ) )
10844  {
10846  data.algorithm = PerformanceAnalysisAlgorithm::Sampling;
10847  data.length = N;
10848  data.overheadLimit = 32768;
10849  data.itemSize = BytesPerSample();
10850  data.floatingPoint = IsFloatSample();
10851  L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors, N/Thread::OptimalNumberOfThreads( data )/*overheadLimitPx*/ );
10852  }
10853  else
10854  L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors, 1/*overheadLimitPx*/ );
10855 
10856  bool useAffinity = m_parallel && Thread::IsRootThread();
10858  for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
10859  threads.Add( new DSmpThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ) ) );
10860  if ( threads.Length() > 1 )
10861  {
10862  int n = 0;
10863  for ( DSmpThread& thread : threads )
10864  thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 );
10865  for ( DSmpThread& thread : threads )
10866  thread.Wait();
10867  }
10868  else
10869  threads[0].Run();
10870 
10871  Array<double> values;
10872  for ( DSmpThread& thread : threads )
10873  if ( thread.n > 0 )
10874  {
10875  values.Add( thread.values.Begin(), thread.values.At( thread.n ) );
10876  thread.values.Clear();
10877  }
10878 
10879  threads.Destroy();
10880  m_status += N;
10881  return values;
10882  }
10883 
10884  // -------------------------------------------------------------------------
10885 
10905  sample MinimumSampleValue( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1,
10906  int maxProcessors = 0 ) const
10907  {
10908  Rect r = rect;
10909  if ( !ParseSelection( r, firstChannel, lastChannel ) )
10910  return 0;
10911 
10912  size_type N = r.IntegerArea()*(1 + lastChannel - firstChannel);
10913  if ( m_status.IsInitializationEnabled() )
10914  m_status.Initialize( "Computing minimum pixel sample value", N );
10915 
10916  Array<size_type> L;
10917  {
10919  data.algorithm = PerformanceAnalysisAlgorithm::MinMax;
10920  data.length = N;
10921  data.overheadLimit = 32768;
10922  data.itemSize = BytesPerSample();
10923  data.floatingPoint = IsFloatSample();
10924  L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors, N/Thread::OptimalNumberOfThreads( data )/*overheadLimitPx*/ );
10925  }
10926  bool useAffinity = m_parallel && Thread::IsRootThread();
10927  ReferenceArray<MinThread> threads;
10928  for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
10929  threads.Add( new MinThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ) ) );
10930  if ( threads.Length() > 1 )
10931  {
10932  int n = 0;
10933  for ( MinThread& thread : threads )
10934  thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 );
10935  for ( MinThread& thread : threads )
10936  thread.Wait();
10937  }
10938  else
10939  threads[0].Run();
10940 
10941  sample min = P::MinSampleValue();
10942  for ( size_type i = 0; i < threads.Length(); ++i )
10943  if ( threads[i].count > 0 )
10944  {
10945  min = threads[i].min;
10946  while ( ++i < threads.Length() )
10947  if ( threads[i].count > 0 )
10948  if ( threads[i].min < min )
10949  min = threads[i].min;
10950  break;
10951  }
10952 
10953  threads.Destroy();
10954  m_status += N;
10955  return min;
10956  }
10957 
10963  sample MinimumPixelValue( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1,
10964  int maxProcessors = 0 ) const
10965  {
10966  return MinimumSampleValue( rect, firstChannel, lastChannel, maxProcessors );
10967  }
10968 
10988  sample MaximumSampleValue( const Rect& rect = Rect( 0 ),
10989  int firstChannel = -1, int lastChannel = -1, int maxProcessors = 0 ) const
10990  {
10991  Rect r = rect;
10992  if ( !ParseSelection( r, firstChannel, lastChannel ) )
10993  return 0;
10994 
10995  size_type N = r.IntegerArea()*(1 + lastChannel - firstChannel);
10996  if ( m_status.IsInitializationEnabled() )
10997  m_status.Initialize( "Computing maximum pixel sample value", N );
10998 
10999  Array<size_type> L;
11000  {
11002  data.algorithm = PerformanceAnalysisAlgorithm::MinMax;
11003  data.length = N;
11004  data.overheadLimit = 32768;
11005  data.itemSize = BytesPerSample();
11006  data.floatingPoint = IsFloatSample();
11007  L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors, N/Thread::OptimalNumberOfThreads( data )/*overheadLimitPx*/ );
11008  }
11009  bool useAffinity = m_parallel && Thread::IsRootThread();
11010  ReferenceArray<MaxThread> threads;
11011  for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
11012  threads.Add( new MaxThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ) ) );
11013  if ( threads.Length() > 1 )
11014  {
11015  int n = 0;
11016  for ( MaxThread& thread : threads )
11017  thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 );
11018  for ( MaxThread& thread : threads )
11019  thread.Wait();
11020  }
11021  else
11022  threads[0].Run();
11023 
11024  sample max = P::MinSampleValue();
11025  for ( size_type i = 0; i < threads.Length(); ++i )
11026  if ( threads[i].count > 0 )
11027  {
11028  max = threads[i].max;
11029  while ( ++i < threads.Length() )
11030  if ( threads[i].count > 0 )
11031  if ( max < threads[i].max )
11032  max = threads[i].max;
11033  break;
11034  }
11035 
11036  threads.Destroy();
11037  m_status += N;
11038  return max;
11039  }
11040 
11046  sample MaximumPixelValue( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1,
11047  int maxProcessors = 0 ) const
11048  {
11049  return MaximumSampleValue( rect, firstChannel, lastChannel, maxProcessors );
11050  }
11051 
11078  template <typename T>
11079  void GetExtremeSampleValues( T& min, T& max,
11080  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1,
11081  int maxProcessors = 0, bool __performanceAnalysis__ = false ) const
11082  {
11083  Rect r = rect;
11084  if ( !ParseSelection( r, firstChannel, lastChannel ) )
11085  {
11086  // ### Required for compatibility with PCL 1.x
11087  P::FromSample( min, P::MinSampleValue() );
11088  max = min;
11089  return;
11090  }
11091 
11092  size_type N = r.IntegerArea()*(1 + lastChannel - firstChannel);
11093  if ( m_status.IsInitializationEnabled() )
11094  m_status.Initialize( "Computing extreme pixel sample values", N );
11095 
11096  Array<size_type> L;
11097  if ( likely( !__performanceAnalysis__ ) )
11098  {
11100  data.algorithm = PerformanceAnalysisAlgorithm::MinMax;
11101  data.length = N;
11102  data.overheadLimit = 32768;
11103  data.itemSize = BytesPerSample();
11104  data.floatingPoint = IsFloatSample();
11105  L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors, N/Thread::OptimalNumberOfThreads( data )/*overheadLimitPx*/ );
11106  }
11107  else
11108  L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors, 1/*overheadLimitPx*/ );
11109 
11110  bool useAffinity = m_parallel && Thread::IsRootThread();
11112  for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
11113  threads.Add( new MinMaxThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ) ) );
11114  if ( threads.Length() > 1 )
11115  {
11116  int n = 0;
11117  for ( MinMaxThread& thread : threads )
11118  thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 );
11119  for ( MinMaxThread& thread : threads )
11120  thread.Wait();
11121  }
11122  else
11123  threads[0].Run();
11124 
11125  sample vmin = P::MinSampleValue();
11126  sample vmax = P::MinSampleValue();
11127  for ( size_type i = 0; i < threads.Length(); ++i )
11128  if ( threads[i].count > 0 )
11129  {
11130  vmin = threads[i].min;
11131  vmax = threads[i].max;
11132  while ( ++i < threads.Length() )
11133  if ( threads[i].count > 0 )
11134  {
11135  if ( threads[i].min < vmin )
11136  vmin = threads[i].min;
11137  if ( vmax < threads[i].max )
11138  vmax = threads[i].max;
11139  }
11140  break;
11141  }
11142 
11143  threads.Destroy();
11144  m_status += N;
11145  P::FromSample( min, vmin );
11146  P::FromSample( max, vmax );
11147  }
11148 
11155  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1,
11156  int maxProcessors = 0 ) const
11157  {
11158  GetExtremeSampleValues( min, max, rect, firstChannel, lastChannel, maxProcessors );
11159  }
11160 
11187  sample LocateMinimumSampleValue( int& xmin, int& ymin,
11188  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1,
11189  int maxProcessors = 0 ) const
11190  {
11191  Rect r = rect;
11192  if ( !ParseSelection( r, firstChannel, lastChannel ) )
11193  {
11194  // ### Required for compatibility with PCL 1.x
11195  xmin = ymin = 0;
11196  return P::MinSampleValue();
11197  }
11198 
11199  size_type N = r.IntegerArea()*(1 + lastChannel - firstChannel);
11200  if ( m_status.IsInitializationEnabled() )
11201  m_status.Initialize( "Locating minimum pixel sample value", N );
11202 
11203  Array<size_type> L;
11204  {
11206  data.algorithm = PerformanceAnalysisAlgorithm::MinMax;
11207  data.length = N;
11208  data.overheadLimit = 32768;
11209  data.itemSize = BytesPerSample();
11210  data.floatingPoint = IsFloatSample();
11211  L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors, N/Thread::OptimalNumberOfThreads( data )/*overheadLimitPx*/ );
11212  }
11213  bool useAffinity = m_parallel && Thread::IsRootThread();
11215  for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
11216  threads.Add( new MinPosThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ) ) );
11217  if ( threads.Length() > 1 )
11218  {
11219  int n = 0;
11220  for ( MinPosThread& thread : threads )
11221  thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 );
11222  for ( MinPosThread& thread : threads )
11223  thread.Wait();
11224  }
11225  else
11226  threads[0].Run();
11227 
11228  xmin = ymin = -1;
11229  sample min = P::MinSampleValue();
11230  for ( size_type i = 0; i < threads.Length(); ++i )
11231  if ( threads[i].count > 0 )
11232  {
11233  min = threads[i].min;
11234  xmin = threads[i].pmin.x;
11235  ymin = threads[i].pmin.y;
11236  while ( ++i < threads.Length() )
11237  if ( threads[i].count > 0 )
11238  if ( threads[i].min < min )
11239  {
11240  min = threads[i].min;
11241  xmin = threads[i].pmin.x;
11242  ymin = threads[i].pmin.y;
11243  }
11244  break;
11245  }
11246 
11247  threads.Destroy();
11248  m_status += N;
11249  return min;
11250  }
11251 
11275  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1,
11276  int maxProcessors = 0 ) const
11277  {
11278  return LocateMinimumSampleValue( pmin.x, pmin.y, rect, firstChannel, lastChannel, maxProcessors );
11279  }
11280 
11286  sample LocateMinimumPixelValue( int& xmin, int& ymin,
11287  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1,
11288  int maxProcessors = 0 ) const
11289  {
11290  return LocateMinimumSampleValue( xmin, ymin, rect, firstChannel, lastChannel, maxProcessors );
11291  }
11292 
11299  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1,
11300  int maxProcessors = 0 ) const
11301  {
11302  return LocateMinimumSampleValue( pmin, rect, firstChannel, lastChannel, maxProcessors );
11303  }
11304 
11331  sample LocateMaximumSampleValue( int& xmax, int& ymax,
11332  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1,
11333  int maxProcessors = 0 ) const
11334  {
11335  Rect r = rect;
11336  if ( !ParseSelection( r, firstChannel, lastChannel ) )
11337  {
11338  // ### Required for compatibility with PCL 1.x
11339  xmax = ymax = 0;
11340  return P::MaxSampleValue();
11341  }
11342 
11343  size_type N = r.IntegerArea()*(1 + lastChannel - firstChannel);
11344  if ( m_status.IsInitializationEnabled() )
11345  m_status.Initialize( "Locating maximum pixel sample value", N );
11346 
11347  Array<size_type> L;
11348  {
11350  data.algorithm = PerformanceAnalysisAlgorithm::MinMax;
11351  data.length = N;
11352  data.overheadLimit = 32768;
11353  data.itemSize = BytesPerSample();
11354  data.floatingPoint = IsFloatSample();
11355  L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors, N/Thread::OptimalNumberOfThreads( data )/*overheadLimitPx*/ );
11356  }
11357  bool useAffinity = m_parallel && Thread::IsRootThread();
11359  for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
11360  threads.Add( new MaxPosThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ) ) );
11361  if ( threads.Length() > 1 )
11362  {
11363  int n = 0;
11364  for ( MaxPosThread& thread : threads )
11365  thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 );
11366  for ( MaxPosThread& thread : threads )
11367  thread.Wait();
11368  }
11369  else
11370  threads[0].Run();
11371 
11372  xmax = ymax = -1;
11373  sample max = P::MinSampleValue();
11374  for ( size_type i = 0; i < threads.Length(); ++i )
11375  if ( threads[i].count > 0 )
11376  {
11377  max = threads[i].max;
11378  xmax = threads[i].pmax.x;
11379  ymax = threads[i].pmax.y;
11380  while ( ++i < threads.Length() )
11381  if ( threads[i].count > 0 )
11382  if ( max < threads[i].max )
11383  {
11384  max = threads[i].max;
11385  xmax = threads[i].pmax.x;
11386  ymax = threads[i].pmax.y;
11387  }
11388  break;
11389  }
11390 
11391  threads.Destroy();
11392  m_status += N;
11393  return max;
11394  }
11395 
11419  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1,
11420  int maxProcessors = 0 ) const
11421  {
11422  return LocateMaximumSampleValue( pmax.x, pmax.y, rect, firstChannel, lastChannel, maxProcessors );
11423  }
11424 
11430  sample LocateMaximumPixelValue( int& xmax, int& ymax,
11431  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1,
11432  int maxProcessors = 0 ) const
11433  {
11434  return LocateMaximumSampleValue( xmax, ymax, rect, firstChannel, lastChannel, maxProcessors );
11435  }
11436 
11443  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1,
11444  int maxProcessors = 0 ) const
11445  {
11446  return LocateMaximumSampleValue( pmax, rect, firstChannel, lastChannel, maxProcessors );
11447  }
11448 
11485  template <typename T>
11486  void LocateExtremeSampleValues( int& xmin, int& ymin, T& min,
11487  int& xmax, int& ymax, T& max,
11488  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1,
11489  int maxProcessors = 0 ) const
11490  {
11491  Rect r = rect;
11492  if ( !ParseSelection( r, firstChannel, lastChannel ) )
11493  {
11494  // ### Required for compatibility with PCL 1.x
11495  xmin = ymin = xmax = ymax = 0;
11496  P::FromSample( min, P::MinSampleValue() );
11497  max = min;
11498  return;
11499  }
11500 
11501  size_type N = r.IntegerArea()*(1 + lastChannel - firstChannel);
11502  if ( m_status.IsInitializationEnabled() )
11503  m_status.Initialize( "Locating extreme pixel sample values", N );
11504 
11505  Array<size_type> L;
11506  {
11508  data.algorithm = PerformanceAnalysisAlgorithm::MinMax;
11509  data.length = N;
11510  data.overheadLimit = 32768;
11511  data.itemSize = BytesPerSample();
11512  data.floatingPoint = IsFloatSample();
11513  L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors, N/Thread::OptimalNumberOfThreads( data )/*overheadLimitPx*/ );
11514  }
11515  bool useAffinity = m_parallel && Thread::IsRootThread();
11517  for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
11518  threads.Add( new MinMaxPosThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ) ) );
11519  if ( threads.Length() > 1 )
11520  {
11521  int n = 0;
11522  for ( MinMaxPosThread& thread : threads )
11523  thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 );
11524  for ( MinMaxPosThread& thread : threads )
11525  thread.Wait();
11526  }
11527  else
11528  threads[0].Run();
11529 
11530  xmin = ymin = xmax = ymax = -1;
11531  sample vmin = P::MinSampleValue();
11532  sample vmax = P::MinSampleValue();
11533  for ( size_type i = 0; i < threads.Length(); ++i )
11534  if ( threads[i].count > 0 )
11535  {
11536  vmin = threads[i].min;
11537  xmin = threads[i].pmin.x;
11538  ymin = threads[i].pmin.y;
11539  vmax = threads[i].max;
11540  xmax = threads[i].pmax.x;
11541  ymax = threads[i].pmax.y;
11542  while ( ++i < threads.Length() )
11543  if ( threads[i].count > 0 )
11544  {
11545  if ( threads[i].min < vmin )
11546  {
11547  vmin = threads[i].min;
11548  xmin = threads[i].pmin.x;
11549  ymin = threads[i].pmin.y;
11550  }
11551  if ( vmax < threads[i].max )
11552  {
11553  vmax = threads[i].max;
11554  xmax = threads[i].pmax.x;
11555  ymax = threads[i].pmax.y;
11556  }
11557  }
11558  break;
11559  }
11560 
11561  threads.Destroy();
11562  m_status += N;
11563  P::FromSample( min, vmin );
11564  P::FromSample( max, vmax );
11565  }
11566 
11594  template <typename T>
11595  void LocateExtremeSampleValues( Point& pmin, T& min,
11596  Point& pmax, T& max,
11597  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1,
11598  int maxProcessors = 0 ) const
11599  {
11600  LocateExtremeSampleValues( pmin.x, pmin.y, min,
11601  pmax.x, pmax.y, max,
11602  rect, firstChannel, lastChannel, maxProcessors );
11603  }
11604 
11630  size_type Count( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1,
11631  int maxProcessors = 0 ) const
11632  {
11633  Rect r = rect;
11634  if ( !ParseSelection( r, firstChannel, lastChannel ) )
11635  return 0;
11636 
11637  size_type N = r.IntegerArea()*(1 + lastChannel - firstChannel);
11638  if ( m_status.IsInitializationEnabled() )
11639  m_status.Initialize( "Counting pixel samples", N );
11640 
11641  if ( !this->IsRangeClippingEnabled() )
11642  {
11643  m_status += N;
11644  return size_type( r.Width() ) * size_type( r.Height() ) * size_type( 1 + lastChannel - firstChannel );
11645  }
11646 
11647  Array<size_type> L;
11648  {
11650  data.algorithm = PerformanceAnalysisAlgorithm::MinMax;
11651  data.length = N;
11652  data.overheadLimit = 32768;
11653  data.itemSize = BytesPerSample();
11654  data.floatingPoint = IsFloatSample();
11655  L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors, N/Thread::OptimalNumberOfThreads( data )/*overheadLimitPx*/ );
11656  }
11657  bool useAffinity = m_parallel && Thread::IsRootThread();
11659  for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
11660  threads.Add( new CountThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ) ) );
11661  if ( threads.Length() > 1 )
11662  {
11663  int n = 0;
11664  for ( CountThread& thread : threads )
11665  thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 );
11666  for ( CountThread& thread : threads )
11667  thread.Wait();
11668  }
11669  else
11670  threads[0].Run();
11671 
11672  size_type count = 0;
11673  for ( const CountThread& thread : threads )
11674  count += thread.count;
11675 
11676  threads.Destroy();
11677  m_status += N;
11678  return count;
11679  }
11680 
11709  double Mean( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1,
11710  int maxProcessors = 0, bool __performanceAnalysis__ = false ) const
11711  {
11712  Rect r = rect;
11713  if ( !ParseSelection( r, firstChannel, lastChannel ) )
11714  return 0;
11715 
11716  size_type N = r.IntegerArea()*(1 + lastChannel - firstChannel);
11717  if ( m_status.IsInitializationEnabled() )
11718  m_status.Initialize( "Computing mean pixel sample value", N );
11719 
11720  Array<size_type> L;
11721  if ( likely( !__performanceAnalysis__ ) )
11722  {
11724  data.algorithm = PerformanceAnalysisAlgorithm::Sum;
11725  data.length = N;
11726  data.overheadLimit = 32768;
11727  data.itemSize = BytesPerSample();
11728  data.floatingPoint = IsFloatSample();
11729  L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors, N/Thread::OptimalNumberOfThreads( data )/*overheadLimitPx*/ );
11730  }
11731  else
11732  L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors, 1/*overheadLimitPx*/ );
11733 
11734  bool useAffinity = m_parallel && Thread::IsRootThread();
11735  ReferenceArray<SumThread> threads;
11736  for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
11737  threads.Add( new SumThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ) ) );
11738  if ( threads.Length() > 1 )
11739  {
11740  int n = 0;
11741  for ( SumThread& thread : threads )
11742  thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 );
11743  for ( SumThread& thread : threads )
11744  thread.Wait();
11745  }
11746  else
11747  threads[0].Run();
11748 
11749  double s = 0;
11750  double e = 0;
11751  size_type n = 0;
11752  for ( const SumThread& thread : threads )
11753  {
11754  double y = thread.s - e;
11755  double t = s + y;
11756  e = (t - s) - y;
11757  s = t;
11758  n += thread.n;
11759  }
11760 
11761  threads.Destroy();
11762 
11763  m_status += N;
11764 
11765  if ( n == 0 )
11766  return 0;
11767  return s/n;
11768  }
11769 
11795  double Median( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1,
11796  int maxProcessors = 0 ) const
11797  {
11798  Rect r = rect;
11799  if ( !ParseSelection( r, firstChannel, lastChannel ) )
11800  return 0;
11801 
11802  size_type N = r.IntegerArea()*(1 + lastChannel - firstChannel);
11803  if ( m_status.IsInitializationEnabled() )
11804  m_status.Initialize( "Computing median pixel sample value", N );
11805 
11806  if ( N <= 4096 )
11807  {
11808  SmpThread S( *this, r, firstChannel, lastChannel, 0, r.Height() );
11809  S.Run();
11810  if ( S.n == 0 )
11811  {
11812  m_status += N;
11813  return 0;
11814  }
11815  double m = double( *pcl::Select( S.samples.Begin(), S.samples.At( S.n ), S.n >> 1 ) )/double( P::MaxSampleValue() );
11816  if ( S.n & 1 )
11817  {
11818  m_status += N;
11819  return m;
11820  }
11821  m = (m + double( *pcl::Select( S.samples.Begin(), S.samples.At( S.n ), (S.n >> 1)-1 ) )/double( P::MaxSampleValue() ))/2;
11822  m_status += N;
11823  return m;
11824  }
11825 
11826  bool useAffinity = m_parallel && Thread::IsRootThread();
11827 
11828  double low, high;
11829  size_type count = 0;
11830  {
11831  Array<size_type> L;
11832  {
11834  data.algorithm = PerformanceAnalysisAlgorithm::MinMax;
11835  data.length = N;
11836  data.overheadLimit = 32768;
11837  data.itemSize = BytesPerSample();
11838  data.floatingPoint = IsFloatSample();
11839  L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors, N/Thread::OptimalNumberOfThreads( data )/*overheadLimitPx*/ );
11840  }
11842  for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
11843  threads << new MinMaxThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ) );
11844 
11845  if ( threads.Length() > 1 )
11846  {
11847  int i = 0;
11848  for ( MinMaxThread& thread : threads )
11849  thread.Start( ThreadPriority::DefaultMax, useAffinity ? i++ : -1 );
11850  for ( MinMaxThread& thread : threads )
11851  thread.Wait();
11852  }
11853  else
11854  threads[0].Run();
11855 
11856  sample slow = 0, shigh = 0;
11857  for ( size_type i = 0; i < threads.Length(); ++i )
11858  if ( threads[i].count > 0 )
11859  {
11860  slow = threads[i].min;
11861  shigh = threads[i].max;
11862  count = threads[i].count;
11863  while ( ++i < threads.Length() )
11864  if ( threads[i].count > 0 )
11865  {
11866  if ( threads[i].min < slow )
11867  slow = threads[i].min;
11868  if ( shigh < threads[i].max )
11869  shigh = threads[i].max;
11870  count += threads[i].count;
11871  }
11872  break;
11873  }
11874 
11875  threads.Destroy();
11876 
11877  low = double( slow );
11878  high = double( shigh );
11879  }
11880 
11881  if ( count == 0 )
11882  {
11883  m_status += N;
11884  return 0;
11885  }
11886 
11887  const double eps = P::IsComplexSample() ? 2*std::numeric_limits<double>::epsilon() :
11888  (P::IsFloatSample() ? 2*std::numeric_limits<typename P::component>::epsilon() :
11889  0.5/Pow2( double( P::BitsPerSample() ) ));
11890  if ( high - low < eps )
11891  {
11892  m_status += N;
11893  return low/double( P::MaxSampleValue() );
11894  }
11895 
11896  Array<size_type> L;
11897  {
11899  data.algorithm = PerformanceAnalysisAlgorithm::FastMedian;
11900  data.length = N;
11901  data.overheadLimit = __PCL_MEDIAN_HISTOGRAM_OVERHEAD;
11902  data.itemSize = BytesPerSample();
11903  data.floatingPoint = IsFloatSample();
11904  L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors, N/Thread::OptimalNumberOfThreads( data )/*overheadLimitPx*/ );
11905  }
11907  for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
11908  threads << new HistogramThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ), low, high );
11909 
11910  double mh = 0, l0 = low;
11911  SzVector H0;
11912 
11913  for ( size_type n = 0, n2 = count >> 1, step = 0, it = 0;; ++it )
11914  {
11915  SzVector H;
11916  if ( it == 0 && step )
11917  H = H0;
11918  else
11919  {
11920  if ( threads.Length() > 1 )
11921  {
11922  int i = 0;
11923  for ( HistogramThread& thread : threads )
11924  thread.Start( ThreadPriority::DefaultMax, useAffinity ? i++ : -1 );
11925  for ( HistogramThread& thread : threads )
11926  thread.Wait();
11927  }
11928  else
11929  threads[0].Run();
11930 
11931  H = threads[0].H;
11932  for ( size_type i = 1; i < threads.Length(); ++i )
11933  H += threads[i].H;
11934  if ( it == 0 )
11935  if ( (count & 1) == 0 )
11936  H0 = H;
11937  }
11938 
11939  for ( int i = 0; ; n += H[i++] )
11940  if ( n + H[i] > n2 )
11941  {
11942  double range = high - low;
11943  high = (range * (i + 1))/(__PCL_MEDIAN_HISTOGRAM_LENGTH - 1) + low;
11944  low = (range * i)/(__PCL_MEDIAN_HISTOGRAM_LENGTH - 1) + low;
11945  if ( high - low < eps )
11946  {
11947  if ( count & 1 )
11948  {
11949  threads.Destroy();
11950  m_status += N;
11951  return low/double( P::MaxSampleValue() );
11952  }
11953  if ( step )
11954  {
11955  threads.Destroy();
11956  m_status += N;
11957  return (low + mh)/2/double( P::MaxSampleValue() );
11958  }
11959  mh = low;
11960  low = l0;
11961  n = 0;
11962  --n2;
11963  ++step;
11964  it = 0;
11965  }
11966  break;
11967  }
11968  }
11969  }
11970 
12002  double OrderStatistic( double k,
12003  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1,
12004  int maxProcessors = 0 ) const
12005  {
12006  if ( k < 0 || k > 1 )
12007  return 0;
12008 
12009  Rect r = rect;
12010  if ( !ParseSelection( r, firstChannel, lastChannel ) )
12011  return 0;
12012 
12013  size_type N = r.IntegerArea()*(1 + lastChannel - firstChannel);
12014  if ( m_status.IsInitializationEnabled() )
12015  m_status.Initialize( "Computing order statistic", N );
12016 
12017  if ( N <= 4096 )
12018  {
12019  SmpThread S( *this, r, firstChannel, lastChannel, 0, r.Height() );
12020  S.Run();
12021  m_status += N;
12022  if ( S.n == 0 )
12023  return 0;
12024  return double( *pcl::Select( S.samples.Begin(), S.samples.At( S.n ), distance_type( k*(S.n - 1) ) ) )/double( P::MaxSampleValue() );
12025  }
12026 
12027  bool useAffinity = m_parallel && Thread::IsRootThread();
12028 
12029  double low, high;
12030  size_type count = 0;
12031  {
12032  Array<size_type> L;
12033  {
12035  data.algorithm = PerformanceAnalysisAlgorithm::MinMax;
12036  data.length = N;
12037  data.overheadLimit = 32768;
12038  data.itemSize = BytesPerSample();
12039  data.floatingPoint = IsFloatSample();
12040  L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors, N/Thread::OptimalNumberOfThreads( data )/*overheadLimitPx*/ );
12041  }
12043  for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
12044  threads << new MinMaxThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ) );
12045 
12046  if ( threads.Length() > 1 )
12047  {
12048  int i = 0;
12049  for ( MinMaxThread& thread : threads )
12050  thread.Start( ThreadPriority::DefaultMax, useAffinity ? i++ : -1 );
12051  for ( MinMaxThread& thread : threads )
12052  thread.Wait();
12053  }
12054  else
12055  threads[0].Run();
12056 
12057  sample slow = 0, shigh = 0;
12058  for ( size_type i = 0; i < threads.Length(); ++i )
12059  if ( threads[i].count > 0 )
12060  {
12061  slow = threads[i].min;
12062  shigh = threads[i].max;
12063  count = threads[i].count;
12064  while ( ++i < threads.Length() )
12065  if ( threads[i].count > 0 )
12066  {
12067  if ( threads[i].min < slow )
12068  slow = threads[i].min;
12069  if ( shigh < threads[i].max )
12070  shigh = threads[i].max;
12071  count += threads[i].count;
12072  }
12073  break;
12074  }
12075 
12076  threads.Destroy();
12077 
12078  low = double( slow );
12079  high = double( shigh );
12080  }
12081 
12082  if ( count == 0 )
12083  {
12084  m_status += N;
12085  return 0;
12086  }
12087 
12088  if ( k == 1 )
12089  {
12090  m_status += N;
12091  return high/double( P::MaxSampleValue() );
12092  }
12093 
12094  const double eps = P::IsComplexSample() ? 2*std::numeric_limits<double>::epsilon() :
12095  (P::IsFloatSample() ? 2*std::numeric_limits<typename P::component>::epsilon() :
12096  0.5/Pow2( double( P::BitsPerSample() ) ));
12097  if ( k == 0 || high - low < eps )
12098  {
12099  m_status += N;
12100  return low/double( P::MaxSampleValue() );
12101  }
12102 
12103  size_type index = size_type( k*(count - 1) );
12104 
12105  Array<size_type> L;
12106  {
12108  data.algorithm = PerformanceAnalysisAlgorithm::FastMedian;
12109  data.length = N;
12110  data.overheadLimit = __PCL_MEDIAN_HISTOGRAM_OVERHEAD;
12111  data.itemSize = BytesPerSample();
12112  data.floatingPoint = IsFloatSample();
12113  L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors, N/Thread::OptimalNumberOfThreads( data )/*overheadLimitPx*/ );
12114  }
12116  for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
12117  threads << new HistogramThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ), low, high );
12118 
12119  for ( size_type n = 0;; )
12120  {
12121  if ( threads.Length() > 1 )
12122  {
12123  int i = 0;
12124  for ( HistogramThread& thread : threads )
12125  thread.Start( ThreadPriority::DefaultMax, useAffinity ? i++ : -1 );
12126  for ( HistogramThread& thread : threads )
12127  thread.Wait();
12128  }
12129  else
12130  threads[0].Run();
12131 
12132  SzVector H = threads[0].H;
12133  for ( size_type i = 1; i < threads.Length(); ++i )
12134  H += threads[i].H;
12135 
12136  for ( int i = 0; ; n += H[i++] )
12137  if ( n + H[i] > index )
12138  {
12139  double range = high - low;
12140  high = (range * (i + 1))/(__PCL_MEDIAN_HISTOGRAM_LENGTH - 1) + low;
12141  low = (range * i)/(__PCL_MEDIAN_HISTOGRAM_LENGTH - 1) + low;
12142  if ( high - low < eps )
12143  {
12144  threads.Destroy();
12145  m_status += N;
12146  return low/double( P::MaxSampleValue() );
12147  }
12148  break;
12149  }
12150  }
12151  }
12152 
12187  double Variance( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1,
12188  int maxProcessors = 0 ) const
12189  {
12190  Rect r = rect;
12191  if ( !ParseSelection( r, firstChannel, lastChannel ) )
12192  return 0;
12193 
12194  size_type N = r.IntegerArea()*(1 + lastChannel - firstChannel);
12195  if ( m_status.IsInitializationEnabled() )
12196  m_status.Initialize( "Computing variance", N );
12197 
12198  Array<size_type> L;
12199  {
12201  data.algorithm = PerformanceAnalysisAlgorithm::Sum;
12202  data.length = N;
12203  data.overheadLimit = 32768;
12204  data.itemSize = BytesPerSample();
12205  data.floatingPoint = IsFloatSample();
12206  L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors, N/Thread::OptimalNumberOfThreads( data )/*overheadLimitPx*/ );
12207  }
12208  bool useAffinity = m_parallel && Thread::IsRootThread();
12209  ReferenceArray<SumThread> sumThreads;
12210  for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
12211  sumThreads.Add( new SumThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ) ) );
12212  if ( sumThreads.Length() > 1 )
12213  {
12214  int n = 0;
12215  for ( SumThread& thread : sumThreads )
12216  thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 );
12217  for ( SumThread& thread : sumThreads )
12218  thread.Wait();
12219  }
12220  else
12221  sumThreads[0].Run();
12222 
12223  double s = 0;
12224  double e = 0;
12225  size_type n = 0;
12226  for ( const SumThread& thread : sumThreads )
12227  {
12228  double y = thread.s - e;
12229  double t = s + y;
12230  e = (t - s) - y;
12231  s = t;
12232  n += thread.n;
12233  }
12234 
12235  sumThreads.Destroy();
12236 
12237  if ( n < 2 )
12238  return 0;
12239  double mean = s/n;
12240 
12241  ReferenceArray<VarThread> varThreads;
12242  for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
12243  varThreads.Add( new VarThread( *this, mean, r, firstChannel, lastChannel, n, n + int( L[i] ) ) );
12244  if ( varThreads.Length() > 1 )
12245  {
12246  int n = 0;
12247  for ( VarThread& thread : varThreads )
12248  thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 );
12249  for ( VarThread& thread : varThreads )
12250  thread.Wait();
12251  }
12252  else
12253  varThreads[0].Run();
12254 
12255  double var = 0, eps = 0;
12256  for ( const VarThread& thread : varThreads )
12257  var += thread.var, eps += thread.eps;
12258 
12259  varThreads.Destroy();
12260  m_status += N;
12261  return (var - eps*eps/n)/(n - 1);
12262  }
12263 
12296  double StdDev( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1,
12297  int maxProcessors = 0 ) const
12298  {
12299  return pcl::Sqrt( Variance( rect, firstChannel, lastChannel, maxProcessors ) );
12300  }
12301 
12341  double AvgDev( double center,
12342  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1,
12343  int maxProcessors = 0 ) const
12344  {
12345  Rect r = rect;
12346  if ( !ParseSelection( r, firstChannel, lastChannel ) )
12347  return 0;
12348 
12349  size_type N = r.IntegerArea()*(1 + lastChannel - firstChannel);
12350  if ( m_status.IsInitializationEnabled() )
12351  m_status.Initialize( "Computing average absolute deviation", N );
12352 
12353  Array<size_type> L;
12354  {
12356  data.algorithm = PerformanceAnalysisAlgorithm::Sum;
12357  data.length = N;
12358  data.overheadLimit = 32768;
12359  data.itemSize = BytesPerSample();
12360  data.floatingPoint = IsFloatSample();
12361  L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors, N/Thread::OptimalNumberOfThreads( data )/*overheadLimitPx*/ );
12362  }
12363  bool useAffinity = m_parallel && Thread::IsRootThread();
12365  for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
12366  threads.Add( new SumAbsDevThread( *this, center, r, firstChannel, lastChannel, n, n + int( L[i] ) ) );
12367  if ( threads.Length() > 1 )
12368  {
12369  int n = 0;
12370  for ( SumAbsDevThread& thread : threads )
12371  thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 );
12372  for ( SumAbsDevThread& thread : threads )
12373  thread.Wait();
12374  }
12375  else
12376  threads[0].Run();
12377 
12378  double s = 0;
12379  double e = 0;
12380  size_type n = 0;
12381  for ( const SumAbsDevThread& thread : threads )
12382  {
12383  double y = thread.s - e;
12384  double t = s + y;
12385  e = (t - s) - y;
12386  s = t;
12387  n += thread.n;
12388  }
12389 
12390  threads.Destroy();
12391 
12392  m_status += N;
12393 
12394  if ( n == 0 )
12395  return 0;
12396  return s/n;
12397  }
12398 
12420  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1,
12421  int maxProcessors = 0 ) const
12422  {
12423  Rect r = rect;
12424  if ( !ParseSelection( r, firstChannel, lastChannel ) )
12425  return 0;
12426 
12427  size_type N = r.IntegerArea()*(1 + lastChannel - firstChannel);
12428  if ( m_status.IsInitializationEnabled() )
12429  m_status.Initialize( "Computing two-sided average absolute deviation", N );
12430 
12431  Array<size_type> L;
12432  {
12434  data.algorithm = PerformanceAnalysisAlgorithm::Sum;
12435  data.length = N;
12436  data.overheadLimit = 32768;
12437  data.itemSize = BytesPerSample();
12438  data.floatingPoint = IsFloatSample();
12439  L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors, N/Thread::OptimalNumberOfThreads( data )/*overheadLimitPx*/ );
12440  }
12441  bool useAffinity = m_parallel && Thread::IsRootThread();
12443  for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
12444  threads.Add( new TwoSidedSumAbsDevThread( *this, center, r, firstChannel, lastChannel, n, n + int( L[i] ) ) );
12445  if ( threads.Length() > 1 )
12446  {
12447  int n = 0;
12448  for ( TwoSidedSumAbsDevThread& thread : threads )
12449  thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 );
12450  for ( TwoSidedSumAbsDevThread& thread : threads )
12451  thread.Wait();
12452  }
12453  else
12454  threads[0].Run();
12455 
12456  double s0 = 0, s1 = 0;
12457  double e0 = 0, e1 = 0;
12458  size_type n0 = 0, n1 = 0;
12459  for ( const TwoSidedSumAbsDevThread& thread : threads )
12460  {
12461  double y = thread.s0 - e0;
12462  double t = s0 + y;
12463  e0 = (t - s0) - y;
12464  s0 = t;
12465  n0 += thread.n0;
12466  y = thread.s1 - e1;
12467  t = s1 + y;
12468  e1 = (t - s1) - y;
12469  s1 = t;
12470  n1 += thread.n1;
12471  }
12472 
12473  threads.Destroy();
12474 
12475  m_status += N;
12476 
12477  return { (n0 > 0) ? s0/n0 : 0.0,
12478  (n1 > 0) ? s1/n1 : 0.0 };
12479  }
12480 
12515  double MAD( double center,
12516  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1,
12517  int maxProcessors = 0 ) const
12518  {
12519  Rect r = rect;
12520  if ( !ParseSelection( r, firstChannel, lastChannel ) )
12521  return 0;
12522 
12523  size_type N = r.IntegerArea()*(1 + lastChannel - firstChannel);
12524  if ( m_status.IsInitializationEnabled() )
12525  m_status.Initialize( "Computing median absolute deviation", N );
12526 
12527  if ( N <= 4096 )
12528  {
12529  AbsDevSmpThread S( *this, center, r, firstChannel, lastChannel, 0, r.Height() );
12530  S.Run();
12531  if ( S.n == 0 )
12532  {
12533  m_status += N;
12534  return 0;
12535  }
12536  double m = *pcl::Select( S.values.Begin(), S.values.At( S.n ), S.n >> 1 );
12537  if ( S.n & 1 )
12538  {
12539  m_status += N;
12540  return m;
12541  }
12542  m = (m + *pcl::Select( S.values.Begin(), S.values.At( S.n ), (S.n >> 1)-1 ))/2;
12543  m_status += N;
12544  return m;
12545  }
12546 
12547  bool useAffinity = m_parallel && Thread::IsRootThread();
12548 
12549  double low, high;
12550  size_type count = 0;
12551  {
12552  Array<size_type> L;
12553  {
12555  data.algorithm = PerformanceAnalysisAlgorithm::MinMax;
12556  data.length = N;
12557  data.overheadLimit = 32768;
12558  data.itemSize = BytesPerSample();
12559  data.floatingPoint = IsFloatSample();
12560  L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors, N/Thread::OptimalNumberOfThreads( data )/*overheadLimitPx*/ );
12561  }
12563  for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
12564  threads << new ExtremeAbsDevThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ), center );
12565 
12566  if ( threads.Length() > 1 )
12567  {
12568  int n = 0;
12569  for ( ExtremeAbsDevThread& thread : threads )
12570  thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 );
12571  for ( ExtremeAbsDevThread& thread : threads )
12572  thread.Wait();
12573  }
12574  else
12575  threads[0].Run();
12576 
12577  for ( size_type i = 0; i < threads.Length(); ++i )
12578  if ( threads[i].count > 0 )
12579  {
12580  low = threads[i].minAbsDev;
12581  high = threads[i].maxAbsDev;
12582  count += threads[i].count;
12583  while ( ++i < threads.Length() )
12584  if ( threads[i].count > 0 )
12585  {
12586  if ( threads[i].minAbsDev < low )
12587  low = threads[i].minAbsDev;
12588  if ( threads[i].maxAbsDev > high )
12589  high = threads[i].maxAbsDev;
12590  count += threads[i].count;
12591  }
12592  break;
12593  }
12594 
12595  threads.Destroy();
12596  }
12597 
12598  const double eps = 2*std::numeric_limits<double>::epsilon();
12599  if ( count == 0 || high - low < eps )
12600  {
12601  m_status += N;
12602  return 0;
12603  }
12604 
12605  Array<size_type> L;
12606  {
12608  data.algorithm = PerformanceAnalysisAlgorithm::FastMedian;
12609  data.length = N;
12610  data.overheadLimit = __PCL_MEDIAN_HISTOGRAM_OVERHEAD;
12611  data.itemSize = BytesPerSample();
12612  data.floatingPoint = IsFloatSample();
12613  L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors, N/Thread::OptimalNumberOfThreads( data )/*overheadLimitPx*/ );
12614  }
12616  for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
12617  threads << new AbsDevHistogramThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ), center, low, high );
12618 
12619  double mh = 0, l0 = low;
12620  SzVector H0;
12621 
12622  for ( size_type n = 0, n2 = count >> 1, step = 0, it = 0;; ++it )
12623  {
12624  SzVector H;
12625  if ( it == 0 && step )
12626  H = H0;
12627  else
12628  {
12629  if ( threads.Length() > 1 )
12630  {
12631  int i = 0;
12632  for ( AbsDevHistogramThread& thread : threads )
12633  thread.Start( ThreadPriority::DefaultMax, useAffinity ? i++ : -1 );
12634  for ( AbsDevHistogramThread& thread : threads )
12635  thread.Wait();
12636  }
12637  else
12638  threads[0].Run();
12639 
12640  H = threads[0].H;
12641  for ( size_type i = 1; i < threads.Length(); ++i )
12642  H += threads[i].H;
12643  if ( it == 0 )
12644  if ( (count & 1) == 0 )
12645  H0 = H;
12646  }
12647 
12648  for ( int i = 0; ; n += H[i++] )
12649  if ( n + H[i] > n2 )
12650  {
12651  double range = high - low;
12652  high = (range * (i + 1))/(__PCL_MEDIAN_HISTOGRAM_LENGTH - 1) + low;
12653  low = (range * i)/(__PCL_MEDIAN_HISTOGRAM_LENGTH - 1) + low;
12654  if ( high - low < eps )
12655  {
12656  if ( count & 1 )
12657  {
12658  threads.Destroy();
12659  m_status += N;
12660  return low;
12661  }
12662  if ( step )
12663  {
12664  threads.Destroy();
12665  m_status += N;
12666  return (low + mh)/2;
12667  }
12668  mh = low;
12669  low = l0;
12670  n = 0;
12671  --n2;
12672  ++step;
12673  it = 0;
12674  }
12675  break;
12676  }
12677  }
12678  }
12679 
12701  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1,
12702  int maxProcessors = 0 ) const
12703  {
12704  Rect r = rect;
12705  if ( !ParseSelection( r, firstChannel, lastChannel ) )
12706  return 0;
12707 
12708  size_type N = r.IntegerArea()*(1 + lastChannel - firstChannel);
12709  if ( m_status.IsInitializationEnabled() )
12710  m_status.Initialize( "Computing two-sided median absolute deviation", N );
12711 
12712  if ( N <= 4096 )
12713  {
12714  TwoSidedAbsDevSmpThread S( *this, center, r, firstChannel, lastChannel, 0, r.Height() );
12715  S.Run();
12716  m_status += N;
12717  return { pcl::Median( S.values.Begin(), S.p ),
12718  pcl::Median( S.q, S.values.End() ) };
12719  }
12720 
12721  bool useAffinity = m_parallel && Thread::IsRootThread();
12722 
12723  double minLow = 0, minHigh = 0, maxLow = 0, maxHigh = 0;
12724  size_type nLow = 0, nHigh = 0;
12725  {
12726  Array<size_type> L;
12727  {
12729  data.algorithm = PerformanceAnalysisAlgorithm::MinMax;
12730  data.length = N;
12731  data.overheadLimit = 32768;
12732  data.itemSize = BytesPerSample();
12733  data.floatingPoint = IsFloatSample();
12734  L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors, N/Thread::OptimalNumberOfThreads( data )/*overheadLimitPx*/ );
12735  }
12737  for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
12738  threads << new TwoSidedExtremeAbsDevThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ), center );
12739 
12740  if ( threads.Length() > 1 )
12741  {
12742  int n = 0;
12743  for ( TwoSidedExtremeAbsDevThread& thread : threads )
12744  thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 );
12745  for ( TwoSidedExtremeAbsDevThread& thread : threads )
12746  thread.Wait();
12747  }
12748  else
12749  threads[0].Run();
12750 
12751  for ( size_type i = 0; i < threads.Length(); ++i )
12752  if ( threads[i].nLow > 0 )
12753  {
12754  minLow = threads[i].minAbsDevLow;
12755  maxLow = threads[i].maxAbsDevLow;
12756  nLow = threads[i].nLow;
12757  while ( ++i < threads.Length() )
12758  if ( threads[i].nLow > 0 )
12759  {
12760  if ( threads[i].minAbsDevLow < minLow )
12761  minLow = threads[i].minAbsDevLow;
12762  if ( threads[i].maxAbsDevLow > maxLow )
12763  maxLow = threads[i].maxAbsDevLow;
12764  nLow += threads[i].nLow;
12765  }
12766  break;
12767  }
12768 
12769  for ( size_type i = 0; i < threads.Length(); ++i )
12770  if ( threads[i].nHigh > 0 )
12771  {
12772  minHigh = threads[i].minAbsDevHigh;
12773  maxHigh = threads[i].maxAbsDevHigh;
12774  nHigh = threads[i].nHigh;
12775  while ( ++i < threads.Length() )
12776  if ( threads[i].nHigh > 0 )
12777  {
12778  if ( threads[i].minAbsDevHigh < minHigh )
12779  minHigh = threads[i].minAbsDevHigh;
12780  if ( threads[i].maxAbsDevHigh > maxHigh )
12781  maxHigh = threads[i].maxAbsDevHigh;
12782  nHigh += threads[i].nHigh;
12783  }
12784  break;
12785  }
12786 
12787  threads.Destroy();
12788  }
12789 
12790  Array<size_type> L;
12791  {
12793  data.algorithm = PerformanceAnalysisAlgorithm::FastMedian;
12794  data.length = N;
12795  data.overheadLimit = __PCL_MEDIAN_HISTOGRAM_OVERHEAD;
12796  data.itemSize = BytesPerSample();
12797  data.floatingPoint = IsFloatSample();
12798  L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors, N/Thread::OptimalNumberOfThreads( data )/*overheadLimitPx*/ );
12799  }
12800  int side;
12801  double sideLow, sideHigh;
12803  for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
12804  threads << new TwoSidedAbsDevHistogramThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ), center, side, sideLow, sideHigh );
12805 
12806  const double eps = 2*std::numeric_limits<double>::epsilon();
12807  double mad[ 2 ];
12808  for ( side = 0; side < 2; ++side )
12809  {
12810  size_type n = side ? nHigh : nLow;
12811  if ( n < 2 )
12812  {
12813  mad[side] = 0;
12814  continue;
12815  }
12816 
12817  sideLow = side ? minHigh : minLow;
12818  sideHigh = side ? maxHigh : maxLow;
12819  if ( sideHigh - sideLow < eps )
12820  {
12821  mad[side] = 0;
12822  continue;
12823  }
12824 
12825  double mh = 0, h0 = sideHigh;
12826  SzVector H0;
12827 
12828  for ( size_type count = 0, n2 = n >> 1, step = 0, it = 0;; ++it )
12829  {
12830  SzVector H;
12831  if ( it == 0 && step )
12832  H = H0;
12833  else
12834  {
12835  if ( threads.Length() > 1 )
12836  {
12837  int i = 0;
12838  for ( TwoSidedAbsDevHistogramThread& thread : threads )
12839  thread.Start( ThreadPriority::DefaultMax, useAffinity ? i++ : -1 );
12840  for ( TwoSidedAbsDevHistogramThread& thread : threads )
12841  thread.Wait();
12842  }
12843  else
12844  threads[0].Run();
12845 
12846  H = threads[0].H;
12847  for ( size_type i = 1; i < threads.Length(); ++i )
12848  H += threads[i].H;
12849  if ( it == 0 )
12850  if ( (n & 1) == 0 )
12851  H0 = H;
12852  }
12853 
12854  for ( int i = 0; ; count += H[i++] )
12855  if ( count + H[i] > n2 )
12856  {
12857  double range = sideHigh - sideLow;
12858  sideHigh = (range * (i + 1))/(__PCL_MEDIAN_HISTOGRAM_LENGTH - 1) + sideLow;
12859  sideLow = (range * i)/(__PCL_MEDIAN_HISTOGRAM_LENGTH - 1) + sideLow;
12860  if ( sideHigh - sideLow < eps )
12861  {
12862  if ( n & 1 )
12863  {
12864  mad[side] = sideLow;
12865  goto __madNextSide;
12866  }
12867  if ( step )
12868  {
12869  mad[side] = (sideLow + mh)/2;
12870  goto __madNextSide;
12871  }
12872  mh = sideLow;
12873  sideLow = 0;
12874  sideHigh = h0;
12875  count = 0;
12876  --n2;
12877  ++step;
12878  it = 0;
12879  }
12880  break;
12881  }
12882  }
12883 
12884 __madNextSide:
12885 ;
12886  }
12887 
12888  threads.Destroy();
12889  m_status += N;
12890  return { mad[0], mad[1] };
12891  }
12892 
12937  double BiweightMidvariance( double center, double sigma, int k = 9, bool reducedLength = false,
12938  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1,
12939  int maxProcessors = 0, bool __performanceAnalysis__ = false ) const
12940  {
12941  Rect r = rect;
12942  if ( !ParseSelection( r, firstChannel, lastChannel ) )
12943  return 0;
12944 
12945  size_type N = r.IntegerArea()*(1 + lastChannel - firstChannel);
12946  double kd = k * sigma;
12947  if ( kd < 0 || 1 + kd == 1 )
12948  {
12949  m_status += N;
12950  return 0;
12951  }
12952 
12953  if ( m_status.IsInitializationEnabled() )
12954  m_status.Initialize( "Computing biweight midvariance", N );
12955 
12956  Array<size_type> L;
12957  if ( likely( !__performanceAnalysis__ ) )
12958  {
12960  data.algorithm = PerformanceAnalysisAlgorithm::BiweightMidvariance;
12961  data.length = N;
12962  data.overheadLimit = 32768;
12963  data.itemSize = BytesPerSample();
12964  data.floatingPoint = IsFloatSample();
12965  L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors, N/Thread::OptimalNumberOfThreads( data )/*overheadLimitPx*/ );
12966  }
12967  else
12968  L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors, 1/*overheadLimitPx*/ );
12969 
12970  bool useAffinity = m_parallel && Thread::IsRootThread();
12972  for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
12973  threads.Add( new BWMVThread( *this, center, kd, r, firstChannel, lastChannel, n, n + int( L[i] ) ) );
12974  if ( threads.Length() > 1 )
12975  {
12976  int n = 0;
12977  for ( BWMVThread& thread : threads )
12978  thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 );
12979  for ( BWMVThread& thread : threads )
12980  thread.Wait();
12981  }
12982  else
12983  threads[0].Run();
12984 
12985  double num = 0, den = 0;
12986  size_type n = 0, nr = 0;
12987  for ( const BWMVThread& thread : threads )
12988  {
12989  num += thread.num;
12990  den += thread.den;
12991  n += thread.n;
12992  nr += thread.nr;
12993  }
12994 
12995  threads.Destroy();
12996 
12997  m_status += N;
12998  den *= den;
12999  return (n >= 2 && 1 + den != 1) ? (reducedLength ? nr : n)*num/den : 0.0;
13000  }
13001 
13030  TwoSidedEstimate TwoSidedBiweightMidvariance( double center, const TwoSidedEstimate& sigma, int k = 9, bool reducedLength = false,
13031  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1,
13032  int maxProcessors = 0 ) const
13033  {
13034  Rect r = rect;
13035  if ( !ParseSelection( r, firstChannel, lastChannel ) )
13036  return 0;
13037 
13038  size_type N = r.IntegerArea()*(1 + lastChannel - firstChannel);
13039  double kd0 = k * sigma.low;
13040  double kd1 = k * sigma.high;
13041  if ( kd0 < 0 || 1 + kd0 == 1 || kd1 < 0 || 1 + kd1 == 1 )
13042  {
13043  m_status += N;
13044  return 0;
13045  }
13046 
13047  if ( m_status.IsInitializationEnabled() )
13048  m_status.Initialize( "Computing two-sided biweight midvariance", N );
13049 
13050  Array<size_type> L;
13051  {
13053  data.algorithm = PerformanceAnalysisAlgorithm::BiweightMidvariance;
13054  data.length = N;
13055  data.overheadLimit = 32768;
13056  data.itemSize = BytesPerSample();
13057  data.floatingPoint = IsFloatSample();
13058  L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors, N/Thread::OptimalNumberOfThreads( data )/*overheadLimitPx*/ );
13059  }
13060  bool useAffinity = m_parallel && Thread::IsRootThread();
13062  for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
13063  threads.Add( new TwoSidedBWMVThread( *this, center, kd0, kd1, r, firstChannel, lastChannel, n, n + int( L[i] ) ) );
13064  if ( threads.Length() > 1 )
13065  {
13066  int n = 0;
13067  for ( TwoSidedBWMVThread& thread : threads )
13068  thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 );
13069  for ( TwoSidedBWMVThread& thread : threads )
13070  thread.Wait();
13071  }
13072  else
13073  threads[0].Run();
13074 
13075  double num0 = 0, den0 = 0, num1 = 0, den1 = 0;
13076  size_type n0 = 0, n1 = 0, nr0 = 0, nr1 = 0;
13077  for ( const TwoSidedBWMVThread& thread : threads )
13078  {
13079  num0 += thread.num0;
13080  den0 += thread.den0;
13081  num1 += thread.num1;
13082  den1 += thread.den1;
13083  n0 += thread.n0;
13084  n1 += thread.n1;
13085  nr0 += thread.nr0;
13086  nr1 += thread.nr1;
13087  }
13088 
13089  threads.Destroy();
13090 
13091  m_status += N;
13092 
13093  den0 *= den0;
13094  den1 *= den1;
13095  return { (n0 >= 2 && 1 + den0 != 1) ? (reducedLength ? nr0 : n0)*num0/den0 : 0.0,
13096  (n1 >= 2 && 1 + den1 != 1) ? (reducedLength ? nr1 : n1)*num1/den1 : 0.0 };
13097  }
13098 
13142  double BendMidvariance( double center, double beta = 0.2,
13143  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1,
13144  int maxProcessors = 0 ) const
13145  {
13146  Rect r = rect;
13147  if ( !ParseSelection( r, firstChannel, lastChannel ) )
13148  return 0;
13149 
13150  size_type N = r.IntegerArea()*(1 + lastChannel - firstChannel);
13151  if ( m_status.IsInitializationEnabled() )
13152  m_status.Initialize( "Computing percentage bend midvariance", N );
13153 
13154  Array<size_type> L;
13155  {
13157  data.algorithm = PerformanceAnalysisAlgorithm::Sampling;
13158  data.length = N;
13159  data.overheadLimit = 32768;
13160  data.itemSize = BytesPerSample();
13161  data.floatingPoint = IsFloatSample();
13162  L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors, N/Thread::OptimalNumberOfThreads( data )/*overheadLimitPx*/ );
13163  }
13164  bool useAffinity = m_parallel && Thread::IsRootThread();
13166  for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
13167  threads.Add( new DSmpThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ) ) );
13168  if ( threads.Length() > 1 )
13169  {
13170  int n = 0;
13171  for ( DSmpThread& thread : threads )
13172  thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 );
13173  for ( DSmpThread& thread : threads )
13174  thread.Wait();
13175  }
13176  else
13177  threads[0].Run();
13178 
13179  Array<double> values;
13180  for ( DSmpThread& thread : threads )
13181  if ( thread.n > 0 )
13182  {
13183  values.Add( thread.values.Begin(), thread.values.At( thread.n ) );
13184  thread.values.Clear();
13185  }
13186 
13187  threads.Destroy();
13188 
13189  double pbmv = pcl::BendMidvariance( values.Begin(), values.End(), center, beta );
13190  m_status += N;
13191  return pbmv;
13192  }
13193 
13237  double Sn( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1, int maxProcessors = 0 ) const
13238  {
13239  Rect r = rect;
13240  if ( !ParseSelection( r, firstChannel, lastChannel ) )
13241  return 0;
13242 
13243  size_type N = r.IntegerArea()*(1 + lastChannel - firstChannel);
13244  if ( m_status.IsInitializationEnabled() )
13245  m_status.Initialize( "Computing Sn scale estimate", N );
13246 
13247  Array<size_type> L;
13248  {
13250  data.algorithm = PerformanceAnalysisAlgorithm::Sampling;
13251  data.length = N;
13252  data.overheadLimit = 32768;
13253  data.itemSize = BytesPerSample();
13254  data.floatingPoint = IsFloatSample();
13255  L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors, N/Thread::OptimalNumberOfThreads( data )/*overheadLimitPx*/ );
13256  }
13257  bool useAffinity = m_parallel && Thread::IsRootThread();
13259  for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
13260  threads.Add( new DSmpThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ) ) );
13261  if ( threads.Length() > 1 )
13262  {
13263  int n = 0;
13264  for ( DSmpThread& thread : threads )
13265  thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 );
13266  for ( DSmpThread& thread : threads )
13267  thread.Wait();
13268  }
13269  else
13270  threads[0].Run();
13271 
13272  Array<double> values;
13273  for ( DSmpThread& thread : threads )
13274  if ( thread.n > 0 )
13275  {
13276  values.Add( thread.values.Begin(), thread.values.At( thread.n ) );
13277  thread.values.Clear();
13278  }
13279 
13280  threads.Destroy();
13281 
13282  double sn = pcl::Sn( values.Begin(), values.End() );
13283  m_status += N;
13284  return sn;
13285  }
13286 
13329  double Qn( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1, int maxProcessors = 0 ) const
13330  {
13331  Rect r = rect;
13332  if ( !ParseSelection( r, firstChannel, lastChannel ) )
13333  return 0;
13334 
13335  size_type N = r.IntegerArea()*(1 + lastChannel - firstChannel);
13336  if ( m_status.IsInitializationEnabled() )
13337  m_status.Initialize( "Computing Qn scale estimate", N );
13338 
13339  Array<size_type> L;
13340  {
13342  data.algorithm = PerformanceAnalysisAlgorithm::Sampling;
13343  data.length = N;
13344  data.overheadLimit = 32768;
13345  data.itemSize = BytesPerSample();
13346  data.floatingPoint = IsFloatSample();
13347  L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors, N/Thread::OptimalNumberOfThreads( data )/*overheadLimitPx*/ );
13348  }
13349  bool useAffinity = m_parallel && Thread::IsRootThread();
13351  for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
13352  threads.Add( new DSmpThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ) ) );
13353  if ( threads.Length() > 1 )
13354  {
13355  int n = 0;
13356  for ( DSmpThread& thread : threads )
13357  thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 );
13358  for ( DSmpThread& thread : threads )
13359  thread.Wait();
13360  }
13361  else
13362  threads[0].Run();
13363 
13364  Array<double> values;
13365  for ( DSmpThread& thread : threads )
13366  if ( thread.n > 0 )
13367  {
13368  values.Add( thread.values.Begin(), thread.values.At( thread.n ) );
13369  thread.values.Clear();
13370  }
13371 
13372  threads.Destroy();
13373 
13374  double qn = pcl::Qn( values.Begin(), values.End() );
13375  m_status += N;
13376  return qn;
13377  }
13378 
13416  double Norm( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1,
13417  int maxProcessors = 0 ) const
13418  {
13419  Rect r = rect;
13420  if ( !ParseSelection( r, firstChannel, lastChannel ) )
13421  return 0;
13422 
13423  size_type N = r.IntegerArea()*(1 + lastChannel - firstChannel);
13424  if ( m_status.IsInitializationEnabled() )
13425  m_status.Initialize( "Computing norm", N );
13426 
13427  Array<size_type> L;
13428  {
13430  data.algorithm = PerformanceAnalysisAlgorithm::Sum;
13431  data.length = N;
13432  data.overheadLimit = 32768;
13433  data.itemSize = BytesPerSample();
13434  data.floatingPoint = IsFloatSample();
13435  L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors, N/Thread::OptimalNumberOfThreads( data )/*overheadLimitPx*/ );
13436  }
13437  bool useAffinity = m_parallel && Thread::IsRootThread();
13438  ReferenceArray<SumThread> threads;
13439  for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
13440  threads.Add( new SumThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ) ) );
13441  if ( threads.Length() > 1 )
13442  {
13443  int n = 0;
13444  for ( SumThread& thread : threads )
13445  thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 );
13446  for ( SumThread& thread : threads )
13447  thread.Wait();
13448  }
13449  else
13450  threads[0].Run();
13451 
13452  double s = 0;
13453  double e = 0;
13454  for ( const SumThread& thread : threads )
13455  {
13456  double y = thread.s - e;
13457  double t = s + y;
13458  e = (t - s) - y;
13459  s = t;
13460  }
13461 
13462  threads.Destroy();
13463  m_status += N;
13464  return (1 + s != 1) ? s : 0.0; // don't return insignificant nonzero values
13465  }
13466 
13497  double Modulus( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1,
13498  int maxProcessors = 0 ) const
13499  {
13500  Rect r = rect;
13501  if ( !ParseSelection( r, firstChannel, lastChannel ) )
13502  return 0;
13503 
13504  size_type N = r.IntegerArea()*(1 + lastChannel - firstChannel);
13505  if ( m_status.IsInitializationEnabled() )
13506  m_status.Initialize( "Computing modulus", N );
13507 
13508  Array<size_type> L;
13509  {
13511  data.algorithm = PerformanceAnalysisAlgorithm::Sum;
13512  data.length = N;
13513  data.overheadLimit = 32768;
13514  data.itemSize = BytesPerSample();
13515  data.floatingPoint = IsFloatSample();
13516  L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors, N/Thread::OptimalNumberOfThreads( data )/*overheadLimitPx*/ );
13517  }
13518  bool useAffinity = m_parallel && Thread::IsRootThread();
13520  for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
13521  threads.Add( new SumAbsThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ) ) );
13522  if ( threads.Length() > 1 )
13523  {
13524  int n = 0;
13525  for ( SumAbsThread& thread : threads )
13526  thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 );
13527  for ( SumAbsThread& thread : threads )
13528  thread.Wait();
13529  }
13530  else
13531  threads[0].Run();
13532 
13533  double s = 0;
13534  double e = 0;
13535  for ( const SumAbsThread& thread : threads )
13536  {
13537  double y = thread.s - e;
13538  double t = s + y;
13539  e = (t - s) - y;
13540  s = t;
13541  }
13542 
13543  threads.Destroy();
13544  m_status += N;
13545  return (1 + s != 1) ? s : 0.0; // don't return insignificant nonzero values
13546  }
13547 
13576  double SumOfSquares( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1,
13577  int maxProcessors = 0 ) const
13578  {
13579  Rect r = rect;
13580  if ( !ParseSelection( r, firstChannel, lastChannel ) )
13581  return 0;
13582 
13583  size_type N = r.IntegerArea()*(1 + lastChannel - firstChannel);
13584  if ( m_status.IsInitializationEnabled() )
13585  m_status.Initialize( "Computing sum of squares", N );
13586 
13587  Array<size_type> L;
13588  {
13590  data.algorithm = PerformanceAnalysisAlgorithm::Sum;
13591  data.length = N;
13592  data.overheadLimit = 32768;
13593  data.itemSize = BytesPerSample();
13594  data.floatingPoint = IsFloatSample();
13595  L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors, N/Thread::OptimalNumberOfThreads( data )/*overheadLimitPx*/ );
13596  }
13597  bool useAffinity = m_parallel && Thread::IsRootThread();
13599  for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
13600  threads.Add( new SumSqrThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ) ) );
13601  if ( threads.Length() > 1 )
13602  {
13603  int n = 0;
13604  for ( SumSqrThread& thread : threads )
13605  thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 );
13606  for ( SumSqrThread& thread : threads )
13607  thread.Wait();
13608  }
13609  else
13610  threads[0].Run();
13611 
13612  double s = 0;
13613  double e = 0;
13614  for ( const SumSqrThread& thread : threads )
13615  {
13616  double y = thread.s - e;
13617  double t = s + y;
13618  e = (t - s) - y;
13619  s = t;
13620  }
13621 
13622  threads.Destroy();
13623  m_status += N;
13624  return (1 + s != 1) ? s : 0.0; // don't return insignificant nonzero values
13625  }
13626 
13655  double MeanOfSquares( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1,
13656  int maxProcessors = 0 ) const
13657  {
13658  Rect r = rect;
13659  if ( !ParseSelection( r, firstChannel, lastChannel ) )
13660  return 0;
13661 
13662  size_type N = r.IntegerArea()*(1 + lastChannel - firstChannel);
13663  if ( m_status.IsInitializationEnabled() )
13664  m_status.Initialize( "Computing mean of squares", N );
13665 
13666  Array<size_type> L;
13667  {
13669  data.algorithm = PerformanceAnalysisAlgorithm::Sum;
13670  data.length = N;
13671  data.overheadLimit = 32768;
13672  data.itemSize = BytesPerSample();
13673  data.floatingPoint = IsFloatSample();
13674  L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors, N/Thread::OptimalNumberOfThreads( data )/*overheadLimitPx*/ );
13675  }
13676  bool useAffinity = m_parallel && Thread::IsRootThread();
13678  for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
13679  threads.Add( new SumSqrThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ) ) );
13680  if ( threads.Length() > 1 )
13681  {
13682  int n = 0;
13683  for ( SumSqrThread& thread : threads )
13684  thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 );
13685  for ( SumSqrThread& thread : threads )
13686  thread.Wait();
13687  }
13688  else
13689  threads[0].Run();
13690 
13691  double s = 0;
13692  double e = 0;
13693  size_type n = 0;
13694  for ( const SumSqrThread& thread : threads )
13695  {
13696  double y = thread.s - e;
13697  double t = s + y;
13698  e = (t - s) - y;
13699  s = t;
13700  n += thread.n;
13701  }
13702 
13703  threads.Destroy();
13704 
13705  m_status += N;
13706 
13707  if ( n < 1 )
13708  return 0;
13709  return s/n;
13710  }
13711 
13749  Vector Norms( int maxDegree = 2, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1,
13750  int maxProcessors = 0 ) const
13751  {
13752  PCL_PRECONDITION( maxDegree > 0 )
13753  maxDegree = pcl::Max( 1, maxDegree );
13754 
13755  Rect r = rect;
13756  if ( !ParseSelection( r, firstChannel, lastChannel ) )
13757  return 0;
13758 
13759  size_type N = r.IntegerArea()*(1 + lastChannel - firstChannel);
13760  if ( m_status.IsInitializationEnabled() )
13761  m_status.Initialize( "Computing norms", N );
13762 
13763  Array<size_type> L;
13764  {
13766  data.algorithm = PerformanceAnalysisAlgorithm::Sum;
13767  data.length = N;
13768  data.overheadLimit = 32768;
13769  data.itemSize = BytesPerSample();
13770  data.floatingPoint = IsFloatSample();
13771  L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors, N/Thread::OptimalNumberOfThreads( data )/*overheadLimitPx*/ );
13772  }
13773  bool useAffinity = m_parallel && Thread::IsRootThread();
13775  for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
13776  threads.Add( new NormThread( *this, maxDegree, r, firstChannel, lastChannel, n, n + int( L[i] ) ) );
13777  if ( threads.Length() > 1 )
13778  {
13779  int n = 0;
13780  for ( NormThread& thread : threads )
13781  thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 );
13782  for ( NormThread& thread : threads )
13783  thread.Wait();
13784  }
13785  else
13786  threads[0].Run();
13787 
13788  Vector R( 0.0, maxDegree );
13789  Vector e( 0.0, maxDegree );
13790  for ( const NormThread& thread : threads )
13791  for ( int i = 0; i < maxDegree; ++i )
13792  {
13793  double y = thread.R[i] - e[i];
13794  double t = R[i] + y;
13795  e[i] = (t - R[i]) - y;
13796  R[i] = t;
13797  }
13798  for ( int i = 0; i < maxDegree; ++i )
13799  if ( 1 + R[i] == 1 ) // don't return insignificant nonzero values
13800  R[i] = 0;
13801 
13802  threads.Destroy();
13803  m_status += N;
13804  return R;
13805  }
13806 
13819  uint64 Hash64( int channel = -1, uint64 seed = 0 ) const noexcept
13820  {
13821  if ( !ParseChannel( channel ) )
13822  return 0;
13823  return pcl::Hash64( m_channelData( channel ), ChannelSize(), seed );
13824  }
13825 
13838  uint32 Hash32( int channel = -1, uint32 seed = 0 ) const noexcept
13839  {
13840  if ( !ParseChannel( channel ) )
13841  return 0;
13842  return pcl::Hash32( m_channelData( channel ), ChannelSize(), seed );
13843  }
13844 
13849  uint64 Hash( int channel = -1, uint64 seed = 0 ) const noexcept
13850  {
13851  return Hash64( channel, seed );
13852  }
13853 
13854  // -------------------------------------------------------------------------
13855 
13873  GenericImage& Write( File& file, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) const
13874  {
13875  Rect r = rect;
13876  if ( !ParseSelection( r, firstChannel, lastChannel ) )
13877  return const_cast<GenericImage&>( *this );
13878 
13879  size_type N = r.IntegerArea();
13880  int numberOfChannels = 1 + lastChannel - firstChannel;
13881 
13882  if ( m_status.IsInitializationEnabled() )
13883  m_status.Initialize( "Writing to raw-storage image stream", N*numberOfChannels );
13884 
13885  file.WriteUI32( r.Width() );
13886  file.WriteUI32( r.Height() );
13887  file.WriteUI32( numberOfChannels );
13888  file.WriteUI32( (firstChannel == 0 && lastChannel >= 2) ? m_colorSpace : ColorSpace::Gray );
13889 
13890  if ( r == Bounds() )
13891  {
13892  for ( int i = firstChannel; i <= lastChannel; ++i, m_status += N )
13893  file.Write( (const void*)m_pixelData[i], fsize_type( ChannelSize() ) );
13894  }
13895  else
13896  {
13897  for ( int i = firstChannel, w = r.Width(), h = r.Height(); i <= lastChannel; ++i )
13898  {
13899  const sample* p = PixelAddress( r.LeftTop(), i );
13900  for ( int j = 0; j < h; ++j, p += m_width, m_status += w )
13901  file.Write( (const void*)p, w*BytesPerSample() );
13902  }
13903  }
13904 
13905  return const_cast<GenericImage&>( *this );
13906  }
13907 
13918  GenericImage& Write( const String& filePath,
13919  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) const
13920  {
13921  File file = File::CreateFileForWriting( filePath );
13922  return Write( file, rect, firstChannel, lastChannel );
13923  }
13924 
13962  {
13963  int width, height, numberOfChannels, colorSpace;
13964  file.ReadUI32( width );
13965  file.ReadUI32( height );
13966  file.ReadUI32( numberOfChannels );
13967  file.ReadUI32( colorSpace );
13968 
13969  AllocateData( width, height, numberOfChannels, color_space( colorSpace ) );
13970 
13971  ResetSelections();
13972 
13973  size_type N = NumberOfPixels();
13974  if ( N > 0 )
13975  {
13976  if ( m_status.IsInitializationEnabled() )
13977  m_status.Initialize( "Reading from raw-storage image stream", N*m_numberOfChannels );
13978  for ( int i = 0; i < m_numberOfChannels; ++i, m_status += N )
13979  file.Read( (void*)m_pixelData[i], fsize_type( ChannelSize() ) );
13980  }
13981 
13982  return *this;
13983  }
13984 
13996  GenericImage& Read( const String& filePath )
13997  {
13998  File file = File::OpenFileForReading( filePath );
13999  return Read( file );
14000  }
14001 
14002  // -------------------------------------------------------------------------
14003 
14034  template <typename T>
14035  GenericImage& CropBy( int left, int top, int right, int bottom, const GenericVector<T>& fillValues )
14036  {
14037  if ( left == 0 && top == 0 && right == 0 && bottom == 0 )
14038  return *this;
14039 
14040  if ( m_width+left+right <= 0 || m_height+top+bottom <= 0 )
14041  {
14042  FreeData();
14043  return *this;
14044  }
14045 
14046  Rect r( -left, -top, m_width+right, m_height+bottom );
14047  int width = r.Width();
14048  int height = r.Height();
14049 
14050  if ( !Intersects( r ) )
14051  return AllocateData( width, height, m_numberOfChannels, m_colorSpace ).Fill( fillValues );
14052 
14053  size_type N = size_type( width )*size_type( height );
14054 
14055  EnsureUnique();
14056 
14057  if ( m_status.IsInitializationEnabled() )
14058  m_status.Initialize( String().Format( "Crop margins: %+d, %+d, %+d, %+d",
14059  left, top, right, bottom ), N*m_numberOfChannels );
14060 
14061  sample** newData = nullptr;
14062 
14063  try
14064  {
14065  newData = m_allocator.AllocateChannelSlots( m_numberOfChannels );
14066 
14067  for ( int c = 0; c < m_numberOfChannels; ++c, m_status += N )
14068  {
14069  sample* __restrict__ f = newData[c] = m_allocator.AllocatePixels( N );
14070  sample v = (c < fillValues.Length()) ? P::ToSample( fillValues[c] ) : P::MinSampleValue();
14071 
14072  for ( int i = r.y0, j; i < r.y1; )
14073  {
14074  for ( ; i < 0; ++i )
14075  {
14076  PCL_IVDEP
14077  for ( int j = 0; j < width; ++j )
14078  *f++ = v;
14079  }
14080 
14081  PCL_IVDEP
14082  for ( j = r.x0; j < 0; ++j )
14083  *f++ = v;
14084 
14085  for ( const sample* f0 = PixelAddress( j, i, c ); j < r.x1; )
14086  {
14087  *f++ = *f0++;
14088  if ( ++j == m_width )
14089  {
14090  PCL_IVDEP
14091  for ( ; j < r.x1; ++j )
14092  *f++ = v;
14093  }
14094  }
14095 
14096  if ( ++i == m_height )
14097  for ( ; i < r.y1; ++i )
14098  {
14099  PCL_IVDEP
14100  for ( int j = 0; j < width; ++j )
14101  *f++ = v;
14102  }
14103  }
14104  }
14105 
14106  try
14107  {
14108  for ( int c = 0; c < m_numberOfChannels; ++c )
14109  {
14110  m_allocator.Deallocate( m_pixelData[c] );
14111  m_pixelData[c] = newData[c];
14112  }
14113  m_allocator.Deallocate( newData );
14114  newData = nullptr;
14115 
14116  m_allocator.SetSharedGeometry( m_width = width, m_height = height, m_numberOfChannels );
14117 
14118  ResetPoint();
14119  ResetSelection();
14120  return *this;
14121  }
14122  catch ( ... )
14123  {
14124  m_data->Deallocate();
14125  ResetSelections();
14126  throw;
14127  }
14128  }
14129  catch ( ... )
14130  {
14131  if ( newData != nullptr )
14132  {
14133  for ( int i = 0; i < m_numberOfChannels; ++i )
14134  if ( newData[i] != nullptr )
14135  m_allocator.Deallocate( newData[i] ), newData[i] = nullptr;
14136  m_allocator.Deallocate( newData );
14137  }
14138  throw;
14139  }
14140  }
14141 
14153  GenericImage& CropBy( int left, int top, int right, int bottom )
14154  {
14155  return CropBy( left, top, right, bottom, sample_vector() );
14156  }
14157 
14168  template <typename T>
14169  GenericImage& CropTo( const Rect& rect, const GenericVector<T>& fillValues )
14170  {
14171  Rect r = rect.Ordered();
14172  return CropBy( -r.x0, -r.y0, r.x1 - m_width, r.y1 - m_height, fillValues );
14173  }
14174 
14186  GenericImage& CropTo( const Rect& rect )
14187  {
14188  return CropTo( rect, sample_vector() );
14189  }
14190 
14201  template <typename T>
14202  GenericImage& CropTo( int x0, int y0, int x1, int y1, const GenericVector<T>& fillValues )
14203  {
14204  return CropTo( Rect( x0, y0, x1, y1 ), fillValues );
14205  }
14206 
14218  GenericImage& CropTo( int x0, int y0, int x1, int y1 )
14219  {
14220  return CropTo( x0, y0, x1, y1, sample_vector() );
14221  }
14222 
14233  template <typename T>
14234  GenericImage& Crop( const GenericVector<T>& fillValues )
14235  {
14236  return CropTo( m_rectangle, fillValues );
14237  }
14238 
14251  {
14252  return Crop( sample_vector() );
14253  }
14254 
14276  template <typename T>
14277  GenericImage& ShiftBy( int dx, int dy, const GenericVector<T>& fillValues )
14278  {
14279  return CropBy( dx, dy, -dx, -dy, fillValues );
14280  }
14281 
14293  GenericImage& ShiftBy( int dx, int dy )
14294  {
14295  return ShiftBy( dx, dy, sample_vector() );
14296  }
14297 
14304  template <typename T>
14305  GenericImage& ShiftTo( int x, int y, const GenericVector<T>& fillValues )
14306  {
14307  return ShiftBy( x, y, fillValues );
14308  }
14309 
14321  GenericImage& ShiftTo( int x, int y )
14322  {
14323  return ShiftTo( x, y, sample_vector() );
14324  }
14325 
14336  template <typename T>
14337  GenericImage& ShiftTo( const Point& p, const GenericVector<T>& fillValues )
14338  {
14339  return ShiftBy( p.x, p.y, fillValues );
14340  }
14341 
14354  {
14355  return ShiftTo( p, sample_vector() );
14356  }
14357 
14362  template <typename T>
14363  GenericImage& ShiftToCenter( int width, int height, const GenericVector<T>& fillValues )
14364  {
14365  int dx2 = (width - m_width) >> 1;
14366  int dy2 = (height - m_height) >> 1;
14367  return CropBy( dx2, dy2, width-m_width-dx2, height-m_height-dy2, fillValues );
14368  }
14369 
14382  GenericImage& ShiftToCenter( int width, int height )
14383  {
14384  return ShiftToCenter( width, height, sample_vector() );
14385  }
14386 
14391  template <typename T>
14392  GenericImage& ShiftToTopLeft( int width, int height, const GenericVector<T>& fillValues )
14393  {
14394  int dx = width - m_width;
14395  int dy = height - m_height;
14396  return CropBy( 0, 0, dx, dy, fillValues );
14397  }
14398 
14411  GenericImage& ShiftToTopLeft( int width, int height )
14412  {
14413  return ShiftToTopLeft( width, height, sample_vector() );
14414  }
14415 
14420  template <typename T>
14421  GenericImage& ShiftToTopRight( int width, int height, const GenericVector<T>& fillValues )
14422  {
14423  int dx = width - m_width;
14424  int dy = height - m_height;
14425  return CropBy( dx, 0, 0, dy, fillValues );
14426  }
14427 
14440  GenericImage& ShiftToTopRight( int width, int height )
14441  {
14442  return ShiftToTopRight( width, height, sample_vector() );
14443  }
14444 
14449  template <typename T>
14450  GenericImage& ShiftToBottomLeft( int width, int height, const GenericVector<T>& fillValues )
14451  {
14452  int dx = width - m_width;
14453  int dy = height - m_height;
14454  return CropBy( 0, dy, dx, 0, fillValues );
14455  }
14456 
14469  GenericImage& ShiftToBottomLeft( int width, int height )
14470  {
14471  return ShiftToBottomLeft( width, height, sample_vector() );
14472  }
14473 
14478  template <typename T>
14479  GenericImage& ShiftToBottomRight( int width, int height, const GenericVector<T>& fillValues )
14480  {
14481  int dx = width - m_width;
14482  int dy = height - m_height;
14483  return CropBy( dx, dy, 0, 0, fillValues );
14484  }
14485 
14498  GenericImage& ShiftToBottomRight( int width, int height )
14499  {
14500  return ShiftToBottomRight( width, height, sample_vector() );
14501  }
14502 
14513  template <typename T>
14514  GenericImage& Shift( const GenericVector<T>& fillValues )
14515  {
14516  return ShiftTo( m_point, fillValues );
14517  }
14518 
14531  {
14532  return Shift( sample_vector() );
14533  }
14534 
14535  // -------------------------------------------------------------------------
14536 
14570  GenericImage& SetColorSpace( color_space colorSpace, int maxProcessors = 0, bool __performanceAnalysis__ = false )
14571  {
14572  size_type N = NumberOfPixels();
14573  if ( N == 0 )
14574  return *this;
14575 
14576  if ( colorSpace == m_colorSpace )
14577  {
14578  m_status += N;
14579  return *this;
14580  }
14581 
14582  EnsureUnique();
14583 
14584  if ( m_status.IsInitializationEnabled() )
14585  m_status.Initialize( "In-place color space conversion: "
14586  + ColorSpace::Name( m_colorSpace )
14587  + " -> "
14588  + ColorSpace::Name( colorSpace ), N );
14589 
14590  int n = m_numberOfChannels;
14591 
14592  if ( m_colorSpace == ColorSpace::Gray )
14593  {
14594  sample** oldData = m_pixelData;
14595  sample** newData = nullptr;
14596 
14597  try
14598  {
14599  // Make room for the G and B channels.
14600  newData = m_allocator.AllocateChannelSlots( 2+n );
14601 
14602  // Put the nominal gray channel into the R slot
14603  newData[0] = oldData[0];
14604 
14605  // Allocate and copy the G and B channels
14606  newData[1] = m_allocator.AllocatePixels( N );
14607  ::memcpy( newData[1], oldData[0], ChannelSize() );
14608  newData[2] = m_allocator.AllocatePixels( N );
14609  ::memcpy( newData[2], oldData[0], ChannelSize() );
14610 
14611  // Put existing alpha channels in their corresponding slots
14612  for ( int i = 1; i < n; ++i )
14613  newData[i+2] = oldData[i];
14614 
14615  try
14616  {
14617  m_pixelData = newData;
14618  newData = nullptr;
14619  m_numberOfChannels += 2;
14620  m_colorSpace = ColorSpace::RGB;
14621  m_data->UpdateSharedImage();
14622 
14623  ResetChannelRange();
14624 
14625  m_allocator.Deallocate( oldData );
14626  }
14627  catch ( ... )
14628  {
14629  m_data->Deallocate();
14630  ResetSelections();
14631  throw;
14632  }
14633  }
14634  catch ( ... )
14635  {
14636  if ( newData != nullptr )
14637  {
14638  newData[0] = nullptr;
14639  if ( newData[1] != nullptr )
14640  m_allocator.Deallocate( newData[1] ), newData[1] = nullptr;
14641  if ( newData[2] != nullptr )
14642  m_allocator.Deallocate( newData[2] ), newData[2] = nullptr;
14643  m_allocator.Deallocate( newData );
14644  throw;
14645  }
14646  }
14647 
14648  if ( colorSpace == ColorSpace::RGB )
14649  {
14650  m_status += N;
14651  return *this;
14652  }
14653 
14654  n += 2;
14655  }
14656 
14657  Array<size_type> L;
14658  if ( likely( !__performanceAnalysis__ ) )
14659  {
14661  data.algorithm = PerformanceAnalysisAlgorithm::ColorSpaceConversion;
14662  data.length = N;
14663  data.overheadLimit = 32768;
14664  data.itemSize = BytesPerSample();
14665  data.floatingPoint = IsFloatSample();
14666  L = Thread::OptimalThreadLoads( N, N/Thread::OptimalNumberOfThreads( data )/*overheadLimit*/,
14667  m_parallel ? ((maxProcessors > 0) ? maxProcessors : m_maxProcessors) : 1 );
14668  }
14669  else
14670  L = Thread::OptimalThreadLoads( N, 1/*overheadLimit*/, maxProcessors );
14671 
14672  ThreadData data( *this, N );
14674  for ( size_type i = 0, n = 0; i < L.Length(); n += L[i++] )
14675  threads.Add( new ColorSpaceConversionThread( *this, data, colorSpace, n, n + L[i] ) );
14676  RunThreads( threads, data );
14677  threads.Destroy();
14678 
14679  m_status = data.status;
14680 
14681  if ( colorSpace == ColorSpace::Gray )
14682  {
14683  sample** oldData = m_pixelData;
14684  sample** newData = nullptr;
14685 
14686  try
14687  {
14688  newData = m_allocator.AllocateChannelSlots( n-2 );
14689  newData[0] = oldData[0];
14690  for ( int i = 3; i < n; ++i )
14691  newData[i-2] = oldData[i];
14692 
14693  m_pixelData = newData;
14694  newData = nullptr;
14695  m_numberOfChannels -= 2;
14696  m_colorSpace = ColorSpace::Gray;
14697  m_data->UpdateSharedImage();
14698 
14699  ResetChannelRange();
14700 
14701  m_allocator.Deallocate( oldData[1] );
14702  m_allocator.Deallocate( oldData[2] );
14703  m_allocator.Deallocate( oldData );
14704  }
14705  catch ( ... )
14706  {
14707  m_data->Deallocate();
14708  ResetSelections();
14709  if ( newData != nullptr )
14710  m_allocator.Deallocate( newData );
14711  throw;
14712  }
14713  }
14714  else
14715  {
14716  m_allocator.SetSharedColor( m_colorSpace = colorSpace, m_RGBWS );
14717  }
14718 
14719  return *this;
14720  }
14721 
14722  // -------------------------------------------------------------------------
14723 
14789  template <class P1>
14790  void GetLuminance( GenericImage<P1>& Y, const Rect& rect = Rect( 0 ), int maxProcessors = 0 ) const
14791  {
14792  Rect r = rect;
14793  if ( !ParseRect( r ) )
14794  {
14795  Y.FreeData();
14796  return;
14797  }
14798 
14799  Y.AllocateData( r );
14800  if ( !Y.IsShared() )
14801  Y.SetRGBWorkingSpace( m_RGBWS );
14802 
14803  size_type N = Y.NumberOfPixels();
14804 
14805  if ( m_colorSpace == ColorSpace::Gray || m_colorSpace == ColorSpace::CIEXYZ )
14806  {
14807  if ( m_status.IsInitializationEnabled() )
14808  m_status.Initialize( "Transferring pixel data", N );
14809 
14810  typename GenericImage<P1>::sample* __restrict__ g = *Y;
14811  int cY = (m_colorSpace == ColorSpace::Gray) ? 0 : 1;
14812  if ( r == Bounds() )
14813  {
14814  const sample* __restrict__ f = m_pixelData[cY];
14815  P1::Copy( g, f, N );
14816  }
14817  else
14818  {
14819  const sample* __restrict__ f = PixelAddress( r.LeftTop(), cY );
14820  for ( int i = 0; i < Y.Height(); ++i, f += m_width, g += Y.Width() )
14821  P1::Copy( g, f, Y.Width() );
14822  }
14823 
14824  m_status += N;
14825  }
14826  else
14827  {
14828  if ( m_status.IsInitializationEnabled() )
14829  m_status.Initialize( "Computing CIE Y component", N );
14830 
14831  Array<size_type> L;
14832  {
14834  data.algorithm = PerformanceAnalysisAlgorithm::GetLightness;
14835  data.length = N;
14836  data.overheadLimit = 32768;
14837  data.itemSize = BytesPerSample();
14838  data.floatingPoint = IsFloatSample();
14839  L = OptimalThreadRows( Y.Height(), Y.Width(), maxProcessors, N/Thread::OptimalNumberOfThreads( data )/*overheadLimitPx*/ );
14840  }
14841  ThreadData data( *this, N );
14843  for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
14844  threads.Add( new GetLuminanceThread<P1>( Y, *this, data, r, n, n + int( L[i] ) ) );
14845  RunThreads( threads, data );
14846  threads.Destroy();
14847 
14848  m_status = data.status;
14849  }
14850  }
14851 
14861  void GetLuminance( ImageVariant& Y, const Rect& rect = Rect( 0 ), int maxProcessors = 0 ) const;
14862  // Implemented in pcl/ImageVariant.h
14863 
14930  template <class P1>
14931  void GetLightness( GenericImage<P1>& L, const Rect& rect = Rect( 0 ), int maxProcessors = 0,
14932  bool __performanceAnalysis__ = false ) const
14933  {
14934  Rect r = rect;
14935  if ( !ParseRect( r ) )
14936  {
14937  L.FreeData();
14938  return;
14939  }
14940 
14941  L.AllocateData( r );
14942  if ( !L.IsShared() )
14943  L.SetRGBWorkingSpace( m_RGBWS );
14944 
14945  size_type N = L.NumberOfPixels();
14946 
14947  if ( m_colorSpace == ColorSpace::Gray || m_colorSpace == ColorSpace::CIELab || m_colorSpace == ColorSpace::CIELch )
14948  {
14949  if ( m_status.IsInitializationEnabled() )
14950  m_status.Initialize( "Transferring pixel data", N );
14951 
14952  typename GenericImage<P1>::sample* g = *L;
14953  if ( r == Bounds() )
14954  {
14955  const sample* __restrict__ f = *m_pixelData;
14956  P1::Copy( g, f, N );
14957  }
14958  else
14959  {
14960  const sample* __restrict__ f = PixelAddress( r.LeftTop() );
14961  for ( int i = 0; i < L.Height(); ++i, f += m_width, g += L.Width() )
14962  P1::Copy( g, f, L.Width() );
14963  }
14964 
14965  m_status += N;
14966  }
14967  else
14968  {
14969  if ( m_status.IsInitializationEnabled() )
14970  m_status.Initialize( "Computing CIE L* component", N );
14971 
14972  Array<size_type> R;
14973  if ( likely( !__performanceAnalysis__ ) )
14974  {
14976  data.algorithm = PerformanceAnalysisAlgorithm::GetLightness;
14977  data.length = N;
14978  data.overheadLimit = 32768;
14979  data.itemSize = BytesPerSample();
14980  data.floatingPoint = IsFloatSample();
14981  R = OptimalThreadRows( L.Height(), L.Width(), maxProcessors, N/Thread::OptimalNumberOfThreads( data )/*overheadLimitPx*/ );
14982  }
14983  else
14984  R = OptimalThreadRows( L.Height(), L.Width(), maxProcessors, 1/*overheadLimitPx*/ );
14985 
14986  ThreadData data( *this, N );
14988  for ( int i = 0, n = 0; i < int( R.Length() ); n += int( R[i++] ) )
14989  threads.Add( new GetLightnessThread<P1>( L, *this, data, r, n, n + int( R[i] ) ) );
14990  RunThreads( threads, data );
14991  threads.Destroy();
14992 
14993  m_status = data.status;
14994  }
14995  }
14996 
15006  void GetLightness( ImageVariant& L, const Rect& rect = Rect( 0 ), int maxProcessors = 0 ) const;
15007  // Implemented in pcl/ImageVariant.h
15008 
15067  template <class P1>
15068  void GetIntensity( GenericImage<P1>& I, const Rect& rect = Rect( 0 ), int maxProcessors = 0,
15069  bool __performanceAnalysis__ = false ) const
15070  {
15071  Rect r = rect;
15072  if ( !ParseRect( r ) )
15073  {
15074  I.FreeData();
15075  return;
15076  }
15077 
15078  I.AllocateData( r );
15079  if ( !I.IsShared() )
15080  I.SetRGBWorkingSpace( m_RGBWS );
15081 
15082  size_type N = I.NumberOfPixels();
15083 
15084  if ( m_colorSpace == ColorSpace::Gray || m_colorSpace == ColorSpace::HSI )
15085  {
15086  if ( m_status.IsInitializationEnabled() )
15087  m_status.Initialize( "Transferring pixel data", N );
15088 
15089  typename GenericImage<P1>::sample* __restrict__ g = *I;
15090  int cI = (m_colorSpace == ColorSpace::Gray) ? 0 : 2;
15091  if ( r == Bounds() )
15092  {
15093  const sample* __restrict__ f = m_pixelData[cI];
15094  P1::Copy( g, f, N );
15095  }
15096  else
15097  {
15098  const sample* __restrict__ f = PixelAddress( r.LeftTop(), cI );
15099  for ( int i = 0; i < I.Height(); ++i, f += m_width, g += I.Width() )
15100  P1::Copy( g, f, I.Width() );
15101  }
15102 
15103  m_status += N;
15104  }
15105  else
15106  {
15107  if ( m_status.IsInitializationEnabled() )
15108  m_status.Initialize( "Computing intensity component", N );
15109 
15110  Array<size_type> L;
15111  if ( likely( !__performanceAnalysis__ ) )
15112  {
15114  data.algorithm = PerformanceAnalysisAlgorithm::GetIntensity;
15115  data.length = N;
15116  data.overheadLimit = 32768;
15117  data.itemSize = BytesPerSample();
15118  data.floatingPoint = IsFloatSample();
15119  L = OptimalThreadRows( I.Height(), I.Width(), maxProcessors, N/Thread::OptimalNumberOfThreads( data )/*overheadLimitPx*/ );
15120  }
15121  else
15122  L = OptimalThreadRows( I.Height(), I.Width(), maxProcessors, 1/*overheadLimitPx*/ );
15123 
15124  ThreadData data( *this, N );
15126  for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
15127  threads.Add( new GetIntensityThread<P1>( I, *this, data, r, n, n + int( L[i] ) ) );
15128  RunThreads( threads, data );
15129  threads.Destroy();
15130 
15131  m_status = data.status;
15132  }
15133  }
15134 
15144  void GetIntensity( ImageVariant& I, const Rect& rect = Rect( 0 ), int maxProcessors = 0 ) const;
15145  // Implemented in pcl/ImageVariant.h
15146 
15147  // -------------------------------------------------------------------------
15148 
15207  template <class P1>
15209  const Point& point = Point( int_max ), const Rect& rect = Rect( 0 ),
15210  int maxProcessors = 0 )
15211  {
15212  Rect r = rect;
15213  if ( !Y.ParseRect( r ) )
15214  return *this;
15215 
15216  Point p = point;
15217  if ( p.x == int_max || p.y == int_max )
15218  p = m_point;
15219 
15220  if ( p.x < 0 )
15221  {
15222  if ( (r.x0 -= p.x) >= r.x1 )
15223  return *this;
15224  p.x = 0;
15225  }
15226  else if ( p.x >= m_width )
15227  return *this;
15228 
15229  if ( p.y < 0 )
15230  {
15231  if ( (r.y0 -= p.y) >= r.y1 )
15232  return *this;
15233  p.y = 0;
15234  }
15235  else if ( p.y >= m_height )
15236  return *this;
15237 
15238  r.ResizeTo( pcl::Min( m_width - p.x, r.Width() ),
15239  pcl::Min( m_height - p.y, r.Height() ) );
15240 
15241  size_type N = r.IntegerArea();
15242 
15243  EnsureUnique();
15244 
15245  if ( m_colorSpace == ColorSpace::Gray &&
15246  (Y.ColorSpace() == ColorSpace::Gray || Y.ColorSpace() == ColorSpace::CIEXYZ) )
15247  {
15248  if ( m_status.IsInitializationEnabled() )
15249  m_status.Initialize( "Transferring pixel data", N );
15250 
15251  int c0 = (Y.ColorSpace() == ColorSpace::Gray) ? 0 : 1;
15252  const typename GenericImage<P1>::sample* __restrict__ g = Y.PixelAddress( r.LeftTop(), c0 );
15253  sample* __restrict__ f = PixelAddress( p );
15254  for ( int i = 0, w = r.Width(), h = r.Height(); i < h; ++i, f += m_width, g += Y.Width(), m_status += w )
15255  P::Copy( f, g, w );
15256  }
15257  else
15258  {
15259  if ( m_status.IsInitializationEnabled() )
15260  m_status.Initialize( "Importing CIE Y component", N );
15261 
15262  Array<size_type> L;
15263  {
15265  data.algorithm = PerformanceAnalysisAlgorithm::GetLightness;
15266  data.length = N;
15267  data.overheadLimit = 32768;
15268  data.itemSize = BytesPerSample();
15269  data.floatingPoint = IsFloatSample();
15270  L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors, N/Thread::OptimalNumberOfThreads( data )/*overheadLimitPx*/ );
15271  }
15272  ThreadData data( *this, N );
15274  for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
15275  threads.Add( new SetLuminanceThread<P1>( *this, Y, data, p, r, n, n + int( L[i] ) ) );
15276  RunThreads( threads, data );
15277  threads.Destroy();
15278 
15279  m_status = data.status;
15280  }
15281 
15282  return *this;
15283  }
15284 
15294  GenericImage& SetLuminance( const ImageVariant& Y,
15295  const Point& point = Point( int_max ), const Rect& rect = Rect( 0 ),
15296  int maxProcessors = 0 );
15297  // Implemented in pcl/ImageVariant.h
15298 
15357  template <class P1>
15359  const Point& point = Point( int_max ), const Rect& rect = Rect( 0 ),
15360  int maxProcessors = 0 )
15361  {
15362  Rect r = rect;
15363  if ( !L.ParseRect( r ) )
15364  return *this;
15365 
15366  Point p = point;
15367  if ( p.x == int_max || p.y == int_max )
15368  p = m_point;
15369 
15370  if ( p.x < 0 )
15371  {
15372  if ( (r.x0 -= p.x) >= r.x1 )
15373  return *this;
15374  p.x = 0;
15375  }
15376  else if ( p.x >= m_width )
15377  return *this;
15378 
15379  if ( p.y < 0 )
15380  {
15381  if ( (r.y0 -= p.y) >= r.y1 )
15382  return *this;
15383  p.y = 0;
15384  }
15385  else if ( p.y >= m_height )
15386  return *this;
15387 
15388  r.ResizeTo( pcl::Min( m_width - p.x, r.Width() ),
15389  pcl::Min( m_height - p.y, r.Height() ) );
15390 
15391  size_type N = r.IntegerArea();
15392 
15393  EnsureUnique();
15394 
15395  if ( m_colorSpace == ColorSpace::Gray &&
15396  (L.ColorSpace() == ColorSpace::Gray ||
15397  L.ColorSpace() == ColorSpace::CIELab || L.ColorSpace() == ColorSpace::CIELch) )
15398  {
15399  if ( m_status.IsInitializationEnabled() )
15400  m_status.Initialize( "Transferring pixel data", N );
15401 
15402  const typename GenericImage<P1>::sample* __restrict__ g = L.PixelAddress( r.LeftTop() );
15403  sample* __restrict__ f = PixelAddress( p );
15404  for ( int i = 0, w = r.Width(), h = r.Height(); i < h; ++i, f += m_width, g += L.Width(), m_status += w )
15405  P::Copy( f, g, w );
15406  }
15407  else
15408  {
15409  if ( m_status.IsInitializationEnabled() )
15410  m_status.Initialize( "Importing CIE L* component", N );
15411 
15412  Array<size_type> R;
15413  {
15415  data.algorithm = PerformanceAnalysisAlgorithm::GetLightness;
15416  data.length = N;
15417  data.overheadLimit = 32768;
15418  data.itemSize = BytesPerSample();
15419  data.floatingPoint = IsFloatSample();
15420  R = OptimalThreadRows( r.Height(), r.Width(), maxProcessors, N/Thread::OptimalNumberOfThreads( data )/*overheadLimitPx*/ );
15421  }
15422  ThreadData data( *this, N );
15424  for ( int i = 0, n = 0; i < int( R.Length() ); n += int( R[i++] ) )
15425  threads.Add( new SetLightnessThread<P1>( *this, L, data, p, r, n, n + int( R[i] ) ) );
15426  RunThreads( threads, data );
15427  threads.Destroy();
15428 
15429  m_status = data.status;
15430  }
15431 
15432  return *this;
15433  }
15434 
15444  GenericImage& SetLightness( const ImageVariant& L,
15445  const Point& point = Point( int_max ), const Rect& rect = Rect( 0 ),
15446  int maxProcessors = 0 );
15447  // Implemented in pcl/ImageVariant.h
15448 
15488  const Rect& rect = Rect( 0 ), int channel = -1,
15489  Compression::Performance* perf = nullptr ) const
15490  {
15491  Rect r = rect;
15492  if ( !ParseSelection( r, channel ) )
15493  return Compression::subblock_list();
15494  if ( r == Bounds() )
15495  return compressor.Compress( m_channelData( channel ), ChannelSize(), perf );
15496  GenericImage<P> subimage( *this, r, channel, channel );
15497  return compressor.Compress( subimage[0], subimage.ChannelSize(), perf );
15498  }
15499 
15500  // -------------------------------------------------------------------------
15501 
15502 private:
15503 
15509  struct Data : public ReferenceCounter
15510  {
15518  sample** data = nullptr;
15519 
15530  pixel_allocator allocator;
15531 
15535  Geometry geometry;
15536 
15540  Color color;
15541 
15545  Data( GenericImage* image )
15546  {
15547  LinkWithClientImage( image );
15548  }
15549 
15553  Data( GenericImage* image, void* handle )
15554  : allocator( handle )
15555  {
15556  SynchronizeWithSharedImage();
15557  LinkWithClientImage( image );
15558  }
15559 
15563  Data( GenericImage* image, int width, int height, int numberOfChannels, int colorSpace )
15564  : allocator( width, height, numberOfChannels, colorSpace )
15565  {
15566  SynchronizeWithSharedImage();
15567  LinkWithClientImage( image );
15568  }
15569 
15570  void Attach( GenericImage* image )
15571  {
15572  ReferenceCounter::Attach();
15573  LinkWithClientImage( image );
15574  }
15575 
15576  ~Data()
15577  {
15578  if ( IsShared() )
15579  Reset();
15580  else
15581  Deallocate();
15582  }
15583 
15584  bool IsShared() const noexcept
15585  {
15586  return allocator.IsShared();
15587  }
15588 
15589  bool IsEmpty() const noexcept
15590  {
15591  return data == nullptr;
15592  }
15593 
15594  void Allocate( int width, int height, int numberOfChannels, color_space colorSpace )
15595  {
15596  if ( width <= 0 || height <= 0 || numberOfChannels <= 0 )
15597  {
15598  Deallocate();
15599  return;
15600  }
15601 
15602  if ( numberOfChannels < ColorSpace::NumberOfNominalChannels( colorSpace ) )
15603  throw Error( "GenericImage::Data::Allocate(): Insufficient number of channels" );
15604 
15605  if ( data != nullptr )
15606  {
15607  if ( size_type( width )*size_type( height ) == geometry.NumberOfPixels() )
15608  {
15609  if ( numberOfChannels != geometry.numberOfChannels )
15610  {
15611  sample** newData = nullptr;
15612  int m = pcl::Min( geometry.numberOfChannels, numberOfChannels );
15613 
15614  try
15615  {
15616  newData = allocator.AllocateChannelSlots( numberOfChannels );
15617  for ( int i = 0; i < m; ++i )
15618  newData[i] = data[i];
15619  for ( int i = m; i < numberOfChannels; ++i )
15620  newData[i] = allocator.AllocatePixels( width, height );
15621 
15622  try
15623  {
15624  for ( int i = m; i < geometry.numberOfChannels; ++i )
15625  if ( data[i] != nullptr )
15626  allocator.Deallocate( data[i] ), data[i] = nullptr;
15627  allocator.Deallocate( data );
15628  data = newData;
15629  newData = nullptr;
15630  }
15631  catch ( ... )
15632  {
15633  Deallocate();
15634  throw;
15635  }
15636  }
15637  catch ( ... )
15638  {
15639  if ( newData != nullptr )
15640  {
15641  for ( int i = m; i < numberOfChannels; ++i )
15642  if ( newData[i] != nullptr )
15643  allocator.Deallocate( newData[i] ), newData[i] = nullptr;
15644  allocator.Deallocate( newData );
15645  }
15646  throw;
15647  }
15648  }
15649  }
15650  else
15651  {
15652  sample** newData = nullptr;
15653 
15654  try
15655  {
15656  newData = allocator.AllocateChannelSlots( numberOfChannels );
15657  for ( int i = 0; i < numberOfChannels; ++i )
15658  newData[i] = allocator.AllocatePixels( width, height );
15659 
15660  try
15661  {
15662  for ( int i = 0; i < geometry.numberOfChannels; ++i )
15663  if ( data[i] != nullptr )
15664  allocator.Deallocate( data[i] ), data[i] = nullptr;
15665  allocator.Deallocate( data );
15666  data = newData;
15667  newData = nullptr;
15668  }
15669  catch ( ... )
15670  {
15671  Deallocate();
15672  throw;
15673  }
15674  }
15675  catch ( ... )
15676  {
15677  if ( newData != nullptr )
15678  {
15679  for ( int i = 0; i < numberOfChannels; ++i )
15680  if ( newData[i] != nullptr )
15681  allocator.Deallocate( newData[i] ), newData[i] = nullptr;
15682  allocator.Deallocate( newData );
15683  }
15684  throw;
15685  }
15686  }
15687  }
15688  else
15689  {
15690  try
15691  {
15692  data = allocator.AllocateChannelSlots( numberOfChannels );
15693  for ( int i = 0; i < numberOfChannels; ++i )
15694  data[i] = allocator.AllocatePixels( width, height );
15695  }
15696  catch ( ... )
15697  {
15698  if ( data != nullptr )
15699  {
15700  for ( int i = 0; i < numberOfChannels; ++i )
15701  if ( data[i] != nullptr )
15702  allocator.Deallocate( data[i] ), data[i] = nullptr;
15703  allocator.Deallocate( data );
15704  Reset();
15705  }
15706  throw;
15707  }
15708  }
15709 
15710  geometry.width = width;
15711  geometry.height = height;
15712  geometry.numberOfChannels = numberOfChannels;
15713 
15714  color.colorSpace = colorSpace;
15715 
15716  UpdateSharedImage();
15717  }
15718 
15719  void Import( sample** newData, int width, int height, int numberOfChannels, color_space colorSpace )
15720  {
15721  if ( newData != data )
15722  {
15723  Deallocate();
15724 
15725  if ( newData != nullptr && width > 0 && height > 0 && numberOfChannels > 0 )
15726  {
15727  if ( numberOfChannels < ColorSpace::NumberOfNominalChannels( colorSpace ) )
15728  throw Error( "GenericImage::Data::Import(): Insufficient number of channels" );
15729 
15730  data = newData;
15731 
15732  geometry.width = width;
15733  geometry.height = height;
15734  geometry.numberOfChannels = numberOfChannels;
15735 
15736  color.colorSpace = colorSpace;
15737 
15738  UpdateSharedImage();
15739  }
15740  }
15741  }
15742 
15743  sample** Release()
15744  {
15745  sample** oldData = data;
15746  Reset();
15747  UpdateSharedImage();
15748  return oldData;
15749  }
15750 
15751  void Deallocate()
15752  {
15753  if ( data != nullptr )
15754  {
15755  for ( int i = 0; i < geometry.numberOfChannels; ++i )
15756  if ( data[i] != nullptr )
15757  allocator.Deallocate( data[i] ), data[i] = nullptr;
15758  allocator.Deallocate( data );
15759  Reset();
15760  UpdateSharedImage();
15761  }
15762  }
15763 
15764  Data* Clone( GenericImage* image ) const
15765  {
15766  Data* clone = nullptr;
15767  try
15768  {
15769  clone = new Data;
15770 
15771  if ( !IsEmpty() )
15772  {
15773  clone->data = clone->allocator.AllocateChannelSlots( geometry.numberOfChannels );
15774  for ( int i = 0; i < geometry.numberOfChannels; ++i )
15775  {
15776  clone->data[i] = clone->allocator.AllocatePixels( geometry.width, geometry.height );
15777  P::Copy( clone->data[i], data[i], geometry.NumberOfPixels() );
15778  }
15779 
15780  clone->geometry.Assign( geometry );
15781  clone->color.Assign( color );
15782  }
15783 
15784  clone->LinkWithClientImage( image );
15785  return clone;
15786  }
15787  catch ( ... )
15788  {
15789  if ( clone != nullptr )
15790  {
15791  clone->Deallocate();
15792  delete clone;
15793  }
15794  throw;
15795  }
15796  }
15797 
15798  void Reset()
15799  {
15800  data = nullptr;
15801  geometry.Reset();
15802  color.Reset();
15803  }
15804 
15805  void UpdateSharedImage()
15806  {
15807  allocator.SetSharedData( data );
15808  allocator.SetSharedGeometry( geometry.width, geometry.height, geometry.numberOfChannels );
15809  allocator.SetSharedColor( color.colorSpace, color.RGBWS );
15810  }
15811 
15812  void SynchronizeWithSharedImage()
15813  {
15814  data = allocator.GetSharedData();
15815  allocator.GetSharedGeometry( geometry.width, geometry.height, geometry.numberOfChannels );
15816  allocator.GetSharedColor( color.colorSpace, color.RGBWS );
15817  }
15818 
15819  private:
15820 
15821  Data() = default;
15822 
15823  void LinkWithClientImage( GenericImage* image )
15824  {
15825  if ( image != nullptr )
15826  {
15827  image->m_geometry = &geometry;
15828  image->m_color = &color;
15829  }
15830  }
15831  };
15832 
15837  Data* m_data = nullptr;
15838 
15843  void DetachFromData()
15844  {
15845  if ( !m_data->Detach() )
15846  delete m_data;
15847  }
15848 
15849  // -------------------------------------------------------------------------
15850 
15851  class RectThreadBase : public Thread
15852  {
15853  public:
15854 
15855  RectThreadBase( const GenericImage& image, const Rect& rect, int ch1, int ch2, int firstRow, int endRow )
15856  : m_image( image )
15857  , m_rect( rect )
15858  , m_ch1( ch1 )
15859  , m_ch2( ch2 )
15860  , m_firstRow( firstRow )
15861  , m_endRow( endRow )
15862  {
15863  }
15864 
15865  virtual void Run() = 0;
15866 
15867  protected:
15868 
15869  const GenericImage& m_image;
15870  const Rect& m_rect;
15871  int m_ch1, m_ch2;
15872  int m_firstRow, m_endRow;
15873 
15874  bool HasSimpleSelection() const
15875  {
15876  return m_rect.Width() == m_image.Width()
15877  && !m_image.IsLowRangeClippingEnabled()
15878  && !m_image.IsHighRangeClippingEnabled();
15879  }
15880 
15881  template <class F> void Execute( F process ) noexcept
15882  {
15883  int w = m_rect.Width();
15884  int dw = m_image.Width() - w;
15885 
15886  if ( dw == 0 )
15887  {
15888  size_type n = size_type( m_endRow - m_firstRow ) * size_type( w );
15889 
15890  if ( m_image.IsLowRangeClippingEnabled() )
15891  {
15892  sample clipLow = P::ToSample( m_image.RangeClipLow() );
15893  if ( m_image.IsHighRangeClippingEnabled() )
15894  {
15895  sample clipHigh = P::ToSample( m_image.RangeClipHigh() );
15896  for ( int i = m_ch1; i <= m_ch2; ++i )
15897  {
15898  const sample* __restrict__ f = m_image.PixelAddress( 0, m_rect.y0+m_firstRow, i );
15899  PCL_IVDEP
15900  for ( size_type j = 0; j < n; ++j, ++f )
15901  if ( clipLow < *f )
15902  if ( *f < clipHigh )
15903  process( f );
15904  }
15905  }
15906  else
15907  {
15908  for ( int i = m_ch1; i <= m_ch2; ++i )
15909  {
15910  const sample* __restrict__ f = m_image.PixelAddress( 0, m_rect.y0+m_firstRow, i );
15911  PCL_IVDEP
15912  for ( size_type j = 0; j < n; ++j, ++f )
15913  if ( clipLow < *f )
15914  process( f );
15915  }
15916  }
15917  }
15918  else if ( m_image.IsHighRangeClippingEnabled() )
15919  {
15920  sample clipHigh = P::ToSample( m_image.RangeClipHigh() );
15921  for ( int i = m_ch1; i <= m_ch2; ++i )
15922  {
15923  const sample* __restrict__ f = m_image.PixelAddress( 0, m_rect.y0+m_firstRow, i );
15924  PCL_IVDEP
15925  for ( size_type j = 0; j < n; ++j, ++f )
15926  if ( *f < clipHigh )
15927  process( f );
15928  }
15929  }
15930  else
15931  {
15932  // Simple selection
15933  for ( int i = m_ch1; i <= m_ch2; ++i )
15934  {
15935  const sample* __restrict__ f = m_image.PixelAddress( 0, m_rect.y0+m_firstRow, i );
15936  PCL_IVDEP
15937  for ( size_type j = 0; j < n; ++j, ++f )
15938  process( f );
15939  }
15940  }
15941  }
15942  else
15943  {
15944  if ( m_image.IsLowRangeClippingEnabled() )
15945  {
15946  sample clipLow = P::ToSample( m_image.RangeClipLow() );
15947  if ( m_image.IsHighRangeClippingEnabled() )
15948  {
15949  sample clipHigh = P::ToSample( m_image.RangeClipHigh() );
15950  for ( int i = m_ch1; i <= m_ch2; ++i )
15951  {
15952  const sample* __restrict__ f = m_image.PixelAddress( m_rect.x0, m_rect.y0+m_firstRow, i );
15953  for ( int j = m_firstRow; j < m_endRow; ++j, f += dw )
15954  {
15955  PCL_IVDEP
15956  for ( int k = 0; k < w; ++k, ++f )
15957  if ( clipLow < *f )
15958  if ( *f < clipHigh )
15959  process( f );
15960  }
15961  }
15962  }
15963  else
15964  {
15965  for ( int i = m_ch1; i <= m_ch2; ++i )
15966  {
15967  const sample* __restrict__ f = m_image.PixelAddress( m_rect.x0, m_rect.y0+m_firstRow, i );
15968  for ( int j = m_firstRow; j < m_endRow; ++j, f += dw )
15969  {
15970  PCL_IVDEP
15971  for ( int k = 0; k < w; ++k, ++f )
15972  if ( clipLow < *f )
15973  process( f );
15974  }
15975  }
15976  }
15977  }
15978  else if ( m_image.IsHighRangeClippingEnabled() )
15979  {
15980  sample clipHigh = P::ToSample( m_image.RangeClipHigh() );
15981  for ( int i = m_ch1; i <= m_ch2; ++i )
15982  {
15983  const sample* __restrict__ f = m_image.PixelAddress( m_rect.x0, m_rect.y0+m_firstRow, i );
15984  for ( int j = m_firstRow; j < m_endRow; ++j, f += dw )
15985  {
15986  PCL_IVDEP
15987  for ( int k = 0; k < w; ++k, ++f )
15988  if ( *f < clipHigh )
15989  process( f );
15990  }
15991  }
15992  }
15993  else
15994  {
15995  for ( int i = m_ch1; i <= m_ch2; ++i )
15996  {
15997  const sample* __restrict__ f = m_image.PixelAddress( m_rect.x0, m_rect.y0+m_firstRow, i );
15998  for ( int j = m_firstRow; j < m_endRow; ++j, f += dw )
15999  {
16000  PCL_IVDEP
16001  for ( int k = 0; k < w; ++k, ++f )
16002  process( f );
16003  }
16004  }
16005  }
16006  }
16007  }
16008  };
16009 
16010  // -------------------------------------------------------------------------
16011 
16012  class CountThread : public RectThreadBase
16013  {
16014  public:
16015 
16016  size_type count = 0;
16017 
16018  CountThread( const GenericImage& image, const Rect& rect, int ch1, int ch2, int firstRow, int endRow )
16019  : RectThreadBase( image, rect, ch1, ch2, firstRow, endRow )
16020  {
16021  }
16022 
16023  void Run() final
16024  {
16025  /*
16026  * N.B. These threads are only used when range clipping is enabled.
16027  */
16028  int w = this->m_rect.Width();
16029  int dw = this->m_image.Width() - w;
16030 
16031  if ( this->m_image.IsLowRangeClippingEnabled() )
16032  {
16033  sample clipLow = P::ToSample( this->m_image.RangeClipLow() );
16034  if ( this->m_image.IsHighRangeClippingEnabled() )
16035  {
16036  sample clipHigh = P::ToSample( this->m_image.RangeClipHigh() );
16037  for ( int i = this->m_ch1; i <= this->m_ch2; ++i )
16038  {
16039  const sample* __restrict__ f = this->m_image.PixelAddress( this->m_rect.x0, this->m_rect.y0+this->m_firstRow, i );
16040  for ( int j = this->m_firstRow; j < this->m_endRow; ++j, f += dw )
16041  {
16042  PCL_IVDEP
16043  for ( int k = 0; k < w; ++k, ++f )
16044  if ( clipLow < *f )
16045  if ( *f < clipHigh )
16046  ++count;
16047  }
16048  }
16049  }
16050  else
16051  {
16052  for ( int i = this->m_ch1; i <= this->m_ch2; ++i )
16053  {
16054  const sample* __restrict__ f = this->m_image.PixelAddress( this->m_rect.x0, this->m_rect.y0+this->m_firstRow, i );
16055  for ( int j = this->m_firstRow; j < this->m_endRow; ++j, f += dw )
16056  {
16057  PCL_IVDEP
16058  for ( int k = 0; k < w; ++k, ++f )
16059  if ( clipLow < *f )
16060  ++count;
16061  }
16062  }
16063  }
16064  }
16065  else if ( this->m_image.IsHighRangeClippingEnabled() )
16066  {
16067  sample clipHigh = P::ToSample( this->m_image.RangeClipHigh() );
16068  for ( int i = this->m_ch1; i <= this->m_ch2; ++i )
16069  {
16070  const sample* __restrict__ f = this->m_image.PixelAddress( this->m_rect.x0, this->m_rect.y0+this->m_firstRow, i );
16071  for ( int j = this->m_firstRow; j < this->m_endRow; ++j, f += dw )
16072  {
16073  PCL_IVDEP
16074  for ( int k = 0; k < w; ++k, ++f )
16075  if ( *f < clipHigh )
16076  ++count;
16077  }
16078  }
16079  }
16080  else // ?! this should not happen
16081  count = size_type( 1 + this->m_ch2 - this->m_ch1 ) * size_type( w ) * size_type( this->m_rect.Height() );
16082  }
16083  };
16084 
16085  // -------------------------------------------------------------------------
16086 
16087  class MinThread : public RectThreadBase
16088  {
16089  public:
16090 
16091  sample min;
16092  size_type count = 0;
16093 
16094  MinThread( const GenericImage& image, const Rect& rect, int ch1, int ch2, int firstRow, int endRow )
16095  : RectThreadBase( image, rect, ch1, ch2, firstRow, endRow )
16096  {
16097  }
16098 
16099  void Run() final
16100  {
16101  min = P::HighestSampleValue();
16102  count = 0;
16103  this->Execute( [=]( const sample* __restrict__ f )
16104  {
16105  if ( *f < min )
16106  min = *f;
16107  ++count;
16108  } );
16109  }
16110  };
16111 
16112  // -------------------------------------------------------------------------
16113 
16114  class MaxThread : public RectThreadBase
16115  {
16116  public:
16117 
16118  sample max;
16119  size_type count = 0;
16120 
16121  MaxThread( const GenericImage& image, const Rect& rect, int ch1, int ch2, int firstRow, int endRow )
16122  : RectThreadBase( image, rect, ch1, ch2, firstRow, endRow )
16123  {
16124  }
16125 
16126  void Run() final
16127  {
16128  max = P::LowestSampleValue();
16129  count = 0;
16130  this->Execute( [=]( const sample* __restrict__ f )
16131  {
16132  if ( max < *f )
16133  max = *f;
16134  ++count;
16135  } );
16136  }
16137  };
16138 
16139  // -------------------------------------------------------------------------
16140 
16141  class MinMaxThread : public RectThreadBase
16142  {
16143  public:
16144 
16145  sample min;
16146  sample max;
16147  size_type count = 0;
16148 
16149  MinMaxThread( const GenericImage& image, const Rect& rect, int ch1, int ch2, int firstRow, int endRow )
16150  : RectThreadBase( image, rect, ch1, ch2, firstRow, endRow )
16151  {
16152  }
16153 
16154  void Run() final
16155  {
16156  min = P::HighestSampleValue();
16157  max = P::LowestSampleValue();
16158  count = 0;
16159  this->Execute( [=]( const sample* __restrict__ f )
16160  {
16161  if ( *f < min )
16162  min = *f;
16163  if ( max < *f )
16164  max = *f;
16165  ++count;
16166  } );
16167  }
16168  };
16169 
16170  // -------------------------------------------------------------------------
16171 
16172  class ExtremePosThreadBase : public RectThreadBase
16173  {
16174  public:
16175 
16176  int cmin, cmax;
16177  Point pmin, pmax;
16178  size_type count = 0;
16179 
16180  ExtremePosThreadBase( const GenericImage& image, const Rect& rect, int ch1, int ch2, int firstRow, int endRow )
16181  : RectThreadBase( image, rect, ch1, ch2, firstRow, endRow )
16182  {
16183  }
16184 
16185  void Run() final
16186  {
16187  count = 0;
16188  m_amin = m_amax = nullptr;
16189  DoExecute();
16190 
16191  if ( count > 0 )
16192  {
16193  if ( m_amin != nullptr )
16194  for ( int i = this->m_ch1; i <= this->m_ch2; ++i )
16195  if ( m_amin >= this->m_image[i] && m_amin < (this->m_image[i] + this->m_image.NumberOfPixels()) )
16196  {
16197  cmin = i;
16198  pmin.x = (m_amin - this->m_image[i]) % this->m_image.Width();
16199  pmin.y = (m_amin - this->m_image[i]) / this->m_image.Width();
16200  break;
16201  }
16202  if ( m_amax != nullptr )
16203  for ( int i = this->m_ch1; i <= this->m_ch2; ++i )
16204  if ( m_amax >= this->m_image[i] && m_amax < (this->m_image[i] + this->m_image.NumberOfPixels()) )
16205  {
16206  cmax = i;
16207  pmax.x = (m_amax - this->m_image[i]) % this->m_image.Width();
16208  pmax.y = (m_amax - this->m_image[i]) / this->m_image.Width();
16209  break;
16210  }
16211  }
16212  }
16213 
16214  protected:
16215 
16216  const sample* m_amin;
16217  const sample* m_amax;
16218 
16219  virtual void DoExecute() = 0;
16220  };
16221 
16222  // -------------------------------------------------------------------------
16223 
16224  class MinPosThread : public ExtremePosThreadBase
16225  {
16226  public:
16227 
16228  sample min;
16229 
16230  MinPosThread( const GenericImage& image, const Rect& rect, int ch1, int ch2, int firstRow, int endRow )
16231  : ExtremePosThreadBase( image, rect, ch1, ch2, firstRow, endRow )
16232  {
16233  }
16234 
16235  private:
16236 
16237  void DoExecute() override
16238  {
16239  min = P::HighestSampleValue();
16240  this->Execute( [=]( const sample* __restrict__ f )
16241  {
16242  if ( *f < min )
16243  min = *(this->m_amin = f);
16244  ++this->count;
16245  } );
16246  }
16247  };
16248 
16249  // -------------------------------------------------------------------------
16250 
16251  class MaxPosThread : public ExtremePosThreadBase
16252  {
16253  public:
16254 
16255  sample max;
16256 
16257  MaxPosThread( const GenericImage& image, const Rect& rect, int ch1, int ch2, int firstRow, int endRow )
16258  : ExtremePosThreadBase( image, rect, ch1, ch2, firstRow, endRow )
16259  {
16260  }
16261 
16262  private:
16263 
16264  void DoExecute() override
16265  {
16266  max = P::LowestSampleValue();
16267  this->Execute( [=]( const sample* __restrict__ f )
16268  {
16269  if ( max < *f )
16270  max = *(this->m_amax = f);
16271  ++this->count;
16272  } );
16273  }
16274  };
16275 
16276  // -------------------------------------------------------------------------
16277 
16278  class MinMaxPosThread : public ExtremePosThreadBase
16279  {
16280  public:
16281 
16282  sample min, max;
16283 
16284  MinMaxPosThread( const GenericImage& image, const Rect& rect, int ch1, int ch2, int firstRow, int endRow )
16285  : ExtremePosThreadBase( image, rect, ch1, ch2, firstRow, endRow )
16286  {
16287  }
16288 
16289  private:
16290 
16291  void DoExecute() override
16292  {
16293  min = P::HighestSampleValue();
16294  max = P::LowestSampleValue();
16295  this->Execute( [=]( const sample* __restrict__ f )
16296  {
16297  if ( *f < min )
16298  min = *(this->m_amin = f);
16299  if ( max < *f )
16300  max = *(this->m_amax = f);
16301  ++this->count;
16302  } );
16303  }
16304  };
16305 
16306  // -------------------------------------------------------------------------
16307 
16308  class SumThread : public RectThreadBase
16309  {
16310  public:
16311 
16312  double s = 0;
16313  size_type n = 0;
16314 
16315  SumThread( const GenericImage& image, const Rect& rect, int ch1, int ch2, int firstRow, int endRow )
16316  : RectThreadBase( image, rect, ch1, ch2, firstRow, endRow )
16317  {
16318  }
16319 
16320  void Run() final
16321  {
16322  s = e = 0;
16323  n = 0;
16324  DoExecute();
16325  }
16326 
16327  protected:
16328 
16329  double e = 0;
16330 
16331  void SumStep( double x ) noexcept
16332  {
16333  double y = x - e;
16334  double t = s + y;
16335  e = (t - s) - y;
16336  s = t;
16337  ++n;
16338  }
16339 
16340  virtual void DoExecute()
16341  {
16342  this->Execute( [=]( const sample* __restrict__ f )
16343  {
16344  double v; P::FromSample( v, *f );
16345  SumStep( v );
16346  } );
16347  }
16348  };
16349 
16350  // -------------------------------------------------------------------------
16351 
16352  class SumSqrThread : public SumThread
16353  {
16354  public:
16355 
16356  SumSqrThread( const GenericImage& image, const Rect& rect, int ch1, int ch2, int firstRow, int endRow )
16357  : SumThread( image, rect, ch1, ch2, firstRow, endRow )
16358  {
16359  }
16360 
16361  private:
16362 
16363  void DoExecute() override
16364  {
16365  this->Execute( [=]( const sample* __restrict__ f )
16366  {
16367  double v; P::FromSample( v, *f );
16368  this->SumStep( v*v );
16369  } );
16370  }
16371  };
16372 
16373  // -------------------------------------------------------------------------
16374 
16375  class SumAbsThread : public SumThread
16376  {
16377  public:
16378 
16379  SumAbsThread( const GenericImage& image, const Rect& rect, int ch1, int ch2, int firstRow, int endRow )
16380  : SumThread( image, rect, ch1, ch2, firstRow, endRow )
16381  {
16382  }
16383 
16384  private:
16385 
16386  void DoExecute() override
16387  {
16388  this->Execute( [=]( const sample* __restrict__ f )
16389  {
16390  double v; P::FromSample( v, *f );
16391  this->SumStep( pcl::Abs( v ) );
16392  } );
16393  }
16394  };
16395 
16396  // -------------------------------------------------------------------------
16397 
16398  class NormThread : public RectThreadBase
16399  {
16400  public:
16401 
16402  Vector R;
16403  size_type n = 0;
16404 
16405  NormThread( const GenericImage& image, int degree, const Rect& rect, int ch1, int ch2, int firstRow, int endRow )
16406  : RectThreadBase( image, rect, ch1, ch2, firstRow, endRow )
16407  , R( degree )
16408  , e( degree )
16409  {
16410  }
16411 
16412  void Run() final
16413  {
16414  R = e = 0.0;
16415  n = 0;
16416  this->Execute( [=]( const sample* __restrict__ f )
16417  {
16418  double v; P::FromSample( v, *f );
16419  for ( int i = 0;; )
16420  {
16421  NormStep( i, v );
16422  if ( ++i == R.Length() )
16423  break;
16424  v *= v;
16425  }
16426  ++n;
16427  } );
16428  }
16429 
16430  protected:
16431 
16432  Vector e;
16433 
16434  void NormStep( int i, double x ) noexcept
16435  {
16436  double y = x - e[i];
16437  double t = R[i] + y;
16438  e[i] = (t - R[i]) - y;
16439  R[i] = t;
16440  }
16441  };
16442 
16443  // -------------------------------------------------------------------------
16444 
16445  class HistogramThread : public RectThreadBase
16446  {
16447  public:
16448 
16449  SzVector H;
16450 
16451  HistogramThread( const GenericImage& image, const Rect& rect, int ch1, int ch2, int firstRow, int endRow,
16452  const double& low, const double& high )
16453  : RectThreadBase( image, rect, ch1, ch2, firstRow, endRow )
16454  , H( __PCL_MEDIAN_HISTOGRAM_LENGTH )
16455  , m_low( low )
16456  , m_high( high )
16457  {
16458  }
16459 
16460  void Run() final
16461  {
16462  H = size_type( 0 );
16463 
16464  if ( this->HasSimpleSelection() )
16465  {
16466  size_type n = size_type( this->m_endRow - this->m_firstRow ) * size_type( this->m_rect.Width() );
16467  for ( int i = this->m_ch1; i <= this->m_ch2; ++i )
16468  Build( H, this->m_image.PixelAddress( 0, this->m_rect.y0+this->m_firstRow, i ), n, m_low, m_high );
16469  }
16470  else
16471  {
16472  m_range = m_high - m_low;
16473  if ( 1 + m_range != 1 )
16474  {
16475  const double scale = (__PCL_MEDIAN_HISTOGRAM_LENGTH - 1)/m_range;
16476  this->Execute( [=]( const sample* __restrict__ f )
16477  {
16478  const int k = TruncInt( scale*(*f - m_low) );
16479  if ( k >= 0 && k < __PCL_MEDIAN_HISTOGRAM_LENGTH )
16480  ++H[k];
16481  } );
16482  }
16483  }
16484  }
16485 
16486  private:
16487 
16488  const double& m_low;
16489  const double& m_high;
16490  double m_range;
16491 
16492  template <typename T1>
16493  static void Build( SzVector& H, const T1* __restrict__ A, size_type N, double low, double high )
16494  {
16495  const double range = high - low;
16496  if ( 1 + range != 1 )
16497  {
16498  const double scale = (__PCL_MEDIAN_HISTOGRAM_LENGTH - 1)/range;
16499  PCL_IVDEP
16500  for ( size_type i = 0; i < N; ++i )
16501  {
16502  const int k = TruncInt( scale*(A[i] - low) );
16503  if ( k >= 0 && k < __PCL_MEDIAN_HISTOGRAM_LENGTH )
16504  ++H[k];
16505  }
16506  }
16507  }
16508 
16509 #ifdef __PCL_AVX2
16510 
16511  PCL_HOT_FUNCTION
16512  static void Build( SzVector& H, const float* __restrict__ A, size_type N, double low, double high )
16513  {
16514  const double range = high - low;
16515  if ( 1 + range == 1 )
16516  return;
16517 
16518  const float s = float( (__PCL_MEDIAN_HISTOGRAM_LENGTH - 1)/range );
16519  const float l = float( low );
16520  const __m256 S = _mm256_set1_ps( s );
16521  const __m256 L = _mm256_set1_ps( l );
16522  const size_type stepLength = 0x40000000u;
16523 
16524  for ( size_type p = 0; p < N; p += stepLength )
16525  {
16526  const float* __restrict__ V = A + p;
16527  const int n = int( pcl::Min( stepLength, N - p ) );
16528  const int n8 = n >> 3;
16529 
16530  if ( ((ptrdiff_t)V) & 31 )
16531  for ( int i = 0; i < n8; ++i )
16532  {
16533  __m256 v = _mm256_loadu_ps( (const float* __restrict__)(V + i*8) );
16534  __m256i K = _mm256_cvttps_epi32( _mm256_mul_ps( _mm256_sub_ps( v, L ), S ) );
16535  for ( int j = 0; j < 8; ++j )
16536  {
16537  const int k = ((const int* __restrict__)&K)[j];
16538  if ( k >= 0 && k < __PCL_MEDIAN_HISTOGRAM_LENGTH )
16539  ++H[k];
16540  }
16541  }
16542  else
16543  for ( int i = 0; i < n8; ++i )
16544  {
16545  __m256i K = _mm256_cvttps_epi32( _mm256_mul_ps( _mm256_sub_ps( ((const __m256* __restrict__)V)[i], L ), S ) );
16546  for ( int j = 0; j < 8; ++j )
16547  {
16548  const int k = ((const int* __restrict__)&K)[j];
16549  if ( k >= 0 && k < __PCL_MEDIAN_HISTOGRAM_LENGTH )
16550  ++H[k];
16551  }
16552  }
16553 
16554  for ( int i = n8 << 3; i < n; ++i )
16555  {
16556  const int k = TruncInt( s * (V[i] - l) );
16557  if ( k >= 0 && k < __PCL_MEDIAN_HISTOGRAM_LENGTH )
16558  ++H[k];
16559  }
16560  }
16561  }
16562 
16563  PCL_HOT_FUNCTION
16564  static void Build( SzVector& H, const double* __restrict__ A, size_type N, double low, double high )
16565  {
16566  const double range = high - low;
16567  if ( 1 + range == 1 )
16568  return;
16569 
16570  const double s = (__PCL_MEDIAN_HISTOGRAM_LENGTH - 1)/range;
16571  const __m256d S = _mm256_set1_pd( s );
16572  const __m256d L = _mm256_set1_pd( low );
16573  const size_type stepLength = 0x40000000u;
16574 
16575  for ( size_type p = 0; p < N; p += stepLength )
16576  {
16577  const double* __restrict__ V = A + p;
16578  const int n = int( pcl::Min( stepLength, N - p ) );
16579  const int n4 = n >> 2;
16580 
16581  if ( ((ptrdiff_t)V) & 31 )
16582  for ( int i = 0; i < n4; ++i )
16583  {
16584  __m256d v = _mm256_loadu_pd( (const double* __restrict__)(V + i*4) );
16585  __m128i K = _mm256_cvttpd_epi32( _mm256_mul_pd( _mm256_sub_pd( v, L ), S ) );
16586  for ( int j = 0; j < 4; ++j )
16587  {
16588  const int k = ((const int* __restrict__)&K)[j];
16589  if ( k >= 0 && k < __PCL_MEDIAN_HISTOGRAM_LENGTH )
16590  ++H[k];
16591  }
16592  }
16593  else
16594  for ( int i = 0; i < n4; ++i )
16595  {
16596  __m128i K = _mm256_cvttpd_epi32( _mm256_mul_pd( _mm256_sub_pd( ((const __m256d* __restrict__)V)[i], L ), S ) );
16597  for ( int j = 0; j < 4; ++j )
16598  {
16599  const int k = ((const int* __restrict__)&K)[j];
16600  if ( k >= 0 && k < __PCL_MEDIAN_HISTOGRAM_LENGTH )
16601  ++H[k];
16602  }
16603  }
16604 
16605  for ( int i = n4 << 2; i < n; ++i )
16606  {
16607  const int k = TruncInt( s * (V[i] - low) );
16608  if ( k >= 0 && k < __PCL_MEDIAN_HISTOGRAM_LENGTH )
16609  ++H[k];
16610  }
16611  }
16612  }
16613 
16614 #endif // __PCL_AVX2
16615  };
16616 
16617  // -------------------------------------------------------------------------
16618 
16619  class ExtremeAbsDevThread : public RectThreadBase
16620  {
16621  public:
16622 
16623  double minAbsDev = 0, maxAbsDev = 0;
16624  size_type count = 0;
16625 
16626  ExtremeAbsDevThread( const GenericImage& image, const Rect& rect, int ch1, int ch2, int firstRow, int endRow, double center )
16627  : RectThreadBase( image, rect, ch1, ch2, firstRow, endRow )
16628  , m_center( center )
16629  {
16630  }
16631 
16632  void Run() final
16633  {
16634  minAbsDev = std::numeric_limits<double>::max();
16635  maxAbsDev = 0;
16636  count = 0;
16637  this->Execute( [=]( const sample* __restrict__ f )
16638  {
16639  double d; P::FromSample( d, *f );
16640  d = pcl::Abs( d - m_center );
16641  if ( d < minAbsDev )
16642  minAbsDev = d;
16643  if ( d > maxAbsDev )
16644  maxAbsDev = d;
16645  ++count;
16646  } );
16647  }
16648 
16649  private:
16650 
16651  double m_center;
16652  };
16653 
16654  // -------------------------------------------------------------------------
16655 
16656  class TwoSidedExtremeAbsDevThread : public RectThreadBase
16657  {
16658  public:
16659 
16660  double minAbsDevLow = 0, minAbsDevHigh = 0;
16661  double maxAbsDevLow = 0, maxAbsDevHigh = 0;
16662  size_type nLow = 0, nHigh = 0;
16663 
16664  TwoSidedExtremeAbsDevThread( const GenericImage& image, const Rect& rect, int ch1, int ch2, int firstRow, int endRow, double center )
16665  : RectThreadBase( image, rect, ch1, ch2, firstRow, endRow )
16666  , m_center( center )
16667  {
16668  }
16669 
16670  void Run() final
16671  {
16672  minAbsDevLow = minAbsDevHigh = std::numeric_limits<double>::max();
16673  maxAbsDevLow = maxAbsDevHigh = 0;
16674  nLow = nHigh = 0;
16675  this->Execute( [=]( const sample* __restrict__ f )
16676  {
16677  double x; P::FromSample( x, *f );
16678  if ( x <= m_center )
16679  {
16680  double d = m_center - x;
16681  if ( d < minAbsDevLow )
16682  minAbsDevLow = d;
16683  if ( d > maxAbsDevLow )
16684  maxAbsDevLow = d;
16685  ++nLow;
16686  }
16687  else
16688  {
16689  double d = x - m_center;
16690  if ( d < minAbsDevHigh )
16691  minAbsDevHigh = d;
16692  if ( d > maxAbsDevHigh )
16693  maxAbsDevHigh = d;
16694  ++nHigh;
16695  }
16696  } );
16697  }
16698 
16699  private:
16700 
16701  double m_center;
16702  };
16703 
16704  // -------------------------------------------------------------------------
16705 
16706  class AbsDevHistogramThread : public RectThreadBase
16707  {
16708  public:
16709 
16710  SzVector H;
16711 
16712  AbsDevHistogramThread( const GenericImage& image, const Rect& rect, int ch1, int ch2, int firstRow, int endRow,
16713  double center, const double& low, const double& high )
16714  : RectThreadBase( image, rect, ch1, ch2, firstRow, endRow )
16715  , H( __PCL_MEDIAN_HISTOGRAM_LENGTH )
16716  , m_center( center )
16717  , m_low( low )
16718  , m_high( high )
16719  {
16720  }
16721 
16722  void Run() final
16723  {
16724  H = size_type( 0 );
16725  m_range = m_high - m_low;
16726 // Workaround for clang compiler bug on macOS:
16727 // https://pixinsight.com/forum/index.php?threads/pi-always-crash-when-stf.15830/
16728 #ifdef __PCL_MACOSX
16729  if ( 1 + m_range != 1 )
16730 #endif
16731  this->Execute( [=]( const sample* __restrict__ f )
16732  {
16733  double d; P::FromSample( d, *f );
16734  d = pcl::Abs( d - m_center );
16735  if ( d >= m_low )
16736  if ( d <= m_high )
16737  ++H[TruncInt( (__PCL_MEDIAN_HISTOGRAM_LENGTH - 1) * (d - m_low)/m_range )];
16738  } );
16739  }
16740 
16741  private:
16742 
16743  double m_center;
16744  const double& m_low;
16745  const double& m_high;
16746  double m_range;
16747  };
16748 
16749  // -------------------------------------------------------------------------
16750 
16751  class TwoSidedAbsDevHistogramThread : public RectThreadBase
16752  {
16753  public:
16754 
16755  SzVector H;
16756 
16757  TwoSidedAbsDevHistogramThread( const GenericImage& image, const Rect& rect, int ch1, int ch2, int firstRow, int endRow,
16758  double center, const int& side, const double& low, const double& high )
16759  : RectThreadBase( image, rect, ch1, ch2, firstRow, endRow )
16760  , H( __PCL_MEDIAN_HISTOGRAM_LENGTH )
16761  , m_center( center )
16762  , m_side( side )
16763  , m_low( low )
16764  , m_high( high )
16765  {
16766  }
16767 
16768  void Run() final
16769  {
16770  H = size_type( 0 );
16771  m_range = m_high - m_low;
16772 // Workaround for clang compiler bug on macOS:
16773 // https://pixinsight.com/forum/index.php?threads/pi-always-crash-when-stf.15830/
16774 #ifdef __PCL_MACOSX
16775  if ( 1 + m_range != 1 )
16776 #endif
16777  this->Execute( [=]( const sample* __restrict__ f )
16778  {
16779  double x; P::FromSample( x, *f );
16780  if ( m_side > 0 == x > m_center )
16781  {
16782  double d = m_side ? x - m_center : m_center - x;
16783  if ( d >= m_low )
16784  if ( d <= m_high )
16785  ++H[TruncInt( (__PCL_MEDIAN_HISTOGRAM_LENGTH - 1) * (d - m_low)/m_range )];
16786  }
16787  } );
16788  }
16789 
16790  private:
16791 
16792  double m_center;
16793  const int& m_side;
16794  const double& m_low;
16795  const double& m_high;
16796  double m_range;
16797  };
16798 
16799  // -------------------------------------------------------------------------
16800 
16801  class VarThread : public RectThreadBase
16802  {
16803  public:
16804 
16805  double var = 0, eps = 0;
16806 
16807  VarThread( const GenericImage& image, double mean, const Rect& rect, int ch1, int ch2, int firstRow, int endRow )
16808  : RectThreadBase( image, rect, ch1, ch2, firstRow, endRow )
16809  , m_mean( mean )
16810  {
16811  }
16812 
16813  void Run() final
16814  {
16815  var = eps = 0;
16816  this->Execute( [=]( const sample* __restrict__ f )
16817  {
16818  double d; P::FromSample( d, *f );
16819  d -= m_mean;
16820  var += d*d;
16821  eps += d;
16822  } );
16823  }
16824 
16825  private:
16826 
16827  double m_mean;
16828  };
16829 
16830  // -------------------------------------------------------------------------
16831 
16832  class SumAbsDevThread : public SumThread
16833  {
16834  public:
16835 
16836  SumAbsDevThread( const GenericImage& image, double center, const Rect& rect, int ch1, int ch2, int firstRow, int endRow )
16837  : SumThread( image, rect, ch1, ch2, firstRow, endRow )
16838  , m_center( center )
16839  {
16840  }
16841 
16842  private:
16843 
16844  double m_center;
16845 
16846  void DoExecute() override
16847  {
16848  this->Execute( [=]( const sample* __restrict__ f )
16849  {
16850  double v; P::FromSample( v, *f );
16851  this->SumStep( pcl::Abs( v - m_center ) );
16852  } );
16853  }
16854  };
16855 
16856  // -------------------------------------------------------------------------
16857 
16858  class TwoSidedSumAbsDevThread : public RectThreadBase
16859  {
16860  public:
16861 
16862  double s0 = 0, s1 = 0;
16863  size_type n0 = 0, n1 = 0;
16864 
16865  TwoSidedSumAbsDevThread( const GenericImage& image, double center,
16866  const Rect& rect, int ch1, int ch2, int firstRow, int endRow )
16867  : RectThreadBase( image, rect, ch1, ch2, firstRow, endRow )
16868  , m_center( center )
16869  {
16870  }
16871 
16872  void Run() final
16873  {
16874  s0 = s1 = 0;
16875  n0 = n1 = 0;
16876  this->Execute( [=]( const sample* __restrict__ f )
16877  {
16878  double v; P::FromSample( v, *f );
16879  if ( v <= m_center )
16880  {
16881  s0 += m_center - v;
16882  ++n0;
16883  }
16884  else
16885  {
16886  s1 += v - m_center;
16887  ++n1;
16888  }
16889  } );
16890  }
16891 
16892  private:
16893 
16894  double m_center;
16895  };
16896 
16897  // -------------------------------------------------------------------------
16898 
16899  class BWMVThread : public RectThreadBase
16900  {
16901  public:
16902 
16903  double num = 0, den = 0;
16904  size_type n = 0, nr = 0;
16905 
16906  BWMVThread( const GenericImage& image, double center, double kd,
16907  const Rect& rect, int ch1, int ch2, int firstRow, int endRow )
16908  : RectThreadBase( image, rect, ch1, ch2, firstRow, endRow )
16909  , m_center( center )
16910  , m_kd( kd )
16911  {
16912  }
16913 
16914  void Run() final
16915  {
16916  num = den = 0;
16917  n = nr = 0;
16918  this->Execute( [=]( const sample* __restrict__ f )
16919  {
16920  ++n;
16921  double xc; P::FromSample( xc, *f ); xc -= m_center;
16922  double y = xc/m_kd;
16923  if ( pcl::Abs( y ) < 1 )
16924  {
16925  double y2 = y*y;
16926  double y21 = 1 - y2;
16927  num += xc*xc * y21*y21*y21*y21;
16928  den += y21 * (1 - 5*y2);
16929  ++nr;
16930  }
16931  } );
16932  }
16933 
16934  private:
16935 
16936  double m_center;
16937  double m_kd;
16938  };
16939 
16940  // -------------------------------------------------------------------------
16941 
16942  class TwoSidedBWMVThread : public RectThreadBase
16943  {
16944  public:
16945 
16946  double num0 = 0, den0 = 0;
16947  double num1 = 0, den1 = 0;
16948  size_type n0 = 0, n1 = 0, nr0 = 0, nr1 = 0;
16949 
16950  TwoSidedBWMVThread( const GenericImage& image, double center, double kd0, double kd1,
16951  const Rect& rect, int ch1, int ch2, int firstRow, int endRow )
16952  : RectThreadBase( image, rect, ch1, ch2, firstRow, endRow )
16953  , m_center( center )
16954  , m_kd0( kd0 )
16955  , m_kd1( kd1 )
16956  {
16957  }
16958 
16959  void Run() final
16960  {
16961  num0 = den0 = num1 = den1 = 0;
16962  n0 = n1 = nr0 = nr1 = 0;
16963  this->Execute( [=]( const sample* __restrict__ f )
16964  {
16965  double xc; P::FromSample( xc, *f ); xc -= m_center;
16966  bool low = xc <= 0;
16967  if ( low )
16968  ++n0;
16969  else
16970  ++n1;
16971 
16972  double y = xc/(low ? m_kd0 : m_kd1);
16973  if ( pcl::Abs( y ) < 1 )
16974  {
16975  double y2 = y*y;
16976  double y21 = 1 - y2;
16977  double num = xc*xc * y21*y21*y21*y21;
16978  double den = y21 * (1 - 5*y2);
16979  if ( low )
16980  {
16981  num0 += num;
16982  den0 += den;
16983  ++nr0;
16984  }
16985  else
16986  {
16987  num1 += num;
16988  den1 += den;
16989  ++nr1;
16990  }
16991  }
16992  } );
16993  }
16994 
16995  private:
16996 
16997  double m_center;
16998  double m_kd0;
16999  double m_kd1;
17000  };
17001 
17002  // -------------------------------------------------------------------------
17003 
17004  class SmpThread : public RectThreadBase
17005  {
17006  public:
17007 
17008  Array<sample> samples;
17009  size_type n = 0;
17010 
17011  SmpThread( const GenericImage& image, const Rect& rect, int ch1, int ch2, int firstRow, int endRow )
17012  : RectThreadBase( image, rect, ch1, ch2, firstRow, endRow )
17013  {
17014  size_type N = size_type( this->m_rect.Width() )
17015  * size_type( this->m_endRow - this->m_firstRow )
17016  * (1 + this->m_ch2 - this->m_ch1);
17017  samples = Array<sample>( N );
17018  }
17019 
17020  void Run() final
17021  {
17022  n = 0;
17023  this->Execute( [=]( const sample* __restrict__ f )
17024  {
17025  samples[n++] = *f;
17026  } );
17027  }
17028  };
17029 
17030  // -------------------------------------------------------------------------
17031 
17032  class DSmpThread : public RectThreadBase
17033  {
17034  public:
17035 
17036  Array<double> values;
17037  size_type n = 0;
17038 
17039  DSmpThread( const GenericImage& image, const Rect& rect, int ch1, int ch2, int firstRow, int endRow )
17040  : RectThreadBase( image, rect, ch1, ch2, firstRow, endRow )
17041  {
17042  size_type N = size_type( this->m_rect.Width() )
17043  * size_type( this->m_endRow - this->m_firstRow )
17044  * (1 + this->m_ch2 - this->m_ch1);
17045  values = Array<double>( N );
17046  }
17047 
17048  void Run() final
17049  {
17050  n = 0;
17051  DoExecute();
17052  }
17053 
17054  protected:
17055 
17056  virtual void DoExecute()
17057  {
17058  if ( this->HasSimpleSelection() )
17059  {
17060  size_type N = size_type( this->m_endRow - this->m_firstRow ) * size_type( this->m_rect.Width() );
17061  for ( int i = this->m_ch1; i <= this->m_ch2; ++i )
17062  {
17063  const sample* __restrict__ f = this->m_image.PixelAddress( 0, this->m_rect.y0 + this->m_firstRow, i );
17064  PCL_IVDEP
17065  for ( size_type j = 0; j < N; ++j, ++n, ++f )
17066  P::FromSample( values[n], *f );
17067  }
17068  }
17069  else
17070  {
17071  this->Execute( [=]( const sample* __restrict__ f )
17072  {
17073  P::FromSample( values[n++], *f );
17074  } );
17075  }
17076  }
17077  };
17078 
17079  // -------------------------------------------------------------------------
17080 
17081  class AbsDevSmpThread : public DSmpThread
17082  {
17083  public:
17084 
17085  AbsDevSmpThread( const GenericImage& image, double center, const Rect& rect, int ch1, int ch2, int firstRow, int endRow )
17086  : DSmpThread( image, rect, ch1, ch2, firstRow, endRow )
17087  , m_center( center )
17088  {
17089  }
17090 
17091  private:
17092 
17093  double m_center;
17094 
17095  void DoExecute() override
17096  {
17097  this->Execute( [=]( const sample* __restrict__ f )
17098  {
17099  double d; P::FromSample( d, *f );
17100  this->values[this->n++] = pcl::Abs( d - m_center );
17101  } );
17102  }
17103  };
17104 
17105  // -------------------------------------------------------------------------
17106 
17107  class TwoSidedAbsDevSmpThread : public DSmpThread
17108  {
17109  public:
17110 
17112 
17113  TwoSidedAbsDevSmpThread( const GenericImage& image, double center, const Rect& rect, int ch1, int ch2, int firstRow, int endRow )
17114  : DSmpThread( image, rect, ch1, ch2, firstRow, endRow )
17115  , m_center( center )
17116  {
17117  }
17118 
17119  private:
17120 
17121  double m_center;
17122 
17123  void DoExecute() override
17124  {
17125  p = this->values.Begin();
17126  q = this->values.End();
17127  this->Execute( [=]( const sample* __restrict__ f )
17128  {
17129  double x; P::FromSample( x, *f );
17130  if ( x <= m_center )
17131  *p++ = m_center - x;
17132  else
17133  *--q = x - m_center;
17134  ++this->n;
17135  } );
17136  }
17137  };
17138 
17139  // -------------------------------------------------------------------------
17140 
17141  class ColorSpaceConversionThread : public Thread
17142  {
17143  public:
17144 
17145  ColorSpaceConversionThread( GenericImage& image, ThreadData& data,
17146  color_space toColorSpace, size_type begin, size_type end )
17147  : m_image( image )
17148  , m_data( data )
17149  , m_toColorSpace( toColorSpace )
17150  , m_begin( begin )
17151  , m_end( end )
17152  {
17153  }
17154 
17155  void Run() final
17156  {
17158 
17159  const RGBColorSystem& rgbws = m_image.RGBWorkingSpace();
17160 
17161  typename P::sample* f0 = m_image[0] + m_begin;
17162  typename P::sample* f1 = m_image[1] + m_begin;
17163  typename P::sample* f2 = m_image[2] + m_begin;
17164  typename P::sample* fN = m_image[0] + m_end;
17165 
17166  for ( ; f0 < fN; ++f0, ++f1, ++f2 )
17167  {
17168  RGBColorSystem::sample r0, r1, r2;
17169  P::FromSample( r0, *f0 );
17170  P::FromSample( r1, *f1 );
17171  P::FromSample( r2, *f2 );
17172 
17173  switch ( m_image.ColorSpace() )
17174  {
17175  case ColorSpace::RGB :
17176  switch ( m_toColorSpace )
17177  {
17178  case ColorSpace::Gray :
17179  rgbws.RGBToGray( r0, r0, r1, r2 );
17180  break;
17181  case ColorSpace::HSV :
17182  rgbws.RGBToHSV( r0, r1, r2, r0, r1, r2 );
17183  break;
17184  case ColorSpace::HSI :
17185  rgbws.RGBToHSI( r0, r1, r2, r0, r1, r2 );
17186  break;
17187  case ColorSpace::CIEXYZ :
17188  rgbws.RGBToCIEXYZ( r0, r1, r2, r0, r1, r2 );
17189  break;
17190  case ColorSpace::CIELab :
17191  rgbws.RGBToCIELab( r0, r1, r2, r0, r1, r2 );
17192  break;
17193  case ColorSpace::CIELch :
17194  rgbws.RGBToCIELch( r0, r1, r2, r0, r1, r2 );
17195  break;
17196  default:
17197  break;
17198  }
17199  break;
17200  case ColorSpace::HSV :
17201  rgbws.HSVToRGB( r0, r1, r2, r0, r1, r2 );
17202  switch ( m_toColorSpace )
17203  {
17204  case ColorSpace::Gray :
17205  rgbws.RGBToGray( r0, r0, r1, r2 );
17206  break;
17207  case ColorSpace::RGB :
17208  break;
17209  case ColorSpace::HSI :
17210  rgbws.RGBToHSI( r0, r1, r2, r0, r1, r2 );
17211  break;
17212  case ColorSpace::CIEXYZ :
17213  rgbws.RGBToCIEXYZ( r0, r1, r2, r0, r1, r2 );
17214  break;
17215  case ColorSpace::CIELab :
17216  rgbws.RGBToCIELab( r0, r1, r2, r0, r1, r2 );
17217  break;
17218  case ColorSpace::CIELch :
17219  rgbws.RGBToCIELch( r0, r1, r2, r0, r1, r2 );
17220  break;
17221  default:
17222  break;
17223  }
17224  break;
17225  case ColorSpace::HSI :
17226  rgbws.HSIToRGB( r0, r1, r2, r0, r1, r2 );
17227  switch ( m_toColorSpace )
17228  {
17229  case ColorSpace::Gray :
17230  rgbws.RGBToGray( r0, r0, r1, r2 );
17231  break;
17232  case ColorSpace::RGB :
17233  break;
17234  case ColorSpace::HSV :
17235  rgbws.RGBToHSV( r0, r1, r2, r0, r1, r2 );
17236  break;
17237  case ColorSpace::CIEXYZ :
17238  rgbws.RGBToCIEXYZ( r0, r1, r2, r0, r1, r2 );
17239  break;
17240  case ColorSpace::CIELab :
17241  rgbws.RGBToCIELab( r0, r1, r2, r0, r1, r2 );
17242  break;
17243  case ColorSpace::CIELch :
17244  rgbws.RGBToCIELch( r0, r1, r2, r0, r1, r2 );
17245  break;
17246  default:
17247  break;
17248  }
17249  break;
17250  case ColorSpace::CIEXYZ :
17251  switch ( m_toColorSpace )
17252  {
17253  case ColorSpace::Gray :
17254  rgbws.CIEXYZToRGB( r0, r1, r2, r0, r1, r2 );
17255  rgbws.RGBToGray( r0, r0, r1, r2 );
17256  break;
17257  case ColorSpace::RGB :
17258  rgbws.CIEXYZToRGB( r0, r1, r2, r0, r1, r2 );
17259  break;
17260  case ColorSpace::HSV :
17261  rgbws.CIEXYZToRGB( r0, r1, r2, r0, r1, r2 );
17262  rgbws.RGBToHSV( r0, r1, r2, r0, r1, r2 );
17263  break;
17264  case ColorSpace::HSI :
17265  rgbws.CIEXYZToRGB( r0, r1, r2, r0, r1, r2 );
17266  rgbws.RGBToHSI( r0, r1, r2, r0, r1, r2 );
17267  break;
17268  case ColorSpace::CIELab :
17269  rgbws.CIEXYZToCIELab( r0, r1, r2, r0, r1, r2 );
17270  break;
17271  case ColorSpace::CIELch :
17272  rgbws.CIEXYZToCIELab( r0, r1, r2, r0, r1, r2 );
17273  rgbws.CIELabToCIELch( r0, r1, r2, r0, r1, r2 );
17274  break;
17275  default:
17276  break;
17277  }
17278  break;
17279  case ColorSpace::CIELab :
17280  switch ( m_toColorSpace )
17281  {
17282  case ColorSpace::Gray :
17283  rgbws.CIELabToRGB( r0, r1, r2, r0, r1, r2 );
17284  rgbws.RGBToGray( r0, r0, r1, r2 );
17285  break;
17286  case ColorSpace::RGB :
17287  rgbws.CIELabToRGB( r0, r1, r2, r0, r1, r2 );
17288  break;
17289  case ColorSpace::HSV :
17290  rgbws.CIELabToRGB( r0, r1, r2, r0, r1, r2 );
17291  rgbws.RGBToHSV( r0, r1, r2, r0, r1, r2 );
17292  break;
17293  case ColorSpace::HSI :
17294  rgbws.CIELabToRGB( r0, r1, r2, r0, r1, r2 );
17295  rgbws.RGBToHSI( r0, r1, r2, r0, r1, r2 );
17296  break;
17297  case ColorSpace::CIEXYZ :
17298  rgbws.CIELabToCIEXYZ( r0, r1, r2, r0, r1, r2 );
17299  break;
17300  case ColorSpace::CIELch :
17301  rgbws.CIELabToCIELch( r0, r1, r2, r0, r1, r2 );
17302  break;
17303  default:
17304  break;
17305  }
17306  break;
17307  case ColorSpace::CIELch :
17308  switch ( m_toColorSpace )
17309  {
17310  case ColorSpace::Gray :
17311  rgbws.CIELchToRGB( r0, r1, r2, r0, r1, r2 );
17312  rgbws.RGBToGray( r0, r0, r1, r2 );
17313  break;
17314  case ColorSpace::RGB :
17315  rgbws.CIELchToRGB( r0, r1, r2, r0, r1, r2 );
17316  break;
17317  case ColorSpace::HSV :
17318  rgbws.CIELchToRGB( r0, r1, r2, r0, r1, r2 );
17319  rgbws.RGBToHSV( r0, r1, r2, r0, r1, r2 );
17320  break;
17321  case ColorSpace::HSI :
17322  rgbws.CIELchToRGB( r0, r1, r2, r0, r1, r2 );
17323  rgbws.RGBToHSI( r0, r1, r2, r0, r1, r2 );
17324  break;
17325  case ColorSpace::CIEXYZ :
17326  rgbws.CIELchToCIELab( r0, r1, r2, r0, r1, r2 );
17327  rgbws.CIELabToCIEXYZ( r0, r1, r2, r0, r1, r2 );
17328  break;
17329  case ColorSpace::CIELab :
17330  rgbws.CIELchToCIELab( r0, r1, r2, r0, r1, r2 );
17331  break;
17332  default:
17333  break;
17334  }
17335  break;
17336  default:
17337  break;
17338  }
17339 
17340  *f0 = P::ToSample( r0 );
17341 
17342  if ( m_toColorSpace != ColorSpace::Gray )
17343  {
17344  *f1 = P::ToSample( r1 );
17345  *f2 = P::ToSample( r2 );
17346  }
17347 
17348  UPDATE_THREAD_MONITOR( 65536 )
17349  }
17350  }
17351 
17352  private:
17353 
17354  GenericImage& m_image;
17355  ThreadData& m_data;
17356  color_space m_toColorSpace;
17357  size_type m_begin, m_end;
17358  };
17359 
17360  // -------------------------------------------------------------------------
17361 
17362  template <class P1>
17363  class GetLuminanceThread : public Thread
17364  {
17365  public:
17366 
17367  GetLuminanceThread( GenericImage<P1>& luminance, const GenericImage& image, ThreadData& data,
17368  const Rect& rect, int firstRow, int endRow )
17369  : m_luminance( luminance )
17370  , m_image( image )
17371  , m_data( data )
17372  , m_rect( rect )
17373  , m_firstRow( firstRow )
17374  , m_endRow( endRow )
17375  {
17376  }
17377 
17378  void Run() final
17379  {
17381 
17382  const RGBColorSystem& rgbws = m_image.RGBWorkingSpace();
17383 
17384  const typename P::sample* f0 = m_image.PixelAddress( m_rect.x0, m_rect.y0 + m_firstRow, 0 );
17385  const typename P::sample* f1 = m_image.PixelAddress( m_rect.x0, m_rect.y0 + m_firstRow, 1 );
17386  const typename P::sample* f2 = m_image.PixelAddress( m_rect.x0, m_rect.y0 + m_firstRow, 2 );
17387 
17388  typename P1::sample* fY = m_luminance.ScanLine( m_firstRow );
17389 
17390  for ( int y = m_firstRow, dw = m_image.Width()-m_rect.Width();
17391  y < m_endRow;
17392  ++y, f0 += dw, f1 += dw, f2 += dw )
17393  {
17394  const typename P::sample* fN = f0 + m_rect.Width();
17395 
17396  for ( ; f0 < fN; ++f0, ++f1, ++f2, ++fY )
17397  {
17398  RGBColorSystem::sample r0, r1, r2, rY;
17399  P::FromSample( r0, *f0 );
17400  P::FromSample( r1, *f1 );
17401  P::FromSample( r2, *f2 );
17402 
17403  switch ( m_image.ColorSpace() )
17404  {
17405  case ColorSpace::RGB:
17406  case ColorSpace::HSV:
17407  case ColorSpace::HSI:
17408  switch ( m_image.ColorSpace() )
17409  {
17410  case ColorSpace::HSV:
17411  rgbws.HSVToRGB( r0, r1, r2, r0, r1, r2 );
17412  break;
17413  case ColorSpace::HSI:
17414  rgbws.HSIToRGB( r0, r1, r2, r0, r1, r2 );
17415  break;
17416  default:
17417  break;
17418  }
17419  rY = rgbws.CIEY( r0, r1, r2 );
17420  break;
17421 
17422  case ColorSpace::CIELch:
17423  rgbws.CIELchToCIELab( r0, r1, r2, r0, r1, r2 );
17424  // fall through
17425  case ColorSpace::CIELab:
17426  rgbws.CIELabToCIEXYZ( r0, rY, r2, r0, r1, r2 );
17427  break;
17428 
17429  default: // ?!
17430  rY = 0;
17431  break;
17432  }
17433 
17434  *fY = P1::ToSample( rY );
17435 
17436  UPDATE_THREAD_MONITOR( 65536 )
17437  }
17438  }
17439  }
17440 
17441  private:
17442 
17443  GenericImage<P1>& m_luminance;
17444  const GenericImage& m_image;
17445  ThreadData& m_data;
17446  const Rect& m_rect;
17447  int m_firstRow, m_endRow;
17448  };
17449 
17450  // -------------------------------------------------------------------------
17451 
17452  template <class P1>
17453  class GetLightnessThread : public Thread
17454  {
17455  public:
17456 
17457  GetLightnessThread( GenericImage<P1>& lightness, const GenericImage& image, ThreadData& data,
17458  const Rect& rect, int firstRow, int endRow )
17459  : m_lightness( lightness )
17460  , m_image( image )
17461  , m_data( data )
17462  , m_rect( rect )
17463  , m_firstRow( firstRow )
17464  , m_endRow( endRow )
17465  {
17466  }
17467 
17468  void Run() final
17469  {
17471 
17472  const RGBColorSystem& rgbws = m_image.RGBWorkingSpace();
17473 
17474  const typename P::sample* f0 = m_image.PixelAddress( m_rect.x0, m_rect.y0 + m_firstRow, 0 );
17475  const typename P::sample* f1 = m_image.PixelAddress( m_rect.x0, m_rect.y0 + m_firstRow, 1 );
17476  const typename P::sample* f2 = m_image.PixelAddress( m_rect.x0, m_rect.y0 + m_firstRow, 2 );
17477 
17478  typename P1::sample* fL = m_lightness.ScanLine( m_firstRow );
17479 
17480  for ( int y = m_firstRow, dw = m_image.Width()-m_rect.Width();
17481  y < m_endRow;
17482  ++y, f0 += dw, f1 += dw, f2 += dw )
17483  {
17484  const typename P::sample* fN = f0 + m_rect.Width();
17485 
17486  for ( ; f0 < fN; ++f0, ++f1, ++f2, ++fL )
17487  {
17488  RGBColorSystem::sample r0, r1, r2, rL;
17489  P::FromSample( r0, *f0 );
17490  P::FromSample( r1, *f1 );
17491  P::FromSample( r2, *f2 );
17492 
17493  switch ( m_image.ColorSpace() )
17494  {
17495  case ColorSpace::RGB:
17496  case ColorSpace::HSV:
17497  case ColorSpace::HSI:
17498  case ColorSpace::CIEXYZ:
17499  switch ( m_image.ColorSpace() )
17500  {
17501  case ColorSpace::HSV:
17502  rgbws.HSVToRGB( r0, r1, r2, r0, r1, r2 );
17503  break;
17504  case ColorSpace::HSI:
17505  rgbws.HSIToRGB( r0, r1, r2, r0, r1, r2 );
17506  break;
17507  case ColorSpace::CIEXYZ:
17508  rgbws.CIEXYZToRGB( r0, r1, r2, r0, r1, r2 );
17509  break;
17510  default:
17511  break;
17512  }
17513  rL = rgbws.CIEL( r0, r1, r2 );
17514  break;
17515 
17516  default: // ?!
17517  rL = 0;
17518  break;
17519  }
17520 
17521  *fL = P1::ToSample( rL );
17522 
17523  UPDATE_THREAD_MONITOR( 65536 )
17524  }
17525  }
17526  }
17527 
17528  private:
17529 
17530  GenericImage<P1>& m_lightness;
17531  const GenericImage& m_image;
17532  ThreadData& m_data;
17533  const Rect& m_rect;
17534  int m_firstRow, m_endRow;
17535  };
17536 
17537  // -------------------------------------------------------------------------
17538 
17539  template <class P1>
17540  class GetIntensityThread : public Thread
17541  {
17542  public:
17543 
17544  GetIntensityThread( GenericImage<P1>& luminance, const GenericImage& image, ThreadData& data,
17545  const Rect& rect, int firstRow, int endRow )
17546  : m_intensity( luminance )
17547  , m_image( image )
17548  , m_data( data )
17549  , m_rect( rect )
17550  , m_firstRow( firstRow )
17551  , m_endRow( endRow )
17552  {
17553  }
17554 
17555  void Run() final
17556  {
17558 
17559  const RGBColorSystem& rgbws = m_image.RGBWorkingSpace();
17560 
17561  const typename P::sample* f0 = m_image.PixelAddress( m_rect.x0, m_rect.y0 + m_firstRow, 0 );
17562  const typename P::sample* f1 = m_image.PixelAddress( m_rect.x0, m_rect.y0 + m_firstRow, 1 );
17563  const typename P::sample* f2 = m_image.PixelAddress( m_rect.x0, m_rect.y0 + m_firstRow, 2 );
17564 
17565  typename P1::sample* fI = m_intensity.ScanLine( m_firstRow );
17566 
17567  for ( int y = m_firstRow, dw = m_image.Width()-m_rect.Width();
17568  y < m_endRow;
17569  ++y, f0 += dw, f1 += dw, f2 += dw )
17570  {
17571  const typename P::sample* fN = f0 + m_rect.Width();
17572 
17573  for ( ; f0 < fN; ++f0, ++f1, ++f2, ++fI )
17574  {
17575  RGBColorSystem::sample r0, r1, r2;
17576  P::FromSample( r0, *f0 );
17577  P::FromSample( r1, *f1 );
17578  P::FromSample( r2, *f2 );
17579 
17580  switch ( m_image.ColorSpace() )
17581  {
17582  case ColorSpace::RGB:
17583  break;
17584  case ColorSpace::HSV:
17585  rgbws.HSVToRGB( r0, r1, r2, r0, r1, r2 );
17586  break;
17587  case ColorSpace::CIEXYZ:
17588  rgbws.CIEXYZToRGB( r0, r1, r2, r0, r1, r2 );
17589  break;
17590  case ColorSpace::CIELab:
17591  rgbws.CIELabToRGB( r0, r1, r2, r0, r1, r2 );
17592  break;
17593  case ColorSpace::CIELch:
17594  rgbws.CIELchToRGB( r0, r1, r2, r0, r1, r2 );
17595  break;
17596  default:
17597  break;
17598  }
17599 
17600  *fI = P1::ToSample( rgbws.Intensity( r0, r1, r2 ) );
17601 
17602  UPDATE_THREAD_MONITOR( 65536 )
17603  }
17604  }
17605  }
17606 
17607  private:
17608 
17609  GenericImage<P1>& m_intensity;
17610  const GenericImage& m_image;
17611  ThreadData& m_data;
17612  const Rect& m_rect;
17613  int m_firstRow, m_endRow;
17614  };
17615 
17616  // -------------------------------------------------------------------------
17617 
17618  template <class P1>
17619  class SetLuminanceThread : public Thread
17620  {
17621  public:
17622 
17623  SetLuminanceThread( GenericImage& image, const GenericImage<P1>& luminance, ThreadData& data,
17624  const Point& target, const Rect& rect, int firstRow, int endRow )
17625  : m_image( image )
17626  , m_luminance( luminance )
17627  , m_data( data )
17628  , m_target( target )
17629  , m_rect( rect )
17630  , m_firstRow( firstRow )
17631  , m_endRow( endRow )
17632  {
17633  }
17634 
17635  void Run() final
17636  {
17638 
17639  const RGBColorSystem& sourceRGBWS = m_luminance.RGBWorkingSpace();
17640  const RGBColorSystem& targetRGBWS = m_image.RGBWorkingSpace();
17641 
17642  typename P::sample* f0 = m_image.PixelAddress( m_target.x, m_target.y + m_firstRow, 0 );
17643  typename P::sample* f1 = m_image.PixelAddress( m_target.x, m_target.y + m_firstRow, 1 );
17644  typename P::sample* f2 = m_image.PixelAddress( m_target.x, m_target.y + m_firstRow, 2 );
17645 
17646  if ( m_luminance.ColorSpace() == ColorSpace::Gray || m_luminance.ColorSpace() == ColorSpace::CIEXYZ )
17647  {
17648  int cY = (m_luminance.ColorSpace() == ColorSpace::Gray) ? 0 : 1;
17649  const typename P1::sample* fY = m_luminance.PixelAddress( m_rect.x0, m_rect.y0 + m_firstRow, cY );
17650 
17651  for ( int y = m_firstRow, dw = m_image.Width()-m_rect.Width(), dwY = m_luminance.Width()-m_rect.Width();
17652  y < m_endRow;
17653  ++y, f0 += dw, f1 += dw, f2 += dw, fY += dwY )
17654  {
17655  const typename P::sample* fN = f0 + m_rect.Width();
17656 
17657  for ( ; f0 < fN; ++f0, ++f1, ++f2, ++fY )
17658  {
17659  RGBColorSystem::sample r0, r1, r2, rY;
17660  P::FromSample( r0, *f0 );
17661  P::FromSample( r1, *f1 );
17662  P::FromSample( r2, *f2 );
17663  P1::FromSample( rY, *fY );
17664 
17665  switch ( m_image.ColorSpace() )
17666  {
17667  case ColorSpace::RGB:
17668  case ColorSpace::HSV:
17669  case ColorSpace::HSI:
17670  switch ( m_image.ColorSpace() )
17671  {
17672  case ColorSpace::HSV:
17673  targetRGBWS.HSVToRGB( r0, r1, r2, r0, r1, r2 );
17674  break;
17675  case ColorSpace::HSI:
17676  targetRGBWS.HSIToRGB( r0, r1, r2, r0, r1, r2 );
17677  break;
17678  default: // RGB
17679  break;
17680  }
17681  targetRGBWS.RGBToCIEab( r1, r2, r0, r1, r2 );
17682  targetRGBWS.CIELabToRGB( r0, r1, r2, sourceRGBWS.CIEYToCIEL( rY ), r1, r2 );
17683  switch ( m_image.ColorSpace() )
17684  {
17685  case ColorSpace::HSV:
17686  targetRGBWS.RGBToHSV( r0, r1, r2, r0, r1, r2 );
17687  break;
17688  case ColorSpace::HSI:
17689  targetRGBWS.RGBToHSI( r0, r1, r2, r0, r1, r2 );
17690  break;
17691  default: // RGB
17692  break;
17693  }
17694  break;
17695 
17696  case ColorSpace::CIEXYZ:
17697  targetRGBWS.CIEXYZToCIELab( r0, r1, r2, r0, r1, r2 );
17698  targetRGBWS.CIELabToCIEXYZ( r0, r1, r2, sourceRGBWS.CIEYToCIEL( rY ), r1, r2 );
17699  break;
17700 
17701  case ColorSpace::CIELab:
17702  case ColorSpace::CIELch:
17703  r0 = sourceRGBWS.CIEYToCIEL( rY );
17704  break;
17705 
17706  default: // ???
17707  break;
17708  }
17709 
17710  *f0 = P::ToSample( r0 );
17711  *f1 = P::ToSample( r1 );
17712  *f2 = P::ToSample( r2 );
17713 
17714  UPDATE_THREAD_MONITOR( 65536 )
17715  }
17716  }
17717  }
17718  else
17719  {
17720  const typename P1::sample* g0 = m_luminance.PixelAddress( m_rect.x0, m_rect.y0 + m_firstRow, 0 );
17721  const typename P1::sample* g1 = m_luminance.PixelAddress( m_rect.x0, m_rect.y0 + m_firstRow, 1 );
17722  const typename P1::sample* g2 = m_luminance.PixelAddress( m_rect.x0, m_rect.y0 + m_firstRow, 2 );
17723 
17724  for ( int y = m_firstRow, dw = m_image.Width()-m_rect.Width(), dwY = m_luminance.Width()-m_rect.Width();
17725  y < m_endRow;
17726  ++y, f0 += dw, f1 += dw, f2 += dw, g0 += dwY, g1 += dwY, g2 += dwY )
17727  {
17728  const typename P::sample* fN = f0 + m_rect.Width();
17729 
17730  for ( ; f0 < fN; ++f0, ++f1, ++f2, ++g0, ++g1, ++g2 )
17731  {
17732  typename RGBColorSystem::sample r0, r1, r2, s0, s1, s2;
17733  P::FromSample( r0, *f0 );
17734  P::FromSample( r1, *f1 );
17735  P::FromSample( r2, *f2 );
17736  P1::FromSample( s0, *g0 );
17737  P1::FromSample( s1, *g1 );
17738  P1::FromSample( s2, *g2 );
17739 
17740  switch ( m_image.ColorSpace() )
17741  {
17742  case ColorSpace::RGB:
17743  case ColorSpace::HSV:
17744  case ColorSpace::HSI:
17745  case ColorSpace::CIEXYZ:
17746  case ColorSpace::CIELab:
17747  case ColorSpace::CIELch:
17748  switch ( m_image.ColorSpace() )
17749  {
17750  case ColorSpace::RGB:
17751  case ColorSpace::HSV:
17752  case ColorSpace::HSI:
17753  switch ( m_image.ColorSpace() )
17754  {
17755  case ColorSpace::HSV:
17756  targetRGBWS.HSVToRGB( r0, r1, r2, r0, r1, r2 );
17757  break;
17758  case ColorSpace::HSI:
17759  targetRGBWS.HSIToRGB( r0, r1, r2, r0, r1, r2 );
17760  break;
17761  default:
17762  break;
17763  }
17764  targetRGBWS.RGBToCIEab( r1, r2, r0, r1, r2 );
17765  break;
17766  case ColorSpace::CIEXYZ:
17767  targetRGBWS.CIEXYZToCIELab( r0, r1, r2, r0, r1, r2 );
17768  break;
17769  case ColorSpace::CIELab: // NOOP
17770  case ColorSpace::CIELch: // NOOP
17771  break;
17772  default: // ?!
17773  break;
17774  }
17775 
17776  switch ( m_luminance.ColorSpace() )
17777  {
17778  case ColorSpace::RGB:
17779  case ColorSpace::HSV:
17780  case ColorSpace::HSI:
17781  switch ( m_luminance.ColorSpace() )
17782  {
17783  case ColorSpace::HSV:
17784  sourceRGBWS.HSVToRGB( s0, s1, s2, s0, s1, s2 );
17785  break;
17786  case ColorSpace::HSI:
17787  sourceRGBWS.HSIToRGB( s0, s1, s2, s0, s1, s2 );
17788  break;
17789  default:
17790  break;
17791  }
17792  r0 = sourceRGBWS.CIEL( s0, s1, s2 );
17793  break;
17794  case ColorSpace::CIEXYZ:
17795  sourceRGBWS.CIEXYZToCIELab( r0, s1, s2, s0, s1, s2 );
17796  break;
17797  case ColorSpace::CIELab:
17798  case ColorSpace::CIELch:
17799  r0 = s0;
17800  break;
17801  default: // ?!
17802  break;
17803  }
17804 
17805  switch ( m_image.ColorSpace() )
17806  {
17807  case ColorSpace::RGB:
17808  case ColorSpace::HSV:
17809  case ColorSpace::HSI:
17810  targetRGBWS.CIELabToRGB( r0, r1, r2, r0, r1, r2 );
17811  switch ( m_image.ColorSpace() )
17812  {
17813  case ColorSpace::HSV:
17814  targetRGBWS.RGBToHSV( r0, r1, r2, r0, r1, r2 );
17815  break;
17816  case ColorSpace::HSI:
17817  targetRGBWS.RGBToHSI( r0, r1, r2, r0, r1, r2 );
17818  break;
17819  default: // RGB
17820  break;
17821  }
17822  break;
17823  case ColorSpace::CIEXYZ:
17824  targetRGBWS.CIELabToCIEXYZ( r0, r1, r2, r0, r1, r2 );
17825  break;
17826  case ColorSpace::CIELab: // NOOP
17827  case ColorSpace::CIELch: // NOOP
17828  break;
17829  default: // ?!
17830  break;
17831  }
17832  break;
17833 
17834  default: // ?!
17835  break;
17836  }
17837 
17838  *f0 = P::ToSample( r0 );
17839  *f1 = P::ToSample( r1 );
17840  *f2 = P::ToSample( r2 );
17841 
17842  UPDATE_THREAD_MONITOR( 65536 )
17843  }
17844  }
17845  }
17846  }
17847 
17848  private:
17849 
17850  GenericImage& m_image;
17851  const GenericImage<P1>& m_luminance;
17852  ThreadData& m_data;
17853  const Point& m_target;
17854  const Rect& m_rect;
17855  int m_firstRow, m_endRow;
17856  };
17857 
17858  // -------------------------------------------------------------------------
17859 
17860  template <class P1>
17861  class SetLightnessThread : public Thread
17862  {
17863  public:
17864 
17865  SetLightnessThread( GenericImage& image, const GenericImage<P1>& lightness, ThreadData& data,
17866  const Point& target, const Rect& rect, int firstRow, int endRow )
17867  : m_image( image )
17868  , m_lightness( lightness )
17869  , m_data( data )
17870  , m_target( target )
17871  , m_rect( rect )
17872  , m_firstRow( firstRow )
17873  , m_endRow( endRow )
17874  {
17875  }
17876 
17877  void Run() final
17878  {
17880 
17881  const RGBColorSystem& sourceRGBWS = m_lightness.RGBWorkingSpace();
17882  const RGBColorSystem& targetRGBWS = m_image.RGBWorkingSpace();
17883 
17884  typename P::sample* f0 = m_image.PixelAddress( m_target.x, m_target.y + m_firstRow, 0 );
17885  typename P::sample* f1 = m_image.PixelAddress( m_target.x, m_target.y + m_firstRow, 1 );
17886  typename P::sample* f2 = m_image.PixelAddress( m_target.x, m_target.y + m_firstRow, 2 );
17887 
17888  if ( m_lightness.ColorSpace() == ColorSpace::Gray ||
17889  m_lightness.ColorSpace() == ColorSpace::CIELab || m_lightness.ColorSpace() == ColorSpace::CIELch )
17890  {
17891  const typename P1::sample* fL = m_lightness.PixelAddress( m_rect.x0, m_rect.y0 + m_firstRow, 0 );
17892 
17893  for ( int y = m_firstRow, dw = m_image.Width()-m_rect.Width(), dwL = m_lightness.Width()-m_rect.Width();
17894  y < m_endRow;
17895  ++y, f0 += dw, f1 += dw, f2 += dw, fL += dwL )
17896  {
17897  const typename P::sample* fN = f0 + m_rect.Width();
17898 
17899  for ( ; f0 < fN; ++f0, ++f1, ++f2, ++fL )
17900  {
17901  RGBColorSystem::sample r0, r1, r2, rL;
17902  P::FromSample( r0, *f0 );
17903  P::FromSample( r1, *f1 );
17904  P::FromSample( r2, *f2 );
17905  P1::FromSample( rL, *fL );
17906 
17907  switch ( m_image.ColorSpace() )
17908  {
17909  case ColorSpace::RGB:
17910  case ColorSpace::HSV:
17911  case ColorSpace::HSI:
17912  switch ( m_image.ColorSpace() )
17913  {
17914  case ColorSpace::HSV:
17915  targetRGBWS.HSVToRGB( r0, r1, r2, r0, r1, r2 );
17916  break;
17917  case ColorSpace::HSI:
17918  targetRGBWS.HSIToRGB( r0, r1, r2, r0, r1, r2 );
17919  break;
17920  default:
17921  break;
17922  }
17923  targetRGBWS.RGBToCIEab( r1, r2, r0, r1, r2 );
17924  targetRGBWS.CIELabToRGB( r0, r1, r2, rL, r1, r2 );
17925  switch ( m_image.ColorSpace() )
17926  {
17927  case ColorSpace::HSV:
17928  targetRGBWS.RGBToHSV( r0, r1, r2, r0, r1, r2 );
17929  break;
17930  case ColorSpace::HSI:
17931  targetRGBWS.RGBToHSI( r0, r1, r2, r0, r1, r2 );
17932  break;
17933  default:
17934  break;
17935  }
17936  break;
17937 
17938  case ColorSpace::CIEXYZ:
17939  r1 = targetRGBWS.CIELToCIEY( rL );
17940  break;
17941 
17942  case ColorSpace::CIELab:
17943  case ColorSpace::CIELch:
17944  r0 = rL;
17945  break;
17946 
17947  default: // ???
17948  break;
17949  }
17950 
17951  *f0 = P::ToSample( r0 );
17952  *f1 = P::ToSample( r1 );
17953  *f2 = P::ToSample( r2 );
17954 
17955  UPDATE_THREAD_MONITOR( 65536 )
17956  }
17957  }
17958  }
17959  else
17960  {
17961  const typename P1::sample* g0 = m_lightness.PixelAddress( m_rect.x0, m_rect.y0 + m_firstRow, 0 );
17962  const typename P1::sample* g1 = m_lightness.PixelAddress( m_rect.x0, m_rect.y0 + m_firstRow, 1 );
17963  const typename P1::sample* g2 = m_lightness.PixelAddress( m_rect.x0, m_rect.y0 + m_firstRow, 2 );
17964 
17965  for ( int y = m_firstRow, dw = m_image.Width()-m_rect.Width(), dwL = m_lightness.Width()-m_rect.Width();
17966  y < m_endRow;
17967  ++y, f0 += dw, f1 += dw, f2 += dw, g0 += dwL, g1 += dwL, g2 += dwL )
17968  {
17969  const typename P::sample* fN = f0 + m_rect.Width();
17970 
17971  for ( ; f0 < fN; ++f0, ++f1, ++f2, ++g0, ++g1, ++g2 )
17972  {
17973  typename RGBColorSystem::sample r0, r1, r2, s0, s1, s2;
17974  P::FromSample( r0, *f0 );
17975  P::FromSample( r1, *f1 );
17976  P::FromSample( r2, *f2 );
17977  P1::FromSample( s0, *g0 );
17978  P1::FromSample( s1, *g1 );
17979  P1::FromSample( s2, *g2 );
17980 
17981  switch ( m_image.ColorSpace() )
17982  {
17983  case ColorSpace::RGB:
17984  case ColorSpace::HSV:
17985  case ColorSpace::HSI:
17986  case ColorSpace::CIEXYZ:
17987  case ColorSpace::CIELab:
17988  case ColorSpace::CIELch:
17989  switch ( m_image.ColorSpace() )
17990  {
17991  case ColorSpace::RGB:
17992  case ColorSpace::HSV:
17993  case ColorSpace::HSI:
17994  switch ( m_image.ColorSpace() )
17995  {
17996  case ColorSpace::HSV:
17997  targetRGBWS.HSVToRGB( r0, r1, r2, r0, r1, r2 );
17998  break;
17999  case ColorSpace::HSI:
18000  targetRGBWS.HSIToRGB( r0, r1, r2, r0, r1, r2 );
18001  break;
18002  default:
18003  break;
18004  }
18005  targetRGBWS.RGBToCIEab( r1, r2, r0, r1, r2 );
18006  break;
18007  case ColorSpace::CIEXYZ:
18008  targetRGBWS.CIEXYZToCIELab( r0, r1, r2, r0, r1, r2 );
18009  break;
18010  case ColorSpace::CIELch:
18011  targetRGBWS.CIELchToCIELab( r0, r1, r2, r0, r1, r2 );
18012  break;
18013  default:
18014  break;
18015  }
18016 
18017  switch ( m_lightness.ColorSpace() )
18018  {
18019  case ColorSpace::RGB:
18020  case ColorSpace::HSV:
18021  case ColorSpace::HSI:
18022  switch ( m_lightness.ColorSpace() )
18023  {
18024  case ColorSpace::HSV:
18025  sourceRGBWS.HSVToRGB( s0, s1, s2, s0, s1, s2 );
18026  break;
18027  case ColorSpace::HSI:
18028  sourceRGBWS.HSIToRGB( s0, s1, s2, s0, s1, s2 );
18029  break;
18030  default:
18031  break;
18032  }
18033  r0 = sourceRGBWS.CIEL( s0, s1, s2 );
18034  break;
18035  case ColorSpace::CIEXYZ:
18036  r0 = sourceRGBWS.CIEYToCIEL( s1 );
18037  break;
18038  default: // ???
18039  break;
18040  }
18041 
18042  switch ( m_image.ColorSpace() )
18043  {
18044  case ColorSpace::RGB:
18045  case ColorSpace::HSV:
18046  case ColorSpace::HSI:
18047  targetRGBWS.CIELabToRGB( r0, r1, r2, r0, r1, r2 );
18048  switch ( m_image.ColorSpace() )
18049  {
18050  case ColorSpace::HSV:
18051  targetRGBWS.RGBToHSV( r0, r1, r2, r0, r1, r2 );
18052  break;
18053  case ColorSpace::HSI:
18054  targetRGBWS.RGBToHSI( r0, r1, r2, r0, r1, r2 );
18055  break;
18056  default:
18057  break;
18058  }
18059  break;
18060  case ColorSpace::CIEXYZ:
18061  targetRGBWS.CIELabToCIEXYZ( r0, r1, r2, r0, r1, r2 );
18062  break;
18063  case ColorSpace::CIELch:
18064  targetRGBWS.CIELabToCIELch( r0, r1, r2, r0, r1, r2 );
18065  break;
18066  default:
18067  break;
18068  }
18069 
18070  default: // ???
18071  break;
18072  }
18073 
18074  *f0 = P::ToSample( r0 );
18075  *f1 = P::ToSample( r1 );
18076  *f2 = P::ToSample( r2 );
18077 
18078  UPDATE_THREAD_MONITOR( 65536 )
18079  }
18080  }
18081  }
18082  }
18083 
18084  private:
18085 
18086  GenericImage& m_image;
18087  const GenericImage<P1>& m_lightness;
18088  ThreadData& m_data;
18089  const Point& m_target;
18090  const Rect& m_rect;
18091  int m_firstRow, m_endRow;
18092  };
18093 
18094  // -------------------------------------------------------------------------
18095 
18096 #ifdef __PCL_BUILDING_PIXINSIGHT_APPLICATION
18097  friend class pi::SharedImage;
18098 #endif
18099 };
18100 
18101 #undef m_pixelData
18102 #undef m_channelData
18103 #undef m_allocator
18104 #undef m_width
18105 #undef m_height
18106 #undef m_numberOfChannels
18107 #undef m_colorSpace
18108 #undef m_RGBWS
18109 #undef m_channel
18110 #undef m_lastChannel
18111 #undef m_point
18112 #undef m_rectangle
18113 #undef m_clipLow
18114 #undef m_clipHigh
18115 #undef m_clippedLow
18116 #undef m_clippedHigh
18117 
18118 // ----------------------------------------------------------------------------
18119 
18129 template <class P, typename T> inline
18130 GenericImage<P> operator +( const GenericImage<P>& image, T scalar )
18131 {
18132  GenericImage<P> result( image );
18133  (void)(result += scalar);
18134  return result;
18135 }
18136 
18145 template <class P, typename T> inline
18146 GenericImage<P> operator +( T scalar, const GenericImage<P>& image )
18147 {
18148  return image + scalar;
18149 }
18150 
18156 template <class P, typename T> inline
18157 GenericImage<P> operator -( const GenericImage<P>& image, T scalar )
18158 {
18159  GenericImage<P> result( image );
18160  (void)(result -= scalar);
18161  return result;
18162 }
18163 
18169 template <class P, typename T> inline
18170 GenericImage<P> operator *( const GenericImage<P>& image, T scalar )
18171 {
18172  GenericImage<P> result( image );
18173  (void)(result *= scalar);
18174  return result;
18175 }
18176 
18186 template <class P, typename T> inline
18187 GenericImage<P> operator *( T scalar, const GenericImage<P>& image )
18188 {
18189  return image * scalar;
18190 }
18191 
18197 template <class P, typename T> inline
18198 GenericImage<P> operator /( const GenericImage<P>& image, T scalar )
18199 {
18200  GenericImage<P> result( image );
18201  (void)(result /= scalar);
18202  return result;
18203 }
18204 
18210 template <class P, typename T> inline
18211 GenericImage<P> operator ^( const GenericImage<P>& image, T scalar )
18212 {
18213  GenericImage<P> result( image );
18214  (void)(result ^= scalar);
18215  return result;
18216 }
18217 
18218 // ----------------------------------------------------------------------------
18219 
18236 template <class P1, class P2> inline
18238 {
18239  GenericImage<P1> result( image1 );
18240  (void)(result += image2);
18241  return result;
18242 }
18243 
18256 template <class P1, class P2> inline
18258 {
18259  GenericImage<P1> result( image1 );
18260  (void)(result -= image2);
18261  return result;
18262 }
18263 
18276 template <class P1, class P2> inline
18278 {
18279  GenericImage<P1> result( image1 );
18280  (void)(result *= image2);
18281  return result;
18282 }
18283 
18297 template <class P1, class P2> inline
18299 {
18300  GenericImage<P1> result( image1 );
18301  (void)(result /= image2);
18302  return result;
18303 }
18304 
18318 template <class P1, class P2> inline
18320 {
18321  GenericImage<P1> result( image1 );
18322  (void)(result ^= image2);
18323  return result;
18324 }
18325 
18326 // ----------------------------------------------------------------------------
18327 
18328 #ifndef __PCL_NO_IMAGE_INSTANTIATE
18329 
18341 using FImage = GenericImage<FloatPixelTraits>;
18342 
18350 using DImage = GenericImage<DoublePixelTraits>;
18351 
18360 using FComplexImage = GenericImage<ComplexPixelTraits>;
18361 
18370 using DComplexImage = GenericImage<DComplexPixelTraits>;
18371 
18380 using UInt8Image = GenericImage<UInt8PixelTraits>;
18381 
18390 using UInt16Image = GenericImage<UInt16PixelTraits>;
18391 
18400 using UInt32Image = GenericImage<UInt32PixelTraits>;
18401 
18410 using Image = FImage;
18411 
18420 using ComplexImage = FComplexImage;
18421 
18422 #endif // __PCL_NO_IMAGE_INSTANTIATE
18423 
18424 // ----------------------------------------------------------------------------
18425 
18426 } // pcl
18427 
18428 // ----------------------------------------------------------------------------
18429 
18430 #ifndef __PCL_NO_IMAGE_VARIANT_AUTO
18431 
18432 #ifndef __PCL_ImageVariant_h
18433 #include <pcl/ImageVariant.h>
18434 #endif
18435 
18436 #endif // __PCL_NO_IMAGE_VARIANT_AUTO
18437 
18438 // ----------------------------------------------------------------------------
18439 
18440 #endif // __PCL_Image_h
18441 
18442 // ----------------------------------------------------------------------------
18443 // EOF pcl/Image.h - Released 2025-02-21T12:13:32Z
Base class of all two-dimensional images in PCL.
int LastSelectedChannel() const noexcept
bool IsCompletelySelected() const noexcept
int FirstSelectedChannel() const noexcept
void ResetSelections() const noexcept
StatusMonitor & Status() const noexcept
bool ParseSelection(Rect &rect, int &firstChannel, int &lastChannel) const noexcept
iterator Begin()
Definition: Array.h:438
void Add(const Array &x)
Definition: Array.h:1017
iterator End()
Definition: Array.h:463
double * iterator
Definition: Array.h:125
Root base class for bidirectional PCL image transformations.
Client-side interface to a PixInsight Bitmap object.
Definition: Bitmap.h:204
pcl::Rect Bounds() const
Definition: Bitmap.h:536
32-bit floating point complex image.
Generic complex number.
Definition: Complex.h:84
Abstract base class of data compression algorithm implementations.
Definition: Compression.h:84
subblock_list Compress(const void *data, size_type size, Performance *perf=nullptr) const
Image cropping/expansion algorithm
Definition: Crop.h:103
64-bit floating point complex image.
64-bit floating point real image.
A simple exception with an associated error message.
Definition: Exception.h:256
32-bit floating point complex image.
32-bit floating point real image.
A platform-independent interface to the local file system.
Definition: File.h:344
virtual void Write(const void *buffer, fsize_type len)
void ReadUI32(T &x)
Definition: File.h:935
void WriteUI32(const T &x)
Definition: File.h:998
virtual void Read(void *buffer, fsize_type len)
Immutable filter pixel iterator.
Definition: Image.h:5022
const_filter_pixel_iterator(const const_filter_pixel_iterator &)=default
typename image_type::sample sample
Definition: Image.h:5038
const_filter_pixel_iterator & MoveBy(int dx, int dy) noexcept
Definition: Image.h:5250
const_filter_pixel_iterator(const image_type &image, const F &filter)
Definition: Image.h:5061
const_filter_pixel_iterator(const const_pixel_iterator &i, const F &filter)
Definition: Image.h:5070
const filter_type & Filter() const noexcept
Definition: Image.h:5117
typename image_type::pixel_traits pixel_traits
Definition: Image.h:5033
const image_type & Image() const noexcept
Definition: Image.h:5108
const sample * Position(int channel) const noexcept
Definition: Image.h:5135
Immutable filter pixel sample iterator.
Definition: Image.h:2276
const_filter_sample_iterator(const image_type &image, const F &filter, int channel=-1)
Definition: Image.h:2323
const_filter_sample_iterator & MoveBy(int dx, int dy) noexcept
Definition: Image.h:2558
const image_type & Image() const noexcept
Definition: Image.h:2422
const_filter_sample_iterator(const image_type &image, const F &filter, const sample *i, const sample *j)
Definition: Image.h:2346
typename image_type::sample sample
Definition: Image.h:2292
const sample * Position() const noexcept
Definition: Image.h:2449
const filter_type & Filter() const noexcept
Definition: Image.h:2431
const_filter_sample_iterator(const const_filter_sample_iterator &)=default
typename image_type::pixel_traits pixel_traits
Definition: Image.h:2287
const_filter_sample_iterator(const sample_iterator &i, const F &filter)
Definition: Image.h:2355
const_filter_sample_iterator(const filter_sample_iterator< F > &i)
Definition: Image.h:2373
const_filter_sample_iterator(const const_sample_iterator &i, const F &filter)
Definition: Image.h:2364
Immutable pixel iterator.
Definition: Image.h:3747
const image_type & Image() const noexcept
Definition: Image.h:3810
const_pixel_iterator(const image_type &image)
Definition: Image.h:3775
typename image_type::sample sample
Definition: Image.h:3763
const_pixel_iterator(const const_pixel_iterator &)=default
bool IsValid() const noexcept
Definition: Image.h:3801
const_pixel_iterator & MoveBy(int dx, int dy) noexcept
Definition: Image.h:3926
typename image_type::pixel_traits pixel_traits
Definition: Image.h:3758
const sample * Position(int channel) const noexcept
Definition: Image.h:3819
Immutable region-of-interest, filter pixel iterator.
Definition: Image.h:5692
const_roi_filter_pixel_iterator(const const_roi_pixel_iterator &i, const F &filter)
Definition: Image.h:5750
const_roi_filter_pixel_iterator(const image_type &image, const F &filter, const Rect &rect=Rect(0))
Definition: Image.h:5741
const_roi_filter_pixel_iterator(const const_roi_filter_pixel_iterator &)=default
const image_type & Image() const noexcept
Definition: Image.h:5788
const filter_type & Filter() const noexcept
Definition: Image.h:5797
const sample * Position(int channel) const noexcept
Definition: Image.h:5815
typename image_type::pixel_traits pixel_traits
Definition: Image.h:5703
const_roi_filter_pixel_iterator & MoveBy(int dx, int dy) noexcept
Definition: Image.h:5928
Immutable region-of-interest, filter pixel sample iterator.
Definition: Image.h:3109
const sample * Position() const noexcept
Definition: Image.h:3262
const_roi_filter_sample_iterator(const image_type &image, const F &filter, const sample *i, const sample *j)
Definition: Image.h:3188
const_roi_filter_sample_iterator(const const_roi_sample_iterator &i, const F &filter)
Definition: Image.h:3197
const_roi_filter_sample_iterator(const const_roi_filter_sample_iterator &)=default
const filter_type & Filter() const noexcept
Definition: Image.h:3244
typename image_type::pixel_traits pixel_traits
Definition: Image.h:3120
const_roi_filter_sample_iterator(const image_type &image, const F &filter, const Rect &rect=Rect(0), int channel=-1)
Definition: Image.h:3165
const image_type & Image() const noexcept
Definition: Image.h:3235
const_roi_filter_sample_iterator & MoveBy(int dx, int dy) noexcept
Definition: Image.h:3375
Immutable region-of-interest pixel iterator.
Definition: Image.h:4353
typename image_type::sample sample
Definition: Image.h:4369
const_roi_pixel_iterator & MoveBy(int dx, int dy) noexcept
Definition: Image.h:4540
const sample * Position(int channel) const noexcept
Definition: Image.h:4429
const image_type & Image() const noexcept
Definition: Image.h:4420
const_roi_pixel_iterator(const const_roi_pixel_iterator &)=default
const_roi_pixel_iterator(const image_type &image, const Rect &rect=Rect(0))
Definition: Image.h:4392
typename image_type::pixel_traits pixel_traits
Definition: Image.h:4364
Immutable region-of-interest pixel sample iterator.
Definition: Image.h:1454
const_roi_sample_iterator(const const_roi_sample_iterator &)=default
const_roi_sample_iterator(const image_type &image, const Rect &rect=Rect(0), int channel=-1)
Definition: Image.h:1501
const sample * Position() const noexcept
Definition: Image.h:1586
const_roi_sample_iterator(const image_type &image, const sample *i, const sample *j)
Definition: Image.h:1522
const_roi_sample_iterator & MoveBy(int dx, int dy) noexcept
Definition: Image.h:1697
const image_type & Image() const noexcept
Definition: Image.h:1577
const_roi_sample_iterator(const roi_sample_iterator &i)
Definition: Image.h:1535
typename image_type::sample sample
Definition: Image.h:1470
typename image_type::pixel_traits pixel_traits
Definition: Image.h:1465
Immutable pixel sample iterator.
Definition: Image.h:689
const_sample_iterator(const image_type &image, int channel=-1)
Definition: Image.h:725
const_sample_iterator(const sample_iterator &i)
Definition: Image.h:765
typename image_type::sample sample
Definition: Image.h:705
typename image_type::pixel_traits pixel_traits
Definition: Image.h:700
const_sample_iterator & MoveBy(int dx, int dy) noexcept
Definition: Image.h:917
bool IsValid() const noexcept
Definition: Image.h:798
const image_type & Image() const noexcept
Definition: Image.h:807
const_sample_iterator(const image_type &image, const sample *i, const sample *j)
Definition: Image.h:751
const_sample_iterator(const const_sample_iterator &)=default
const sample * Position() const noexcept
Definition: Image.h:816
Mutable filter pixel iterator.
Definition: Image.h:4691
typename image_type::pixel_traits pixel_traits
Definition: Image.h:4702
bool IsValid() const noexcept
Definition: Image.h:4768
filter_pixel_iterator & MoveBy(int dx, int dy) noexcept
Definition: Image.h:4918
sample * Position(int channel) const noexcept
Definition: Image.h:4803
filter_pixel_iterator(const pixel_iterator &i, const F &filter)
Definition: Image.h:4739
filter_pixel_iterator(const filter_pixel_iterator &)=default
typename image_type::sample sample
Definition: Image.h:4707
const filter_type & Filter() const noexcept
Definition: Image.h:4785
filter_type & Filter() noexcept
Definition: Image.h:4794
filter_pixel_iterator(image_type &image, const F &filter)
Definition: Image.h:4730
image_type & Image() const noexcept
Definition: Image.h:4776
Mutable filter pixel sample iterator.
Definition: Image.h:1887
sample * Position() const noexcept
Definition: Image.h:2029
filter_sample_iterator(const sample_iterator &i, const F &filter)
Definition: Image.h:1966
filter_sample_iterator(const filter_sample_iterator &i)=default
filter_sample_iterator(image_type &image, const F &filter, int channel=-1)
Definition: Image.h:1934
filter_type & Filter() noexcept
Definition: Image.h:2021
image_type & Image() const noexcept
Definition: Image.h:2003
filter_sample_iterator & MoveBy(int dx, int dy) noexcept
Definition: Image.h:2138
const filter_type & Filter() const noexcept
Definition: Image.h:2012
filter_sample_iterator(image_type &image, const F &filter, sample *i, sample *j)
Definition: Image.h:1957
typename image_type::sample sample
Definition: Image.h:1903
typename image_type::pixel_traits pixel_traits
Definition: Image.h:1898
Mutable pixel iterator.
Definition: Image.h:3484
typename image_type::pixel_traits pixel_traits
Definition: Image.h:3495
pixel_iterator & MoveBy(int dx, int dy) noexcept
Definition: Image.h:3663
pixel_iterator(image_type &image)
Definition: Image.h:3512
image_type & Image() const noexcept
Definition: Image.h:3547
sample * Position(int channel) const noexcept
Definition: Image.h:3556
pixel_iterator(const pixel_iterator &)=default
typename image_type::sample sample
Definition: Image.h:3500
bool IsValid() const noexcept
Definition: Image.h:3539
Mutable region-of-interest, filter pixel iterator.
Definition: Image.h:5382
roi_filter_pixel_iterator(const roi_filter_pixel_iterator &)=default
sample * Position(int channel) const noexcept
Definition: Image.h:5502
roi_filter_pixel_iterator(const roi_pixel_iterator &i, const F &filter)
Definition: Image.h:5439
roi_filter_pixel_iterator & MoveBy(int dx, int dy) noexcept
Definition: Image.h:5615
filter_type & Filter() noexcept
Definition: Image.h:5493
typename image_type::sample sample
Definition: Image.h:5398
image_type & Image() const noexcept
Definition: Image.h:5475
roi_filter_pixel_iterator(image_type &image, const F &filter, const Rect &rect=Rect(0))
Definition: Image.h:5430
const filter_type & Filter() const noexcept
Definition: Image.h:5484
typename image_type::pixel_traits pixel_traits
Definition: Image.h:5393
Mutable region-of-interest, filter pixel sample iterator.
Definition: Image.h:2734
roi_filter_sample_iterator(const roi_filter_sample_iterator &)=default
roi_filter_sample_iterator(image_type &image, const F &filter, const Rect &rect=Rect(0), int channel=-1)
Definition: Image.h:2790
roi_filter_sample_iterator(image_type &image, const F &filter, sample *i, sample *j)
Definition: Image.h:2813
sample * Position() const noexcept
Definition: Image.h:2885
typename image_type::sample sample
Definition: Image.h:2750
roi_filter_sample_iterator & MoveBy(int dx, int dy) noexcept
Definition: Image.h:2998
const filter_type & Filter() const noexcept
Definition: Image.h:2868
image_type & Image() const noexcept
Definition: Image.h:2859
typename image_type::pixel_traits pixel_traits
Definition: Image.h:2745
roi_filter_sample_iterator(const roi_sample_iterator &i, const F &filter)
Definition: Image.h:2822
Mutable region-of-interest pixel iterator.
Definition: Image.h:4097
roi_pixel_iterator(const roi_pixel_iterator &)=default
sample * Position(int channel) const noexcept
Definition: Image.h:4172
typename image_type::sample sample
Definition: Image.h:4113
roi_pixel_iterator(image_type &image, const Rect &rect=Rect(0))
Definition: Image.h:4136
typename image_type::pixel_traits pixel_traits
Definition: Image.h:4108
image_type & Image() const noexcept
Definition: Image.h:4163
bool IsValid() const noexcept
Definition: Image.h:4155
roi_pixel_iterator & MoveBy(int dx, int dy) noexcept
Definition: Image.h:4283
Mutable region-of-interest pixel sample iterator.
Definition: Image.h:1133
sample * Position() const noexcept
Definition: Image.h:1236
roi_sample_iterator & MoveBy(int dx, int dy) noexcept
Definition: Image.h:1347
roi_sample_iterator(image_type &image, const Rect &rect=Rect(0), int channel=-1)
Definition: Image.h:1180
roi_sample_iterator(const roi_sample_iterator &)=default
typename image_type::pixel_traits pixel_traits
Definition: Image.h:1144
image_type & Image() const noexcept
Definition: Image.h:1228
typename image_type::sample sample
Definition: Image.h:1149
roi_sample_iterator(image_type &image, sample *i, sample *j)
Definition: Image.h:1201
bool IsValid() const noexcept
Definition: Image.h:1220
Mutable pixel sample iterator.
Definition: Image.h:355
sample_iterator(image_type &image, sample *i, sample *j)
Definition: Image.h:418
sample_iterator & MoveBy(int dx, int dy) noexcept
Definition: Image.h:556
typename image_type::sample sample
Definition: Image.h:371
sample * Position() const noexcept
Definition: Image.h:455
sample_iterator(image_type &image, int channel=-1)
Definition: Image.h:391
sample_iterator(const sample_iterator &)=default
typename image_type::pixel_traits pixel_traits
Definition: Image.h:366
bool IsValid() const noexcept
Definition: Image.h:439
image_type & Image() const noexcept
Definition: Image.h:447
Implements a generic, two-dimensional, shared or local image.
Definition: Image.h:278
GenericImage & ShiftToBottomRight(int width, int height, const GenericVector< T > &fillValues)
Definition: Image.h:14479
double Norm(const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1, int maxProcessors=0) const
Definition: Image.h:13416
const sample * ScanLine(int y, int channel=0) const noexcept
Definition: Image.h:6834
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
Definition: Image.h:11595
GenericImage & CropBy(int left, int top, int right, int bottom, const GenericVector< T > &fillValues)
Definition: Image.h:14035
double Sn(const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1, int maxProcessors=0) const
Definition: Image.h:13237
GenericImage & Write(File &file, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1) const
Definition: Image.h:13873
GenericImage & ShiftToBottomRight(int width, int height)
Definition: Image.h:14498
GenericImage Rescaled(T lowerBound, T upperBound, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1) const
Definition: Image.h:8527
GenericImage & Fill(T scalar, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1)
Definition: Image.h:7881
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)
Definition: Image.h:10551
uint32 Hash32(int channel=-1, uint32 seed=0) const noexcept
Definition: Image.h:13838
GenericImage & ShiftToTopLeft(int width, int height, const GenericVector< T > &fillValues)
Definition: Image.h:14392
GenericImage & ShiftTo(const Point &p, const GenericVector< T > &fillValues)
Definition: Image.h:14337
~GenericImage() override
Definition: Image.h:6306
GenericImage Applied(const ImageTransformation &transformation, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1) const
Definition: Image.h:9489
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)
Definition: Image.h:10322
GenericImage Divided(T scalar, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1) const
Definition: Image.h:9959
GenericImage & FreeData()
Definition: Image.h:6527
GenericImage Added(T scalar, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1) const
Definition: Image.h:9797
GenericImage & Assign(const GenericImage< P1 > &image, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1)
Definition: Image.h:7150
GenericImage & SetMinimum(T scalar, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1)
Definition: Image.h:10074
GenericImage & Xnor(T scalar, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1)
Definition: Image.h:10248
GenericImage & Transfer(GenericImage &image)
Definition: Image.h:7299
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)
Definition: Image.h:9231
GenericImage Blended(const Bitmap &bitmap, const Point &point=Point(int_max), const Rect &rect=Rect(0)) const
Definition: Image.h:9724
GenericImage & SetMaximum(T scalar, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1)
Definition: Image.h:10118
static int BytesPerSample() noexcept
Definition: Image.h:6015
void GetExtremePixelValues(sample &min, sample &max, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1, int maxProcessors=0) const
Definition: Image.h:11154
GenericImage(const GenericImage< P1 > &image, const Rect &rect, int firstChannel=-1, int lastChannel=-1)
Definition: Image.h:6180
sample & Pixel(int x, int y, int channel=0)
Definition: Image.h:7017
sample & Pixel(const Point &p, int channel=0)
Definition: Image.h:7041
double Qn(const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1, int maxProcessors=0) const
Definition: Image.h:13329
GenericImage Raised(T scalar, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1) const
Definition: Image.h:10011
double Mean(const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1, int maxProcessors=0, bool __performanceAnalysis__=false) const
Definition: Image.h:11709
GenericImage & ForgetAlphaChannels()
Definition: Image.h:7818
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)
Definition: Image.h:10627
GenericImage & Raise(T scalar, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1)
Definition: Image.h:9977
bool SameSampleType(const GenericImage< P1 > &image) const noexcept
Definition: Image.h:6037
GenericImage & Sub(T scalar, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1)
Definition: Image.h:9824
GenericImage & ShiftToBottomLeft(int width, int height, const GenericVector< T > &fillValues)
Definition: Image.h:14450
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
Definition: Image.h:11486
const sample * PixelAddress(const Point &p, int channel=0) const noexcept
Definition: Image.h:6919
GenericImage Inverted(const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1) const
Definition: Image.h:8177
GenericImage Truncated(const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1) const
Definition: Image.h:8368
GenericImage Subtracted(T scalar, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1) const
Definition: Image.h:9849
GenericImage & CropTo(int x0, int y0, int x1, int y1, const GenericVector< T > &fillValues)
Definition: Image.h:14202
sample * PixelAddress(int x, int y, int channel=0)
Definition: Image.h:6864
GenericImage & Crop(const GenericVector< T > &fillValues)
Definition: Image.h:14234
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)
Definition: Image.h:10266
GenericImage & Mov(T scalar, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1)
Definition: Image.h:9756
size_type ImageSize() const noexcept
Definition: Image.h:6682
GenericImage & Read(File &file)
Definition: Image.h:13961
TwoSidedEstimate TwoSidedAvgDev(double center, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1, int maxProcessors=0) const
Definition: Image.h:12419
GenericImage & Or(T scalar, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1)
Definition: Image.h:10163
pixel_allocator & Allocator() const noexcept
Definition: Image.h:6434
GenericImage & SetColumn(const T *buffer, int x, int channel=-1)
Definition: Image.h:7546
sample LocateMaximumPixelValue(Point &pmax, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1, int maxProcessors=0) const
Definition: Image.h:11442
uint64 Hash(int channel=-1, uint64 seed=0) const noexcept
Definition: Image.h:13849
static bool IsComplexSample() noexcept
Definition: Image.h:6006
GenericImage & Nand(T scalar, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1)
Definition: Image.h:10231
GenericImage & Multiply(T scalar, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1)
Definition: Image.h:9867
Array< double > PixelSamples(const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1, int maxProcessors=0, bool __performanceAnalysis__=false)
Definition: Image.h:10831
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
Definition: Image.h:9420
GenericImage Applied(T scalar, image_op op=ImageOp::Mov, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1) const
Definition: Image.h:9176
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)
Definition: Image.h:10510
GenericImage & SetLightness(const GenericImage< P1 > &L, const Point &point=Point(int_max), const Rect &rect=Rect(0), int maxProcessors=0)
Definition: Image.h:15358
GenericImage & Crop()
Definition: Image.h:14250
GenericImage Multiplied(T scalar, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1) const
Definition: Image.h:9901
sample LocateMinimumPixelValue(int &xmin, int &ymin, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1, int maxProcessors=0) const
Definition: Image.h:11286
GenericImage Binarized(const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1) const
Definition: Image.h:8898
double MeanOfSquares(const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1, int maxProcessors=0) const
Definition: Image.h:13655
GenericImage & White(const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1)
Definition: Image.h:8067
GenericImage & ShiftToTopRight(int width, int height, const GenericVector< T > &fillValues)
Definition: Image.h:14421
GenericImage & DeleteAlphaChannel(int channel)
Definition: Image.h:7732
GenericImage & CropTo(int x0, int y0, int x1, int y1)
Definition: Image.h:14218
GenericImage & Binarize(T threshold, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1)
Definition: Image.h:8808
sample Pixel(int x, int y, int channel=0) const noexcept
Definition: Image.h:7029
GenericImage Binarized(T threshold, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1) const
Definition: Image.h:8858
GenericImage & SetAbsoluteValue(const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1)
Definition: Image.h:8918
GenericImage AbsoluteValue(const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1) const
Definition: Image.h:8972
GenericImage & ShiftToCenter(int width, int height, const GenericVector< T > &fillValues)
Definition: Image.h:14363
bool SameSampleType(const GenericImage &image) const noexcept
Definition: Image.h:6049
GenericImage & Nor(T scalar, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1)
Definition: Image.h:10214
bool IsUnique() const noexcept
Definition: Image.h:6348
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, bool __performanceAnalysis__=false) const
Definition: Image.h:12937
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)
Definition: Image.h:10491
GenericImage & CropTo(const Rect &rect)
Definition: Image.h:14186
void GetIntensity(GenericImage< P1 > &I, const Rect &rect=Rect(0), int maxProcessors=0, bool __performanceAnalysis__=false) const
Definition: Image.h:15068
GenericImage & EnsureLocal()
Definition: Image.h:6365
GenericImage(int width, int height, color_space colorSpace=ColorSpace::Gray)
Definition: Image.h:6204
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)
Definition: Image.h:10333
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)
Definition: Image.h:10277
GenericImage & Black(const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1)
Definition: Image.h:8049
size_type LineSize() const noexcept
Definition: Image.h:6662
static int BitsPerSample() noexcept
Definition: Image.h:6024
GenericImage & Invert(T scalar, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1)
Definition: Image.h:8089
GenericImage AbsoluteDifference(T scalar, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1) const
Definition: Image.h:10056
void GetColumn(T *buffer, int x, int channel=-1) const
Definition: Image.h:7479
double AvgDev(double center, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1, int maxProcessors=0) const
Definition: Image.h:12341
bool IsShared() const noexcept
Definition: Image.h:6333
sample * PixelData(int channel=0)
Definition: Image.h:6721
sample LocateMaximumSampleValue(Point &pmax, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1, int maxProcessors=0) const
Definition: Image.h:11418
GenericImage & CropTo(const Rect &rect, const GenericVector< T > &fillValues)
Definition: Image.h:14169
GenericImage Truncated(T lowerBound, T upperBound, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1) const
Definition: Image.h:8324
GenericImage & ReleaseAlphaChannel(GenericImage &image, int channel)
Definition: Image.h:7679
GenericImage(GenericImage &&image)
Definition: Image.h:6113
TwoSidedEstimate TwoSidedMAD(double center, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1, int maxProcessors=0) const
Definition: Image.h:12700
double SumOfSquares(const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1, int maxProcessors=0) const
Definition: Image.h:13576
GenericImage & ShiftTo(int x, int y)
Definition: Image.h:14321
GenericImage & Truncate(T lowerBound, T upperBound, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1)
Definition: Image.h:8262
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)
Definition: Image.h:10589
GenericImage Maximum(T scalar, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1) const
Definition: Image.h:10144
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)
Definition: Image.h:10402
sample MaximumSampleValue(const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1, int maxProcessors=0) const
Definition: Image.h:10988
sample_vector ColVector(int x, int channel=0) const
Definition: Image.h:7419
GenericImage & Shift()
Definition: Image.h:14530
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)
Definition: Image.h:10441
GenericImage & Mul(T scalar, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1)
Definition: Image.h:9876
sample_vector ColumnVector(int x, int channel=-1) const
Definition: Image.h:7403
size_type Count(const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1, int maxProcessors=0) const
Definition: Image.h:11630
GenericImage & Normalize(const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1)
Definition: Image.h:8756
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)
Definition: Image.h:10646
GenericImage & Not(const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1)
Definition: Image.h:8209
GenericImage & SetColorSpace(color_space colorSpace, int maxProcessors=0, bool __performanceAnalysis__=false)
Definition: Image.h:14570
GenericImage & ShiftToTopRight(int width, int height)
Definition: Image.h:14440
GenericImage & Div(T scalar, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1)
Definition: Image.h:9931
sample MaximumPixelValue(const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1, int maxProcessors=0) const
Definition: Image.h:11046
GenericImage & SetRow(const T *buffer, int y, int channel=-1)
Definition: Image.h:7514
double Variance(const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1, int maxProcessors=0) const
Definition: Image.h:12187
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)
Definition: Image.h:10480
void GetRow(T *buffer, int y, int channel=-1) const
Definition: Image.h:7448
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)
Definition: Image.h:10413
void GetExtremeSampleValues(T &min, T &max, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1, int maxProcessors=0, bool __performanceAnalysis__=false) const
Definition: Image.h:11079
GenericImage & ForgetAlphaChannel(int channel)
Definition: Image.h:7762
GenericImage & EnsureUnique()
Definition: Image.h:6401
GenericImage Minimum(T scalar, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1) const
Definition: Image.h:10100
typename pixel_traits::sample sample
Definition: Image.h:305
GenericImage(void *handle)
Definition: Image.h:6250
void SetRGBWorkingSpace(const RGBColorSystem &RGBWS) override
Definition: Image.h:7068
GenericImage Filled(const GenericVector< T > &values, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1) const
Definition: Image.h:7990
GenericImage(void *, int width, int height, color_space colorSpace=ColorSpace::Gray)
Definition: Image.h:6283
GenericImage & ImportData(sample **data, int width, int height, int numberOfChannels=1, color_space colorSpace=ColorSpace::Gray)
Definition: Image.h:6597
GenericImage & And(T scalar, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1)
Definition: Image.h:10180
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)
Definition: Image.h:10371
size_type ChannelSize() const noexcept
Definition: Image.h:6671
sample LocateMaximumSampleValue(int &xmax, int &ymax, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1, int maxProcessors=0) const
Definition: Image.h:11331
GenericImage & Rescale(T lowerBound, T upperBound, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1)
Definition: Image.h:8404
sample ** ReleaseData()
Definition: Image.h:6647
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)
Definition: Image.h:10712
GenericImage & Blend(const Bitmap &bitmap, const Point &point=Point(int_max), const Rect &rect=Rect(0))
Definition: Image.h:9567
GenericImage & ShiftBy(int dx, int dy)
Definition: Image.h:14293
sample * ScanLine(int y, int channel=0)
Definition: Image.h:6820
GenericImage & ShiftToBottomLeft(int width, int height)
Definition: Image.h:14469
GenericImage & Fill(const GenericVector< T > &values, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1)
Definition: Image.h:7928
GenericImage & Divide(T scalar, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1)
Definition: Image.h:9922
GenericImage & Rescale(const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1)
Definition: Image.h:8551
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)
Definition: Image.h:10295
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)
Definition: Image.h:10452
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)
Definition: Image.h:10540
sample LocateMinimumSampleValue(Point &pmin, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1, int maxProcessors=0) const
Definition: Image.h:11274
GenericImage & ShiftBy(int dx, int dy, const GenericVector< T > &fillValues)
Definition: Image.h:14277
const sample * PixelData(int channel=0) const noexcept
Definition: Image.h:6734
uint64 Hash64(int channel=-1, uint64 seed=0) const noexcept
Definition: Image.h:13819
GenericImage(const GenericImage< P1 > &image)
Definition: Image.h:6133
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)
Definition: Image.h:10608
GenericImage & AllocateData(const Rect &rect, int numberOfChannels=1, color_space colorSpace=ColorSpace::Gray)
Definition: Image.h:6509
GenericImage & Min(T scalar, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1)
Definition: Image.h:10083
GenericImage & Apply(T scalar, image_op op=ImageOp::Mov, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1)
Definition: Image.h:9119
GenericImage & SetAbsoluteDifference(T scalar, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1)
Definition: Image.h:10030
static bool IsFloatSample() noexcept
Definition: Image.h:5997
void GetLuminance(GenericImage< P1 > &Y, const Rect &rect=Rect(0), int maxProcessors=0) const
Definition: Image.h:14790
GenericImage & Invert(const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1)
Definition: Image.h:8161
GenericImage & One(const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1)
Definition: Image.h:8031
sample_vector RowVector(int y, int channel=-1) const
Definition: Image.h:7374
Vector Norms(int maxDegree=2, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1, int maxProcessors=0) const
Definition: Image.h:13749
GenericImage & Move(T scalar, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1)
Definition: Image.h:9747
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)
Definition: Image.h:10795
double StdDev(const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1, int maxProcessors=0) const
Definition: Image.h:12296
GenericImage & Subtract(T scalar, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1)
Definition: Image.h:9815
Compression::subblock_list Compress(const Compression &compressor, const Rect &rect=Rect(0), int channel=-1, Compression::Performance *perf=nullptr) const
Definition: Image.h:15487
GenericImage & AllocateData(int width, int height, int numberOfChannels=1, color_space colorSpace=ColorSpace::Gray)
Definition: Image.h:6484
sample * PixelAddress(const Point &p, int channel=0)
Definition: Image.h:6907
sample MinimumPixelValue(const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1, int maxProcessors=0) const
Definition: Image.h:10963
GenericImage & SetLuminance(const GenericImage< P1 > &Y, const Point &point=Point(int_max), const Rect &rect=Rect(0), int maxProcessors=0)
Definition: Image.h:15208
sample Pixel(const Point &p, int channel=0) const noexcept
Definition: Image.h:7054
GenericImage & ShiftToCenter(int width, int height)
Definition: Image.h:14382
sample LocateMaximumPixelValue(int &xmax, int &ymax, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1, int maxProcessors=0) const
Definition: Image.h:11430
GenericImage & Add(T scalar, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1)
Definition: Image.h:9772
GenericImage & Write(const String &filePath, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1) const
Definition: Image.h:13918
GenericImage Inverted(T scalar, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1) const
Definition: Image.h:8137
GenericImage & CropBy(int left, int top, int right, int bottom)
Definition: Image.h:14153
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)
Definition: Image.h:10521
GenericImage & Transfer(GenericImage &&image)
Definition: Image.h:7315
double OrderStatistic(double k, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1, int maxProcessors=0) const
Definition: Image.h:12002
GenericImage & Truncate(const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1)
Definition: Image.h:8353
sample MinimumSampleValue(const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1, int maxProcessors=0) const
Definition: Image.h:10905
GenericImage & ShiftToTopLeft(int width, int height)
Definition: Image.h:14411
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)
Definition: Image.h:10570
double MAD(double center, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1, int maxProcessors=0) const
Definition: Image.h:12515
friend void Swap(GenericImage &x1, GenericImage &x2) noexcept
Definition: Image.h:7346
GenericImage & CreateAlphaChannels(int n)
Definition: Image.h:7571
GenericImage Normalized(const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1) const
Definition: Image.h:8771
GenericImage(File &stream)
Definition: Image.h:6225
GenericImage & Normalize(T lowerBound, T upperBound, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1)
Definition: Image.h:8608
GenericImage & Max(T scalar, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1)
Definition: Image.h:10127
GenericImage & Pow(T scalar, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1)
Definition: Image.h:9986
double Median(const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1, int maxProcessors=0) const
Definition: Image.h:11795
GenericImage & DeleteAlphaChannels()
Definition: Image.h:7794
void GetLightness(GenericImage< P1 > &L, const Rect &rect=Rect(0), int maxProcessors=0, bool __performanceAnalysis__=false) const
Definition: Image.h:14931
size_type NominalSize() const noexcept
Definition: Image.h:6692
GenericImage Filled(T scalar, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1) const
Definition: Image.h:7971
GenericImage & Read(const String &filePath)
Definition: Image.h:13996
GenericImage & Binarize(const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1)
Definition: Image.h:8882
GenericImage & Dif(T scalar, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1)
Definition: Image.h:10039
GenericImage(const GenericImage &image)
Definition: Image.h:6094
sample LocateMinimumPixelValue(Point &pmin, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1, int maxProcessors=0) const
Definition: Image.h:11298
GenericImage Normalized(T lowerBound, T upperBound, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1) const
Definition: Image.h:8732
GenericImage & Shift(const GenericVector< T > &fillValues)
Definition: Image.h:14514
GenericImage & Xor(T scalar, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1)
Definition: Image.h:10197
sample LocateMinimumSampleValue(int &xmin, int &ymin, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1, int maxProcessors=0) const
Definition: Image.h:11187
GenericImage & ShiftTo(int x, int y, const GenericVector< T > &fillValues)
Definition: Image.h:14305
double Modulus(const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1, int maxProcessors=0) const
Definition: Image.h:13497
GenericImage & AddAlphaChannel(sample *data=nullptr)
Definition: Image.h:7620
const sample * PixelAddress(int x, int y, int channel=0) const noexcept
Definition: Image.h:6879
GenericImage & ShiftTo(const Point &p)
Definition: Image.h:14353
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)
Definition: Image.h:10665
double BendMidvariance(double center, double beta=0.2, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1, int maxProcessors=0) const
Definition: Image.h:13142
size_type AlphaSize() const noexcept
Definition: Image.h:6702
GenericImage & Abs(const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1)
Definition: Image.h:8957
GenericImage Rescaled(const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1) const
Definition: Image.h:8566
GenericImage & Zero(const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1)
Definition: Image.h:8012
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)
Definition: Image.h:10360
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
Definition: Image.h:13030
A generic point in the two-dimensional space.
Definition: Point.h:100
component x
Abscissa (horizontal, or X-axis coordinate).
Definition: Point.h:111
component y
Ordinate (vertical, or Y-axis coordinate).
Definition: Point.h:112
A generic rectangle in the two-dimensional space.
Definition: Rectangle.h:316
point LeftTop() const noexcept
Definition: Rectangle.h:529
component x1
Horizontal coordinate of the lower right corner.
Definition: Rectangle.h:336
component y1
Vertical coordinate of the lower right corner.
Definition: Rectangle.h:337
size_type IntegerArea() const
Definition: Rectangle.h:687
bool IsRect() const noexcept
Definition: Rectangle.h:775
void ResizeTo(T1 w, T1 h) noexcept
Definition: Rectangle.h:1713
component y0
Vertical coordinate of the upper left corner.
Definition: Rectangle.h:335
component x0
Horizontal coordinate of the upper left corner.
Definition: Rectangle.h:334
GenericRectangle MovedTo(const pcl::GenericPoint< T1 > &p) const noexcept
Definition: Rectangle.h:1604
GenericRectangle Ordered() const noexcept
Definition: Rectangle.h:807
component Width() const noexcept
Definition: Rectangle.h:637
component Height() const noexcept
Definition: Rectangle.h:646
GenericRectangle Intersection(const GenericRectangle< T1 > &r) const noexcept
Definition: Rectangle.h:1492
Generic vector of arbitrary length.
Definition: Vector.h:107
iterator Begin()
Definition: Vector.h:1918
int Length() const noexcept
Definition: Vector.h:1802
const RGBColorSystem & RGBWorkingSpace() const noexcept
Definition: ImageColor.h:110
color_space ColorSpace() const noexcept
Definition: ImageColor.h:178
int Width() const noexcept
Definition: ImageGeometry.h:90
Rect Bounds() const noexcept
Root base class of all PCL image transformations.
Acts like a union for all types of images in PCL, with optional class-wide ownership of transported i...
Definition: ImageVariant.h:322
32-bit floating point real image.
Manages transparent allocation and deallocation of shared and local pixel data.
32-bit integer point on the plane.
Colorimetrically defined RGB working color space.
32-bit integer rectangle on the plane.
Dynamic array of pointers to objects providing direct iteration and element access by reference.
size_type Length() const
void Destroy(iterator i, size_type n=1)
void Add(const ReferenceArray &x)
Thread-safe reference counter for copy-on-write data structures.
Unicode (UTF-16) string.
Definition: String.h:8148
size_type integer vector.
16-bit unsigned integer image.
32-bit unsigned integer image.
8-bit unsigned integer image.
64-bit floating point real vector.
bool operator==(const Array< T, A > &x1, const Array< T, A > &x2) noexcept
Definition: Array.h:2285
bool operator<(const Array< T, A > &x1, const Array< T, A > &x2) noexcept
Definition: Array.h:2296
const RGBA * ScanLine(int y) const
Complex< T > Sqrt(const Complex< T > &c) noexcept
Definition: Complex.h:688
T Abs(const Complex< T > &c) noexcept
Definition: Complex.h:443
int64 fsize_type
Definition: Defs.h:1185
uint64 Hash64(const void *data, size_type size, uint64 seed=0) noexcept
Definition: Math.h:4830
uint32 Hash32(const void *data, size_type size, uint32 seed=0) noexcept
Definition: Math.h:4984
GenericImage< P1 > operator-(const GenericImage< P1 > &image1, const GenericImage< P2 > &image2)
Definition: Image.h:18257
GenericImage< P1 > operator/(const GenericImage< P1 > &image1, const GenericImage< P2 > &image2)
Definition: Image.h:18298
GenericImage< P1 > operator+(const GenericImage< P1 > &image1, const GenericImage< P2 > &image2)
Definition: Image.h:18237
GenericImage< P1 > operator^(const GenericImage< P1 > &image1, const GenericImage< P2 > &image2)
Definition: Image.h:18319
GenericImage< P1 > operator*(const GenericImage< P1 > &image1, const GenericImage< P2 > &image2)
Definition: Image.h:18277
int TruncInt(T x) noexcept
Definition: Math.h:1203
constexpr T Pow2(T x) noexcept
Definition: Math.h:1748
void Swap(GenericPoint< T > &p1, GenericPoint< T > &p2) noexcept
Definition: Point.h:1459
unsigned long long uint64
Definition: Defs.h:682
unsigned char uint8
Definition: Defs.h:642
unsigned int uint32
Definition: Defs.h:666
uint32 RGBA
Definition: Color.h:92
RI Select(RI i, RI j, distance_type k)
Definition: Selection.h:165
ptrdiff_t distance_type
Definition: Defs.h:615
size_t size_type
Definition: Defs.h:609
double Qn(T *__restrict__ x, T *__restrict__ xn)
Definition: Math.h:4250
double BendMidvariance(const T *__restrict__ x, const T *__restrict__ xn, double center, double beta=0.2)
Definition: Math.h:4560
double Variance(const T *__restrict__ i, const T *__restrict__ j, double center) noexcept
Definition: Math.h:2784
double Median(const T *__restrict__ i, const T *__restrict__ j, double eps=0)
Definition: Math.h:2917
double Sn(T *__restrict__ x, T *__restrict__ xn)
Definition: Math.h:4004
#define INIT_THREAD_MONITOR()
Declares and initializes local variables used for synchronization of thread status monitoring.
#define UPDATE_THREAD_MONITOR(N)
Synchronized status monitoring of a set of image processing threads.
constexpr const T & Min(const T &a, const T &b) noexcept
Definition: Utility.h:90
constexpr const T & Range(const T &x, const T &a, const T &b) noexcept
Definition: Utility.h:190
constexpr const T & Max(const T &a, const T &b) noexcept
Definition: Utility.h:119
void Apply(FI i, FI j, F f) noexcept(noexcept(f))
Definition: Utility.h:249
int NumberOfNominalChannels(int colorSpace)
Definition: ColorSpace.h:102
String Name(int colorSpace)
bool IsBitwiseLogicalOperator(int op) noexcept
Definition: Image.h:183
bool IsPixelCompositionOperator(int op) noexcept
Definition: Image.h:201
String Id(value_type operation)
bool IsArithmeticOperator(int op) noexcept
Definition: Image.h:174
bool IsMoveOperator(int op) noexcept
Definition: Image.h:192
PCL root namespace.
Definition: AbstractImage.h:77
Thread synchronization data for status monitoring of parallel image processing tasks.
StatusMonitor status
Status monitoring object.
Compression/decompression performance measurements.
Definition: Compression.h:103
Working parameters for performance analysis thread optimization.
Definition: Thread.h:257
size_type overheadLimit
Minimum number of items per thread. Used when no performance analysis data is available.
Definition: Thread.h:262
size_type length
Total number of items to be processed.
Definition: Thread.h:260
bool floatingPoint
True when processing floating point data; false when processing integers.
Definition: Thread.h:264
int itemSize
Size of a processed item in bytes.
Definition: Thread.h:263
performance_analysis_algorithm algorithm
Thread performance analysis algorithm.
Definition: Thread.h:259
Two-sided descriptive statistical estimate.
Definition: Math.h:3592
double high
High estimate component.
Definition: Math.h:3594
double low
Low estimate component.
Definition: Math.h:3593