PCL
Image.h
Go to the documentation of this file.
1 // ____ ______ __
2 // / __ \ / ____// /
3 // / /_/ // / / /
4 // / ____// /___ / /___ PixInsight Class Library
5 // /_/ \____//_____/ PCL 2.8.4
6 // ----------------------------------------------------------------------------
7 // pcl/Image.h - Released 2024-12-23T11:32:56Z
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-2024 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 != 0 )
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 != 0 )
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 != 0 )
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 != 0 )
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 = size_type( r.Width() )*size_type( r.Height() );
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 = size_type( r.Width() )*size_type( r.Height() );
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 = size_type( r.Width() )*size_type( r.Height() );
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 = size_type( r.Width() )*size_type( r.Height() );
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 = size_type( r.Width() )*size_type( r.Height() );
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 = size_type( r.Width() )*size_type( r.Height() );
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 = size_type( r.Width() )*size_type( r.Height() );
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 = size_type( r.Width() )*size_type( r.Height() );
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 = size_type( r.Width() )*size_type( r.Height() );
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 = size_type( r.Width() )*size_type( r.Height() );
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 = size_type( r.Width() )*size_type( r.Height() );
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 = size_type( r.Width() )*size_type( r.Height() );
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 
10823  sample MinimumSampleValue( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1,
10824  int maxProcessors = 0 ) const
10825  {
10826  Rect r = rect;
10827  if ( !ParseSelection( r, firstChannel, lastChannel ) )
10828  return 0;
10829 
10830  size_type N = size_type( r.Width() )*size_type( r.Height() )*(1 + lastChannel - firstChannel);
10831  if ( m_status.IsInitializationEnabled() )
10832  m_status.Initialize( "Computing minimum pixel sample value", N );
10833 
10834  Array<size_type> L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors );
10835  bool useAffinity = m_parallel && Thread::IsRootThread();
10836  ReferenceArray<MinThread> threads;
10837  for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
10838  threads.Add( new MinThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ) ) );
10839  if ( threads.Length() > 1 )
10840  {
10841  int n = 0;
10842  for ( MinThread& thread : threads )
10843  thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 );
10844  for ( MinThread& thread : threads )
10845  thread.Wait();
10846  }
10847  else
10848  threads[0].Run();
10849 
10850  sample min = P::MinSampleValue();
10851  for ( size_type i = 0; i < threads.Length(); ++i )
10852  if ( threads[i].count > 0 )
10853  {
10854  min = threads[i].min;
10855  while ( ++i < threads.Length() )
10856  if ( threads[i].count > 0 )
10857  if ( threads[i].min < min )
10858  min = threads[i].min;
10859  break;
10860  }
10861 
10862  threads.Destroy();
10863  m_status += N;
10864  return min;
10865  }
10866 
10872  sample MinimumPixelValue( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1,
10873  int maxProcessors = 0 ) const
10874  {
10875  return MinimumSampleValue( rect, firstChannel, lastChannel, maxProcessors );
10876  }
10877 
10897  sample MaximumSampleValue( const Rect& rect = Rect( 0 ),
10898  int firstChannel = -1, int lastChannel = -1, int maxProcessors = 0 ) const
10899  {
10900  Rect r = rect;
10901  if ( !ParseSelection( r, firstChannel, lastChannel ) )
10902  return 0;
10903 
10904  size_type N = size_type( r.Width() )*size_type( r.Height() )*(1 + lastChannel - firstChannel);
10905  if ( m_status.IsInitializationEnabled() )
10906  m_status.Initialize( "Computing maximum pixel sample value", N );
10907 
10908  Array<size_type> L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors );
10909  bool useAffinity = m_parallel && Thread::IsRootThread();
10910  ReferenceArray<MaxThread> threads;
10911  for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
10912  threads.Add( new MaxThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ) ) );
10913  if ( threads.Length() > 1 )
10914  {
10915  int n = 0;
10916  for ( MaxThread& thread : threads )
10917  thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 );
10918  for ( MaxThread& thread : threads )
10919  thread.Wait();
10920  }
10921  else
10922  threads[0].Run();
10923 
10924  sample max = P::MinSampleValue();
10925  for ( size_type i = 0; i < threads.Length(); ++i )
10926  if ( threads[i].count > 0 )
10927  {
10928  max = threads[i].max;
10929  while ( ++i < threads.Length() )
10930  if ( threads[i].count > 0 )
10931  if ( max < threads[i].max )
10932  max = threads[i].max;
10933  break;
10934  }
10935 
10936  threads.Destroy();
10937  m_status += N;
10938  return max;
10939  }
10940 
10946  sample MaximumPixelValue( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1,
10947  int maxProcessors = 0 ) const
10948  {
10949  return MaximumSampleValue( rect, firstChannel, lastChannel, maxProcessors );
10950  }
10951 
10978  template <typename T>
10979  void GetExtremeSampleValues( T& min, T& max,
10980  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1,
10981  int maxProcessors = 0 ) const
10982  {
10983  Rect r = rect;
10984  if ( !ParseSelection( r, firstChannel, lastChannel ) )
10985  {
10986  // ### Required for compatibility with PCL 1.x
10987  P::FromSample( min, P::MinSampleValue() );
10988  max = min;
10989  return;
10990  }
10991 
10992  size_type N = size_type( r.Width() )*size_type( r.Height() )*(1 + lastChannel - firstChannel);
10993  if ( m_status.IsInitializationEnabled() )
10994  m_status.Initialize( "Computing extreme pixel sample values", N );
10995 
10996  Array<size_type> L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors );
10997  bool useAffinity = m_parallel && Thread::IsRootThread();
10999  for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
11000  threads.Add( new MinMaxThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ) ) );
11001  if ( threads.Length() > 1 )
11002  {
11003  int n = 0;
11004  for ( MinMaxThread& thread : threads )
11005  thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 );
11006  for ( MinMaxThread& thread : threads )
11007  thread.Wait();
11008  }
11009  else
11010  threads[0].Run();
11011 
11012  sample vmin = P::MinSampleValue();
11013  sample vmax = P::MinSampleValue();
11014  for ( size_type i = 0; i < threads.Length(); ++i )
11015  if ( threads[i].count > 0 )
11016  {
11017  vmin = threads[i].min;
11018  vmax = threads[i].max;
11019  while ( ++i < threads.Length() )
11020  if ( threads[i].count > 0 )
11021  {
11022  if ( threads[i].min < vmin )
11023  vmin = threads[i].min;
11024  if ( vmax < threads[i].max )
11025  vmax = threads[i].max;
11026  }
11027  break;
11028  }
11029 
11030  threads.Destroy();
11031  m_status += N;
11032  P::FromSample( min, vmin );
11033  P::FromSample( max, vmax );
11034  }
11035 
11042  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1,
11043  int maxProcessors = 0 ) const
11044  {
11045  GetExtremeSampleValues( min, max, rect, firstChannel, lastChannel, maxProcessors );
11046  }
11047 
11074  sample LocateMinimumSampleValue( int& xmin, int& ymin,
11075  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1,
11076  int maxProcessors = 0 ) const
11077  {
11078  Rect r = rect;
11079  if ( !ParseSelection( r, firstChannel, lastChannel ) )
11080  {
11081  // ### Required for compatibility with PCL 1.x
11082  xmin = ymin = 0;
11083  return P::MinSampleValue();
11084  }
11085 
11086  size_type N = size_type( r.Width() )*size_type( r.Height() )*(1 + lastChannel - firstChannel);
11087  if ( m_status.IsInitializationEnabled() )
11088  m_status.Initialize( "Locating minimum pixel sample value", N );
11089 
11090  Array<size_type> L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors );
11091  bool useAffinity = m_parallel && Thread::IsRootThread();
11093  for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
11094  threads.Add( new MinPosThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ) ) );
11095  if ( threads.Length() > 1 )
11096  {
11097  int n = 0;
11098  for ( MinPosThread& thread : threads )
11099  thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 );
11100  for ( MinPosThread& thread : threads )
11101  thread.Wait();
11102  }
11103  else
11104  threads[0].Run();
11105 
11106  xmin = ymin = -1;
11107  sample min = P::MinSampleValue();
11108  for ( size_type i = 0; i < threads.Length(); ++i )
11109  if ( threads[i].count > 0 )
11110  {
11111  min = threads[i].min;
11112  xmin = threads[i].pmin.x;
11113  ymin = threads[i].pmin.y;
11114  while ( ++i < threads.Length() )
11115  if ( threads[i].count > 0 )
11116  if ( threads[i].min < min )
11117  {
11118  min = threads[i].min;
11119  xmin = threads[i].pmin.x;
11120  ymin = threads[i].pmin.y;
11121  }
11122  break;
11123  }
11124 
11125  threads.Destroy();
11126  m_status += N;
11127  return min;
11128  }
11129 
11153  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1,
11154  int maxProcessors = 0 ) const
11155  {
11156  return LocateMinimumSampleValue( pmin.x, pmin.y, rect, firstChannel, lastChannel, maxProcessors );
11157  }
11158 
11164  sample LocateMinimumPixelValue( int& xmin, int& ymin,
11165  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1,
11166  int maxProcessors = 0 ) const
11167  {
11168  return LocateMinimumSampleValue( xmin, ymin, rect, firstChannel, lastChannel, maxProcessors );
11169  }
11170 
11177  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1,
11178  int maxProcessors = 0 ) const
11179  {
11180  return LocateMinimumSampleValue( pmin, rect, firstChannel, lastChannel, maxProcessors );
11181  }
11182 
11209  sample LocateMaximumSampleValue( int& xmax, int& ymax,
11210  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1,
11211  int maxProcessors = 0 ) const
11212  {
11213  Rect r = rect;
11214  if ( !ParseSelection( r, firstChannel, lastChannel ) )
11215  {
11216  // ### Required for compatibility with PCL 1.x
11217  xmax = ymax = 0;
11218  return P::MaxSampleValue();
11219  }
11220 
11221  size_type N = size_type( r.Width() )*size_type( r.Height() )*(1 + lastChannel - firstChannel);
11222  if ( m_status.IsInitializationEnabled() )
11223  m_status.Initialize( "Locating maximum pixel sample value", N );
11224 
11225  Array<size_type> L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors );
11226  bool useAffinity = m_parallel && Thread::IsRootThread();
11228  for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
11229  threads.Add( new MaxPosThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ) ) );
11230  if ( threads.Length() > 1 )
11231  {
11232  int n = 0;
11233  for ( MaxPosThread& thread : threads )
11234  thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 );
11235  for ( MaxPosThread& thread : threads )
11236  thread.Wait();
11237  }
11238  else
11239  threads[0].Run();
11240 
11241  xmax = ymax = -1;
11242  sample max = P::MinSampleValue();
11243  for ( size_type i = 0; i < threads.Length(); ++i )
11244  if ( threads[i].count > 0 )
11245  {
11246  max = threads[i].max;
11247  xmax = threads[i].pmax.x;
11248  ymax = threads[i].pmax.y;
11249  while ( ++i < threads.Length() )
11250  if ( threads[i].count > 0 )
11251  if ( max < threads[i].max )
11252  {
11253  max = threads[i].max;
11254  xmax = threads[i].pmax.x;
11255  ymax = threads[i].pmax.y;
11256  }
11257  break;
11258  }
11259 
11260  threads.Destroy();
11261  m_status += N;
11262  return max;
11263  }
11264 
11288  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1,
11289  int maxProcessors = 0 ) const
11290  {
11291  return LocateMaximumSampleValue( pmax.x, pmax.y, rect, firstChannel, lastChannel, maxProcessors );
11292  }
11293 
11299  sample LocateMaximumPixelValue( int& xmax, int& ymax,
11300  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1,
11301  int maxProcessors = 0 ) const
11302  {
11303  return LocateMaximumSampleValue( xmax, ymax, rect, firstChannel, lastChannel, maxProcessors );
11304  }
11305 
11312  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1,
11313  int maxProcessors = 0 ) const
11314  {
11315  return LocateMaximumSampleValue( pmax, rect, firstChannel, lastChannel, maxProcessors );
11316  }
11317 
11354  template <typename T>
11355  void LocateExtremeSampleValues( int& xmin, int& ymin, T& min,
11356  int& xmax, int& ymax, T& max,
11357  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1,
11358  int maxProcessors = 0 ) const
11359  {
11360  Rect r = rect;
11361  if ( !ParseSelection( r, firstChannel, lastChannel ) )
11362  {
11363  // ### Required for compatibility with PCL 1.x
11364  xmin = ymin = xmax = ymax = 0;
11365  P::FromSample( min, P::MinSampleValue() );
11366  max = min;
11367  return;
11368  }
11369 
11370  size_type N = size_type( r.Width() )*size_type( r.Height() )*(1 + lastChannel - firstChannel);
11371  if ( m_status.IsInitializationEnabled() )
11372  m_status.Initialize( "Locating extreme pixel sample values", N );
11373 
11374  Array<size_type> L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors );
11375  bool useAffinity = m_parallel && Thread::IsRootThread();
11377  for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
11378  threads.Add( new MinMaxPosThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ) ) );
11379  if ( threads.Length() > 1 )
11380  {
11381  int n = 0;
11382  for ( MinMaxPosThread& thread : threads )
11383  thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 );
11384  for ( MinMaxPosThread& thread : threads )
11385  thread.Wait();
11386  }
11387  else
11388  threads[0].Run();
11389 
11390  xmin = ymin = xmax = ymax = -1;
11391  sample vmin = P::MinSampleValue();
11392  sample vmax = P::MinSampleValue();
11393  for ( size_type i = 0; i < threads.Length(); ++i )
11394  if ( threads[i].count > 0 )
11395  {
11396  vmin = threads[i].min;
11397  xmin = threads[i].pmin.x;
11398  ymin = threads[i].pmin.y;
11399  vmax = threads[i].max;
11400  xmax = threads[i].pmax.x;
11401  ymax = threads[i].pmax.y;
11402  while ( ++i < threads.Length() )
11403  if ( threads[i].count > 0 )
11404  {
11405  if ( threads[i].min < vmin )
11406  {
11407  vmin = threads[i].min;
11408  xmin = threads[i].pmin.x;
11409  ymin = threads[i].pmin.y;
11410  }
11411  if ( vmax < threads[i].max )
11412  {
11413  vmax = threads[i].max;
11414  xmax = threads[i].pmax.x;
11415  ymax = threads[i].pmax.y;
11416  }
11417  }
11418  break;
11419  }
11420 
11421  threads.Destroy();
11422  m_status += N;
11423  P::FromSample( min, vmin );
11424  P::FromSample( max, vmax );
11425  }
11426 
11454  template <typename T>
11455  void LocateExtremeSampleValues( Point& pmin, T& min,
11456  Point& pmax, T& max,
11457  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1,
11458  int maxProcessors = 0 ) const
11459  {
11460  LocateExtremeSampleValues( pmin.x, pmin.y, min,
11461  pmax.x, pmax.y, max,
11462  rect, firstChannel, lastChannel, maxProcessors );
11463  }
11464 
11490  size_type Count( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1,
11491  int maxProcessors = 0 ) const
11492  {
11493  Rect r = rect;
11494  if ( !ParseSelection( r, firstChannel, lastChannel ) )
11495  return 0;
11496 
11497  size_type N = size_type( r.Width() )*size_type( r.Height() )*(1 + lastChannel - firstChannel);
11498  if ( m_status.IsInitializationEnabled() )
11499  m_status.Initialize( "Counting pixel samples", N );
11500 
11501  if ( !this->IsRangeClippingEnabled() )
11502  {
11503  m_status += N;
11504  return size_type( r.Width() ) * size_type( r.Height() ) * size_type( 1 + lastChannel - firstChannel );
11505  }
11506 
11507  Array<size_type> L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors );
11508  bool useAffinity = m_parallel && Thread::IsRootThread();
11510  for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
11511  threads.Add( new CountThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ) ) );
11512  if ( threads.Length() > 1 )
11513  {
11514  int n = 0;
11515  for ( CountThread& thread : threads )
11516  thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 );
11517  for ( CountThread& thread : threads )
11518  thread.Wait();
11519  }
11520  else
11521  threads[0].Run();
11522 
11523  size_type count = 0;
11524  for ( const CountThread& thread : threads )
11525  count += thread.count;
11526 
11527  threads.Destroy();
11528  m_status += N;
11529  return count;
11530  }
11531 
11560  double Mean( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1,
11561  int maxProcessors = 0 ) const
11562  {
11563  Rect r = rect;
11564  if ( !ParseSelection( r, firstChannel, lastChannel ) )
11565  return 0;
11566 
11567  size_type N = size_type( r.Width() )*size_type( r.Height() )*(1 + lastChannel - firstChannel);
11568  if ( m_status.IsInitializationEnabled() )
11569  m_status.Initialize( "Computing mean pixel sample value", N );
11570 
11571  Array<size_type> L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors );
11572  bool useAffinity = m_parallel && Thread::IsRootThread();
11573  ReferenceArray<SumThread> threads;
11574  for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
11575  threads.Add( new SumThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ) ) );
11576  if ( threads.Length() > 1 )
11577  {
11578  int n = 0;
11579  for ( SumThread& thread : threads )
11580  thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 );
11581  for ( SumThread& thread : threads )
11582  thread.Wait();
11583  }
11584  else
11585  threads[0].Run();
11586 
11587  double s = 0;
11588  double e = 0;
11589  size_type n = 0;
11590  for ( const SumThread& thread : threads )
11591  {
11592  double y = thread.s - e;
11593  double t = s + y;
11594  e = (t - s) - y;
11595  s = t;
11596  n += thread.n;
11597  }
11598 
11599  threads.Destroy();
11600 
11601  m_status += N;
11602 
11603  if ( n == 0 )
11604  return 0;
11605  return s/n;
11606  }
11607 
11633  double Median( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1,
11634  int maxProcessors = 0 ) const
11635  {
11636  Rect r = rect;
11637  if ( !ParseSelection( r, firstChannel, lastChannel ) )
11638  return 0;
11639 
11640  size_type N = size_type( r.Width() )*size_type( r.Height() )*(1 + lastChannel - firstChannel);
11641  if ( m_status.IsInitializationEnabled() )
11642  m_status.Initialize( "Computing median pixel sample value", N );
11643 
11644  if ( N <= __PCL_MEDIAN_HISTOGRAM_OVERHEAD )
11645  {
11646  SmpThread S( *this, r, firstChannel, lastChannel, 0, r.Height() );
11647  S.Run();
11648  if ( S.n == 0 )
11649  {
11650  m_status += N;
11651  return 0;
11652  }
11653  double m = double( *pcl::Select( S.samples.Begin(), S.samples.At( S.n ), S.n >> 1 ) )/double( P::MaxSampleValue() );
11654  if ( S.n & 1 )
11655  {
11656  m_status += N;
11657  return m;
11658  }
11659  m = (m + double( *pcl::Select( S.samples.Begin(), S.samples.At( S.n ), (S.n >> 1)-1 ) )/double( P::MaxSampleValue() ))/2;
11660  m_status += N;
11661  return m;
11662  }
11663 
11664  Array<size_type> L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors, __PCL_MEDIAN_HISTOGRAM_OVERHEAD );
11665  bool useAffinity = m_parallel && Thread::IsRootThread();
11666 
11667  double low, high;
11668  size_type count = 0;
11669  {
11671  for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
11672  threads << new MinMaxThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ) );
11673 
11674  if ( threads.Length() > 1 )
11675  {
11676  int i = 0;
11677  for ( MinMaxThread& thread : threads )
11678  thread.Start( ThreadPriority::DefaultMax, useAffinity ? i++ : -1 );
11679  for ( MinMaxThread& thread : threads )
11680  thread.Wait();
11681  }
11682  else
11683  threads[0].Run();
11684 
11685  sample slow = 0, shigh = 0;
11686  for ( size_type i = 0; i < threads.Length(); ++i )
11687  if ( threads[i].count > 0 )
11688  {
11689  slow = threads[i].min;
11690  shigh = threads[i].max;
11691  count = threads[i].count;
11692  while ( ++i < threads.Length() )
11693  if ( threads[i].count > 0 )
11694  {
11695  if ( threads[i].min < slow )
11696  slow = threads[i].min;
11697  if ( shigh < threads[i].max )
11698  shigh = threads[i].max;
11699  count += threads[i].count;
11700  }
11701  break;
11702  }
11703 
11704  threads.Destroy();
11705 
11706  low = double( slow );
11707  high = double( shigh );
11708  }
11709 
11710  const double eps = P::IsComplexSample() ? 2*std::numeric_limits<double>::epsilon() :
11711  (P::IsFloatSample() ? 2*std::numeric_limits<typename P::component>::epsilon() :
11712  0.5/Pow2( double( P::BitsPerSample() ) ));
11713  if ( count == 0 )
11714  {
11715  m_status += N;
11716  return 0;
11717  }
11718  if ( high - low < eps )
11719  {
11720  m_status += N;
11721  return low/double( P::MaxSampleValue() );
11722  }
11723 
11725  for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
11726  threads << new HistogramThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ), low, high );
11727 
11728  double mh = 0, l0 = low;
11729  SzVector H0;
11730 
11731  for ( size_type n = 0, n2 = count >> 1, step = 0, it = 0;; ++it )
11732  {
11733  SzVector H;
11734  if ( it == 0 && step )
11735  H = H0;
11736  else
11737  {
11738  if ( threads.Length() > 1 )
11739  {
11740  int i = 0;
11741  for ( HistogramThread& thread : threads )
11742  thread.Start( ThreadPriority::DefaultMax, useAffinity ? i++ : -1 );
11743  for ( HistogramThread& thread : threads )
11744  thread.Wait();
11745  }
11746  else
11747  threads[0].Run();
11748 
11749  H = threads[0].H;
11750  for ( size_type i = 1; i < threads.Length(); ++i )
11751  H += threads[i].H;
11752  if ( it == 0 )
11753  if ( (count & 1) == 0 )
11754  H0 = H;
11755  }
11756 
11757  for ( int i = 0; ; n += H[i++] )
11758  if ( n + H[i] > n2 )
11759  {
11760  double range = high - low;
11761  high = (range * (i + 1))/(__PCL_MEDIAN_HISTOGRAM_LENGTH - 1) + low;
11762  low = (range * i)/(__PCL_MEDIAN_HISTOGRAM_LENGTH - 1) + low;
11763  if ( high - low < eps )
11764  {
11765  if ( count & 1 )
11766  {
11767  threads.Destroy();
11768  m_status += N;
11769  return low/double( P::MaxSampleValue() );
11770  }
11771  if ( step )
11772  {
11773  threads.Destroy();
11774  m_status += N;
11775  return (low + mh)/2/double( P::MaxSampleValue() );
11776  }
11777  mh = low;
11778  low = l0;
11779  n = 0;
11780  --n2;
11781  ++step;
11782  it = 0;
11783  }
11784  break;
11785  }
11786  }
11787  }
11788 
11820  double OrderStatistic( double k,
11821  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1,
11822  int maxProcessors = 0 ) const
11823  {
11824  if ( k < 0 || k > 1 )
11825  return 0;
11826 
11827  Rect r = rect;
11828  if ( !ParseSelection( r, firstChannel, lastChannel ) )
11829  return 0;
11830 
11831  size_type N = size_type( r.Width() )*size_type( r.Height() )*(1 + lastChannel - firstChannel);
11832  if ( m_status.IsInitializationEnabled() )
11833  m_status.Initialize( "Computing order statistic", N );
11834 
11835  if ( N <= __PCL_MEDIAN_HISTOGRAM_OVERHEAD )
11836  {
11837  SmpThread S( *this, r, firstChannel, lastChannel, 0, r.Height() );
11838  S.Run();
11839  m_status += N;
11840  if ( S.n == 0 )
11841  return 0;
11842  return double( *pcl::Select( S.samples.Begin(), S.samples.At( S.n ), distance_type( k*(S.n - 1) ) ) )/double( P::MaxSampleValue() );
11843  }
11844 
11845  Array<size_type> L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors, __PCL_MEDIAN_HISTOGRAM_OVERHEAD/*overheadLimitPx*/ );
11846  bool useAffinity = m_parallel && Thread::IsRootThread();
11847 
11848  double low, high;
11849  size_type count = 0;
11850  {
11852  for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
11853  threads << new MinMaxThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ) );
11854 
11855  if ( threads.Length() > 1 )
11856  {
11857  int i = 0;
11858  for ( MinMaxThread& thread : threads )
11859  thread.Start( ThreadPriority::DefaultMax, useAffinity ? i++ : -1 );
11860  for ( MinMaxThread& thread : threads )
11861  thread.Wait();
11862  }
11863  else
11864  threads[0].Run();
11865 
11866  sample slow = 0, shigh = 0;
11867  for ( size_type i = 0; i < threads.Length(); ++i )
11868  if ( threads[i].count > 0 )
11869  {
11870  slow = threads[i].min;
11871  shigh = threads[i].max;
11872  count = threads[i].count;
11873  while ( ++i < threads.Length() )
11874  if ( threads[i].count > 0 )
11875  {
11876  if ( threads[i].min < slow )
11877  slow = threads[i].min;
11878  if ( shigh < threads[i].max )
11879  shigh = threads[i].max;
11880  count += threads[i].count;
11881  }
11882  break;
11883  }
11884 
11885  threads.Destroy();
11886 
11887  low = double( slow );
11888  high = double( shigh );
11889  }
11890 
11891  const double eps = P::IsComplexSample() ? 2*std::numeric_limits<double>::epsilon() :
11892  (P::IsFloatSample() ? 2*std::numeric_limits<typename P::component>::epsilon() :
11893  0.5/Pow2( double( P::BitsPerSample() ) ));
11894  if ( count == 0 )
11895  {
11896  m_status += N;
11897  return 0;
11898  }
11899  if ( k == 0 || high - low < eps )
11900  {
11901  m_status += N;
11902  return low/double( P::MaxSampleValue() );
11903  }
11904  if ( k == 1 )
11905  {
11906  m_status += N;
11907  return high/double( P::MaxSampleValue() );
11908  }
11909 
11910  size_type index = size_type( k*(count - 1) );
11911 
11913  for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
11914  threads << new HistogramThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ), low, high );
11915 
11916  for ( size_type n = 0;; )
11917  {
11918  if ( threads.Length() > 1 )
11919  {
11920  int i = 0;
11921  for ( HistogramThread& thread : threads )
11922  thread.Start( ThreadPriority::DefaultMax, useAffinity ? i++ : -1 );
11923  for ( HistogramThread& thread : threads )
11924  thread.Wait();
11925  }
11926  else
11927  threads[0].Run();
11928 
11929  SzVector H = threads[0].H;
11930  for ( size_type i = 1; i < threads.Length(); ++i )
11931  H += threads[i].H;
11932 
11933  for ( int i = 0; ; n += H[i++] )
11934  if ( n + H[i] > index )
11935  {
11936  double range = high - low;
11937  high = (range * (i + 1))/(__PCL_MEDIAN_HISTOGRAM_LENGTH - 1) + low;
11938  low = (range * i)/(__PCL_MEDIAN_HISTOGRAM_LENGTH - 1) + low;
11939  if ( high - low < eps )
11940  {
11941  threads.Destroy();
11942  m_status += N;
11943  return low/double( P::MaxSampleValue() );
11944  }
11945  break;
11946  }
11947  }
11948  }
11949 
11984  double Variance( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1,
11985  int maxProcessors = 0 ) const
11986  {
11987  Rect r = rect;
11988  if ( !ParseSelection( r, firstChannel, lastChannel ) )
11989  return 0;
11990 
11991  size_type N = size_type( r.Width() )*size_type( r.Height() )*(1 + lastChannel - firstChannel);
11992  if ( m_status.IsInitializationEnabled() )
11993  m_status.Initialize( "Computing variance", N );
11994 
11995  Array<size_type> L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors );
11996  bool useAffinity = m_parallel && Thread::IsRootThread();
11997  ReferenceArray<SumThread> sumThreads;
11998  for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
11999  sumThreads.Add( new SumThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ) ) );
12000  if ( sumThreads.Length() > 1 )
12001  {
12002  int n = 0;
12003  for ( SumThread& thread : sumThreads )
12004  thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 );
12005  for ( SumThread& thread : sumThreads )
12006  thread.Wait();
12007  }
12008  else
12009  sumThreads[0].Run();
12010 
12011  double s = 0;
12012  double e = 0;
12013  size_type n = 0;
12014  for ( const SumThread& thread : sumThreads )
12015  {
12016  double y = thread.s - e;
12017  double t = s + y;
12018  e = (t - s) - y;
12019  s = t;
12020  n += thread.n;
12021  }
12022 
12023  sumThreads.Destroy();
12024 
12025  if ( n < 2 )
12026  return 0;
12027  double mean = s/n;
12028 
12029  ReferenceArray<VarThread> varThreads;
12030  for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
12031  varThreads.Add( new VarThread( *this, mean, r, firstChannel, lastChannel, n, n + int( L[i] ) ) );
12032  if ( varThreads.Length() > 1 )
12033  {
12034  int n = 0;
12035  for ( VarThread& thread : varThreads )
12036  thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 );
12037  for ( VarThread& thread : varThreads )
12038  thread.Wait();
12039  }
12040  else
12041  varThreads[0].Run();
12042 
12043  double var = 0, eps = 0;
12044  for ( const VarThread& thread : varThreads )
12045  var += thread.var, eps += thread.eps;
12046 
12047  varThreads.Destroy();
12048  m_status += N;
12049  return (var - eps*eps/n)/(n - 1);
12050  }
12051 
12084  double StdDev( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1,
12085  int maxProcessors = 0 ) const
12086  {
12087  return pcl::Sqrt( Variance( rect, firstChannel, lastChannel, maxProcessors ) );
12088  }
12089 
12129  double AvgDev( double center,
12130  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1,
12131  int maxProcessors = 0 ) const
12132  {
12133  Rect r = rect;
12134  if ( !ParseSelection( r, firstChannel, lastChannel ) )
12135  return 0;
12136 
12137  size_type N = size_type( r.Width() )*size_type( r.Height() )*(1 + lastChannel - firstChannel);
12138  if ( m_status.IsInitializationEnabled() )
12139  m_status.Initialize( "Computing average absolute deviation", N );
12140 
12141  Array<size_type> L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors );
12142  bool useAffinity = m_parallel && Thread::IsRootThread();
12144  for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
12145  threads.Add( new SumAbsDevThread( *this, center, r, firstChannel, lastChannel, n, n + int( L[i] ) ) );
12146  if ( threads.Length() > 1 )
12147  {
12148  int n = 0;
12149  for ( SumAbsDevThread& thread : threads )
12150  thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 );
12151  for ( SumAbsDevThread& thread : threads )
12152  thread.Wait();
12153  }
12154  else
12155  threads[0].Run();
12156 
12157  double s = 0;
12158  double e = 0;
12159  size_type n = 0;
12160  for ( const SumAbsDevThread& thread : threads )
12161  {
12162  double y = thread.s - e;
12163  double t = s + y;
12164  e = (t - s) - y;
12165  s = t;
12166  n += thread.n;
12167  }
12168 
12169  threads.Destroy();
12170 
12171  m_status += N;
12172 
12173  if ( n == 0 )
12174  return 0;
12175  return s/n;
12176  }
12177 
12199  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1,
12200  int maxProcessors = 0 ) const
12201  {
12202  Rect r = rect;
12203  if ( !ParseSelection( r, firstChannel, lastChannel ) )
12204  return 0;
12205 
12206  size_type N = size_type( r.Width() )*size_type( r.Height() )*(1 + lastChannel - firstChannel);
12207  if ( m_status.IsInitializationEnabled() )
12208  m_status.Initialize( "Computing two-sided average absolute deviation", N );
12209 
12210  Array<size_type> L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors );
12211  bool useAffinity = m_parallel && Thread::IsRootThread();
12213  for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
12214  threads.Add( new TwoSidedSumAbsDevThread( *this, center, r, firstChannel, lastChannel, n, n + int( L[i] ) ) );
12215  if ( threads.Length() > 1 )
12216  {
12217  int n = 0;
12218  for ( TwoSidedSumAbsDevThread& thread : threads )
12219  thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 );
12220  for ( TwoSidedSumAbsDevThread& thread : threads )
12221  thread.Wait();
12222  }
12223  else
12224  threads[0].Run();
12225 
12226  double s0 = 0, s1 = 0;
12227  double e0 = 0, e1 = 0;
12228  size_type n0 = 0, n1 = 0;
12229  for ( const TwoSidedSumAbsDevThread& thread : threads )
12230  {
12231  double y = thread.s0 - e0;
12232  double t = s0 + y;
12233  e0 = (t - s0) - y;
12234  s0 = t;
12235  n0 += thread.n0;
12236  y = thread.s1 - e1;
12237  t = s1 + y;
12238  e1 = (t - s1) - y;
12239  s1 = t;
12240  n1 += thread.n1;
12241  }
12242 
12243  threads.Destroy();
12244 
12245  m_status += N;
12246 
12247  return { (n0 > 0) ? s0/n0 : 0.0,
12248  (n1 > 0) ? s1/n1 : 0.0 };
12249  }
12250 
12285  double MAD( double center,
12286  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1,
12287  int maxProcessors = 0 ) const
12288  {
12289  Rect r = rect;
12290  if ( !ParseSelection( r, firstChannel, lastChannel ) )
12291  return 0;
12292 
12293  size_type N = size_type( r.Width() )*size_type( r.Height() )*(1 + lastChannel - firstChannel);
12294  if ( m_status.IsInitializationEnabled() )
12295  m_status.Initialize( "Computing median absolute deviation", N );
12296 
12297  if ( N <= __PCL_MEDIAN_HISTOGRAM_OVERHEAD )
12298  {
12299  AbsDevSmpThread S( *this, center, r, firstChannel, lastChannel, 0, r.Height() );
12300  S.Run();
12301  if ( S.n == 0 )
12302  {
12303  m_status += N;
12304  return 0;
12305  }
12306  double m = *pcl::Select( S.values.Begin(), S.values.At( S.n ), S.n >> 1 );
12307  if ( S.n & 1 )
12308  {
12309  m_status += N;
12310  return m;
12311  }
12312  m = (m + *pcl::Select( S.values.Begin(), S.values.At( S.n ), (S.n >> 1)-1 ))/2;
12313  m_status += N;
12314  return m;
12315  }
12316 
12317  Array<size_type> L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors, __PCL_MEDIAN_HISTOGRAM_OVERHEAD/*overheadLimitPx*/ );
12318  bool useAffinity = m_parallel && Thread::IsRootThread();
12319 
12320  double low, high;
12321  size_type count = 0;
12322  {
12324  for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
12325  threads << new ExtremeAbsDevThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ), center );
12326 
12327  if ( threads.Length() > 1 )
12328  {
12329  int n = 0;
12330  for ( ExtremeAbsDevThread& thread : threads )
12331  thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 );
12332  for ( ExtremeAbsDevThread& thread : threads )
12333  thread.Wait();
12334  }
12335  else
12336  threads[0].Run();
12337 
12338  for ( size_type i = 0; i < threads.Length(); ++i )
12339  if ( threads[i].count > 0 )
12340  {
12341  low = threads[i].minAbsDev;
12342  high = threads[i].maxAbsDev;
12343  count += threads[i].count;
12344  while ( ++i < threads.Length() )
12345  if ( threads[i].count > 0 )
12346  {
12347  if ( threads[i].minAbsDev < low )
12348  low = threads[i].minAbsDev;
12349  if ( threads[i].maxAbsDev > high )
12350  high = threads[i].maxAbsDev;
12351  count += threads[i].count;
12352  }
12353  break;
12354  }
12355 
12356  threads.Destroy();
12357  }
12358 
12359  const double eps = 2*std::numeric_limits<double>::epsilon();
12360  if ( count == 0 || high - low < eps )
12361  {
12362  m_status += N;
12363  return 0;
12364  }
12365 
12367  for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
12368  threads << new AbsDevHistogramThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ), center, low, high );
12369 
12370  double mh = 0, l0 = low;
12371  SzVector H0;
12372 
12373  for ( size_type n = 0, n2 = count >> 1, step = 0, it = 0;; ++it )
12374  {
12375  SzVector H;
12376  if ( it == 0 && step )
12377  H = H0;
12378  else
12379  {
12380  if ( threads.Length() > 1 )
12381  {
12382  int i = 0;
12383  for ( AbsDevHistogramThread& thread : threads )
12384  thread.Start( ThreadPriority::DefaultMax, useAffinity ? i++ : -1 );
12385  for ( AbsDevHistogramThread& thread : threads )
12386  thread.Wait();
12387  }
12388  else
12389  threads[0].Run();
12390 
12391  H = threads[0].H;
12392  for ( size_type i = 1; i < threads.Length(); ++i )
12393  H += threads[i].H;
12394  if ( it == 0 )
12395  if ( (count & 1) == 0 )
12396  H0 = H;
12397  }
12398 
12399  for ( int i = 0; ; n += H[i++] )
12400  if ( n + H[i] > n2 )
12401  {
12402  double range = high - low;
12403  high = (range * (i + 1))/(__PCL_MEDIAN_HISTOGRAM_LENGTH - 1) + low;
12404  low = (range * i)/(__PCL_MEDIAN_HISTOGRAM_LENGTH - 1) + low;
12405  if ( high - low < eps )
12406  {
12407  if ( count & 1 )
12408  {
12409  threads.Destroy();
12410  m_status += N;
12411  return low;
12412  }
12413  if ( step )
12414  {
12415  threads.Destroy();
12416  m_status += N;
12417  return (low + mh)/2;
12418  }
12419  mh = low;
12420  low = l0;
12421  n = 0;
12422  --n2;
12423  ++step;
12424  it = 0;
12425  }
12426  break;
12427  }
12428  }
12429  }
12430 
12452  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1,
12453  int maxProcessors = 0 ) const
12454  {
12455  Rect r = rect;
12456  if ( !ParseSelection( r, firstChannel, lastChannel ) )
12457  return 0;
12458 
12459  size_type N = size_type( r.Width() )*size_type( r.Height() )*(1 + lastChannel - firstChannel);
12460  if ( m_status.IsInitializationEnabled() )
12461  m_status.Initialize( "Computing two-sided median absolute deviation", N );
12462 
12463  if ( N <= __PCL_MEDIAN_HISTOGRAM_OVERHEAD )
12464  {
12465  TwoSidedAbsDevSmpThread S( *this, center, r, firstChannel, lastChannel, 0, r.Height() );
12466  S.Run();
12467  m_status += N;
12468  return { pcl::Median( S.values.Begin(), S.p ),
12469  pcl::Median( S.q, S.values.End() ) };
12470  }
12471 
12472  Array<size_type> L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors, __PCL_MEDIAN_HISTOGRAM_OVERHEAD/*overheadLimitPx*/ );
12473  bool useAffinity = m_parallel && Thread::IsRootThread();
12474 
12475  double minLow = 0, minHigh = 0, maxLow = 0, maxHigh = 0;
12476  size_type nLow = 0, nHigh = 0;
12477  {
12479  for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
12480  threads << new TwoSidedExtremeAbsDevThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ), center );
12481 
12482  if ( threads.Length() > 1 )
12483  {
12484  int n = 0;
12485  for ( TwoSidedExtremeAbsDevThread& thread : threads )
12486  thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 );
12487  for ( TwoSidedExtremeAbsDevThread& thread : threads )
12488  thread.Wait();
12489  }
12490  else
12491  threads[0].Run();
12492 
12493  for ( size_type i = 0; i < threads.Length(); ++i )
12494  if ( threads[i].nLow > 0 )
12495  {
12496  minLow = threads[i].minAbsDevLow;
12497  maxLow = threads[i].maxAbsDevLow;
12498  nLow = threads[i].nLow;
12499  while ( ++i < threads.Length() )
12500  if ( threads[i].nLow > 0 )
12501  {
12502  if ( threads[i].minAbsDevLow < minLow )
12503  minLow = threads[i].minAbsDevLow;
12504  if ( threads[i].maxAbsDevLow > maxLow )
12505  maxLow = threads[i].maxAbsDevLow;
12506  nLow += threads[i].nLow;
12507  }
12508  break;
12509  }
12510 
12511  for ( size_type i = 0; i < threads.Length(); ++i )
12512  if ( threads[i].nHigh > 0 )
12513  {
12514  minHigh = threads[i].minAbsDevHigh;
12515  maxHigh = threads[i].maxAbsDevHigh;
12516  nHigh = threads[i].nHigh;
12517  while ( ++i < threads.Length() )
12518  if ( threads[i].nHigh > 0 )
12519  {
12520  if ( threads[i].minAbsDevHigh < minHigh )
12521  minHigh = threads[i].minAbsDevHigh;
12522  if ( threads[i].maxAbsDevHigh > maxHigh )
12523  maxHigh = threads[i].maxAbsDevHigh;
12524  nHigh += threads[i].nHigh;
12525  }
12526  break;
12527  }
12528 
12529  threads.Destroy();
12530  }
12531 
12532  int side;
12533  double sideLow, sideHigh;
12535  for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
12536  threads << new TwoSidedAbsDevHistogramThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ), center, side, sideLow, sideHigh );
12537 
12538  const double eps = 2*std::numeric_limits<double>::epsilon();
12539  double mad[ 2 ];
12540  for ( side = 0; side < 2; ++side )
12541  {
12542  size_type n = side ? nHigh : nLow;
12543  if ( n < 2 )
12544  {
12545  mad[side] = 0;
12546  continue;
12547  }
12548 
12549  sideLow = side ? minHigh : minLow;
12550  sideHigh = side ? maxHigh : maxLow;
12551  if ( sideHigh - sideLow < eps )
12552  {
12553  mad[side] = 0;
12554  continue;
12555  }
12556 
12557  double mh = 0, h0 = sideHigh;
12558  SzVector H0;
12559 
12560  for ( size_type count = 0, n2 = n >> 1, step = 0, it = 0;; ++it )
12561  {
12562  SzVector H;
12563  if ( it == 0 && step )
12564  H = H0;
12565  else
12566  {
12567  if ( threads.Length() > 1 )
12568  {
12569  int i = 0;
12570  for ( TwoSidedAbsDevHistogramThread& thread : threads )
12571  thread.Start( ThreadPriority::DefaultMax, useAffinity ? i++ : -1 );
12572  for ( TwoSidedAbsDevHistogramThread& thread : threads )
12573  thread.Wait();
12574  }
12575  else
12576  threads[0].Run();
12577 
12578  H = threads[0].H;
12579  for ( size_type i = 1; i < threads.Length(); ++i )
12580  H += threads[i].H;
12581  if ( it == 0 )
12582  if ( (n & 1) == 0 )
12583  H0 = H;
12584  }
12585 
12586  for ( int i = 0; ; count += H[i++] )
12587  if ( count + H[i] > n2 )
12588  {
12589  double range = sideHigh - sideLow;
12590  sideHigh = (range * (i + 1))/(__PCL_MEDIAN_HISTOGRAM_LENGTH - 1) + sideLow;
12591  sideLow = (range * i)/(__PCL_MEDIAN_HISTOGRAM_LENGTH - 1) + sideLow;
12592  if ( sideHigh - sideLow < eps )
12593  {
12594  if ( n & 1 )
12595  {
12596  mad[side] = sideLow;
12597  goto __madNextSide;
12598  }
12599  if ( step )
12600  {
12601  mad[side] = (sideLow + mh)/2;
12602  goto __madNextSide;
12603  }
12604  mh = sideLow;
12605  sideLow = 0;
12606  sideHigh = h0;
12607  count = 0;
12608  --n2;
12609  ++step;
12610  it = 0;
12611  }
12612  break;
12613  }
12614  }
12615 
12616 __madNextSide:
12617 ;
12618  }
12619 
12620  threads.Destroy();
12621  m_status += N;
12622  return { mad[0], mad[1] };
12623  }
12624 
12669  double BiweightMidvariance( double center, double sigma, int k = 9, bool reducedLength = false,
12670  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1,
12671  int maxProcessors = 0 ) const
12672  {
12673  Rect r = rect;
12674  if ( !ParseSelection( r, firstChannel, lastChannel ) )
12675  return 0;
12676 
12677  size_type N = size_type( r.Width() )*size_type( r.Height() )*(1 + lastChannel - firstChannel);
12678  double kd = k * sigma;
12679  if ( kd < 0 || 1 + kd == 1 )
12680  {
12681  m_status += N;
12682  return 0;
12683  }
12684 
12685  if ( m_status.IsInitializationEnabled() )
12686  m_status.Initialize( "Computing biweight midvariance", N );
12687 
12688  Array<size_type> L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors );
12689  bool useAffinity = m_parallel && Thread::IsRootThread();
12691  for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
12692  threads.Add( new BWMVThread( *this, center, kd, r, firstChannel, lastChannel, n, n + int( L[i] ) ) );
12693  if ( threads.Length() > 1 )
12694  {
12695  int n = 0;
12696  for ( BWMVThread& thread : threads )
12697  thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 );
12698  for ( BWMVThread& thread : threads )
12699  thread.Wait();
12700  }
12701  else
12702  threads[0].Run();
12703 
12704  double num = 0, den = 0;
12705  size_type n = 0, nr = 0;
12706  for ( const BWMVThread& thread : threads )
12707  {
12708  num += thread.num;
12709  den += thread.den;
12710  n += thread.n;
12711  nr += thread.nr;
12712  }
12713 
12714  threads.Destroy();
12715 
12716  m_status += N;
12717  den *= den;
12718  return (n >= 2 && 1 + den != 1) ? (reducedLength ? nr : n)*num/den : 0.0;
12719  }
12720 
12749  TwoSidedEstimate TwoSidedBiweightMidvariance( double center, const TwoSidedEstimate& sigma, int k = 9, bool reducedLength = false,
12750  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1,
12751  int maxProcessors = 0 ) const
12752  {
12753  Rect r = rect;
12754  if ( !ParseSelection( r, firstChannel, lastChannel ) )
12755  return 0;
12756 
12757  size_type N = size_type( r.Width() )*size_type( r.Height() )*(1 + lastChannel - firstChannel);
12758  double kd0 = k * sigma.low;
12759  double kd1 = k * sigma.high;
12760  if ( kd0 < 0 || 1 + kd0 == 1 || kd1 < 0 || 1 + kd1 == 1 )
12761  {
12762  m_status += N;
12763  return 0;
12764  }
12765 
12766  if ( m_status.IsInitializationEnabled() )
12767  m_status.Initialize( "Computing two-sided biweight midvariance", N );
12768 
12769  Array<size_type> L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors );
12770  bool useAffinity = m_parallel && Thread::IsRootThread();
12772  for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
12773  threads.Add( new TwoSidedBWMVThread( *this, center, kd0, kd1, r, firstChannel, lastChannel, n, n + int( L[i] ) ) );
12774  if ( threads.Length() > 1 )
12775  {
12776  int n = 0;
12777  for ( TwoSidedBWMVThread& thread : threads )
12778  thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 );
12779  for ( TwoSidedBWMVThread& thread : threads )
12780  thread.Wait();
12781  }
12782  else
12783  threads[0].Run();
12784 
12785  double num0 = 0, den0 = 0, num1 = 0, den1 = 0;
12786  size_type n0 = 0, n1 = 0, nr0 = 0, nr1 = 0;
12787  for ( const TwoSidedBWMVThread& thread : threads )
12788  {
12789  num0 += thread.num0;
12790  den0 += thread.den0;
12791  num1 += thread.num1;
12792  den1 += thread.den1;
12793  n0 += thread.n0;
12794  n1 += thread.n1;
12795  nr0 += thread.nr0;
12796  nr1 += thread.nr1;
12797  }
12798 
12799  threads.Destroy();
12800 
12801  m_status += N;
12802 
12803  den0 *= den0;
12804  den1 *= den1;
12805  return { (n0 >= 2 && 1 + den0 != 1) ? (reducedLength ? nr0 : n0)*num0/den0 : 0.0,
12806  (n1 >= 2 && 1 + den1 != 1) ? (reducedLength ? nr1 : n1)*num1/den1 : 0.0 };
12807  }
12808 
12852  double BendMidvariance( double center, double beta = 0.2,
12853  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1,
12854  int maxProcessors = 0 ) const
12855  {
12856  Rect r = rect;
12857  if ( !ParseSelection( r, firstChannel, lastChannel ) )
12858  return 0;
12859 
12860  size_type N = size_type( r.Width() )*size_type( r.Height() )*(1 + lastChannel - firstChannel);
12861  if ( m_status.IsInitializationEnabled() )
12862  m_status.Initialize( "Computing percentage bend midvariance", N );
12863 
12864  Array<size_type> L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors );
12865  bool useAffinity = m_parallel && Thread::IsRootThread();
12867  for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
12868  threads.Add( new DSmpThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ) ) );
12869  if ( threads.Length() > 1 )
12870  {
12871  int n = 0;
12872  for ( DSmpThread& thread : threads )
12873  thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 );
12874  for ( DSmpThread& thread : threads )
12875  thread.Wait();
12876  }
12877  else
12878  threads[0].Run();
12879 
12880  Array<double> values;
12881  for ( DSmpThread& thread : threads )
12882  if ( !thread.values.IsEmpty() )
12883  {
12884  values.Add( thread.values.Begin(), thread.values.At( thread.n ) );
12885  thread.values.Clear();
12886  }
12887 
12888  threads.Destroy();
12889 
12890  double pbmv = pcl::BendMidvariance( values.Begin(), values.End(), center, beta );
12891  m_status += N;
12892  return pbmv;
12893  }
12894 
12938  double Sn( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1, int maxProcessors = 0 ) const
12939  {
12940  Rect r = rect;
12941  if ( !ParseSelection( r, firstChannel, lastChannel ) )
12942  return 0;
12943 
12944  size_type N = size_type( r.Width() )*size_type( r.Height() )*(1 + lastChannel - firstChannel);
12945  if ( m_status.IsInitializationEnabled() )
12946  m_status.Initialize( "Computing Sn scale estimate", N );
12947 
12948  Array<size_type> L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors );
12949  bool useAffinity = m_parallel && Thread::IsRootThread();
12951  for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
12952  threads.Add( new DSmpThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ) ) );
12953  if ( threads.Length() > 1 )
12954  {
12955  int n = 0;
12956  for ( DSmpThread& thread : threads )
12957  thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 );
12958  for ( DSmpThread& thread : threads )
12959  thread.Wait();
12960  }
12961  else
12962  threads[0].Run();
12963 
12964  Array<double> values;
12965  for ( DSmpThread& thread : threads )
12966  if ( !thread.values.IsEmpty() )
12967  {
12968  values.Add( thread.values.Begin(), thread.values.At( thread.n ) );
12969  thread.values.Clear();
12970  }
12971 
12972  threads.Destroy();
12973 
12974  double sn = pcl::Sn( values.Begin(), values.End() );
12975  m_status += N;
12976  return sn;
12977  }
12978 
13021  double Qn( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1, int maxProcessors = 0 ) const
13022  {
13023  Rect r = rect;
13024  if ( !ParseSelection( r, firstChannel, lastChannel ) )
13025  return 0;
13026 
13027  size_type N = size_type( r.Width() )*size_type( r.Height() )*(1 + lastChannel - firstChannel);
13028  if ( m_status.IsInitializationEnabled() )
13029  m_status.Initialize( "Computing Qn scale estimate", N );
13030 
13031  Array<size_type> L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors );
13032  bool useAffinity = m_parallel && Thread::IsRootThread();
13034  for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
13035  threads.Add( new DSmpThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ) ) );
13036  if ( threads.Length() > 1 )
13037  {
13038  int n = 0;
13039  for ( DSmpThread& thread : threads )
13040  thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 );
13041  for ( DSmpThread& thread : threads )
13042  thread.Wait();
13043  }
13044  else
13045  threads[0].Run();
13046 
13047  Array<double> values;
13048  for ( DSmpThread& thread : threads )
13049  if ( !thread.values.IsEmpty() )
13050  {
13051  values.Add( thread.values.Begin(), thread.values.At( thread.n ) );
13052  thread.values.Clear();
13053  }
13054 
13055  threads.Destroy();
13056 
13057  double qn = pcl::Qn( values.Begin(), values.End() );
13058  m_status += N;
13059  return qn;
13060  }
13061 
13099  double Norm( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1,
13100  int maxProcessors = 0 ) const
13101  {
13102  Rect r = rect;
13103  if ( !ParseSelection( r, firstChannel, lastChannel ) )
13104  return 0;
13105 
13106  size_type N = size_type( r.Width() )*size_type( r.Height() )*(1 + lastChannel - firstChannel);
13107  if ( m_status.IsInitializationEnabled() )
13108  m_status.Initialize( "Computing norm", N );
13109 
13110  Array<size_type> L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors );
13111  bool useAffinity = m_parallel && Thread::IsRootThread();
13112  ReferenceArray<SumThread> threads;
13113  for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
13114  threads.Add( new SumThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ) ) );
13115  if ( threads.Length() > 1 )
13116  {
13117  int n = 0;
13118  for ( SumThread& thread : threads )
13119  thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 );
13120  for ( SumThread& thread : threads )
13121  thread.Wait();
13122  }
13123  else
13124  threads[0].Run();
13125 
13126  double s = 0;
13127  double e = 0;
13128  for ( const SumThread& thread : threads )
13129  {
13130  double y = thread.s - e;
13131  double t = s + y;
13132  e = (t - s) - y;
13133  s = t;
13134  }
13135 
13136  threads.Destroy();
13137  m_status += N;
13138  return (1 + s != 1) ? s : 0.0; // don't return insignificant nonzero values
13139  }
13140 
13171  double Modulus( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1,
13172  int maxProcessors = 0 ) const
13173  {
13174  Rect r = rect;
13175  if ( !ParseSelection( r, firstChannel, lastChannel ) )
13176  return 0;
13177 
13178  size_type N = size_type( r.Width() )*size_type( r.Height() )*(1 + lastChannel - firstChannel);
13179  if ( m_status.IsInitializationEnabled() )
13180  m_status.Initialize( "Computing modulus", N );
13181 
13182  Array<size_type> L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors );
13183  bool useAffinity = m_parallel && Thread::IsRootThread();
13185  for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
13186  threads.Add( new SumAbsThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ) ) );
13187  if ( threads.Length() > 1 )
13188  {
13189  int n = 0;
13190  for ( SumAbsThread& thread : threads )
13191  thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 );
13192  for ( SumAbsThread& thread : threads )
13193  thread.Wait();
13194  }
13195  else
13196  threads[0].Run();
13197 
13198  double s = 0;
13199  double e = 0;
13200  for ( const SumAbsThread& thread : threads )
13201  {
13202  double y = thread.s - e;
13203  double t = s + y;
13204  e = (t - s) - y;
13205  s = t;
13206  }
13207 
13208  threads.Destroy();
13209  m_status += N;
13210  return (1 + s != 1) ? s : 0.0; // don't return insignificant nonzero values
13211  }
13212 
13241  double SumOfSquares( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1,
13242  int maxProcessors = 0 ) const
13243  {
13244  Rect r = rect;
13245  if ( !ParseSelection( r, firstChannel, lastChannel ) )
13246  return 0;
13247 
13248  size_type N = size_type( r.Width() )*size_type( r.Height() )*(1 + lastChannel - firstChannel);
13249  if ( m_status.IsInitializationEnabled() )
13250  m_status.Initialize( "Computing sum of squares", N );
13251 
13252  Array<size_type> L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors );
13253  bool useAffinity = m_parallel && Thread::IsRootThread();
13255  for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
13256  threads.Add( new SumSqrThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ) ) );
13257  if ( threads.Length() > 1 )
13258  {
13259  int n = 0;
13260  for ( SumSqrThread& thread : threads )
13261  thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 );
13262  for ( SumSqrThread& thread : threads )
13263  thread.Wait();
13264  }
13265  else
13266  threads[0].Run();
13267 
13268  double s = 0;
13269  double e = 0;
13270  for ( const SumSqrThread& thread : threads )
13271  {
13272  double y = thread.s - e;
13273  double t = s + y;
13274  e = (t - s) - y;
13275  s = t;
13276  }
13277 
13278  threads.Destroy();
13279  m_status += N;
13280  return (1 + s != 1) ? s : 0.0; // don't return insignificant nonzero values
13281  }
13282 
13311  double MeanOfSquares( const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1,
13312  int maxProcessors = 0 ) const
13313  {
13314  Rect r = rect;
13315  if ( !ParseSelection( r, firstChannel, lastChannel ) )
13316  return 0;
13317 
13318  size_type N = size_type( r.Width() )*size_type( r.Height() )*(1 + lastChannel - firstChannel);
13319  if ( m_status.IsInitializationEnabled() )
13320  m_status.Initialize( "Computing mean of squares", N );
13321 
13322  Array<size_type> L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors );
13323  bool useAffinity = m_parallel && Thread::IsRootThread();
13325  for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
13326  threads.Add( new SumSqrThread( *this, r, firstChannel, lastChannel, n, n + int( L[i] ) ) );
13327  if ( threads.Length() > 1 )
13328  {
13329  int n = 0;
13330  for ( SumSqrThread& thread : threads )
13331  thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 );
13332  for ( SumSqrThread& thread : threads )
13333  thread.Wait();
13334  }
13335  else
13336  threads[0].Run();
13337 
13338  double s = 0;
13339  double e = 0;
13340  size_type n = 0;
13341  for ( const SumSqrThread& thread : threads )
13342  {
13343  double y = thread.s - e;
13344  double t = s + y;
13345  e = (t - s) - y;
13346  s = t;
13347  n += thread.n;
13348  }
13349 
13350  threads.Destroy();
13351 
13352  m_status += N;
13353 
13354  if ( n < 1 )
13355  return 0;
13356  return s/n;
13357  }
13358 
13396  Vector Norms( int maxDegree = 2, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1,
13397  int maxProcessors = 0 ) const
13398  {
13399  PCL_PRECONDITION( maxDegree > 0 )
13400  maxDegree = pcl::Max( 1, maxDegree );
13401 
13402  Rect r = rect;
13403  if ( !ParseSelection( r, firstChannel, lastChannel ) )
13404  return 0;
13405 
13406  size_type N = size_type( r.Width() )*size_type( r.Height() )*(1 + lastChannel - firstChannel);
13407  if ( m_status.IsInitializationEnabled() )
13408  m_status.Initialize( "Computing norms", N );
13409 
13410  Array<size_type> L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors );
13411  bool useAffinity = m_parallel && Thread::IsRootThread();
13413  for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
13414  threads.Add( new NormThread( *this, maxDegree, r, firstChannel, lastChannel, n, n + int( L[i] ) ) );
13415  if ( threads.Length() > 1 )
13416  {
13417  int n = 0;
13418  for ( NormThread& thread : threads )
13419  thread.Start( ThreadPriority::DefaultMax, useAffinity ? n++ : -1 );
13420  for ( NormThread& thread : threads )
13421  thread.Wait();
13422  }
13423  else
13424  threads[0].Run();
13425 
13426  Vector R( 0.0, maxDegree );
13427  Vector e( 0.0, maxDegree );
13428  for ( const NormThread& thread : threads )
13429  for ( int i = 0; i < maxDegree; ++i )
13430  {
13431  double y = thread.R[i] - e[i];
13432  double t = R[i] + y;
13433  e[i] = (t - R[i]) - y;
13434  R[i] = t;
13435  }
13436  for ( int i = 0; i < maxDegree; ++i )
13437  if ( 1 + R[i] == 1 ) // don't return insignificant nonzero values
13438  R[i] = 0;
13439 
13440  threads.Destroy();
13441  m_status += N;
13442  return R;
13443  }
13444 
13457  uint64 Hash64( int channel = -1, uint64 seed = 0 ) const noexcept
13458  {
13459  if ( !ParseChannel( channel ) )
13460  return 0;
13461  return pcl::Hash64( m_channelData( channel ), ChannelSize(), seed );
13462  }
13463 
13476  uint32 Hash32( int channel = -1, uint32 seed = 0 ) const noexcept
13477  {
13478  if ( !ParseChannel( channel ) )
13479  return 0;
13480  return pcl::Hash32( m_channelData( channel ), ChannelSize(), seed );
13481  }
13482 
13487  uint64 Hash( int channel = -1, uint64 seed = 0 ) const noexcept
13488  {
13489  return Hash64( channel, seed );
13490  }
13491 
13492  // -------------------------------------------------------------------------
13493 
13511  GenericImage& Write( File& file, const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) const
13512  {
13513  Rect r = rect;
13514  if ( !ParseSelection( r, firstChannel, lastChannel ) )
13515  return const_cast<GenericImage&>( *this );
13516 
13517  size_type N = size_type( r.Width() )*size_type( r.Height() );
13518  int numberOfChannels = 1 + lastChannel - firstChannel;
13519 
13520  if ( m_status.IsInitializationEnabled() )
13521  m_status.Initialize( "Writing to raw-storage image stream", N*numberOfChannels );
13522 
13523  file.WriteUI32( r.Width() );
13524  file.WriteUI32( r.Height() );
13525  file.WriteUI32( numberOfChannels );
13526  file.WriteUI32( (firstChannel == 0 && lastChannel >= 2) ? m_colorSpace : ColorSpace::Gray );
13527 
13528  if ( r == Bounds() )
13529  {
13530  for ( int i = firstChannel; i <= lastChannel; ++i, m_status += N )
13531  file.Write( (const void*)m_pixelData[i], fsize_type( ChannelSize() ) );
13532  }
13533  else
13534  {
13535  for ( int i = firstChannel, w = r.Width(), h = r.Height(); i <= lastChannel; ++i )
13536  {
13537  const sample* p = PixelAddress( r.LeftTop(), i );
13538  for ( int j = 0; j < h; ++j, p += m_width, m_status += w )
13539  file.Write( (const void*)p, w*BytesPerSample() );
13540  }
13541  }
13542 
13543  return const_cast<GenericImage&>( *this );
13544  }
13545 
13556  GenericImage& Write( const String& filePath,
13557  const Rect& rect = Rect( 0 ), int firstChannel = -1, int lastChannel = -1 ) const
13558  {
13559  File file = File::CreateFileForWriting( filePath );
13560  return Write( file, rect, firstChannel, lastChannel );
13561  }
13562 
13600  {
13601  int width, height, numberOfChannels, colorSpace;
13602  file.ReadUI32( width );
13603  file.ReadUI32( height );
13604  file.ReadUI32( numberOfChannels );
13605  file.ReadUI32( colorSpace );
13606 
13607  AllocateData( width, height, numberOfChannels, color_space( colorSpace ) );
13608 
13609  ResetSelections();
13610 
13611  size_type N = NumberOfPixels();
13612  if ( N > 0 )
13613  {
13614  if ( m_status.IsInitializationEnabled() )
13615  m_status.Initialize( "Reading from raw-storage image stream", N*m_numberOfChannels );
13616  for ( int i = 0; i < m_numberOfChannels; ++i, m_status += N )
13617  file.Read( (void*)m_pixelData[i], fsize_type( ChannelSize() ) );
13618  }
13619 
13620  return *this;
13621  }
13622 
13634  GenericImage& Read( const String& filePath )
13635  {
13636  File file = File::OpenFileForReading( filePath );
13637  return Read( file );
13638  }
13639 
13640  // -------------------------------------------------------------------------
13641 
13672  template <typename T>
13673  GenericImage& CropBy( int left, int top, int right, int bottom, const GenericVector<T>& fillValues )
13674  {
13675  if ( left == 0 && top == 0 && right == 0 && bottom == 0 )
13676  return *this;
13677 
13678  if ( m_width+left+right <= 0 || m_height+top+bottom <= 0 )
13679  {
13680  FreeData();
13681  return *this;
13682  }
13683 
13684  Rect r( -left, -top, m_width+right, m_height+bottom );
13685  int width = r.Width();
13686  int height = r.Height();
13687 
13688  if ( !Intersects( r ) )
13689  return AllocateData( width, height, m_numberOfChannels, m_colorSpace ).Fill( fillValues );
13690 
13691  size_type N = size_type( width )*size_type( height );
13692 
13693  EnsureUnique();
13694 
13695  if ( m_status.IsInitializationEnabled() )
13696  m_status.Initialize( String().Format( "Crop margins: %+d, %+d, %+d, %+d",
13697  left, top, right, bottom ), N*m_numberOfChannels );
13698 
13699  sample** newData = nullptr;
13700 
13701  try
13702  {
13703  newData = m_allocator.AllocateChannelSlots( m_numberOfChannels );
13704 
13705  for ( int c = 0; c < m_numberOfChannels; ++c, m_status += N )
13706  {
13707  sample* __restrict__ f = newData[c] = m_allocator.AllocatePixels( N );
13708  sample v = (c < fillValues.Length()) ? P::ToSample( fillValues[c] ) : P::MinSampleValue();
13709 
13710  for ( int i = r.y0, j; i < r.y1; )
13711  {
13712  for ( ; i < 0; ++i )
13713  {
13714  PCL_IVDEP
13715  for ( int j = 0; j < width; ++j )
13716  *f++ = v;
13717  }
13718 
13719  PCL_IVDEP
13720  for ( j = r.x0; j < 0; ++j )
13721  *f++ = v;
13722 
13723  for ( const sample* f0 = PixelAddress( j, i, c ); j < r.x1; )
13724  {
13725  *f++ = *f0++;
13726  if ( ++j == m_width )
13727  {
13728  PCL_IVDEP
13729  for ( ; j < r.x1; ++j )
13730  *f++ = v;
13731  }
13732  }
13733 
13734  if ( ++i == m_height )
13735  for ( ; i < r.y1; ++i )
13736  {
13737  PCL_IVDEP
13738  for ( int j = 0; j < width; ++j )
13739  *f++ = v;
13740  }
13741  }
13742  }
13743 
13744  try
13745  {
13746  for ( int c = 0; c < m_numberOfChannels; ++c )
13747  {
13748  m_allocator.Deallocate( m_pixelData[c] );
13749  m_pixelData[c] = newData[c];
13750  }
13751  m_allocator.Deallocate( newData );
13752  newData = nullptr;
13753 
13754  m_allocator.SetSharedGeometry( m_width = width, m_height = height, m_numberOfChannels );
13755 
13756  ResetPoint();
13757  ResetSelection();
13758  return *this;
13759  }
13760  catch ( ... )
13761  {
13762  m_data->Deallocate();
13763  ResetSelections();
13764  throw;
13765  }
13766  }
13767  catch ( ... )
13768  {
13769  if ( newData != nullptr )
13770  {
13771  for ( int i = 0; i < m_numberOfChannels; ++i )
13772  if ( newData[i] != nullptr )
13773  m_allocator.Deallocate( newData[i] ), newData[i] = nullptr;
13774  m_allocator.Deallocate( newData );
13775  }
13776  throw;
13777  }
13778  }
13779 
13791  GenericImage& CropBy( int left, int top, int right, int bottom )
13792  {
13793  return CropBy( left, top, right, bottom, sample_vector() );
13794  }
13795 
13806  template <typename T>
13807  GenericImage& CropTo( const Rect& rect, const GenericVector<T>& fillValues )
13808  {
13809  Rect r = rect.Ordered();
13810  return CropBy( -r.x0, -r.y0, r.x1 - m_width, r.y1 - m_height, fillValues );
13811  }
13812 
13824  GenericImage& CropTo( const Rect& rect )
13825  {
13826  return CropTo( rect, sample_vector() );
13827  }
13828 
13839  template <typename T>
13840  GenericImage& CropTo( int x0, int y0, int x1, int y1, const GenericVector<T>& fillValues )
13841  {
13842  return CropTo( Rect( x0, y0, x1, y1 ), fillValues );
13843  }
13844 
13856  GenericImage& CropTo( int x0, int y0, int x1, int y1 )
13857  {
13858  return CropTo( x0, y0, x1, y1, sample_vector() );
13859  }
13860 
13871  template <typename T>
13872  GenericImage& Crop( const GenericVector<T>& fillValues )
13873  {
13874  return CropTo( m_rectangle, fillValues );
13875  }
13876 
13889  {
13890  return Crop( sample_vector() );
13891  }
13892 
13914  template <typename T>
13915  GenericImage& ShiftBy( int dx, int dy, const GenericVector<T>& fillValues )
13916  {
13917  return CropBy( dx, dy, -dx, -dy, fillValues );
13918  }
13919 
13931  GenericImage& ShiftBy( int dx, int dy )
13932  {
13933  return ShiftBy( dx, dy, sample_vector() );
13934  }
13935 
13942  template <typename T>
13943  GenericImage& ShiftTo( int x, int y, const GenericVector<T>& fillValues )
13944  {
13945  return ShiftBy( x, y, fillValues );
13946  }
13947 
13959  GenericImage& ShiftTo( int x, int y )
13960  {
13961  return ShiftTo( x, y, sample_vector() );
13962  }
13963 
13974  template <typename T>
13975  GenericImage& ShiftTo( const Point& p, const GenericVector<T>& fillValues )
13976  {
13977  return ShiftBy( p.x, p.y, fillValues );
13978  }
13979 
13992  {
13993  return ShiftTo( p, sample_vector() );
13994  }
13995 
14000  template <typename T>
14001  GenericImage& ShiftToCenter( int width, int height, const GenericVector<T>& fillValues )
14002  {
14003  int dx2 = (width - m_width) >> 1;
14004  int dy2 = (height - m_height) >> 1;
14005  return CropBy( dx2, dy2, width-m_width-dx2, height-m_height-dy2, fillValues );
14006  }
14007 
14020  GenericImage& ShiftToCenter( int width, int height )
14021  {
14022  return ShiftToCenter( width, height, sample_vector() );
14023  }
14024 
14029  template <typename T>
14030  GenericImage& ShiftToTopLeft( int width, int height, const GenericVector<T>& fillValues )
14031  {
14032  int dx = width - m_width;
14033  int dy = height - m_height;
14034  return CropBy( 0, 0, dx, dy, fillValues );
14035  }
14036 
14049  GenericImage& ShiftToTopLeft( int width, int height )
14050  {
14051  return ShiftToTopLeft( width, height, sample_vector() );
14052  }
14053 
14058  template <typename T>
14059  GenericImage& ShiftToTopRight( int width, int height, const GenericVector<T>& fillValues )
14060  {
14061  int dx = width - m_width;
14062  int dy = height - m_height;
14063  return CropBy( dx, 0, 0, dy, fillValues );
14064  }
14065 
14078  GenericImage& ShiftToTopRight( int width, int height )
14079  {
14080  return ShiftToTopRight( width, height, sample_vector() );
14081  }
14082 
14087  template <typename T>
14088  GenericImage& ShiftToBottomLeft( int width, int height, const GenericVector<T>& fillValues )
14089  {
14090  int dx = width - m_width;
14091  int dy = height - m_height;
14092  return CropBy( 0, dy, dx, 0, fillValues );
14093  }
14094 
14107  GenericImage& ShiftToBottomLeft( int width, int height )
14108  {
14109  return ShiftToBottomLeft( width, height, sample_vector() );
14110  }
14111 
14116  template <typename T>
14117  GenericImage& ShiftToBottomRight( int width, int height, const GenericVector<T>& fillValues )
14118  {
14119  int dx = width - m_width;
14120  int dy = height - m_height;
14121  return CropBy( dx, dy, 0, 0, fillValues );
14122  }
14123 
14136  GenericImage& ShiftToBottomRight( int width, int height )
14137  {
14138  return ShiftToBottomRight( width, height, sample_vector() );
14139  }
14140 
14151  template <typename T>
14152  GenericImage& Shift( const GenericVector<T>& fillValues )
14153  {
14154  return ShiftTo( m_point, fillValues );
14155  }
14156 
14169  {
14170  return Shift( sample_vector() );
14171  }
14172 
14173  // -------------------------------------------------------------------------
14174 
14208  GenericImage& SetColorSpace( color_space colorSpace, int maxProcessors = 0 )
14209  {
14210  size_type N = NumberOfPixels();
14211  if ( N == 0 )
14212  return *this;
14213 
14214  if ( colorSpace == m_colorSpace )
14215  {
14216  m_status += N;
14217  return *this;
14218  }
14219 
14220  EnsureUnique();
14221 
14222  if ( m_status.IsInitializationEnabled() )
14223  m_status.Initialize( "In-place color space conversion: "
14224  + ColorSpace::Name( m_colorSpace )
14225  + " -> "
14226  + ColorSpace::Name( colorSpace ), N );
14227 
14228  int n = m_numberOfChannels;
14229 
14230  if ( m_colorSpace == ColorSpace::Gray )
14231  {
14232  sample** oldData = m_pixelData;
14233  sample** newData = nullptr;
14234 
14235  try
14236  {
14237  // Make room for the G and B channels.
14238  newData = m_allocator.AllocateChannelSlots( 2+n );
14239 
14240  // Put the nominal gray channel into the R slot
14241  newData[0] = oldData[0];
14242 
14243  // Allocate and copy the G and B channels
14244  newData[1] = m_allocator.AllocatePixels( N );
14245  ::memcpy( newData[1], oldData[0], ChannelSize() );
14246  newData[2] = m_allocator.AllocatePixels( N );
14247  ::memcpy( newData[2], oldData[0], ChannelSize() );
14248 
14249  // Put existing alpha channels in their corresponding slots
14250  for ( int i = 1; i < n; ++i )
14251  newData[i+2] = oldData[i];
14252 
14253  try
14254  {
14255  m_pixelData = newData;
14256  newData = nullptr;
14257  m_numberOfChannels += 2;
14258  m_colorSpace = ColorSpace::RGB;
14259  m_data->UpdateSharedImage();
14260 
14261  ResetChannelRange();
14262 
14263  m_allocator.Deallocate( oldData );
14264  }
14265  catch ( ... )
14266  {
14267  m_data->Deallocate();
14268  ResetSelections();
14269  throw;
14270  }
14271  }
14272  catch ( ... )
14273  {
14274  if ( newData != nullptr )
14275  {
14276  newData[0] = nullptr;
14277  if ( newData[1] != nullptr )
14278  m_allocator.Deallocate( newData[1] ), newData[1] = nullptr;
14279  if ( newData[2] != nullptr )
14280  m_allocator.Deallocate( newData[2] ), newData[2] = nullptr;
14281  m_allocator.Deallocate( newData );
14282  throw;
14283  }
14284  }
14285 
14286  if ( colorSpace == ColorSpace::RGB )
14287  {
14288  m_status += N;
14289  return *this;
14290  }
14291 
14292  n += 2;
14293  }
14294 
14295  Array<size_type> L = Thread::OptimalThreadLoads( N,
14296  16u/*overheadLimit*/,
14297  m_parallel ? ((maxProcessors > 0) ? maxProcessors : m_maxProcessors) : 1 );
14298  ThreadData data( *this, N );
14300  for ( size_type i = 0, n = 0; i < L.Length(); n += L[i++] )
14301  threads.Add( new ColorSpaceConversionThread( *this, data, colorSpace, n, n + L[i] ) );
14302  RunThreads( threads, data );
14303  threads.Destroy();
14304 
14305  m_status = data.status;
14306 
14307  if ( colorSpace == ColorSpace::Gray )
14308  {
14309  sample** oldData = m_pixelData;
14310  sample** newData = nullptr;
14311 
14312  try
14313  {
14314  newData = m_allocator.AllocateChannelSlots( n-2 );
14315  newData[0] = oldData[0];
14316  for ( int i = 3; i < n; ++i )
14317  newData[i-2] = oldData[i];
14318 
14319  m_pixelData = newData;
14320  newData = nullptr;
14321  m_numberOfChannels -= 2;
14322  m_colorSpace = ColorSpace::Gray;
14323  m_data->UpdateSharedImage();
14324 
14325  ResetChannelRange();
14326 
14327  m_allocator.Deallocate( oldData[1] );
14328  m_allocator.Deallocate( oldData[2] );
14329  m_allocator.Deallocate( oldData );
14330  }
14331  catch ( ... )
14332  {
14333  m_data->Deallocate();
14334  ResetSelections();
14335  if ( newData != nullptr )
14336  m_allocator.Deallocate( newData );
14337  throw;
14338  }
14339  }
14340  else
14341  {
14342  m_allocator.SetSharedColor( m_colorSpace = colorSpace, m_RGBWS );
14343  }
14344 
14345  return *this;
14346  }
14347 
14348  // -------------------------------------------------------------------------
14349 
14415  template <class P1>
14416  void GetLuminance( GenericImage<P1>& Y, const Rect& rect = Rect( 0 ), int maxProcessors = 0 ) const
14417  {
14418  Rect r = rect;
14419  if ( !ParseRect( r ) )
14420  {
14421  Y.FreeData();
14422  return;
14423  }
14424 
14425  Y.AllocateData( r );
14426  if ( !Y.IsShared() )
14427  Y.SetRGBWorkingSpace( m_RGBWS );
14428 
14429  size_type N = Y.NumberOfPixels();
14430 
14431  if ( m_colorSpace == ColorSpace::Gray || m_colorSpace == ColorSpace::CIEXYZ )
14432  {
14433  if ( m_status.IsInitializationEnabled() )
14434  m_status.Initialize( "Transferring pixel data", N );
14435 
14436  typename GenericImage<P1>::sample* __restrict__ g = *Y;
14437  int cY = (m_colorSpace == ColorSpace::Gray) ? 0 : 1;
14438  if ( r == Bounds() )
14439  {
14440  const sample* __restrict__ f = m_pixelData[cY];
14441  P1::Copy( g, f, N );
14442  }
14443  else
14444  {
14445  const sample* __restrict__ f = PixelAddress( r.LeftTop(), cY );
14446  for ( int i = 0; i < Y.Height(); ++i, f += m_width, g += Y.Width() )
14447  P1::Copy( g, f, Y.Width() );
14448  }
14449 
14450  m_status += N;
14451  }
14452  else
14453  {
14454  if ( m_status.IsInitializationEnabled() )
14455  m_status.Initialize( "Computing CIE Y component", N );
14456 
14457  Array<size_type> L = OptimalThreadRows( Y.Height(), Y.Width(), maxProcessors );
14458  ThreadData data( *this, N );
14460  for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
14461  threads.Add( new GetLuminanceThread<P1>( Y, *this, data, r, n, n + int( L[i] ) ) );
14462  RunThreads( threads, data );
14463  threads.Destroy();
14464 
14465  m_status = data.status;
14466  }
14467  }
14468 
14478  void GetLuminance( ImageVariant& Y, const Rect& rect = Rect( 0 ), int maxProcessors = 0 ) const;
14479  // Implemented in pcl/ImageVariant.h
14480 
14547  template <class P1>
14548  void GetLightness( GenericImage<P1>& L, const Rect& rect = Rect( 0 ), int maxProcessors = 0 ) const
14549  {
14550  Rect r = rect;
14551  if ( !ParseRect( r ) )
14552  {
14553  L.FreeData();
14554  return;
14555  }
14556 
14557  L.AllocateData( r );
14558  if ( !L.IsShared() )
14559  L.SetRGBWorkingSpace( m_RGBWS );
14560 
14561  size_type N = L.NumberOfPixels();
14562 
14563  if ( m_colorSpace == ColorSpace::Gray || m_colorSpace == ColorSpace::CIELab || m_colorSpace == ColorSpace::CIELch )
14564  {
14565  if ( m_status.IsInitializationEnabled() )
14566  m_status.Initialize( "Transferring pixel data", N );
14567 
14568  typename GenericImage<P1>::sample* g = *L;
14569  if ( r == Bounds() )
14570  {
14571  const sample* __restrict__ f = *m_pixelData;
14572  P1::Copy( g, f, N );
14573  }
14574  else
14575  {
14576  const sample* __restrict__ f = PixelAddress( r.LeftTop() );
14577  for ( int i = 0; i < L.Height(); ++i, f += m_width, g += L.Width() )
14578  P1::Copy( g, f, L.Width() );
14579  }
14580 
14581  m_status += N;
14582  }
14583  else
14584  {
14585  if ( m_status.IsInitializationEnabled() )
14586  m_status.Initialize( "Computing CIE L* component", N );
14587 
14588  Array<size_type> R = OptimalThreadRows( L.Height(), L.Width(), maxProcessors );
14589  ThreadData data( *this, N );
14591  for ( int i = 0, n = 0; i < int( R.Length() ); n += int( R[i++] ) )
14592  threads.Add( new GetLightnessThread<P1>( L, *this, data, r, n, n + int( R[i] ) ) );
14593  RunThreads( threads, data );
14594  threads.Destroy();
14595 
14596  m_status = data.status;
14597  }
14598  }
14599 
14609  void GetLightness( ImageVariant& L, const Rect& rect = Rect( 0 ), int maxProcessors = 0 ) const;
14610  // Implemented in pcl/ImageVariant.h
14611 
14670  template <class P1>
14671  void GetIntensity( GenericImage<P1>& I, const Rect& rect = Rect( 0 ), int maxProcessors = 0 ) const
14672  {
14673  Rect r = rect;
14674  if ( !ParseRect( r ) )
14675  {
14676  I.FreeData();
14677  return;
14678  }
14679 
14680  I.AllocateData( r );
14681  if ( !I.IsShared() )
14682  I.SetRGBWorkingSpace( m_RGBWS );
14683 
14684  size_type N = I.NumberOfPixels();
14685 
14686  if ( m_colorSpace == ColorSpace::Gray || m_colorSpace == ColorSpace::HSI )
14687  {
14688  if ( m_status.IsInitializationEnabled() )
14689  m_status.Initialize( "Transferring pixel data", N );
14690 
14691  typename GenericImage<P1>::sample* __restrict__ g = *I;
14692  int cI = (m_colorSpace == ColorSpace::Gray) ? 0 : 2;
14693  if ( r == Bounds() )
14694  {
14695  const sample* __restrict__ f = m_pixelData[cI];
14696  P1::Copy( g, f, N );
14697  }
14698  else
14699  {
14700  const sample* __restrict__ f = PixelAddress( r.LeftTop(), cI );
14701  for ( int i = 0; i < I.Height(); ++i, f += m_width, g += I.Width() )
14702  P1::Copy( g, f, I.Width() );
14703  }
14704 
14705  m_status += N;
14706  }
14707  else
14708  {
14709  if ( m_status.IsInitializationEnabled() )
14710  m_status.Initialize( "Computing intensity component", N );
14711 
14712  Array<size_type> L = OptimalThreadRows( I.Height(), I.Width(), maxProcessors );
14713  ThreadData data( *this, N );
14715  for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
14716  threads.Add( new GetIntensityThread<P1>( I, *this, data, r, n, n + int( L[i] ) ) );
14717  RunThreads( threads, data );
14718  threads.Destroy();
14719 
14720  m_status = data.status;
14721  }
14722  }
14723 
14733  void GetIntensity( ImageVariant& I, const Rect& rect = Rect( 0 ), int maxProcessors = 0 ) const;
14734  // Implemented in pcl/ImageVariant.h
14735 
14736  // -------------------------------------------------------------------------
14737 
14796  template <class P1>
14798  const Point& point = Point( int_max ), const Rect& rect = Rect( 0 ),
14799  int maxProcessors = 0 )
14800  {
14801  Rect r = rect;
14802  if ( !Y.ParseRect( r ) )
14803  return *this;
14804 
14805  Point p = point;
14806  if ( p.x == int_max || p.y == int_max )
14807  p = m_point;
14808 
14809  if ( p.x < 0 )
14810  {
14811  if ( (r.x0 -= p.x) >= r.x1 )
14812  return *this;
14813  p.x = 0;
14814  }
14815  else if ( p.x >= m_width )
14816  return *this;
14817 
14818  if ( p.y < 0 )
14819  {
14820  if ( (r.y0 -= p.y) >= r.y1 )
14821  return *this;
14822  p.y = 0;
14823  }
14824  else if ( p.y >= m_height )
14825  return *this;
14826 
14827  r.ResizeTo( pcl::Min( m_width - p.x, r.Width() ),
14828  pcl::Min( m_height - p.y, r.Height() ) );
14829 
14830  size_type N = size_type( r.Width() )*size_type( r.Height() );
14831 
14832  EnsureUnique();
14833 
14834  if ( m_colorSpace == ColorSpace::Gray &&
14835  (Y.ColorSpace() == ColorSpace::Gray || Y.ColorSpace() == ColorSpace::CIEXYZ) )
14836  {
14837  if ( m_status.IsInitializationEnabled() )
14838  m_status.Initialize( "Transferring pixel data", N );
14839 
14840  int c0 = (Y.ColorSpace() == ColorSpace::Gray) ? 0 : 1;
14841  const typename GenericImage<P1>::sample* __restrict__ g = Y.PixelAddress( r.LeftTop(), c0 );
14842  sample* __restrict__ f = PixelAddress( p );
14843  for ( int i = 0, w = r.Width(), h = r.Height(); i < h; ++i, f += m_width, g += Y.Width(), m_status += w )
14844  P::Copy( f, g, w );
14845  }
14846  else
14847  {
14848  if ( m_status.IsInitializationEnabled() )
14849  m_status.Initialize( "Importing CIE Y component", N );
14850 
14851  Array<size_type> L = OptimalThreadRows( r.Height(), r.Width(), maxProcessors );
14852  ThreadData data( *this, N );
14854  for ( int i = 0, n = 0; i < int( L.Length() ); n += int( L[i++] ) )
14855  threads.Add( new SetLuminanceThread<P1>( *this, Y, data, p, r, n, n + int( L[i] ) ) );
14856  RunThreads( threads, data );
14857  threads.Destroy();
14858 
14859  m_status = data.status;
14860  }
14861 
14862  return *this;
14863  }
14864 
14874  GenericImage& SetLuminance( const ImageVariant& Y,
14875  const Point& point = Point( int_max ), const Rect& rect = Rect( 0 ),
14876  int maxProcessors = 0 );
14877  // Implemented in pcl/ImageVariant.h
14878 
14937  template <class P1>
14939  const Point& point = Point( int_max ), const Rect& rect = Rect( 0 ),
14940  int maxProcessors = 0 )
14941  {
14942  Rect r = rect;
14943  if ( !L.ParseRect( r ) )
14944  return *this;
14945 
14946  Point p = point;
14947  if ( p.x == int_max || p.y == int_max )
14948  p = m_point;
14949 
14950  if ( p.x < 0 )
14951  {
14952  if ( (r.x0 -= p.x) >= r.x1 )
14953  return *this;
14954  p.x = 0;
14955  }
14956  else if ( p.x >= m_width )
14957  return *this;
14958 
14959  if ( p.y < 0 )
14960  {
14961  if ( (r.y0 -= p.y) >= r.y1 )
14962  return *this;
14963  p.y = 0;
14964  }
14965  else if ( p.y >= m_height )
14966  return *this;
14967 
14968  r.ResizeTo( pcl::Min( m_width - p.x, r.Width() ),
14969  pcl::Min( m_height - p.y, r.Height() ) );
14970 
14971  size_type N = size_type( r.Width() )*size_type( r.Height() );
14972 
14973  EnsureUnique();
14974 
14975  if ( m_colorSpace == ColorSpace::Gray &&
14976  (L.ColorSpace() == ColorSpace::Gray ||
14977  L.ColorSpace() == ColorSpace::CIELab || L.ColorSpace() == ColorSpace::CIELch) )
14978  {
14979  if ( m_status.IsInitializationEnabled() )
14980  m_status.Initialize( "Transferring pixel data", N );
14981 
14982  const typename GenericImage<P1>::sample* __restrict__ g = L.PixelAddress( r.LeftTop() );
14983  sample* __restrict__ f = PixelAddress( p );
14984  for ( int i = 0, w = r.Width(), h = r.Height(); i < h; ++i, f += m_width, g += L.Width(), m_status += w )
14985  P::Copy( f, g, w );
14986  }
14987  else
14988  {
14989  if ( m_status.IsInitializationEnabled() )
14990  m_status.Initialize( "Importing CIE L* component", N );
14991 
14992  Array<size_type> R = OptimalThreadRows( r.Height(), r.Width(), maxProcessors );
14993  ThreadData data( *this, N );
14995  for ( int i = 0, n = 0; i < int( R.Length() ); n += int( R[i++] ) )
14996  threads.Add( new SetLightnessThread<P1>( *this, L, data, p, r, n, n + int( R[i] ) ) );
14997  RunThreads( threads, data );
14998  threads.Destroy();
14999 
15000  m_status = data.status;
15001  }
15002 
15003  return *this;
15004  }
15005 
15015  GenericImage& SetLightness( const ImageVariant& L,
15016  const Point& point = Point( int_max ), const Rect& rect = Rect( 0 ),
15017  int maxProcessors = 0 );
15018  // Implemented in pcl/ImageVariant.h
15019 
15059  const Rect& rect = Rect( 0 ), int channel = -1,
15060  Compression::Performance* perf = nullptr ) const
15061  {
15062  Rect r = rect;
15063  if ( !ParseSelection( r, channel ) )
15064  return Compression::subblock_list();
15065  if ( r == Bounds() )
15066  return compressor.Compress( m_channelData( channel ), ChannelSize(), perf );
15067  GenericImage<P> subimage( *this, r, channel, channel );
15068  return compressor.Compress( subimage[0], subimage.ChannelSize(), perf );
15069  }
15070 
15071  // -------------------------------------------------------------------------
15072 
15073 private:
15074 
15080  struct Data : public ReferenceCounter
15081  {
15089  sample** data = nullptr;
15090 
15101  pixel_allocator allocator;
15102 
15106  Geometry geometry;
15107 
15111  Color color;
15112 
15116  Data( GenericImage* image )
15117  {
15118  LinkWithClientImage( image );
15119  }
15120 
15124  Data( GenericImage* image, void* handle )
15125  : allocator( handle )
15126  {
15127  SynchronizeWithSharedImage();
15128  LinkWithClientImage( image );
15129  }
15130 
15134  Data( GenericImage* image, int width, int height, int numberOfChannels, int colorSpace )
15135  : allocator( width, height, numberOfChannels, colorSpace )
15136  {
15137  SynchronizeWithSharedImage();
15138  LinkWithClientImage( image );
15139  }
15140 
15141  void Attach( GenericImage* image )
15142  {
15143  ReferenceCounter::Attach();
15144  LinkWithClientImage( image );
15145  }
15146 
15147  ~Data()
15148  {
15149  if ( IsShared() )
15150  Reset();
15151  else
15152  Deallocate();
15153  }
15154 
15155  bool IsShared() const noexcept
15156  {
15157  return allocator.IsShared();
15158  }
15159 
15160  bool IsEmpty() const noexcept
15161  {
15162  return data == nullptr;
15163  }
15164 
15165  void Allocate( int width, int height, int numberOfChannels, color_space colorSpace )
15166  {
15167  if ( width <= 0 || height <= 0 || numberOfChannels <= 0 )
15168  {
15169  Deallocate();
15170  return;
15171  }
15172 
15173  if ( numberOfChannels < ColorSpace::NumberOfNominalChannels( colorSpace ) )
15174  throw Error( "GenericImage::Data::Allocate(): Insufficient number of channels" );
15175 
15176  if ( data != nullptr )
15177  {
15178  if ( size_type( width )*size_type( height ) == geometry.NumberOfPixels() )
15179  {
15180  if ( numberOfChannels != geometry.numberOfChannels )
15181  {
15182  sample** newData = nullptr;
15183  int m = pcl::Min( geometry.numberOfChannels, numberOfChannels );
15184 
15185  try
15186  {
15187  newData = allocator.AllocateChannelSlots( numberOfChannels );
15188  for ( int i = 0; i < m; ++i )
15189  newData[i] = data[i];
15190  for ( int i = m; i < numberOfChannels; ++i )
15191  newData[i] = allocator.AllocatePixels( width, height );
15192 
15193  try
15194  {
15195  for ( int i = m; i < geometry.numberOfChannels; ++i )
15196  if ( data[i] != nullptr )
15197  allocator.Deallocate( data[i] ), data[i] = nullptr;
15198  allocator.Deallocate( data );
15199  data = newData;
15200  newData = nullptr;
15201  }
15202  catch ( ... )
15203  {
15204  Deallocate();
15205  throw;
15206  }
15207  }
15208  catch ( ... )
15209  {
15210  if ( newData != nullptr )
15211  {
15212  for ( int i = m; i < numberOfChannels; ++i )
15213  if ( newData[i] != nullptr )
15214  allocator.Deallocate( newData[i] ), newData[i] = nullptr;
15215  allocator.Deallocate( newData );
15216  }
15217  throw;
15218  }
15219  }
15220  }
15221  else
15222  {
15223  sample** newData = nullptr;
15224 
15225  try
15226  {
15227  newData = allocator.AllocateChannelSlots( numberOfChannels );
15228  for ( int i = 0; i < numberOfChannels; ++i )
15229  newData[i] = allocator.AllocatePixels( width, height );
15230 
15231  try
15232  {
15233  for ( int i = 0; i < geometry.numberOfChannels; ++i )
15234  if ( data[i] != nullptr )
15235  allocator.Deallocate( data[i] ), data[i] = nullptr;
15236  allocator.Deallocate( data );
15237  data = newData;
15238  newData = nullptr;
15239  }
15240  catch ( ... )
15241  {
15242  Deallocate();
15243  throw;
15244  }
15245  }
15246  catch ( ... )
15247  {
15248  if ( newData != nullptr )
15249  {
15250  for ( int i = 0; i < numberOfChannels; ++i )
15251  if ( newData[i] != nullptr )
15252  allocator.Deallocate( newData[i] ), newData[i] = nullptr;
15253  allocator.Deallocate( newData );
15254  }
15255  throw;
15256  }
15257  }
15258  }
15259  else
15260  {
15261  try
15262  {
15263  data = allocator.AllocateChannelSlots( numberOfChannels );
15264  for ( int i = 0; i < numberOfChannels; ++i )
15265  data[i] = allocator.AllocatePixels( width, height );
15266  }
15267  catch ( ... )
15268  {
15269  if ( data != nullptr )
15270  {
15271  for ( int i = 0; i < numberOfChannels; ++i )
15272  if ( data[i] != nullptr )
15273  allocator.Deallocate( data[i] ), data[i] = nullptr;
15274  allocator.Deallocate( data );
15275  Reset();
15276  }
15277  throw;
15278  }
15279  }
15280 
15281  geometry.width = width;
15282  geometry.height = height;
15283  geometry.numberOfChannels = numberOfChannels;
15284 
15285  color.colorSpace = colorSpace;
15286 
15287  UpdateSharedImage();
15288  }
15289 
15290  void Import( sample** newData, int width, int height, int numberOfChannels, color_space colorSpace )
15291  {
15292  if ( newData != data )
15293  {
15294  Deallocate();
15295 
15296  if ( newData != nullptr && width > 0 && height > 0 && numberOfChannels > 0 )
15297  {
15298  if ( numberOfChannels < ColorSpace::NumberOfNominalChannels( colorSpace ) )
15299  throw Error( "GenericImage::Data::Import(): Insufficient number of channels" );
15300 
15301  data = newData;
15302 
15303  geometry.width = width;
15304  geometry.height = height;
15305  geometry.numberOfChannels = numberOfChannels;
15306 
15307  color.colorSpace = colorSpace;
15308 
15309  UpdateSharedImage();
15310  }
15311  }
15312  }
15313 
15314  sample** Release()
15315  {
15316  sample** oldData = data;
15317  Reset();
15318  UpdateSharedImage();
15319  return oldData;
15320  }
15321 
15322  void Deallocate()
15323  {
15324  if ( data != nullptr )
15325  {
15326  for ( int i = 0; i < geometry.numberOfChannels; ++i )
15327  if ( data[i] != nullptr )
15328  allocator.Deallocate( data[i] ), data[i] = nullptr;
15329  allocator.Deallocate( data );
15330  Reset();
15331  UpdateSharedImage();
15332  }
15333  }
15334 
15335  Data* Clone( GenericImage* image ) const
15336  {
15337  Data* clone = nullptr;
15338  try
15339  {
15340  clone = new Data;
15341 
15342  if ( !IsEmpty() )
15343  {
15344  clone->data = clone->allocator.AllocateChannelSlots( geometry.numberOfChannels );
15345  for ( int i = 0; i < geometry.numberOfChannels; ++i )
15346  {
15347  clone->data[i] = clone->allocator.AllocatePixels( geometry.width, geometry.height );
15348  P::Copy( clone->data[i], data[i], geometry.NumberOfPixels() );
15349  }
15350 
15351  clone->geometry.Assign( geometry );
15352  clone->color.Assign( color );
15353  }
15354 
15355  clone->LinkWithClientImage( image );
15356  return clone;
15357  }
15358  catch ( ... )
15359  {
15360  if ( clone != nullptr )
15361  {
15362  clone->Deallocate();
15363  delete clone;
15364  }
15365  throw;
15366  }
15367  }
15368 
15369  void Reset()
15370  {
15371  data = nullptr;
15372  geometry.Reset();
15373  color.Reset();
15374  }
15375 
15376  void UpdateSharedImage()
15377  {
15378  allocator.SetSharedData( data );
15379  allocator.SetSharedGeometry( geometry.width, geometry.height, geometry.numberOfChannels );
15380  allocator.SetSharedColor( color.colorSpace, color.RGBWS );
15381  }
15382 
15383  void SynchronizeWithSharedImage()
15384  {
15385  data = allocator.GetSharedData();
15386  allocator.GetSharedGeometry( geometry.width, geometry.height, geometry.numberOfChannels );
15387  allocator.GetSharedColor( color.colorSpace, color.RGBWS );
15388  }
15389 
15390  private:
15391 
15392  Data() = default;
15393 
15394  void LinkWithClientImage( GenericImage* image )
15395  {
15396  if ( image != nullptr )
15397  {
15398  image->m_geometry = &geometry;
15399  image->m_color = &color;
15400  }
15401  }
15402  };
15403 
15408  Data* m_data = nullptr;
15409 
15414  void DetachFromData()
15415  {
15416  if ( !m_data->Detach() )
15417  delete m_data;
15418  }
15419 
15420  // -------------------------------------------------------------------------
15421 
15422  class RectThreadBase : public Thread
15423  {
15424  public:
15425 
15426  RectThreadBase( const GenericImage& image, const Rect& rect, int ch1, int ch2, int firstRow, int endRow )
15427  : m_image( image )
15428  , m_rect( rect )
15429  , m_ch1( ch1 )
15430  , m_ch2( ch2 )
15431  , m_firstRow( firstRow )
15432  , m_endRow( endRow )
15433  {
15434  }
15435 
15436  virtual void Run() = 0;
15437 
15438  protected:
15439 
15440  const GenericImage& m_image;
15441  const Rect& m_rect;
15442  int m_ch1, m_ch2;
15443  int m_firstRow, m_endRow;
15444 
15445  bool HasSimpleSelection() const
15446  {
15447  return m_rect.Width() == m_image.Width()
15448  && !m_image.IsLowRangeClippingEnabled()
15449  && !m_image.IsHighRangeClippingEnabled();
15450  }
15451 
15452  template <class F> void Execute( F process ) noexcept
15453  {
15454  int w = m_rect.Width();
15455  int dw = m_image.Width() - w;
15456 
15457  if ( dw == 0 )
15458  {
15459  size_type n = size_type( m_endRow - m_firstRow ) * size_type( w );
15460 
15461  if ( m_image.IsLowRangeClippingEnabled() )
15462  {
15463  sample clipLow = P::ToSample( m_image.RangeClipLow() );
15464  if ( m_image.IsHighRangeClippingEnabled() )
15465  {
15466  sample clipHigh = P::ToSample( m_image.RangeClipHigh() );
15467  for ( int i = m_ch1; i <= m_ch2; ++i )
15468  {
15469  const sample* __restrict__ f = m_image.PixelAddress( 0, m_rect.y0+m_firstRow, i );
15470  PCL_IVDEP
15471  for ( size_type j = 0; j < n; ++j, ++f )
15472  if ( clipLow < *f )
15473  if ( *f < clipHigh )
15474  process( f );
15475  }
15476  }
15477  else
15478  {
15479  for ( int i = m_ch1; i <= m_ch2; ++i )
15480  {
15481  const sample* __restrict__ f = m_image.PixelAddress( 0, m_rect.y0+m_firstRow, i );
15482  PCL_IVDEP
15483  for ( size_type j = 0; j < n; ++j, ++f )
15484  if ( clipLow < *f )
15485  process( f );
15486  }
15487  }
15488  }
15489  else if ( m_image.IsHighRangeClippingEnabled() )
15490  {
15491  sample clipHigh = P::ToSample( m_image.RangeClipHigh() );
15492  for ( int i = m_ch1; i <= m_ch2; ++i )
15493  {
15494  const sample* __restrict__ f = m_image.PixelAddress( 0, m_rect.y0+m_firstRow, i );
15495  PCL_IVDEP
15496  for ( size_type j = 0; j < n; ++j, ++f )
15497  if ( *f < clipHigh )
15498  process( f );
15499  }
15500  }
15501  else
15502  {
15503  // Simple selection
15504  for ( int i = m_ch1; i <= m_ch2; ++i )
15505  {
15506  const sample* __restrict__ f = m_image.PixelAddress( 0, m_rect.y0+m_firstRow, i );
15507  PCL_IVDEP
15508  for ( size_type j = 0; j < n; ++j, ++f )
15509  process( f );
15510  }
15511  }
15512  }
15513  else
15514  {
15515  if ( m_image.IsLowRangeClippingEnabled() )
15516  {
15517  sample clipLow = P::ToSample( m_image.RangeClipLow() );
15518  if ( m_image.IsHighRangeClippingEnabled() )
15519  {
15520  sample clipHigh = P::ToSample( m_image.RangeClipHigh() );
15521  for ( int i = m_ch1; i <= m_ch2; ++i )
15522  {
15523  const sample* __restrict__ f = m_image.PixelAddress( m_rect.x0, m_rect.y0+m_firstRow, i );
15524  for ( int j = m_firstRow; j < m_endRow; ++j, f += dw )
15525  {
15526  PCL_IVDEP
15527  for ( int k = 0; k < w; ++k, ++f )
15528  if ( clipLow < *f )
15529  if ( *f < clipHigh )
15530  process( f );
15531  }
15532  }
15533  }
15534  else
15535  {
15536  for ( int i = m_ch1; i <= m_ch2; ++i )
15537  {
15538  const sample* __restrict__ f = m_image.PixelAddress( m_rect.x0, m_rect.y0+m_firstRow, i );
15539  for ( int j = m_firstRow; j < m_endRow; ++j, f += dw )
15540  {
15541  PCL_IVDEP
15542  for ( int k = 0; k < w; ++k, ++f )
15543  if ( clipLow < *f )
15544  process( f );
15545  }
15546  }
15547  }
15548  }
15549  else if ( m_image.IsHighRangeClippingEnabled() )
15550  {
15551  sample clipHigh = P::ToSample( m_image.RangeClipHigh() );
15552  for ( int i = m_ch1; i <= m_ch2; ++i )
15553  {
15554  const sample* __restrict__ f = m_image.PixelAddress( m_rect.x0, m_rect.y0+m_firstRow, i );
15555  for ( int j = m_firstRow; j < m_endRow; ++j, f += dw )
15556  {
15557  PCL_IVDEP
15558  for ( int k = 0; k < w; ++k, ++f )
15559  if ( *f < clipHigh )
15560  process( f );
15561  }
15562  }
15563  }
15564  else
15565  {
15566  for ( int i = m_ch1; i <= m_ch2; ++i )
15567  {
15568  const sample* __restrict__ f = m_image.PixelAddress( m_rect.x0, m_rect.y0+m_firstRow, i );
15569  for ( int j = m_firstRow; j < m_endRow; ++j, f += dw )
15570  {
15571  PCL_IVDEP
15572  for ( int k = 0; k < w; ++k, ++f )
15573  process( f );
15574  }
15575  }
15576  }
15577  }
15578  }
15579  };
15580 
15581  // -------------------------------------------------------------------------
15582 
15583  class CountThread : public RectThreadBase
15584  {
15585  public:
15586 
15587  size_type count = 0;
15588 
15589  CountThread( const GenericImage& image, const Rect& rect, int ch1, int ch2, int firstRow, int endRow )
15590  : RectThreadBase( image, rect, ch1, ch2, firstRow, endRow )
15591  {
15592  }
15593 
15594  void Run() final
15595  {
15596  /*
15597  * N.B. These threads are only used when range clipping is enabled.
15598  */
15599  int w = this->m_rect.Width();
15600  int dw = this->m_image.Width() - w;
15601 
15602  if ( this->m_image.IsLowRangeClippingEnabled() )
15603  {
15604  sample clipLow = P::ToSample( this->m_image.RangeClipLow() );
15605  if ( this->m_image.IsHighRangeClippingEnabled() )
15606  {
15607  sample clipHigh = P::ToSample( this->m_image.RangeClipHigh() );
15608  for ( int i = this->m_ch1; i <= this->m_ch2; ++i )
15609  {
15610  const sample* __restrict__ f = this->m_image.PixelAddress( this->m_rect.x0, this->m_rect.y0+this->m_firstRow, i );
15611  for ( int j = this->m_firstRow; j < this->m_endRow; ++j, f += dw )
15612  {
15613  PCL_IVDEP
15614  for ( int k = 0; k < w; ++k, ++f )
15615  if ( clipLow < *f )
15616  if ( *f < clipHigh )
15617  ++count;
15618  }
15619  }
15620  }
15621  else
15622  {
15623  for ( int i = this->m_ch1; i <= this->m_ch2; ++i )
15624  {
15625  const sample* __restrict__ f = this->m_image.PixelAddress( this->m_rect.x0, this->m_rect.y0+this->m_firstRow, i );
15626  for ( int j = this->m_firstRow; j < this->m_endRow; ++j, f += dw )
15627  {
15628  PCL_IVDEP
15629  for ( int k = 0; k < w; ++k, ++f )
15630  if ( clipLow < *f )
15631  ++count;
15632  }
15633  }
15634  }
15635  }
15636  else if ( this->m_image.IsHighRangeClippingEnabled() )
15637  {
15638  sample clipHigh = P::ToSample( this->m_image.RangeClipHigh() );
15639  for ( int i = this->m_ch1; i <= this->m_ch2; ++i )
15640  {
15641  const sample* __restrict__ f = this->m_image.PixelAddress( this->m_rect.x0, this->m_rect.y0+this->m_firstRow, i );
15642  for ( int j = this->m_firstRow; j < this->m_endRow; ++j, f += dw )
15643  {
15644  PCL_IVDEP
15645  for ( int k = 0; k < w; ++k, ++f )
15646  if ( *f < clipHigh )
15647  ++count;
15648  }
15649  }
15650  }
15651  else // ?! this should not happen
15652  count = size_type( 1 + this->m_ch2 - this->m_ch1 ) * size_type( w ) * size_type( this->m_rect.Height() );
15653  }
15654  };
15655 
15656  // -------------------------------------------------------------------------
15657 
15658  class MinThread : public RectThreadBase
15659  {
15660  public:
15661 
15662  sample min;
15663  size_type count;
15664 
15665  MinThread( const GenericImage& image, const Rect& rect, int ch1, int ch2, int firstRow, int endRow )
15666  : RectThreadBase( image, rect, ch1, ch2, firstRow, endRow )
15667  {
15668  }
15669 
15670  void Run() final
15671  {
15672  min = P::HighestSampleValue();
15673  count = 0;
15674  this->Execute( [=]( const sample* __restrict__ f )
15675  {
15676  if ( *f < min )
15677  min = *f;
15678  ++count;
15679  } );
15680  }
15681  };
15682 
15683  // -------------------------------------------------------------------------
15684 
15685  class MaxThread : public RectThreadBase
15686  {
15687  public:
15688 
15689  sample max;
15690  size_type count;
15691 
15692  MaxThread( const GenericImage& image, const Rect& rect, int ch1, int ch2, int firstRow, int endRow )
15693  : RectThreadBase( image, rect, ch1, ch2, firstRow, endRow )
15694  {
15695  }
15696 
15697  void Run() final
15698  {
15699  max = P::LowestSampleValue();
15700  count = 0;
15701  this->Execute( [=]( const sample* __restrict__ f )
15702  {
15703  if ( max < *f )
15704  max = *f;
15705  ++count;
15706  } );
15707  }
15708  };
15709 
15710  // -------------------------------------------------------------------------
15711 
15712  class MinMaxThread : public RectThreadBase
15713  {
15714  public:
15715 
15716  sample min;
15717  sample max;
15718  size_type count;
15719 
15720  MinMaxThread( const GenericImage& image, const Rect& rect, int ch1, int ch2, int firstRow, int endRow )
15721  : RectThreadBase( image, rect, ch1, ch2, firstRow, endRow )
15722  {
15723  }
15724 
15725  void Run() final
15726  {
15727  min = P::HighestSampleValue();
15728  max = P::LowestSampleValue();
15729  count = 0;
15730  this->Execute( [=]( const sample* __restrict__ f )
15731  {
15732  if ( *f < min )
15733  min = *f;
15734  if ( max < *f )
15735  max = *f;
15736  ++count;
15737  } );
15738  }
15739  };
15740 
15741  // -------------------------------------------------------------------------
15742 
15743  class ExtremePosThreadBase : public RectThreadBase
15744  {
15745  public:
15746 
15747  int cmin, cmax;
15748  Point pmin, pmax;
15749  size_type count;
15750 
15751  ExtremePosThreadBase( const GenericImage& image, const Rect& rect, int ch1, int ch2, int firstRow, int endRow )
15752  : RectThreadBase( image, rect, ch1, ch2, firstRow, endRow )
15753  {
15754  }
15755 
15756  void Run() final
15757  {
15758  count = 0;
15759  m_amin = m_amax = nullptr;
15760  DoExecute();
15761 
15762  if ( count > 0 )
15763  {
15764  if ( m_amin != 0 )
15765  for ( int i = this->m_ch1; i <= this->m_ch2; ++i )
15766  if ( m_amin >= this->m_image[i] && m_amin < (this->m_image[i] + this->m_image.NumberOfPixels()) )
15767  {
15768  cmin = i;
15769  pmin.x = (m_amin - this->m_image[i]) % this->m_image.Width();
15770  pmin.y = (m_amin - this->m_image[i]) / this->m_image.Width();
15771  break;
15772  }
15773  if ( m_amax != 0 )
15774  for ( int i = this->m_ch1; i <= this->m_ch2; ++i )
15775  if ( m_amax >= this->m_image[i] && m_amax < (this->m_image[i] + this->m_image.NumberOfPixels()) )
15776  {
15777  cmax = i;
15778  pmax.x = (m_amax - this->m_image[i]) % this->m_image.Width();
15779  pmax.y = (m_amax - this->m_image[i]) / this->m_image.Width();
15780  break;
15781  }
15782  }
15783  }
15784 
15785  protected:
15786 
15787  const sample* m_amin;
15788  const sample* m_amax;
15789 
15790  virtual void DoExecute() = 0;
15791  };
15792 
15793  // -------------------------------------------------------------------------
15794 
15795  class MinPosThread : public ExtremePosThreadBase
15796  {
15797  public:
15798 
15799  sample min;
15800 
15801  MinPosThread( const GenericImage& image, const Rect& rect, int ch1, int ch2, int firstRow, int endRow )
15802  : ExtremePosThreadBase( image, rect, ch1, ch2, firstRow, endRow )
15803  {
15804  }
15805 
15806  private:
15807 
15808  void DoExecute() override
15809  {
15810  min = P::HighestSampleValue();
15811  this->Execute( [=]( const sample* __restrict__ f )
15812  {
15813  if ( *f < min )
15814  min = *(this->m_amin = f);
15815  ++this->count;
15816  } );
15817  }
15818  };
15819 
15820  // -------------------------------------------------------------------------
15821 
15822  class MaxPosThread : public ExtremePosThreadBase
15823  {
15824  public:
15825 
15826  sample max;
15827 
15828  MaxPosThread( const GenericImage& image, const Rect& rect, int ch1, int ch2, int firstRow, int endRow )
15829  : ExtremePosThreadBase( image, rect, ch1, ch2, firstRow, endRow )
15830  {
15831  }
15832 
15833  private:
15834 
15835  void DoExecute() override
15836  {
15837  max = P::LowestSampleValue();
15838  this->Execute( [=]( const sample* __restrict__ f )
15839  {
15840  if ( max < *f )
15841  max = *(this->m_amax = f);
15842  ++this->count;
15843  } );
15844  }
15845  };
15846 
15847  // -------------------------------------------------------------------------
15848 
15849  class MinMaxPosThread : public ExtremePosThreadBase
15850  {
15851  public:
15852 
15853  sample min, max;
15854 
15855  MinMaxPosThread( const GenericImage& image, const Rect& rect, int ch1, int ch2, int firstRow, int endRow )
15856  : ExtremePosThreadBase( image, rect, ch1, ch2, firstRow, endRow )
15857  {
15858  }
15859 
15860  private:
15861 
15862  void DoExecute() override
15863  {
15864  min = P::HighestSampleValue();
15865  max = P::LowestSampleValue();
15866  this->Execute( [=]( const sample* __restrict__ f )
15867  {
15868  if ( *f < min )
15869  min = *(this->m_amin = f);
15870  if ( max < *f )
15871  max = *(this->m_amax = f);
15872  ++this->count;
15873  } );
15874  }
15875  };
15876 
15877  // -------------------------------------------------------------------------
15878 
15879  class SumThread : public RectThreadBase
15880  {
15881  public:
15882 
15883  double s;
15884  size_type n;
15885 
15886  SumThread( const GenericImage& image, const Rect& rect, int ch1, int ch2, int firstRow, int endRow )
15887  : RectThreadBase( image, rect, ch1, ch2, firstRow, endRow )
15888  {
15889  }
15890 
15891  void Run() final
15892  {
15893  s = e = 0;
15894  n = 0;
15895  DoExecute();
15896  }
15897 
15898  protected:
15899 
15900  double e;
15901 
15902  void SumStep( double x ) noexcept
15903  {
15904  double y = x - e;
15905  double t = s + y;
15906  e = (t - s) - y;
15907  s = t;
15908  ++n;
15909  }
15910 
15911  virtual void DoExecute()
15912  {
15913  this->Execute( [=]( const sample* __restrict__ f )
15914  {
15915  double v; P::FromSample( v, *f );
15916  SumStep( v );
15917  } );
15918  }
15919  };
15920 
15921  // -------------------------------------------------------------------------
15922 
15923  class SumSqrThread : public SumThread
15924  {
15925  public:
15926 
15927  SumSqrThread( const GenericImage& image, const Rect& rect, int ch1, int ch2, int firstRow, int endRow )
15928  : SumThread( image, rect, ch1, ch2, firstRow, endRow )
15929  {
15930  }
15931 
15932  private:
15933 
15934  void DoExecute() override
15935  {
15936  this->Execute( [=]( const sample* __restrict__ f )
15937  {
15938  double v; P::FromSample( v, *f );
15939  this->SumStep( v*v );
15940  } );
15941  }
15942  };
15943 
15944  // -------------------------------------------------------------------------
15945 
15946  class SumAbsThread : public SumThread
15947  {
15948  public:
15949 
15950  SumAbsThread( const GenericImage& image, const Rect& rect, int ch1, int ch2, int firstRow, int endRow )
15951  : SumThread( image, rect, ch1, ch2, firstRow, endRow )
15952  {
15953  }
15954 
15955  private:
15956 
15957  void DoExecute() override
15958  {
15959  this->Execute( [=]( const sample* __restrict__ f )
15960  {
15961  double v; P::FromSample( v, *f );
15962  this->SumStep( pcl::Abs( v ) );
15963  } );
15964  }
15965  };
15966 
15967  // -------------------------------------------------------------------------
15968 
15969  class NormThread : public RectThreadBase
15970  {
15971  public:
15972 
15973  Vector R;
15974  size_type n;
15975 
15976  NormThread( const GenericImage& image, int degree, const Rect& rect, int ch1, int ch2, int firstRow, int endRow )
15977  : RectThreadBase( image, rect, ch1, ch2, firstRow, endRow )
15978  , R( degree )
15979  , e( degree )
15980  {
15981  }
15982 
15983  void Run() final
15984  {
15985  R = e = 0.0;
15986  n = 0;
15987  this->Execute( [=]( const sample* __restrict__ f )
15988  {
15989  double v; P::FromSample( v, *f );
15990  for ( int i = 0;; )
15991  {
15992  NormStep( i, v );
15993  if ( ++i == R.Length() )
15994  break;
15995  v *= v;
15996  }
15997  ++n;
15998  } );
15999  }
16000 
16001  protected:
16002 
16003  Vector e;
16004 
16005  void NormStep( int i, double x ) noexcept
16006  {
16007  double y = x - e[i];
16008  double t = R[i] + y;
16009  e[i] = (t - R[i]) - y;
16010  R[i] = t;
16011  }
16012  };
16013 
16014  // -------------------------------------------------------------------------
16015 
16016  class HistogramThread : public RectThreadBase
16017  {
16018  public:
16019 
16020  SzVector H;
16021 
16022  HistogramThread( const GenericImage& image, const Rect& rect, int ch1, int ch2, int firstRow, int endRow,
16023  const double& low, const double& high )
16024  : RectThreadBase( image, rect, ch1, ch2, firstRow, endRow )
16025  , H( __PCL_MEDIAN_HISTOGRAM_LENGTH )
16026  , m_low( low )
16027  , m_high( high )
16028  {
16029  }
16030 
16031  void Run() final
16032  {
16033  H = size_type( 0 );
16034 
16035  if ( this->HasSimpleSelection() )
16036  {
16037  size_type n = size_type( this->m_endRow - this->m_firstRow ) * size_type( this->m_rect.Width() );
16038  for ( int i = this->m_ch1; i <= this->m_ch2; ++i )
16039  Build( H, this->m_image.PixelAddress( 0, this->m_rect.y0+this->m_firstRow, i ), n, m_low, m_high );
16040  }
16041  else
16042  {
16043  m_range = m_high - m_low;
16044  if ( 1 + m_range != 1 )
16045  {
16046  const double scale = (__PCL_MEDIAN_HISTOGRAM_LENGTH - 1)/m_range;
16047  this->Execute( [=]( const sample* __restrict__ f )
16048  {
16049  const int k = TruncInt( scale*(*f - m_low) );
16050  if ( k >= 0 && k < __PCL_MEDIAN_HISTOGRAM_LENGTH )
16051  ++H[k];
16052  } );
16053  }
16054  }
16055  }
16056 
16057  private:
16058 
16059  const double& m_low;
16060  const double& m_high;
16061  double m_range;
16062 
16063  template <typename T1>
16064  static void Build( SzVector& H, const T1* __restrict__ A, size_type N, double low, double high )
16065  {
16066  const double range = high - low;
16067  if ( 1 + range != 1 )
16068  {
16069  const double scale = (__PCL_MEDIAN_HISTOGRAM_LENGTH - 1)/range;
16070  PCL_IVDEP
16071  for ( size_type i = 0; i < N; ++i )
16072  {
16073  const int k = TruncInt( scale*(A[i] - low) );
16074  if ( k >= 0 && k < __PCL_MEDIAN_HISTOGRAM_LENGTH )
16075  ++H[k];
16076  }
16077  }
16078  }
16079 
16080 #ifdef __PCL_AVX2
16081 
16082  PCL_HOT_FUNCTION
16083  static void Build( SzVector& H, const float* __restrict__ A, size_type N, double low, double high )
16084  {
16085  const double range = high - low;
16086  if ( 1 + range == 1 )
16087  return;
16088 
16089  const float s = float( (__PCL_MEDIAN_HISTOGRAM_LENGTH - 1)/range );
16090  const float l = float( low );
16091  const __m256 S = _mm256_set1_ps( s );
16092  const __m256 L = _mm256_set1_ps( l );
16093  const size_type stepLength = 0x40000000u;
16094 
16095  for ( size_type p = 0; p < N; p += stepLength )
16096  {
16097  const float* __restrict__ V = A + p;
16098  const int n = int( pcl::Min( stepLength, N - p ) );
16099  const int n8 = n >> 3;
16100 
16101  if ( ((ptrdiff_t)V) & 31 )
16102  for ( int i = 0; i < n8; ++i )
16103  {
16104  __m256 v = _mm256_loadu_ps( (const float* __restrict__)(V + i*8) );
16105  __m256i K = _mm256_cvttps_epi32( _mm256_mul_ps( _mm256_sub_ps( v, L ), S ) );
16106  for ( int j = 0; j < 8; ++j )
16107  {
16108  const int k = ((const int* __restrict__)&K)[j];
16109  if ( k >= 0 && k < __PCL_MEDIAN_HISTOGRAM_LENGTH )
16110  ++H[k];
16111  }
16112  }
16113  else
16114  for ( int i = 0; i < n8; ++i )
16115  {
16116  __m256i K = _mm256_cvttps_epi32( _mm256_mul_ps( _mm256_sub_ps( ((const __m256* __restrict__)V)[i], L ), S ) );
16117  for ( int j = 0; j < 8; ++j )
16118  {
16119  const int k = ((const int* __restrict__)&K)[j];
16120  if ( k >= 0 && k < __PCL_MEDIAN_HISTOGRAM_LENGTH )
16121  ++H[k];
16122  }
16123  }
16124 
16125  for ( int i = n8 << 3; i < n; ++i )
16126  {
16127  const int k = TruncInt( s * (V[i] - l) );
16128  if ( k >= 0 && k < __PCL_MEDIAN_HISTOGRAM_LENGTH )
16129  ++H[k];
16130  }
16131  }
16132  }
16133 
16134  PCL_HOT_FUNCTION
16135  static void Build( SzVector& H, const double* __restrict__ A, size_type N, double low, double high )
16136  {
16137  const double range = high - low;
16138  if ( 1 + range == 1 )
16139  return;
16140 
16141  const double s = (__PCL_MEDIAN_HISTOGRAM_LENGTH - 1)/range;
16142  const __m256d S = _mm256_set1_pd( s );
16143  const __m256d L = _mm256_set1_pd( low );
16144  const size_type stepLength = 0x40000000u;
16145 
16146  for ( size_type p = 0; p < N; p += stepLength )
16147  {
16148  const double* __restrict__ V = A + p;
16149  const int n = int( pcl::Min( stepLength, N - p ) );
16150  const int n4 = n >> 2;
16151 
16152  if ( ((ptrdiff_t)V) & 31 )
16153  for ( int i = 0; i < n4; ++i )
16154  {
16155  __m256d v = _mm256_loadu_pd( (const double* __restrict__)(V + i*4) );
16156  __m128i K = _mm256_cvttpd_epi32( _mm256_mul_pd( _mm256_sub_pd( v, L ), S ) );
16157  for ( int j = 0; j < 4; ++j )
16158  {
16159  const int k = ((const int* __restrict__)&K)[j];
16160  if ( k >= 0 && k < __PCL_MEDIAN_HISTOGRAM_LENGTH )
16161  ++H[k];
16162  }
16163  }
16164  else
16165  for ( int i = 0; i < n4; ++i )
16166  {
16167  __m128i K = _mm256_cvttpd_epi32( _mm256_mul_pd( _mm256_sub_pd( ((const __m256d* __restrict__)V)[i], L ), S ) );
16168  for ( int j = 0; j < 4; ++j )
16169  {
16170  const int k = ((const int* __restrict__)&K)[j];
16171  if ( k >= 0 && k < __PCL_MEDIAN_HISTOGRAM_LENGTH )
16172  ++H[k];
16173  }
16174  }
16175 
16176  for ( int i = n4 << 2; i < n; ++i )
16177  {
16178  const int k = TruncInt( s * (V[i] - low) );
16179  if ( k >= 0 && k < __PCL_MEDIAN_HISTOGRAM_LENGTH )
16180  ++H[k];
16181  }
16182  }
16183  }
16184 
16185 #endif // __PCL_AVX2
16186  };
16187 
16188  // -------------------------------------------------------------------------
16189 
16190  class ExtremeAbsDevThread : public RectThreadBase
16191  {
16192  public:
16193 
16194  double minAbsDev, maxAbsDev;
16195  size_type count;
16196 
16197  ExtremeAbsDevThread( const GenericImage& image, const Rect& rect, int ch1, int ch2, int firstRow, int endRow, double center )
16198  : RectThreadBase( image, rect, ch1, ch2, firstRow, endRow )
16199  , m_center( center )
16200  {
16201  }
16202 
16203  void Run() final
16204  {
16205  minAbsDev = std::numeric_limits<double>::max();
16206  maxAbsDev = 0;
16207  count = 0;
16208  this->Execute( [=]( const sample* __restrict__ f )
16209  {
16210  double d; P::FromSample( d, *f );
16211  d = pcl::Abs( d - m_center );
16212  if ( d < minAbsDev )
16213  minAbsDev = d;
16214  if ( d > maxAbsDev )
16215  maxAbsDev = d;
16216  ++count;
16217  } );
16218  }
16219 
16220  private:
16221 
16222  double m_center;
16223  };
16224 
16225  // -------------------------------------------------------------------------
16226 
16227  class TwoSidedExtremeAbsDevThread : public RectThreadBase
16228  {
16229  public:
16230 
16231  double minAbsDevLow, minAbsDevHigh;
16232  double maxAbsDevLow, maxAbsDevHigh;
16233  size_type nLow, nHigh;
16234 
16235  TwoSidedExtremeAbsDevThread( const GenericImage& image, const Rect& rect, int ch1, int ch2, int firstRow, int endRow, double center )
16236  : RectThreadBase( image, rect, ch1, ch2, firstRow, endRow )
16237  , m_center( center )
16238  {
16239  }
16240 
16241  void Run() final
16242  {
16243  minAbsDevLow = minAbsDevHigh = std::numeric_limits<double>::max();
16244  maxAbsDevLow = maxAbsDevHigh = 0;
16245  nLow = nHigh = 0;
16246  this->Execute( [=]( const sample* __restrict__ f )
16247  {
16248  double x; P::FromSample( x, *f );
16249  if ( x <= m_center )
16250  {
16251  double d = m_center - x;
16252  if ( d < minAbsDevLow )
16253  minAbsDevLow = d;
16254  if ( d > maxAbsDevLow )
16255  maxAbsDevLow = d;
16256  ++nLow;
16257  }
16258  else
16259  {
16260  double d = x - m_center;
16261  if ( d < minAbsDevHigh )
16262  minAbsDevHigh = d;
16263  if ( d > maxAbsDevHigh )
16264  maxAbsDevHigh = d;
16265  ++nHigh;
16266  }
16267  } );
16268  }
16269 
16270  private:
16271 
16272  double m_center;
16273  };
16274 
16275  // -------------------------------------------------------------------------
16276 
16277  class AbsDevHistogramThread : public RectThreadBase
16278  {
16279  public:
16280 
16281  SzVector H;
16282 
16283  AbsDevHistogramThread( const GenericImage& image, const Rect& rect, int ch1, int ch2, int firstRow, int endRow,
16284  double center, const double& low, const double& high )
16285  : RectThreadBase( image, rect, ch1, ch2, firstRow, endRow )
16286  , H( __PCL_MEDIAN_HISTOGRAM_LENGTH )
16287  , m_center( center )
16288  , m_low( low )
16289  , m_high( high )
16290  {
16291  }
16292 
16293  void Run() final
16294  {
16295  H = size_type( 0 );
16296  m_range = m_high - m_low;
16297 // Workaround for clang compiler bug on macOS:
16298 // https://pixinsight.com/forum/index.php?threads/pi-always-crash-when-stf.15830/
16299 #ifdef __PCL_MACOSX
16300  if ( 1 + m_range != 1 )
16301 #endif
16302  this->Execute( [=]( const sample* __restrict__ f )
16303  {
16304  double d; P::FromSample( d, *f );
16305  d = pcl::Abs( d - m_center );
16306  if ( d >= m_low )
16307  if ( d <= m_high )
16308  ++H[TruncInt( (__PCL_MEDIAN_HISTOGRAM_LENGTH - 1) * (d - m_low)/m_range )];
16309  } );
16310  }
16311 
16312  private:
16313 
16314  double m_center;
16315  const double& m_low;
16316  const double& m_high;
16317  double m_range;
16318  };
16319 
16320  // -------------------------------------------------------------------------
16321 
16322  class TwoSidedAbsDevHistogramThread : public RectThreadBase
16323  {
16324  public:
16325 
16326  SzVector H;
16327 
16328  TwoSidedAbsDevHistogramThread( const GenericImage& image, const Rect& rect, int ch1, int ch2, int firstRow, int endRow,
16329  double center, const int& side, const double& low, const double& high )
16330  : RectThreadBase( image, rect, ch1, ch2, firstRow, endRow )
16331  , H( __PCL_MEDIAN_HISTOGRAM_LENGTH )
16332  , m_center( center )
16333  , m_side( side )
16334  , m_low( low )
16335  , m_high( high )
16336  {
16337  }
16338 
16339  void Run() final
16340  {
16341  H = size_type( 0 );
16342  m_range = m_high - m_low;
16343 // Workaround for clang compiler bug on macOS:
16344 // https://pixinsight.com/forum/index.php?threads/pi-always-crash-when-stf.15830/
16345 #ifdef __PCL_MACOSX
16346  if ( 1 + m_range != 1 )
16347 #endif
16348  this->Execute( [=]( const sample* __restrict__ f )
16349  {
16350  double x; P::FromSample( x, *f );
16351  if ( m_side > 0 == x > m_center )
16352  {
16353  double d = m_side ? x - m_center : m_center - x;
16354  if ( d >= m_low )
16355  if ( d <= m_high )
16356  ++H[TruncInt( (__PCL_MEDIAN_HISTOGRAM_LENGTH - 1) * (d - m_low)/m_range )];
16357  }
16358  } );
16359  }
16360 
16361  private:
16362 
16363  double m_center;
16364  const int& m_side;
16365  const double& m_low;
16366  const double& m_high;
16367  double m_range;
16368  };
16369 
16370  // -------------------------------------------------------------------------
16371 
16372  class VarThread : public RectThreadBase
16373  {
16374  public:
16375 
16376  double var, eps;
16377 
16378  VarThread( const GenericImage& image, double mean, const Rect& rect, int ch1, int ch2, int firstRow, int endRow )
16379  : RectThreadBase( image, rect, ch1, ch2, firstRow, endRow )
16380  , m_mean( mean )
16381  {
16382  }
16383 
16384  void Run() final
16385  {
16386  var = eps = 0;
16387  this->Execute( [=]( const sample* __restrict__ f )
16388  {
16389  double d; P::FromSample( d, *f );
16390  d -= m_mean;
16391  var += d*d;
16392  eps += d;
16393  } );
16394  }
16395 
16396  private:
16397 
16398  double m_mean;
16399  };
16400 
16401  // -------------------------------------------------------------------------
16402 
16403  class SumAbsDevThread : public SumThread
16404  {
16405  public:
16406 
16407  SumAbsDevThread( const GenericImage& image, double center, const Rect& rect, int ch1, int ch2, int firstRow, int endRow )
16408  : SumThread( image, rect, ch1, ch2, firstRow, endRow )
16409  , m_center( center )
16410  {
16411  }
16412 
16413  private:
16414 
16415  double m_center;
16416 
16417  void DoExecute() override
16418  {
16419  this->Execute( [=]( const sample* __restrict__ f )
16420  {
16421  double v; P::FromSample( v, *f );
16422  this->SumStep( pcl::Abs( v - m_center ) );
16423  } );
16424  }
16425  };
16426 
16427  // -------------------------------------------------------------------------
16428 
16429  class TwoSidedSumAbsDevThread : public RectThreadBase
16430  {
16431  public:
16432 
16433  double s0, s1;
16434  size_type n0, n1;
16435 
16436  TwoSidedSumAbsDevThread( const GenericImage& image, double center,
16437  const Rect& rect, int ch1, int ch2, int firstRow, int endRow )
16438  : RectThreadBase( image, rect, ch1, ch2, firstRow, endRow )
16439  , m_center( center )
16440  {
16441  }
16442 
16443  void Run() final
16444  {
16445  s0 = s1 = 0;
16446  n0 = n1 = 0;
16447  this->Execute( [=]( const sample* __restrict__ f )
16448  {
16449  double v; P::FromSample( v, *f );
16450  if ( v <= m_center )
16451  {
16452  s0 += m_center - v;
16453  ++n0;
16454  }
16455  else
16456  {
16457  s1 += v - m_center;
16458  ++n1;
16459  }
16460  } );
16461  }
16462 
16463  private:
16464 
16465  double m_center;
16466  };
16467 
16468  // -------------------------------------------------------------------------
16469 
16470  class BWMVThread : public RectThreadBase
16471  {
16472  public:
16473 
16474  double num, den;
16475  size_type n, nr;
16476 
16477  BWMVThread( const GenericImage& image, double center, double kd,
16478  const Rect& rect, int ch1, int ch2, int firstRow, int endRow )
16479  : RectThreadBase( image, rect, ch1, ch2, firstRow, endRow )
16480  , m_center( center )
16481  , m_kd( kd )
16482  {
16483  }
16484 
16485  void Run() final
16486  {
16487  num = den = 0;
16488  n = nr = 0;
16489  this->Execute( [=]( const sample* __restrict__ f )
16490  {
16491  ++n;
16492  double xc; P::FromSample( xc, *f ); xc -= m_center;
16493  double y = xc/m_kd;
16494  if ( pcl::Abs( y ) < 1 )
16495  {
16496  double y2 = y*y;
16497  double y21 = 1 - y2;
16498  num += xc*xc * y21*y21*y21*y21;
16499  den += y21 * (1 - 5*y2);
16500  ++nr;
16501  }
16502  } );
16503  }
16504 
16505  private:
16506 
16507  double m_center;
16508  double m_kd;
16509  };
16510 
16511  // -------------------------------------------------------------------------
16512 
16513  class TwoSidedBWMVThread : public RectThreadBase
16514  {
16515  public:
16516 
16517  double num0, den0;
16518  double num1, den1;
16519  size_type n0, n1, nr0, nr1;
16520 
16521  TwoSidedBWMVThread( const GenericImage& image, double center, double kd0, double kd1,
16522  const Rect& rect, int ch1, int ch2, int firstRow, int endRow )
16523  : RectThreadBase( image, rect, ch1, ch2, firstRow, endRow )
16524  , m_center( center )
16525  , m_kd0( kd0 )
16526  , m_kd1( kd1 )
16527  {
16528  }
16529 
16530  void Run() final
16531  {
16532  num0 = den0 = num1 = den1 = 0;
16533  n0 = n1 = nr0 = nr1 = 0;
16534  this->Execute( [=]( const sample* __restrict__ f )
16535  {
16536  double xc; P::FromSample( xc, *f ); xc -= m_center;
16537  bool low = xc <= 0;
16538  if ( low )
16539  ++n0;
16540  else
16541  ++n1;
16542 
16543  double y = xc/(low ? m_kd0 : m_kd1);
16544  if ( pcl::Abs( y ) < 1 )
16545  {
16546  double y2 = y*y;
16547  double y21 = 1 - y2;
16548  double num = xc*xc * y21*y21*y21*y21;
16549  double den = y21 * (1 - 5*y2);
16550  if ( low )
16551  {
16552  num0 += num;
16553  den0 += den;
16554  ++nr0;
16555  }
16556  else
16557  {
16558  num1 += num;
16559  den1 += den;
16560  ++nr1;
16561  }
16562  }
16563  } );
16564  }
16565 
16566  private:
16567 
16568  double m_center;
16569  double m_kd0;
16570  double m_kd1;
16571  };
16572 
16573  // -------------------------------------------------------------------------
16574 
16575  class SmpThread : public RectThreadBase
16576  {
16577  public:
16578 
16579  Array<sample> samples;
16580  size_type n;
16581 
16582  SmpThread( const GenericImage& image, const Rect& rect, int ch1, int ch2, int firstRow, int endRow )
16583  : RectThreadBase( image, rect, ch1, ch2, firstRow, endRow )
16584  {
16585  size_type N = size_type( this->m_rect.Width() )
16586  * size_type( this->m_endRow - this->m_firstRow )
16587  * (1 + this->m_ch2 - this->m_ch1);
16588  samples = Array<sample>( N );
16589  }
16590 
16591  void Run() final
16592  {
16593  n = 0;
16594  this->Execute( [=]( const sample* __restrict__ f )
16595  {
16596  samples[n++] = *f;
16597  } );
16598  }
16599  };
16600 
16601  // -------------------------------------------------------------------------
16602 
16603  class DSmpThread : public RectThreadBase
16604  {
16605  public:
16606 
16607  Array<double> values;
16608  size_type n;
16609 
16610  DSmpThread( const GenericImage& image, const Rect& rect, int ch1, int ch2, int firstRow, int endRow )
16611  : RectThreadBase( image, rect, ch1, ch2, firstRow, endRow )
16612  {
16613  size_type N = size_type( this->m_rect.Width() )
16614  * size_type( this->m_endRow - this->m_firstRow )
16615  * (1 + this->m_ch2 - this->m_ch1);
16616  values = Array<double>( N );
16617  }
16618 
16619  void Run() final
16620  {
16621  n = 0;
16622  DoExecute();
16623  }
16624 
16625  protected:
16626 
16627  virtual void DoExecute()
16628  {
16629  this->Execute( [=]( const sample* __restrict__ f )
16630  {
16631  P::FromSample( values[n++], *f );
16632  } );
16633  }
16634  };
16635 
16636  // -------------------------------------------------------------------------
16637 
16638  class AbsDevSmpThread : public DSmpThread
16639  {
16640  public:
16641 
16642  AbsDevSmpThread( const GenericImage& image, double center, const Rect& rect, int ch1, int ch2, int firstRow, int endRow )
16643  : DSmpThread( image, rect, ch1, ch2, firstRow, endRow )
16644  , m_center( center )
16645  {
16646  }
16647 
16648  private:
16649 
16650  double m_center;
16651 
16652  void DoExecute() override
16653  {
16654  this->Execute( [=]( const sample* __restrict__ f )
16655  {
16656  double d; P::FromSample( d, *f );
16657  this->values[this->n++] = pcl::Abs( d - m_center );
16658  } );
16659  }
16660  };
16661 
16662  // -------------------------------------------------------------------------
16663 
16664  class TwoSidedAbsDevSmpThread : public DSmpThread
16665  {
16666  public:
16667 
16669 
16670  TwoSidedAbsDevSmpThread( const GenericImage& image, double center, const Rect& rect, int ch1, int ch2, int firstRow, int endRow )
16671  : DSmpThread( image, rect, ch1, ch2, firstRow, endRow )
16672  , m_center( center )
16673  {
16674  }
16675 
16676  private:
16677 
16678  double m_center;
16679 
16680  void DoExecute() override
16681  {
16682  p = this->values.Begin();
16683  q = this->values.End();
16684  this->Execute( [=]( const sample* __restrict__ f )
16685  {
16686  double x; P::FromSample( x, *f );
16687  if ( x <= m_center )
16688  *p++ = m_center - x;
16689  else
16690  *--q = x - m_center;
16691  ++this->n;
16692  } );
16693  }
16694  };
16695 
16696  // -------------------------------------------------------------------------
16697 
16698  class ColorSpaceConversionThread : public Thread
16699  {
16700  public:
16701 
16702  ColorSpaceConversionThread( GenericImage& image, ThreadData& data,
16703  color_space toColorSpace, size_type begin, size_type end )
16704  : m_image( image )
16705  , m_data( data )
16706  , m_toColorSpace( toColorSpace )
16707  , m_begin( begin )
16708  , m_end( end )
16709  {
16710  }
16711 
16712  void Run() final
16713  {
16715 
16716  const RGBColorSystem& rgbws = m_image.RGBWorkingSpace();
16717 
16718  typename P::sample* f0 = m_image[0] + m_begin;
16719  typename P::sample* f1 = m_image[1] + m_begin;
16720  typename P::sample* f2 = m_image[2] + m_begin;
16721  typename P::sample* fN = m_image[0] + m_end;
16722 
16723  for ( ; f0 < fN; ++f0, ++f1, ++f2 )
16724  {
16725  RGBColorSystem::sample r0, r1, r2;
16726  P::FromSample( r0, *f0 );
16727  P::FromSample( r1, *f1 );
16728  P::FromSample( r2, *f2 );
16729 
16730  switch ( m_image.ColorSpace() )
16731  {
16732  case ColorSpace::RGB :
16733  switch ( m_toColorSpace )
16734  {
16735  case ColorSpace::Gray :
16736  rgbws.RGBToGray( r0, r0, r1, r2 );
16737  break;
16738  case ColorSpace::HSV :
16739  rgbws.RGBToHSV( r0, r1, r2, r0, r1, r2 );
16740  break;
16741  case ColorSpace::HSI :
16742  rgbws.RGBToHSI( r0, r1, r2, r0, r1, r2 );
16743  break;
16744  case ColorSpace::CIEXYZ :
16745  rgbws.RGBToCIEXYZ( r0, r1, r2, r0, r1, r2 );
16746  break;
16747  case ColorSpace::CIELab :
16748  rgbws.RGBToCIELab( r0, r1, r2, r0, r1, r2 );
16749  break;
16750  case ColorSpace::CIELch :
16751  rgbws.RGBToCIELch( r0, r1, r2, r0, r1, r2 );
16752  break;
16753  default:
16754  break;
16755  }
16756  break;
16757  case ColorSpace::HSV :
16758  rgbws.HSVToRGB( r0, r1, r2, r0, r1, r2 );
16759  switch ( m_toColorSpace )
16760  {
16761  case ColorSpace::Gray :
16762  rgbws.RGBToGray( r0, r0, r1, r2 );
16763  break;
16764  case ColorSpace::RGB :
16765  break;
16766  case ColorSpace::HSI :
16767  rgbws.RGBToHSI( r0, r1, r2, r0, r1, r2 );
16768  break;
16769  case ColorSpace::CIEXYZ :
16770  rgbws.RGBToCIEXYZ( r0, r1, r2, r0, r1, r2 );
16771  break;
16772  case ColorSpace::CIELab :
16773  rgbws.RGBToCIELab( r0, r1, r2, r0, r1, r2 );
16774  break;
16775  case ColorSpace::CIELch :
16776  rgbws.RGBToCIELch( r0, r1, r2, r0, r1, r2 );
16777  break;
16778  default:
16779  break;
16780  }
16781  break;
16782  case ColorSpace::HSI :
16783  rgbws.HSIToRGB( r0, r1, r2, r0, r1, r2 );
16784  switch ( m_toColorSpace )
16785  {
16786  case ColorSpace::Gray :
16787  rgbws.RGBToGray( r0, r0, r1, r2 );
16788  break;
16789  case ColorSpace::RGB :
16790  break;
16791  case ColorSpace::HSV :
16792  rgbws.RGBToHSV( r0, r1, r2, r0, r1, r2 );
16793  break;
16794  case ColorSpace::CIEXYZ :
16795  rgbws.RGBToCIEXYZ( r0, r1, r2, r0, r1, r2 );
16796  break;
16797  case ColorSpace::CIELab :
16798  rgbws.RGBToCIELab( r0, r1, r2, r0, r1, r2 );
16799  break;
16800  case ColorSpace::CIELch :
16801  rgbws.RGBToCIELch( r0, r1, r2, r0, r1, r2 );
16802  break;
16803  default:
16804  break;
16805  }
16806  break;
16807  case ColorSpace::CIEXYZ :
16808  switch ( m_toColorSpace )
16809  {
16810  case ColorSpace::Gray :
16811  rgbws.CIEXYZToRGB( r0, r1, r2, r0, r1, r2 );
16812  rgbws.RGBToGray( r0, r0, r1, r2 );
16813  break;
16814  case ColorSpace::RGB :
16815  rgbws.CIEXYZToRGB( r0, r1, r2, r0, r1, r2 );
16816  break;
16817  case ColorSpace::HSV :
16818  rgbws.CIEXYZToRGB( r0, r1, r2, r0, r1, r2 );
16819  rgbws.RGBToHSV( r0, r1, r2, r0, r1, r2 );
16820  break;
16821  case ColorSpace::HSI :
16822  rgbws.CIEXYZToRGB( r0, r1, r2, r0, r1, r2 );
16823  rgbws.RGBToHSI( r0, r1, r2, r0, r1, r2 );
16824  break;
16825  case ColorSpace::CIELab :
16826  rgbws.CIEXYZToCIELab( r0, r1, r2, r0, r1, r2 );
16827  break;
16828  case ColorSpace::CIELch :
16829  rgbws.CIEXYZToCIELab( r0, r1, r2, r0, r1, r2 );
16830  rgbws.CIELabToCIELch( r0, r1, r2, r0, r1, r2 );
16831  break;
16832  default:
16833  break;
16834  }
16835  break;
16836  case ColorSpace::CIELab :
16837  switch ( m_toColorSpace )
16838  {
16839  case ColorSpace::Gray :
16840  rgbws.CIELabToRGB( r0, r1, r2, r0, r1, r2 );
16841  rgbws.RGBToGray( r0, r0, r1, r2 );
16842  break;
16843  case ColorSpace::RGB :
16844  rgbws.CIELabToRGB( r0, r1, r2, r0, r1, r2 );
16845  break;
16846  case ColorSpace::HSV :
16847  rgbws.CIELabToRGB( r0, r1, r2, r0, r1, r2 );
16848  rgbws.RGBToHSV( r0, r1, r2, r0, r1, r2 );
16849  break;
16850  case ColorSpace::HSI :
16851  rgbws.CIELabToRGB( r0, r1, r2, r0, r1, r2 );
16852  rgbws.RGBToHSI( r0, r1, r2, r0, r1, r2 );
16853  break;
16854  case ColorSpace::CIEXYZ :
16855  rgbws.CIELabToCIEXYZ( r0, r1, r2, r0, r1, r2 );
16856  break;
16857  case ColorSpace::CIELch :
16858  rgbws.CIELabToCIELch( r0, r1, r2, r0, r1, r2 );
16859  break;
16860  default:
16861  break;
16862  }
16863  break;
16864  case ColorSpace::CIELch :
16865  switch ( m_toColorSpace )
16866  {
16867  case ColorSpace::Gray :
16868  rgbws.CIELchToRGB( r0, r1, r2, r0, r1, r2 );
16869  rgbws.RGBToGray( r0, r0, r1, r2 );
16870  break;
16871  case ColorSpace::RGB :
16872  rgbws.CIELchToRGB( r0, r1, r2, r0, r1, r2 );
16873  break;
16874  case ColorSpace::HSV :
16875  rgbws.CIELchToRGB( r0, r1, r2, r0, r1, r2 );
16876  rgbws.RGBToHSV( r0, r1, r2, r0, r1, r2 );
16877  break;
16878  case ColorSpace::HSI :
16879  rgbws.CIELchToRGB( r0, r1, r2, r0, r1, r2 );
16880  rgbws.RGBToHSI( r0, r1, r2, r0, r1, r2 );
16881  break;
16882  case ColorSpace::CIEXYZ :
16883  rgbws.CIELchToCIELab( r0, r1, r2, r0, r1, r2 );
16884  rgbws.CIELabToCIEXYZ( r0, r1, r2, r0, r1, r2 );
16885  break;
16886  case ColorSpace::CIELab :
16887  rgbws.CIELchToCIELab( r0, r1, r2, r0, r1, r2 );
16888  break;
16889  default:
16890  break;
16891  }
16892  break;
16893  default:
16894  break;
16895  }
16896 
16897  *f0 = P::ToSample( r0 );
16898 
16899  if ( m_toColorSpace != ColorSpace::Gray )
16900  {
16901  *f1 = P::ToSample( r1 );
16902  *f2 = P::ToSample( r2 );
16903  }
16904 
16905  UPDATE_THREAD_MONITOR( 65536 )
16906  }
16907  }
16908 
16909  private:
16910 
16911  GenericImage& m_image;
16912  ThreadData& m_data;
16913  color_space m_toColorSpace;
16914  size_type m_begin, m_end;
16915  };
16916 
16917  // -------------------------------------------------------------------------
16918 
16919  template <class P1>
16920  class GetLuminanceThread : public Thread
16921  {
16922  public:
16923 
16924  GetLuminanceThread( GenericImage<P1>& luminance, const GenericImage& image, ThreadData& data,
16925  const Rect& rect, int firstRow, int endRow )
16926  : m_luminance( luminance )
16927  , m_image( image )
16928  , m_data( data )
16929  , m_rect( rect )
16930  , m_firstRow( firstRow )
16931  , m_endRow( endRow )
16932  {
16933  }
16934 
16935  void Run() final
16936  {
16938 
16939  const RGBColorSystem& rgbws = m_image.RGBWorkingSpace();
16940 
16941  const typename P::sample* f0 = m_image.PixelAddress( m_rect.x0, m_rect.y0 + m_firstRow, 0 );
16942  const typename P::sample* f1 = m_image.PixelAddress( m_rect.x0, m_rect.y0 + m_firstRow, 1 );
16943  const typename P::sample* f2 = m_image.PixelAddress( m_rect.x0, m_rect.y0 + m_firstRow, 2 );
16944 
16945  typename P1::sample* fY = m_luminance.ScanLine( m_firstRow );
16946 
16947  for ( int y = m_firstRow, dw = m_image.Width()-m_rect.Width();
16948  y < m_endRow;
16949  ++y, f0 += dw, f1 += dw, f2 += dw )
16950  {
16951  const typename P::sample* fN = f0 + m_rect.Width();
16952 
16953  for ( ; f0 < fN; ++f0, ++f1, ++f2, ++fY )
16954  {
16955  RGBColorSystem::sample r0, r1, r2, rY;
16956  P::FromSample( r0, *f0 );
16957  P::FromSample( r1, *f1 );
16958  P::FromSample( r2, *f2 );
16959 
16960  switch ( m_image.ColorSpace() )
16961  {
16962  case ColorSpace::RGB:
16963  case ColorSpace::HSV:
16964  case ColorSpace::HSI:
16965  switch ( m_image.ColorSpace() )
16966  {
16967  case ColorSpace::HSV:
16968  rgbws.HSVToRGB( r0, r1, r2, r0, r1, r2 );
16969  break;
16970  case ColorSpace::HSI:
16971  rgbws.HSIToRGB( r0, r1, r2, r0, r1, r2 );
16972  break;
16973  default:
16974  break;
16975  }
16976  rY = rgbws.CIEY( r0, r1, r2 );
16977  break;
16978 
16979  case ColorSpace::CIELch:
16980  rgbws.CIELchToCIELab( r0, r1, r2, r0, r1, r2 );
16981  // fall through
16982  case ColorSpace::CIELab:
16983  rgbws.CIELabToCIEXYZ( r0, rY, r2, r0, r1, r2 );
16984  break;
16985 
16986  default: // ?!
16987  rY = 0;
16988  break;
16989  }
16990 
16991  *fY = P1::ToSample( rY );
16992 
16993  UPDATE_THREAD_MONITOR( 65536 )
16994  }
16995  }
16996  }
16997 
16998  private:
16999 
17000  GenericImage<P1>& m_luminance;
17001  const GenericImage& m_image;
17002  ThreadData& m_data;
17003  const Rect& m_rect;
17004  int m_firstRow, m_endRow;
17005  };
17006 
17007  // -------------------------------------------------------------------------
17008 
17009  template <class P1>
17010  class GetLightnessThread : public Thread
17011  {
17012  public:
17013 
17014  GetLightnessThread( GenericImage<P1>& lightness, const GenericImage& image, ThreadData& data,
17015  const Rect& rect, int firstRow, int endRow )
17016  : m_lightness( lightness )
17017  , m_image( image )
17018  , m_data( data )
17019  , m_rect( rect )
17020  , m_firstRow( firstRow )
17021  , m_endRow( endRow )
17022  {
17023  }
17024 
17025  void Run() final
17026  {
17028 
17029  const RGBColorSystem& rgbws = m_image.RGBWorkingSpace();
17030 
17031  const typename P::sample* f0 = m_image.PixelAddress( m_rect.x0, m_rect.y0 + m_firstRow, 0 );
17032  const typename P::sample* f1 = m_image.PixelAddress( m_rect.x0, m_rect.y0 + m_firstRow, 1 );
17033  const typename P::sample* f2 = m_image.PixelAddress( m_rect.x0, m_rect.y0 + m_firstRow, 2 );
17034 
17035  typename P1::sample* fL = m_lightness.ScanLine( m_firstRow );
17036 
17037  for ( int y = m_firstRow, dw = m_image.Width()-m_rect.Width();
17038  y < m_endRow;
17039  ++y, f0 += dw, f1 += dw, f2 += dw )
17040  {
17041  const typename P::sample* fN = f0 + m_rect.Width();
17042 
17043  for ( ; f0 < fN; ++f0, ++f1, ++f2, ++fL )
17044  {
17045  RGBColorSystem::sample r0, r1, r2, rL;
17046  P::FromSample( r0, *f0 );
17047  P::FromSample( r1, *f1 );
17048  P::FromSample( r2, *f2 );
17049 
17050  switch ( m_image.ColorSpace() )
17051  {
17052  case ColorSpace::RGB:
17053  case ColorSpace::HSV:
17054  case ColorSpace::HSI:
17055  case ColorSpace::CIEXYZ:
17056  switch ( m_image.ColorSpace() )
17057  {
17058  case ColorSpace::HSV:
17059  rgbws.HSVToRGB( r0, r1, r2, r0, r1, r2 );
17060  break;
17061  case ColorSpace::HSI:
17062  rgbws.HSIToRGB( r0, r1, r2, r0, r1, r2 );
17063  break;
17064  case ColorSpace::CIEXYZ:
17065  rgbws.CIEXYZToRGB( r0, r1, r2, r0, r1, r2 );
17066  break;
17067  default:
17068  break;
17069  }
17070  rL = rgbws.CIEL( r0, r1, r2 );
17071  break;
17072 
17073  default: // ?!
17074  rL = 0;
17075  break;
17076  }
17077 
17078  *fL = P1::ToSample( rL );
17079 
17080  UPDATE_THREAD_MONITOR( 65536 )
17081  }
17082  }
17083  }
17084 
17085  private:
17086 
17087  GenericImage<P1>& m_lightness;
17088  const GenericImage& m_image;
17089  ThreadData& m_data;
17090  const Rect& m_rect;
17091  int m_firstRow, m_endRow;
17092  };
17093 
17094  // -------------------------------------------------------------------------
17095 
17096  template <class P1>
17097  class GetIntensityThread : public Thread
17098  {
17099  public:
17100 
17101  GetIntensityThread( GenericImage<P1>& luminance, const GenericImage& image, ThreadData& data,
17102  const Rect& rect, int firstRow, int endRow )
17103  : m_intensity( luminance )
17104  , m_image( image )
17105  , m_data( data )
17106  , m_rect( rect )
17107  , m_firstRow( firstRow )
17108  , m_endRow( endRow )
17109  {
17110  }
17111 
17112  void Run() final
17113  {
17115 
17116  const RGBColorSystem& rgbws = m_image.RGBWorkingSpace();
17117 
17118  const typename P::sample* f0 = m_image.PixelAddress( m_rect.x0, m_rect.y0 + m_firstRow, 0 );
17119  const typename P::sample* f1 = m_image.PixelAddress( m_rect.x0, m_rect.y0 + m_firstRow, 1 );
17120  const typename P::sample* f2 = m_image.PixelAddress( m_rect.x0, m_rect.y0 + m_firstRow, 2 );
17121 
17122  typename P1::sample* fI = m_intensity.ScanLine( m_firstRow );
17123 
17124  for ( int y = m_firstRow, dw = m_image.Width()-m_rect.Width();
17125  y < m_endRow;
17126  ++y, f0 += dw, f1 += dw, f2 += dw )
17127  {
17128  const typename P::sample* fN = f0 + m_rect.Width();
17129 
17130  for ( ; f0 < fN; ++f0, ++f1, ++f2, ++fI )
17131  {
17132  RGBColorSystem::sample r0, r1, r2;
17133  P::FromSample( r0, *f0 );
17134  P::FromSample( r1, *f1 );
17135  P::FromSample( r2, *f2 );
17136 
17137  switch ( m_image.ColorSpace() )
17138  {
17139  case ColorSpace::RGB:
17140  break;
17141  case ColorSpace::HSV:
17142  rgbws.HSVToRGB( r0, r1, r2, r0, r1, r2 );
17143  break;
17144  case ColorSpace::CIEXYZ:
17145  rgbws.CIEXYZToRGB( r0, r1, r2, r0, r1, r2 );
17146  break;
17147  case ColorSpace::CIELab:
17148  rgbws.CIELabToRGB( r0, r1, r2, r0, r1, r2 );
17149  break;
17150  case ColorSpace::CIELch:
17151  rgbws.CIELchToRGB( r0, r1, r2, r0, r1, r2 );
17152  break;
17153  default:
17154  break;
17155  }
17156 
17157  *fI = P1::ToSample( rgbws.Intensity( r0, r1, r2 ) );
17158 
17159  UPDATE_THREAD_MONITOR( 65536 )
17160  }
17161  }
17162  }
17163 
17164  private:
17165 
17166  GenericImage<P1>& m_intensity;
17167  const GenericImage& m_image;
17168  ThreadData& m_data;
17169  const Rect& m_rect;
17170  int m_firstRow, m_endRow;
17171  };
17172 
17173  // -------------------------------------------------------------------------
17174 
17175  template <class P1>
17176  class SetLuminanceThread : public Thread
17177  {
17178  public:
17179 
17180  SetLuminanceThread( GenericImage& image, const GenericImage<P1>& luminance, ThreadData& data,
17181  const Point& target, const Rect& rect, int firstRow, int endRow )
17182  : m_image( image )
17183  , m_luminance( luminance )
17184  , m_data( data )
17185  , m_target( target )
17186  , m_rect( rect )
17187  , m_firstRow( firstRow )
17188  , m_endRow( endRow )
17189  {
17190  }
17191 
17192  void Run() final
17193  {
17195 
17196  const RGBColorSystem& sourceRGBWS = m_luminance.RGBWorkingSpace();
17197  const RGBColorSystem& targetRGBWS = m_image.RGBWorkingSpace();
17198 
17199  typename P::sample* f0 = m_image.PixelAddress( m_target.x, m_target.y + m_firstRow, 0 );
17200  typename P::sample* f1 = m_image.PixelAddress( m_target.x, m_target.y + m_firstRow, 1 );
17201  typename P::sample* f2 = m_image.PixelAddress( m_target.x, m_target.y + m_firstRow, 2 );
17202 
17203  if ( m_luminance.ColorSpace() == ColorSpace::Gray || m_luminance.ColorSpace() == ColorSpace::CIEXYZ )
17204  {
17205  int cY = (m_luminance.ColorSpace() == ColorSpace::Gray) ? 0 : 1;
17206  const typename P1::sample* fY = m_luminance.PixelAddress( m_rect.x0, m_rect.y0 + m_firstRow, cY );
17207 
17208  for ( int y = m_firstRow, dw = m_image.Width()-m_rect.Width(), dwY = m_luminance.Width()-m_rect.Width();
17209  y < m_endRow;
17210  ++y, f0 += dw, f1 += dw, f2 += dw, fY += dwY )
17211  {
17212  const typename P::sample* fN = f0 + m_rect.Width();
17213 
17214  for ( ; f0 < fN; ++f0, ++f1, ++f2, ++fY )
17215  {
17216  RGBColorSystem::sample r0, r1, r2, rY;
17217  P::FromSample( r0, *f0 );
17218  P::FromSample( r1, *f1 );
17219  P::FromSample( r2, *f2 );
17220  P1::FromSample( rY, *fY );
17221 
17222  switch ( m_image.ColorSpace() )
17223  {
17224  case ColorSpace::RGB:
17225  case ColorSpace::HSV:
17226  case ColorSpace::HSI:
17227  switch ( m_image.ColorSpace() )
17228  {
17229  case ColorSpace::HSV:
17230  targetRGBWS.HSVToRGB( r0, r1, r2, r0, r1, r2 );
17231  break;
17232  case ColorSpace::HSI:
17233  targetRGBWS.HSIToRGB( r0, r1, r2, r0, r1, r2 );
17234  break;
17235  default: // RGB
17236  break;
17237  }
17238  targetRGBWS.RGBToCIEab( r1, r2, r0, r1, r2 );
17239  targetRGBWS.CIELabToRGB( r0, r1, r2, sourceRGBWS.CIEYToCIEL( rY ), r1, r2 );
17240  switch ( m_image.ColorSpace() )
17241  {
17242  case ColorSpace::HSV:
17243  targetRGBWS.RGBToHSV( r0, r1, r2, r0, r1, r2 );
17244  break;
17245  case ColorSpace::HSI:
17246  targetRGBWS.RGBToHSI( r0, r1, r2, r0, r1, r2 );
17247  break;
17248  default: // RGB
17249  break;
17250  }
17251  break;
17252 
17253  case ColorSpace::CIEXYZ:
17254  targetRGBWS.CIEXYZToCIELab( r0, r1, r2, r0, r1, r2 );
17255  targetRGBWS.CIELabToCIEXYZ( r0, r1, r2, sourceRGBWS.CIEYToCIEL( rY ), r1, r2 );
17256  break;
17257 
17258  case ColorSpace::CIELab:
17259  case ColorSpace::CIELch:
17260  r0 = sourceRGBWS.CIEYToCIEL( rY );
17261  break;
17262 
17263  default: // ???
17264  break;
17265  }
17266 
17267  *f0 = P::ToSample( r0 );
17268  *f1 = P::ToSample( r1 );
17269  *f2 = P::ToSample( r2 );
17270 
17271  UPDATE_THREAD_MONITOR( 65536 )
17272  }
17273  }
17274  }
17275  else
17276  {
17277  const typename P1::sample* g0 = m_luminance.PixelAddress( m_rect.x0, m_rect.y0 + m_firstRow, 0 );
17278  const typename P1::sample* g1 = m_luminance.PixelAddress( m_rect.x0, m_rect.y0 + m_firstRow, 1 );
17279  const typename P1::sample* g2 = m_luminance.PixelAddress( m_rect.x0, m_rect.y0 + m_firstRow, 2 );
17280 
17281  for ( int y = m_firstRow, dw = m_image.Width()-m_rect.Width(), dwY = m_luminance.Width()-m_rect.Width();
17282  y < m_endRow;
17283  ++y, f0 += dw, f1 += dw, f2 += dw, g0 += dwY, g1 += dwY, g2 += dwY )
17284  {
17285  const typename P::sample* fN = f0 + m_rect.Width();
17286 
17287  for ( ; f0 < fN; ++f0, ++f1, ++f2, ++g0, ++g1, ++g2 )
17288  {
17289  typename RGBColorSystem::sample r0, r1, r2, s0, s1, s2;
17290  P::FromSample( r0, *f0 );
17291  P::FromSample( r1, *f1 );
17292  P::FromSample( r2, *f2 );
17293  P1::FromSample( s0, *g0 );
17294  P1::FromSample( s1, *g1 );
17295  P1::FromSample( s2, *g2 );
17296 
17297  switch ( m_image.ColorSpace() )
17298  {
17299  case ColorSpace::RGB:
17300  case ColorSpace::HSV:
17301  case ColorSpace::HSI:
17302  case ColorSpace::CIEXYZ:
17303  case ColorSpace::CIELab:
17304  case ColorSpace::CIELch:
17305  switch ( m_image.ColorSpace() )
17306  {
17307  case ColorSpace::RGB:
17308  case ColorSpace::HSV:
17309  case ColorSpace::HSI:
17310  switch ( m_image.ColorSpace() )
17311  {
17312  case ColorSpace::HSV:
17313  targetRGBWS.HSVToRGB( r0, r1, r2, r0, r1, r2 );
17314  break;
17315  case ColorSpace::HSI:
17316  targetRGBWS.HSIToRGB( r0, r1, r2, r0, r1, r2 );
17317  break;
17318  default:
17319  break;
17320  }
17321  targetRGBWS.RGBToCIEab( r1, r2, r0, r1, r2 );
17322  break;
17323  case ColorSpace::CIEXYZ:
17324  targetRGBWS.CIEXYZToCIELab( r0, r1, r2, r0, r1, r2 );
17325  break;
17326  case ColorSpace::CIELab: // NOOP
17327  case ColorSpace::CIELch: // NOOP
17328  break;
17329  default: // ?!
17330  break;
17331  }
17332 
17333  switch ( m_luminance.ColorSpace() )
17334  {
17335  case ColorSpace::RGB:
17336  case ColorSpace::HSV:
17337  case ColorSpace::HSI:
17338  switch ( m_luminance.ColorSpace() )
17339  {
17340  case ColorSpace::HSV:
17341  sourceRGBWS.HSVToRGB( s0, s1, s2, s0, s1, s2 );
17342  break;
17343  case ColorSpace::HSI:
17344  sourceRGBWS.HSIToRGB( s0, s1, s2, s0, s1, s2 );
17345  break;
17346  default:
17347  break;
17348  }
17349  r0 = sourceRGBWS.CIEL( s0, s1, s2 );
17350  break;
17351  case ColorSpace::CIEXYZ:
17352  sourceRGBWS.CIEXYZToCIELab( r0, s1, s2, s0, s1, s2 );
17353  break;
17354  case ColorSpace::CIELab:
17355  case ColorSpace::CIELch:
17356  r0 = s0;
17357  break;
17358  default: // ?!
17359  break;
17360  }
17361 
17362  switch ( m_image.ColorSpace() )
17363  {
17364  case ColorSpace::RGB:
17365  case ColorSpace::HSV:
17366  case ColorSpace::HSI:
17367  targetRGBWS.CIELabToRGB( r0, r1, r2, r0, r1, r2 );
17368  switch ( m_image.ColorSpace() )
17369  {
17370  case ColorSpace::HSV:
17371  targetRGBWS.RGBToHSV( r0, r1, r2, r0, r1, r2 );
17372  break;
17373  case ColorSpace::HSI:
17374  targetRGBWS.RGBToHSI( r0, r1, r2, r0, r1, r2 );
17375  break;
17376  default: // RGB
17377  break;
17378  }
17379  break;
17380  case ColorSpace::CIEXYZ:
17381  targetRGBWS.CIELabToCIEXYZ( r0, r1, r2, r0, r1, r2 );
17382  break;
17383  case ColorSpace::CIELab: // NOOP
17384  case ColorSpace::CIELch: // NOOP
17385  break;
17386  default: // ?!
17387  break;
17388  }
17389  break;
17390 
17391  default: // ?!
17392  break;
17393  }
17394 
17395  *f0 = P::ToSample( r0 );
17396  *f1 = P::ToSample( r1 );
17397  *f2 = P::ToSample( r2 );
17398 
17399  UPDATE_THREAD_MONITOR( 65536 )
17400  }
17401  }
17402  }
17403  }
17404 
17405  private:
17406 
17407  GenericImage& m_image;
17408  const GenericImage<P1>& m_luminance;
17409  ThreadData& m_data;
17410  const Point& m_target;
17411  const Rect& m_rect;
17412  int m_firstRow, m_endRow;
17413  };
17414 
17415  // -------------------------------------------------------------------------
17416 
17417  template <class P1>
17418  class SetLightnessThread : public Thread
17419  {
17420  public:
17421 
17422  SetLightnessThread( GenericImage& image, const GenericImage<P1>& lightness, ThreadData& data,
17423  const Point& target, const Rect& rect, int firstRow, int endRow )
17424  : m_image( image )
17425  , m_lightness( lightness )
17426  , m_data( data )
17427  , m_target( target )
17428  , m_rect( rect )
17429  , m_firstRow( firstRow )
17430  , m_endRow( endRow )
17431  {
17432  }
17433 
17434  void Run() final
17435  {
17437 
17438  const RGBColorSystem& sourceRGBWS = m_lightness.RGBWorkingSpace();
17439  const RGBColorSystem& targetRGBWS = m_image.RGBWorkingSpace();
17440 
17441  typename P::sample* f0 = m_image.PixelAddress( m_target.x, m_target.y + m_firstRow, 0 );
17442  typename P::sample* f1 = m_image.PixelAddress( m_target.x, m_target.y + m_firstRow, 1 );
17443  typename P::sample* f2 = m_image.PixelAddress( m_target.x, m_target.y + m_firstRow, 2 );
17444 
17445  if ( m_lightness.ColorSpace() == ColorSpace::Gray ||
17446  m_lightness.ColorSpace() == ColorSpace::CIELab || m_lightness.ColorSpace() == ColorSpace::CIELch )
17447  {
17448  const typename P1::sample* fL = m_lightness.PixelAddress( m_rect.x0, m_rect.y0 + m_firstRow, 0 );
17449 
17450  for ( int y = m_firstRow, dw = m_image.Width()-m_rect.Width(), dwL = m_lightness.Width()-m_rect.Width();
17451  y < m_endRow;
17452  ++y, f0 += dw, f1 += dw, f2 += dw, fL += dwL )
17453  {
17454  const typename P::sample* fN = f0 + m_rect.Width();
17455 
17456  for ( ; f0 < fN; ++f0, ++f1, ++f2, ++fL )
17457  {
17458  RGBColorSystem::sample r0, r1, r2, rL;
17459  P::FromSample( r0, *f0 );
17460  P::FromSample( r1, *f1 );
17461  P::FromSample( r2, *f2 );
17462  P1::FromSample( rL, *fL );
17463 
17464  switch ( m_image.ColorSpace() )
17465  {
17466  case ColorSpace::RGB:
17467  case ColorSpace::HSV:
17468  case ColorSpace::HSI:
17469  switch ( m_image.ColorSpace() )
17470  {
17471  case ColorSpace::HSV:
17472  targetRGBWS.HSVToRGB( r0, r1, r2, r0, r1, r2 );
17473  break;
17474  case ColorSpace::HSI:
17475  targetRGBWS.HSIToRGB( r0, r1, r2, r0, r1, r2 );
17476  break;
17477  default:
17478  break;
17479  }
17480  targetRGBWS.RGBToCIEab( r1, r2, r0, r1, r2 );
17481  targetRGBWS.CIELabToRGB( r0, r1, r2, rL, r1, r2 );
17482  switch ( m_image.ColorSpace() )
17483  {
17484  case ColorSpace::HSV:
17485  targetRGBWS.RGBToHSV( r0, r1, r2, r0, r1, r2 );
17486  break;
17487  case ColorSpace::HSI:
17488  targetRGBWS.RGBToHSI( r0, r1, r2, r0, r1, r2 );
17489  break;
17490  default:
17491  break;
17492  }
17493  break;
17494 
17495  case ColorSpace::CIEXYZ:
17496  r1 = targetRGBWS.CIELToCIEY( rL );
17497  break;
17498 
17499  case ColorSpace::CIELab:
17500  case ColorSpace::CIELch:
17501  r0 = rL;
17502  break;
17503 
17504  default: // ???
17505  break;
17506  }
17507 
17508  *f0 = P::ToSample( r0 );
17509  *f1 = P::ToSample( r1 );
17510  *f2 = P::ToSample( r2 );
17511 
17512  UPDATE_THREAD_MONITOR( 65536 )
17513  }
17514  }
17515  }
17516  else
17517  {
17518  const typename P1::sample* g0 = m_lightness.PixelAddress( m_rect.x0, m_rect.y0 + m_firstRow, 0 );
17519  const typename P1::sample* g1 = m_lightness.PixelAddress( m_rect.x0, m_rect.y0 + m_firstRow, 1 );
17520  const typename P1::sample* g2 = m_lightness.PixelAddress( m_rect.x0, m_rect.y0 + m_firstRow, 2 );
17521 
17522  for ( int y = m_firstRow, dw = m_image.Width()-m_rect.Width(), dwL = m_lightness.Width()-m_rect.Width();
17523  y < m_endRow;
17524  ++y, f0 += dw, f1 += dw, f2 += dw, g0 += dwL, g1 += dwL, g2 += dwL )
17525  {
17526  const typename P::sample* fN = f0 + m_rect.Width();
17527 
17528  for ( ; f0 < fN; ++f0, ++f1, ++f2, ++g0, ++g1, ++g2 )
17529  {
17530  typename RGBColorSystem::sample r0, r1, r2, s0, s1, s2;
17531  P::FromSample( r0, *f0 );
17532  P::FromSample( r1, *f1 );
17533  P::FromSample( r2, *f2 );
17534  P1::FromSample( s0, *g0 );
17535  P1::FromSample( s1, *g1 );
17536  P1::FromSample( s2, *g2 );
17537 
17538  switch ( m_image.ColorSpace() )
17539  {
17540  case ColorSpace::RGB:
17541  case ColorSpace::HSV:
17542  case ColorSpace::HSI:
17543  case ColorSpace::CIEXYZ:
17544  case ColorSpace::CIELab:
17545  case ColorSpace::CIELch:
17546  switch ( m_image.ColorSpace() )
17547  {
17548  case ColorSpace::RGB:
17549  case ColorSpace::HSV:
17550  case ColorSpace::HSI:
17551  switch ( m_image.ColorSpace() )
17552  {
17553  case ColorSpace::HSV:
17554  targetRGBWS.HSVToRGB( r0, r1, r2, r0, r1, r2 );
17555  break;
17556  case ColorSpace::HSI:
17557  targetRGBWS.HSIToRGB( r0, r1, r2, r0, r1, r2 );
17558  break;
17559  default:
17560  break;
17561  }
17562  targetRGBWS.RGBToCIEab( r1, r2, r0, r1, r2 );
17563  break;
17564  case ColorSpace::CIEXYZ:
17565  targetRGBWS.CIEXYZToCIELab( r0, r1, r2, r0, r1, r2 );
17566  break;
17567  case ColorSpace::CIELch:
17568  targetRGBWS.CIELchToCIELab( r0, r1, r2, r0, r1, r2 );
17569  break;
17570  default:
17571  break;
17572  }
17573 
17574  switch ( m_lightness.ColorSpace() )
17575  {
17576  case ColorSpace::RGB:
17577  case ColorSpace::HSV:
17578  case ColorSpace::HSI:
17579  switch ( m_lightness.ColorSpace() )
17580  {
17581  case ColorSpace::HSV:
17582  sourceRGBWS.HSVToRGB( s0, s1, s2, s0, s1, s2 );
17583  break;
17584  case ColorSpace::HSI:
17585  sourceRGBWS.HSIToRGB( s0, s1, s2, s0, s1, s2 );
17586  break;
17587  default:
17588  break;
17589  }
17590  r0 = sourceRGBWS.CIEL( s0, s1, s2 );
17591  break;
17592  case ColorSpace::CIEXYZ:
17593  r0 = sourceRGBWS.CIEYToCIEL( s1 );
17594  break;
17595  default: // ???
17596  break;
17597  }
17598 
17599  switch ( m_image.ColorSpace() )
17600  {
17601  case ColorSpace::RGB:
17602  case ColorSpace::HSV:
17603  case ColorSpace::HSI:
17604  targetRGBWS.CIELabToRGB( r0, r1, r2, r0, r1, r2 );
17605  switch ( m_image.ColorSpace() )
17606  {
17607  case ColorSpace::HSV:
17608  targetRGBWS.RGBToHSV( r0, r1, r2, r0, r1, r2 );
17609  break;
17610  case ColorSpace::HSI:
17611  targetRGBWS.RGBToHSI( r0, r1, r2, r0, r1, r2 );
17612  break;
17613  default:
17614  break;
17615  }
17616  break;
17617  case ColorSpace::CIEXYZ:
17618  targetRGBWS.CIELabToCIEXYZ( r0, r1, r2, r0, r1, r2 );
17619  break;
17620  case ColorSpace::CIELch:
17621  targetRGBWS.CIELabToCIELch( r0, r1, r2, r0, r1, r2 );
17622  break;
17623  default:
17624  break;
17625  }
17626 
17627  default: // ???
17628  break;
17629  }
17630 
17631  *f0 = P::ToSample( r0 );
17632  *f1 = P::ToSample( r1 );
17633  *f2 = P::ToSample( r2 );
17634 
17635  UPDATE_THREAD_MONITOR( 65536 )
17636  }
17637  }
17638  }
17639  }
17640 
17641  private:
17642 
17643  GenericImage& m_image;
17644  const GenericImage<P1>& m_lightness;
17645  ThreadData& m_data;
17646  const Point& m_target;
17647  const Rect& m_rect;
17648  int m_firstRow, m_endRow;
17649  };
17650 
17651  // -------------------------------------------------------------------------
17652 
17653 #ifdef __PCL_BUILDING_PIXINSIGHT_APPLICATION
17654  friend class pi::SharedImage;
17655 #endif
17656 };
17657 
17658 #undef m_pixelData
17659 #undef m_channelData
17660 #undef m_allocator
17661 #undef m_width
17662 #undef m_height
17663 #undef m_numberOfChannels
17664 #undef m_colorSpace
17665 #undef m_RGBWS
17666 #undef m_channel
17667 #undef m_lastChannel
17668 #undef m_point
17669 #undef m_rectangle
17670 #undef m_clipLow
17671 #undef m_clipHigh
17672 #undef m_clippedLow
17673 #undef m_clippedHigh
17674 
17675 // ----------------------------------------------------------------------------
17676 
17686 template <class P, typename T> inline
17687 GenericImage<P> operator +( const GenericImage<P>& image, T scalar )
17688 {
17689  GenericImage<P> result( image );
17690  (void)(result += scalar);
17691  return result;
17692 }
17693 
17702 template <class P, typename T> inline
17703 GenericImage<P> operator +( T scalar, const GenericImage<P>& image )
17704 {
17705  return image + scalar;
17706 }
17707 
17713 template <class P, typename T> inline
17714 GenericImage<P> operator -( const GenericImage<P>& image, T scalar )
17715 {
17716  GenericImage<P> result( image );
17717  (void)(result -= scalar);
17718  return result;
17719 }
17720 
17726 template <class P, typename T> inline
17727 GenericImage<P> operator *( const GenericImage<P>& image, T scalar )
17728 {
17729  GenericImage<P> result( image );
17730  (void)(result *= scalar);
17731  return result;
17732 }
17733 
17743 template <class P, typename T> inline
17744 GenericImage<P> operator *( T scalar, const GenericImage<P>& image )
17745 {
17746  return image * scalar;
17747 }
17748 
17754 template <class P, typename T> inline
17755 GenericImage<P> operator /( const GenericImage<P>& image, T scalar )
17756 {
17757  GenericImage<P> result( image );
17758  (void)(result /= scalar);
17759  return result;
17760 }
17761 
17767 template <class P, typename T> inline
17768 GenericImage<P> operator ^( const GenericImage<P>& image, T scalar )
17769 {
17770  GenericImage<P> result( image );
17771  (void)(result ^= scalar);
17772  return result;
17773 }
17774 
17775 // ----------------------------------------------------------------------------
17776 
17793 template <class P1, class P2> inline
17795 {
17796  GenericImage<P1> result( image1 );
17797  (void)(result += image2);
17798  return result;
17799 }
17800 
17813 template <class P1, class P2> inline
17815 {
17816  GenericImage<P1> result( image1 );
17817  (void)(result -= image2);
17818  return result;
17819 }
17820 
17833 template <class P1, class P2> inline
17835 {
17836  GenericImage<P1> result( image1 );
17837  (void)(result *= image2);
17838  return result;
17839 }
17840 
17854 template <class P1, class P2> inline
17856 {
17857  GenericImage<P1> result( image1 );
17858  (void)(result /= image2);
17859  return result;
17860 }
17861 
17875 template <class P1, class P2> inline
17877 {
17878  GenericImage<P1> result( image1 );
17879  (void)(result ^= image2);
17880  return result;
17881 }
17882 
17883 // ----------------------------------------------------------------------------
17884 
17885 #ifndef __PCL_NO_IMAGE_INSTANTIATE
17886 
17898 using FImage = GenericImage<FloatPixelTraits>;
17899 
17907 using DImage = GenericImage<DoublePixelTraits>;
17908 
17917 using FComplexImage = GenericImage<ComplexPixelTraits>;
17918 
17927 using DComplexImage = GenericImage<DComplexPixelTraits>;
17928 
17937 using UInt8Image = GenericImage<UInt8PixelTraits>;
17938 
17947 using UInt16Image = GenericImage<UInt16PixelTraits>;
17948 
17957 using UInt32Image = GenericImage<UInt32PixelTraits>;
17958 
17967 using Image = FImage;
17968 
17977 using ComplexImage = FComplexImage;
17978 
17979 #endif // __PCL_NO_IMAGE_INSTANTIATE
17980 
17981 // ----------------------------------------------------------------------------
17982 
17983 } // pcl
17984 
17985 // ----------------------------------------------------------------------------
17986 
17987 #ifndef __PCL_NO_IMAGE_VARIANT_AUTO
17988 
17989 #ifndef __PCL_ImageVariant_h
17990 #include <pcl/ImageVariant.h>
17991 #endif
17992 
17993 #endif // __PCL_NO_IMAGE_VARIANT_AUTO
17994 
17995 // ----------------------------------------------------------------------------
17996 
17997 #endif // __PCL_Image_h
17998 
17999 // ----------------------------------------------------------------------------
18000 // EOF pcl/Image.h - Released 2024-12-23T11:32:56Z
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:239
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:14117
double Norm(const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1, int maxProcessors=0) const
Definition: Image.h:13099
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:11455
GenericImage & CropBy(int left, int top, int right, int bottom, const GenericVector< T > &fillValues)
Definition: Image.h:13673
double Sn(const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1, int maxProcessors=0) const
Definition: Image.h:12938
GenericImage & Write(File &file, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1) const
Definition: Image.h:13511
GenericImage & ShiftToBottomRight(int width, int height)
Definition: Image.h:14136
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:13476
GenericImage & ShiftToTopLeft(int width, int height, const GenericVector< T > &fillValues)
Definition: Image.h:14030
GenericImage & ShiftTo(const Point &p, const GenericVector< T > &fillValues)
Definition: Image.h:13975
~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:11041
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:13021
GenericImage Raised(T scalar, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1) const
Definition: Image.h:10011
void GetIntensity(GenericImage< P1 > &I, const Rect &rect=Rect(0), int maxProcessors=0) const
Definition: Image.h:14671
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:14088
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:11355
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:13840
sample * PixelAddress(int x, int y, int channel=0)
Definition: Image.h:6864
GenericImage & Crop(const GenericVector< T > &fillValues)
Definition: Image.h:13872
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
double Mean(const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1, int maxProcessors=0) const
Definition: Image.h:11560
size_type ImageSize() const noexcept
Definition: Image.h:6682
GenericImage & Read(File &file)
Definition: Image.h:13599
TwoSidedEstimate TwoSidedAvgDev(double center, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1, int maxProcessors=0) const
Definition: Image.h:12198
GenericImage & Or(T scalar, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1)
Definition: Image.h:10163
void GetLightness(GenericImage< P1 > &L, const Rect &rect=Rect(0), int maxProcessors=0) const
Definition: Image.h:14548
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:11311
uint64 Hash(int channel=-1, uint64 seed=0) const noexcept
Definition: Image.h:13487
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
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:14938
GenericImage & Crop()
Definition: Image.h:13888
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:11164
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:13311
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:14059
GenericImage & DeleteAlphaChannel(int channel)
Definition: Image.h:7732
GenericImage & CropTo(int x0, int y0, int x1, int y1)
Definition: Image.h:13856
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:14001
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
void GetExtremeSampleValues(T &min, T &max, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1, int maxProcessors=0) const
Definition: Image.h:10979
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:13824
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 & SetColorSpace(color_space colorSpace, int maxProcessors=0)
Definition: Image.h:14208
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:12129
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:11287
GenericImage & CropTo(const Rect &rect, const GenericVector< T > &fillValues)
Definition: Image.h:13807
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:12451
double SumOfSquares(const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1, int maxProcessors=0) const
Definition: Image.h:13241
GenericImage & ShiftTo(int x, int y)
Definition: Image.h:13959
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:10897
sample_vector ColVector(int x, int channel=0) const
Definition: Image.h:7419
GenericImage & Shift()
Definition: Image.h:14168
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:11490
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 & ShiftToTopRight(int width, int height)
Definition: Image.h:14078
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:10946
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:11984
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
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:11209
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:13931
sample * ScanLine(int y, int channel=0)
Definition: Image.h:6820
GenericImage & ShiftToBottomLeft(int width, int height)
Definition: Image.h:14107
double BiweightMidvariance(double center, double sigma, int k=9, bool reducedLength=false, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1, int maxProcessors=0) const
Definition: Image.h:12669
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:11152
GenericImage & ShiftBy(int dx, int dy, const GenericVector< T > &fillValues)
Definition: Image.h:13915
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:13457
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:14416
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:13396
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:12084
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:15058
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:10872
GenericImage & SetLuminance(const GenericImage< P1 > &Y, const Point &point=Point(int_max), const Rect &rect=Rect(0), int maxProcessors=0)
Definition: Image.h:14797
sample Pixel(const Point &p, int channel=0) const noexcept
Definition: Image.h:7054
GenericImage & ShiftToCenter(int width, int height)
Definition: Image.h:14020
sample LocateMaximumPixelValue(int &xmax, int &ymax, const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1, int maxProcessors=0) const
Definition: Image.h:11299
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:13556
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:13791
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:11820
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:10823
GenericImage & ShiftToTopLeft(int width, int height)
Definition: Image.h:14049
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:12285
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:11633
GenericImage & DeleteAlphaChannels()
Definition: Image.h:7794
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:13634
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:11176
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:14152
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:11074
GenericImage & ShiftTo(int x, int y, const GenericVector< T > &fillValues)
Definition: Image.h:13943
double Modulus(const Rect &rect=Rect(0), int firstChannel=-1, int lastChannel=-1, int maxProcessors=0) const
Definition: Image.h:13171
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:13991
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:12852
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:12749
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
bool IsRect() const noexcept
Definition: Rectangle.h:764
void ResizeTo(T1 w, T1 h) noexcept
Definition: Rectangle.h:1702
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:1593
GenericRectangle Ordered() const noexcept
Definition: Rectangle.h:796
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:1481
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:8146
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:17814
GenericImage< P1 > operator/(const GenericImage< P1 > &image1, const GenericImage< P2 > &image2)
Definition: Image.h:17855
GenericImage< P1 > operator+(const GenericImage< P1 > &image1, const GenericImage< P2 > &image2)
Definition: Image.h:17794
GenericImage< P1 > operator^(const GenericImage< P1 > &image1, const GenericImage< P2 > &image2)
Definition: Image.h:17876
GenericImage< P1 > operator*(const GenericImage< P1 > &image1, const GenericImage< P2 > &image2)
Definition: Image.h:17834
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
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