PCL
MorphologicalOperator.h
Go to the documentation of this file.
1 // ____ ______ __
2 // / __ \ / ____// /
3 // / /_/ // / / /
4 // / ____// /___ / /___ PixInsight Class Library
5 // /_/ \____//_____/ PCL 2.8.5
6 // ----------------------------------------------------------------------------
7 // pcl/MorphologicalOperator.h - Released 2024-12-28T16:53:48Z
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_MorphologicalOperator_h
53 #define __PCL_MorphologicalOperator_h
54 
56 
57 #include <pcl/Defs.h>
58 #include <pcl/Diagnostics.h>
59 
60 #include <pcl/Exception.h>
61 #include <pcl/Math.h>
62 #include <pcl/PixelTraits.h>
63 #include <pcl/Selection.h>
64 #include <pcl/Sort.h>
65 #include <pcl/String.h>
66 #include <pcl/Utility.h>
67 
68 namespace pcl
69 {
70 
71 // ----------------------------------------------------------------------------
72 
80 class PCL_CLASS MorphologicalOperator
81 {
82 public:
83 
87  MorphologicalOperator() = default;
88 
93 
98  {
99  }
100 
105  virtual MorphologicalOperator* Clone() const = 0;
106 
110  virtual String Description() const
111  {
112  return String();
113  }
114 
121  virtual bool IsDilation() const
122  {
123  return false;
124  }
125 
131  {
132  throw NotImplemented( *this, "Apply to 32-bit floating-point images" );
133  }
134 
140  {
141  throw NotImplemented( *this, "Apply to 64-bit floating-point images" );
142  }
143 
149  {
150  throw NotImplemented( *this, "Apply to 32-bit complex images" );
151  }
152 
158  {
159  throw NotImplemented( *this, "Apply to 64-bit complex images" );
160  }
161 
167  {
168  throw NotImplemented( *this, "Apply to 8-bit integer images" );
169  }
170 
176  {
177  throw NotImplemented( *this, "Apply to 16-bit integer images" );
178  }
179 
185  {
186  throw NotImplemented( *this, "Apply to 32-bit integer images" );
187  }
188 };
189 
190 // ----------------------------------------------------------------------------
191 
199 class PCL_CLASS ErosionFilter : public MorphologicalOperator
200 {
201 public:
202 
205  MorphologicalOperator* Clone() const override
206  {
207  return new ErosionFilter( *this );
208  }
209 
212  String Description() const override
213  {
214  return "Erosion";
215  }
216 
221  FloatPixelTraits::sample operator ()( FloatPixelTraits::sample* f, size_type n ) const override
222  {
223  return Operate( f, n );
224  }
225 
231  {
232  return Operate( f, n );
233  }
234 
239  UInt8PixelTraits::sample operator ()( UInt8PixelTraits::sample* f, size_type n ) const override
240  {
241  return Operate( f, n );
242  }
243 
249  {
250  return Operate( f, n );
251  }
252 
258  {
259  return Operate( f, n );
260  }
261 
262 private:
263 
264  template <typename T>
265  static T Operate( T* __restrict__ f, size_type n )
266  {
267  T x = *f++;
268  for ( ; --n > 0; ++f )
269  if ( *f < x )
270  x = *f;
271  return x;
272  }
273 
274 #ifdef __PCL_AVX2
275 
276  static float Operate( float* __restrict__ f, size_type n )
277  {
278  if ( unlikely( n < 8 ) )
279  {
280  float min = f[0];
281  for ( size_type i = 1; i < n; ++i )
282  if ( f[i] < min )
283  min = f[i];
284  return min;
285  }
286 
287  __m256 vmin;
288  const size_type n8 = n >> 3;
289  if ( unlikely( ((ptrdiff_t)f) & 31 ) )
290  {
291  vmin = _mm256_loadu_ps( f );
292  for ( size_type i = 1; i < n8; ++i )
293  vmin = _mm256_min_ps( vmin, _mm256_loadu_ps( (const float* __restrict__)(f + i*8) ) );
294  }
295  else
296  {
297  vmin = _mm256_load_ps( f );
298  for ( size_type i = 1; i < n8; ++i )
299  vmin = _mm256_min_ps( vmin, ((const __m256* __restrict__)f)[i] );
300  }
301  float min = ((const float* __restrict__)&vmin)[0];
302  for ( int i = 1; i < 8; ++i )
303  {
304  float vn = ((const float* __restrict__)&vmin)[i];
305  if ( vn < min )
306  min = vn;
307  }
308  for ( size_type i = n8 << 3; i < n; ++i )
309  if ( f[i] < min )
310  min = f[i];
311  return min;
312  }
313 
314  static double Operate( double* __restrict__ f, size_type n )
315  {
316  if ( unlikely( n < 4 ) )
317  {
318  double min = f[0];
319  for ( size_type i = 1; i < n; ++i )
320  if ( f[i] < min )
321  min = f[i];
322  return min;
323  }
324 
325  __m256d vmin;
326  const size_type n4 = n >> 2;
327  if ( unlikely( ((ptrdiff_t)f) & 31 ) )
328  {
329  vmin = _mm256_loadu_pd( f );
330  for ( size_type i = 1; i < n4; ++i )
331  vmin = _mm256_min_pd( vmin, _mm256_loadu_pd( (const double* __restrict__)(f + i*4) ) );
332  }
333  else
334  {
335  vmin = _mm256_load_pd( f );
336  for ( size_type i = 1; i < n4; ++i )
337  vmin = _mm256_min_pd( vmin, ((const __m256d* __restrict__)f)[i] );
338  }
339  double min = ((const double* __restrict__)&vmin)[0];
340  for ( int i = 1; i < 4; ++i )
341  {
342  double vn = ((const double* __restrict__)&vmin)[i];
343  if ( vn < min )
344  min = vn;
345  }
346  for ( size_type i = n4 << 2; i < n; ++i )
347  if ( f[i] < min )
348  min = f[i];
349  return min;
350  }
351 
352 #endif
353 };
354 
355 // ----------------------------------------------------------------------------
356 
364 class PCL_CLASS DilationFilter : public MorphologicalOperator
365 {
366 public:
367 
370  MorphologicalOperator* Clone() const override
371  {
372  return new DilationFilter( *this );
373  }
374 
377  String Description() const override
378  {
379  return "Dilation";
380  }
381 
384  bool IsDilation() const override
385  {
386  return true;
387  }
388 
393  FloatPixelTraits::sample operator ()( FloatPixelTraits::sample* f, size_type n ) const override
394  {
395  return Operate( f, n );
396  }
397 
403  {
404  return Operate( f, n );
405  }
406 
411  UInt8PixelTraits::sample operator ()( UInt8PixelTraits::sample* f, size_type n ) const override
412  {
413  return Operate( f, n );
414  }
415 
421  {
422  return Operate( f, n );
423  }
424 
430  {
431  return Operate( f, n );
432  }
433 
434 private:
435 
436  template <typename T>
437  static T Operate( T* __restrict__ f, size_type n )
438  {
439  T x = *f++;
440  for ( ; --n > 0; ++f )
441  if ( x < *f )
442  x = *f;
443  return x;
444  }
445 
446 #ifdef __PCL_AVX2
447 
448  static float Operate( float* __restrict__ f, size_type n )
449  {
450  if ( unlikely( n < 8 ) )
451  {
452  float max = f[0];
453  for ( size_type i = 1; i < n; ++i )
454  if ( f[i] > max )
455  max = f[i];
456  return max;
457  }
458 
459  __m256 vmax;
460  const size_type n8 = n >> 3;
461  if ( unlikely( ((ptrdiff_t)f) & 31 ) )
462  {
463  vmax = _mm256_loadu_ps( f );
464  for ( size_type i = 1; i < n8; ++i )
465  vmax = _mm256_max_ps( vmax, _mm256_loadu_ps( (const float* __restrict__)(f + i*8) ) );
466  }
467  else
468  {
469  vmax = _mm256_load_ps( f );
470  for ( size_type i = 1; i < n8; ++i )
471  vmax = _mm256_max_ps( vmax, ((const __m256* __restrict__)f)[i] );
472  }
473  float max = ((const float* __restrict__)&vmax)[0];
474  for ( int i = 1; i < 8; ++i )
475  {
476  float vn = ((const float* __restrict__)&vmax)[i];
477  if ( vn > max )
478  max = vn;
479  }
480  for ( size_type i = n8 << 3; i < n; ++i )
481  if ( f[i] > max )
482  max = f[i];
483  return max;
484  }
485 
486  static double Operate( double* __restrict__ f, size_type n )
487  {
488  if ( unlikely( n < 4 ) )
489  {
490  double max = f[0];
491  for ( size_type i = 1; i < n; ++i )
492  if ( f[i] > max )
493  max = f[i];
494  return max;
495  }
496 
497  __m256d vmax;
498  const size_type n4 = n >> 2;
499  if ( unlikely( ((ptrdiff_t)f) & 31 ) )
500  {
501  vmax = _mm256_loadu_pd( f );
502  for ( size_type i = 1; i < n4; ++i )
503  vmax = _mm256_max_pd( vmax, _mm256_loadu_pd( (const double* __restrict__)(f + i*4) ) );
504  }
505  else
506  {
507  vmax = _mm256_load_pd( f );
508  for ( size_type i = 1; i < n4; ++i )
509  vmax = _mm256_max_pd( vmax, ((const __m256d* __restrict__)f)[i] );
510  }
511  double max = ((const double* __restrict__)&vmax)[0];
512  for ( int i = 1; i < 4; ++i )
513  {
514  double vn = ((const double* __restrict__)&vmax)[i];
515  if ( vn > max )
516  max = vn;
517  }
518  for ( size_type i = n4 << 2; i < n; ++i )
519  if ( f[i] > max )
520  max = f[i];
521  return max;
522  }
523 
524 #endif
525 };
526 
527 // ----------------------------------------------------------------------------
528 
536 class PCL_CLASS MedianFilter : public MorphologicalOperator
537 {
538 public:
539 
542  MorphologicalOperator* Clone() const override
543  {
544  return new MedianFilter( *this );
545  }
546 
549  String Description() const override
550  {
551  return "Median";
552  }
553 
558  FloatPixelTraits::sample operator ()( FloatPixelTraits::sample* f, size_type n ) const override
559  {
560  return Operate( f, n, (FloatPixelTraits*)0 );
561  }
562 
568  {
569  return Operate( f, n, (DoublePixelTraits*)0 );
570  }
571 
576  UInt8PixelTraits::sample operator ()( UInt8PixelTraits::sample* f, size_type n ) const override
577  {
578  return Operate( f, n, (UInt8PixelTraits*)0 );
579  }
580 
586  {
587  return Operate( f, n, (UInt16PixelTraits*)0 );
588  }
589 
595  {
596  return Operate( f, n, (UInt32PixelTraits*)0 );
597  }
598 
599 private:
600 
601  template <typename T, class P>
602  static T Operate( T* __restrict__ f, size_type n, P* )
603  {
604  if ( n > __PCL_SMALL_MEDIAN_MAX_LENGTH )
605  {
606  distance_type n2 = distance_type( n >> 1 );
607  if ( n & 1 )
608  return *pcl::Select( f, f+n, n2 );
609  return P::FloatToSample( (double( *pcl::Select( f, f+n, n2 ) )
610  + double( *pcl::Select( f, f+n, n2-1 ) ))/2 );
611  }
612 
613  return P::FloatToSample( pcl::SmallMedianDestructive( f, f+n ) );
614  }
615 };
616 
617 // ----------------------------------------------------------------------------
618 
632 class PCL_CLASS SelectionFilter : public MorphologicalOperator
633 {
634 public:
635 
640  SelectionFilter() = default;
641 
645  SelectionFilter( double k )
646  : m_k( pcl::Range( k, 0.0, 1.0 ) )
647  {
648  PCL_PRECONDITION( 0 <= k && k <= 1 )
649  PCL_CHECK( 0 <= m_k && m_k <= 1 )
650  }
651 
655  SelectionFilter( const SelectionFilter& ) = default;
656 
659  MorphologicalOperator* Clone() const override
660  {
661  return new SelectionFilter( *this );
662  }
663 
667  double SelectionPoint() const
668  {
669  return m_k;
670  }
671 
675  void SetSelectionPoint( double k )
676  {
677  PCL_PRECONDITION( 0 <= k && k <= 1 )
678  m_k = pcl::Range( k, 0.0, 1.0 );
679  }
680 
683  String Description() const override
684  {
685  return String().Format( "Selection, k=%.5f", m_k );
686  }
687 
692  FloatPixelTraits::sample operator ()( FloatPixelTraits::sample* f, size_type n ) const override
693  {
694  return Operate( f, n );
695  }
696 
702  {
703  return Operate( f, n );
704  }
705 
710  UInt8PixelTraits::sample operator ()( UInt8PixelTraits::sample* f, size_type n ) const override
711  {
712  return Operate( f, n );
713  }
714 
720  {
721  return Operate( f, n );
722  }
723 
729  {
730  return Operate( f, n );
731  }
732 
733 private:
734 
735  double m_k = 0.5;
736 
737  template <typename T>
738  T Operate( T* __restrict__ f, size_type n ) const
739  {
740  return *pcl::Select( f, f+n, distance_type( pcl::RoundInt( m_k*(n - 1) ) ) );
741  }
742 };
743 
744 // ----------------------------------------------------------------------------
745 
753 class PCL_CLASS MidpointFilter : public MorphologicalOperator
754 {
755 public:
756 
759  MorphologicalOperator* Clone() const override
760  {
761  return new MidpointFilter( *this );
762  }
763 
766  String Description() const override
767  {
768  return "Midpoint";
769  }
770 
775  FloatPixelTraits::sample operator ()( FloatPixelTraits::sample* f, size_type n ) const override
776  {
777  return Operate( f, n, (FloatPixelTraits*)0 );
778  }
779 
785  {
786  return Operate( f, n, (DoublePixelTraits*)0 );
787  }
788 
793  UInt8PixelTraits::sample operator ()( UInt8PixelTraits::sample* f, size_type n ) const override
794  {
795  return Operate( f, n, (UInt8PixelTraits*)0 );
796  }
797 
803  {
804  return Operate( f, n, (UInt16PixelTraits*)0 );
805  }
806 
812  {
813  return Operate( f, n, (UInt32PixelTraits*)0 );
814  }
815 
816 private:
817 
818  template <typename T, class P>
819  static T Operate( T* __restrict__ f, size_type n, P* )
820  {
821  T* min, * max;
822  pcl::FindExtremeItems( min, max, f, f+n );
823  return P::FloatToSample( (double( *min ) + double( *max ))/2 );
824  }
825 };
826 
827 // ----------------------------------------------------------------------------
828 
845 {
846 public:
847 
855 
861  : m_d( pcl::Range( d, 0.0, 1.0 ) )
862  {
863  PCL_PRECONDITION( 0 <= d && d <= 1 )
864  PCL_CHECK( 0 <= m_d && m_d <= 1 )
865  }
866 
871 
874  MorphologicalOperator* Clone() const override
875  {
876  return new AlphaTrimmedMeanFilter( *this );
877  }
878 
882  double TrimmingFactor() const
883  {
884  return m_d;
885  }
886 
890  void SetTrimmingFactor( double d )
891  {
892  PCL_PRECONDITION( 0 <= d && d <= 1 )
893  m_d = pcl::Range( d, 0.0, 1.0 );
894  }
895 
898  String Description() const override
899  {
900  return String().Format( "Alpha-trimmed mean, d=%.5f", m_d );
901  }
902 
907  FloatPixelTraits::sample operator ()( FloatPixelTraits::sample* f, size_type n ) const override
908  {
909  return Operate( f, n, (FloatPixelTraits*)0 );
910  }
911 
917  {
918  return Operate( f, n, (DoublePixelTraits*)0 );
919  }
920 
925  UInt8PixelTraits::sample operator ()( UInt8PixelTraits::sample* f, size_type n ) const override
926  {
927  return Operate( f, n, (UInt8PixelTraits*)0 );
928  }
929 
935  {
936  return Operate( f, n, (UInt16PixelTraits*)0 );
937  }
938 
944  {
945  return Operate( f, n, (UInt32PixelTraits*)0 );
946  }
947 
948 private:
949 
950  double m_d = 0.2;
951 
952  template <typename T, class P>
953  T Operate( T* __restrict__ f, size_type n, P* ) const
954  {
955  pcl::Sort( f, f+n );
956  double s = 0;
957  size_type i1 = RoundInt( m_d*((n - 1) >> 1) );
958  size_type i2 = n-i1;
959  for ( size_type i = i1; i < i2; ++i )
960  s += f[i];
961  return P::FloatToSample( s/(i2 - i1) );
962  }
963 };
964 
965 // ----------------------------------------------------------------------------
966 
967 } // pcl
968 
969 #endif // __PCL_MorphologicalOperator_h
970 
971 // ----------------------------------------------------------------------------
972 // EOF pcl/MorphologicalOperator.h - Released 2024-12-28T16:53:48Z
Alpha-trimmed mean operator.
AlphaTrimmedMeanFilter(const AlphaTrimmedMeanFilter &)=default
String Description() const override
MorphologicalOperator * Clone() const override
Generic complex number.
Definition: Complex.h:84
Dilation morphological operator.
String Description() const override
MorphologicalOperator * Clone() const override
64-bit IEEE 754 normalized floating point real pixel traits.
Definition: PixelTraits.h:963
traits_type::sample sample
Definition: PixelTraits.h:974
Erosion morphological operator.
MorphologicalOperator * Clone() const override
String Description() const override
32-bit IEEE 754 normalized floating point real pixel traits.
Definition: PixelTraits.h:369
traits_type::sample sample
Definition: PixelTraits.h:380
Morphological median operator.
MorphologicalOperator * Clone() const override
String Description() const override
String Description() const override
MorphologicalOperator * Clone() const override
Abstract base class of all PCL morphological operators.
virtual String Description() const
virtual MorphologicalOperator * Clone() const =0
MorphologicalOperator(const MorphologicalOperator &)=default
An exception that indicates an unsupported feature.
Definition: Exception.h:344
Morphological selection operator.
String Description() const override
SelectionFilter(const SelectionFilter &)=default
MorphologicalOperator * Clone() const override
void SetSelectionPoint(double k)
SelectionFilter()=default
Unicode (UTF-16) string.
Definition: String.h:8146
String & Format(const_c_string8 fmt,...)
Definition: String.h:11129
16-bit unsigned integer pixel traits.
Definition: PixelTraits.h:3934
traits_type::sample sample
Definition: PixelTraits.h:3945
32-bit unsigned integer pixel traits.
Definition: PixelTraits.h:4798
traits_type::sample sample
Definition: PixelTraits.h:4809
8-bit unsigned integer pixel traits.
Definition: PixelTraits.h:3070
traits_type::sample sample
Definition: PixelTraits.h:3081
int RoundInt(T x) noexcept
Definition: Math.h:1550
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
void Sort(BI i, BI j)
Definition: Sort.h:520
void FindExtremeItems(FI &kmin, FI &kmax, FI i, FI j) noexcept
Definition: Utility.h:517
constexpr const T & Range(const T &x, const T &a, const T &b) noexcept
Definition: Utility.h:190
PCL root namespace.
Definition: AbstractImage.h:77