PCL
ShepardInterpolation.h
Go to the documentation of this file.
1 // ____ ______ __
2 // / __ \ / ____// /
3 // / /_/ // / / /
4 // / ____// /___ / /___ PixInsight Class Library
5 // /_/ \____//_____/ PCL 2.7.0
6 // ----------------------------------------------------------------------------
7 // pcl/ShepardInterpolation.h - Released 2024-06-18T15:48:54Z
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_ShepardInterpolation_h
53 #define __PCL_ShepardInterpolation_h
54 
56 
57 #include <pcl/Defs.h>
58 #include <pcl/Diagnostics.h>
59 
60 #include <pcl/QuadTree.h>
61 #include <pcl/Vector.h>
62 
63 #ifndef __PCL_BUILDING_PIXINSIGHT_APPLICATION
64 # include <pcl/Console.h>
65 #endif
66 
67 namespace pcl
68 {
69 
70 // ----------------------------------------------------------------------------
71 
72 /*
73  * Default normalized search radius for local Shepard interpolation. This is an
74  * initial search radius relative to the unit circle for the adaptive quadtree
75  * search algorithm.
76  */
77 #define __PCL_SHEPARD_DEFAULT_SEARCH_RADIUS 0.10
78 
79 /*
80  * Default power parameter for local Shepard interpolation. Larger values tend
81  * to yield more accurate interpolation devices. Small powers lead to more
82  * approximating (smoothing) devices. The chosen default value is intermediate.
83  */
84 #define __PCL_SHEPARD_DEFAULT_POWER 4
85 
86 /*
87  * Default regularization (smoothing) factor for local Shepard interpolation,
88  * in the range [0,1). This is a clipping fraction for Winsorization of nearby
89  * function values in the point interpolation routine.
90  */
91 #define __PCL_SHEPARD_DEFAULT_REGULARIZATION 0
92 
93 // ----------------------------------------------------------------------------
94 
124 template <typename T>
125 class PCL_CLASS ShepardInterpolation
126 {
127 public:
128 
133 
137  using scalar = typename vector_type::scalar;
138 
143 
148 
152  constexpr static int BucketCapacity = 16;
153 
157  ShepardInterpolation() = default;
158 
165 
170 
175  {
176  }
177 
183  ShepardInterpolation& operator =( const ShepardInterpolation& ) = delete;
184 
188  ShepardInterpolation& operator =( ShepardInterpolation&& ) = default;
189 
194  bool IsValid() const
195  {
196  return !m_T.IsEmpty();
197  }
198 
220  void SetPower( int m )
221  {
222  PCL_PRECONDITION( m > 0 )
223  m_mu = (m > 0) ? m : __PCL_SHEPARD_DEFAULT_POWER;
224  }
225 
231  int Power() const
232  {
233  return m_mu;
234  }
235 
262  void SetRadius( double R )
263  {
264  PCL_PRECONDITION( R > 0 )
265  m_R = (R < 0 || 1 + R == 1) ? __PCL_SHEPARD_DEFAULT_SEARCH_RADIUS : R;
266  }
267 
272  double Radius() const
273  {
274  return m_R;
275  }
276 
294  void SetSmoothing( float r )
295  {
296  PCL_PRECONDITION( r >= 0 && r < 1 )
297  m_r = (r < 0 || r >= 1) ? __PCL_SHEPARD_DEFAULT_REGULARIZATION : r;
298  }
299 
304  float Smoothing() const
305  {
306  return m_r;
307  }
308 
332  void Initialize( const T* x, const T* y, const T* z, int n )
333  {
334  DoInitialize( nullptr/*rect*/, x, y, z, n );
335  }
336 
372  void Initialize( const search_rect& rect, const T* x, const T* y, const T* z, int n )
373  {
374  DoInitialize( &rect, x, y, z, n );
375  }
376 
394  T operator ()( double x, double y ) const
395  {
396  PCL_PRECONDITION( !m_T.IsEmpty() )
397 
398  const double dx = m_r0*(x - m_x0);
399  const double dy = m_r0*(y - m_y0);
400 
401  for ( double R = m_R; ; R += m_R )
402  {
403  int m = 0;
404  double R2 = R*R;
405  Array<DPoint> V;
406  m_T.Search( DRect( dx-R, dy-R, dx+R, dy+R ),
407  [&]( const vector_type& v, void* )
408  {
409  double x = dx - v[0];
410  double y = dy - v[1];
411  double r2 = x*x + y*y;
412  if ( r2 < R2 )
413  {
414  ++m;
415  double w = PowI( 1 - Sqrt( r2 )/R, m_mu );
416  /*
417  * N.B. The equivalent code below is about 400 times
418  * slower than the above call to PowI() for m_mu=16.
419  * Measured on a TR 2990WX.
420  *
421  double w = 1 - Sqrt( r2 )/R;
422  for ( int i = 1; i < m_mu; ++i )
423  w *= w;
424  */
425  V << DPoint( w, w*v[2] );
426  }
427  },
428  nullptr );
429  if ( m >= 3 )
430  {
431  if ( m_r > 0 )
432  {
433  /*
434  * Regularization by Winsorization of the weighted sample.
435  */
436  int r = Min( TruncInt( m_r * m ), (m >> 1) - (m & 1)^1 );
437  if ( r > 0 )
438  {
439  V.Sort( []( const DPoint& v1, const DPoint& v2 )
440  {
441  return v1.y < v2.y;
442  } );
443  for ( int i = 0; i < r; ++i )
444  {
445  V[i].y = V[r].y;
446  V[m-i-1].y = V[m-r-1].y;
447  }
448  }
449  }
450  DPoint Wz( 0 );
451  for ( const DPoint& v : V )
452  Wz += v;
453  if ( 1 + Wz.x != 1 )
454  return T( Wz.y/Wz.x );
455  if ( R >= 1 )
456  break; // degenerate!
457  }
458  }
459 
460  return 0; // empty!?
461  }
462 
468  template <typename Tp>
469  T operator ()( const GenericPoint<Tp>& p ) const
470  {
471  return operator ()( double( p.x ), double( p.y ) );
472  }
473 
478  void Clear()
479  {
480  m_T.Clear();
481  }
482 
483 protected:
484 
485  double m_r0 = 1; // scaling factor for normalization of node coordinates
486  double m_x0 = 0; // zero offset for normalization of X node coordinates
487  double m_y0 = 0; // zero offset for normalization of Y node coordinates
488  int m_mu = __PCL_SHEPARD_DEFAULT_POWER; // power parameter (leveling factor)
489  double m_R = __PCL_SHEPARD_DEFAULT_SEARCH_RADIUS; // initial search radius
490  float m_r = __PCL_SHEPARD_DEFAULT_REGULARIZATION; // regularization (clipping fraction)
491  search_tree m_T; // tree points store input coordinates and function values
492 
498  void DoInitialize( const search_rect* rect, const T* x, const T* y, const T* z, int n )
499  {
500  PCL_PRECONDITION( x != nullptr && y != nullptr && z != nullptr )
501  PCL_PRECONDITION( n > 2 )
502 
503  if ( n < 3 )
504  throw Error( "ShepardInterpolation::Initialize(): At least three input nodes must be specified." );
505 
506  Clear();
507 
508  try
509  {
510  if ( rect == nullptr )
511  {
512  /*
513  * Find mean coordinate values.
514  */
515  m_x0 = m_y0 = 0;
516  for ( int i = 0; i < n; ++i )
517  {
518  m_x0 += x[i];
519  m_y0 += y[i];
520  }
521  m_x0 /= n;
522  m_y0 /= n;
523 
524  /*
525  * Find radius of unit circle.
526  */
527  m_r0 = 0;
528  for ( int i = 0; i < n; ++i )
529  {
530  double dx = x[i] - m_x0;
531  double dy = y[i] - m_y0;
532  double r = Sqrt( dx*dx + dy*dy );
533  if ( r > m_r0 )
534  m_r0 = r;
535  }
536  }
537  else
538  {
539  m_x0 = rect->CenterX();
540  m_y0 = rect->CenterY();
541  m_r0 = rect->Diagonal()/2;
542  }
543 
544  if ( 1 + m_r0 == 1 )
545  throw Error( "ShepardInterpolation::Initialize(): Empty or insignificant interpolation space." );
546  m_r0 = 1/m_r0;
547 
548  /*
549  * Build working vector. Transform coordinates to the unit circle.
550  */
552  for ( int i = 0; i < n; ++i )
553  V << vector_type( m_r0*(x[i] - m_x0), m_r0*(y[i] - m_y0), z[i] );
554 
555  /*
556  * Find and remove duplicate input nodes. Two nodes are equal if their
557  * coordinates don't differ more than the machine epsilon for the
558  * floating point type T.
559  */
560  V.Sort( []( const vector_type& p, const vector_type& q )
561  {
562  return (p[0] != q[0]) ? p[0] < q[0] : p[1] < q[1];
563  } );
564  Array<int> remove;
565  for ( int i = 0, j = 1; j < n; ++i, ++j )
566  if ( Abs( V[i][0] - V[j][0] ) <= std::numeric_limits<T>::epsilon() )
567  if ( Abs( V[i][1] - V[j][1] ) <= std::numeric_limits<T>::epsilon() )
568  remove << i;
569  if ( !remove.IsEmpty() )
570  {
572  int i = 0;
573  for ( int j : remove )
574  {
575  for ( ; i < j; ++i )
576  U << V[i];
577  ++i;
578  }
579  for ( ; i < n; ++i )
580  U << V[i];
581  if ( U.Length() < 3 )
582  throw Error( "ShepardInterpolation::Initialize(): Less than three input nodes left after sanitization." );
583  V = U;
584  }
585 
586  /*
587  * Build the point search tree.
588  */
589  if ( rect == nullptr )
590  m_T.Build( V, BucketCapacity );
591  else
592  {
593  m_T.Build( *rect, V, BucketCapacity );
594  if ( m_T.Length() < 3 )
595  throw Error( "ShepardInterpolation::Initialize(): Less than three input nodes in the specified search region." );
596  }
597  }
598  catch ( ... )
599  {
600  Clear();
601  throw;
602  }
603  }
604 };
605 
606 // ----------------------------------------------------------------------------
607 
619 template <class P = DPoint>
621 {
622 public:
623 
627  using point = P;
628 
633 
638 
644 
649 
654 
663  int power = __PCL_SHEPARD_DEFAULT_POWER,
664  double radius = __PCL_SHEPARD_DEFAULT_SEARCH_RADIUS )
665  {
666  Initialize( P1, P2, power, radius );
667  }
668 
676  PointShepardInterpolation( const surface& Sx, const surface& Sy )
677  {
678  Initialize( Sx, Sy );
679  }
680 
686  PointShepardInterpolation& operator =( const PointShepardInterpolation& ) = delete;
687 
692 
725  void Initialize( const point_list& P1, const point_list& P2,
726  int power = __PCL_SHEPARD_DEFAULT_POWER,
727  double radius = __PCL_SHEPARD_DEFAULT_SEARCH_RADIUS,
728  float smoothing = __PCL_SHEPARD_DEFAULT_REGULARIZATION )
729  {
730  PCL_PRECONDITION( P1.Length() >= 3 )
731  PCL_PRECONDITION( P1.Length() <= P2.Length() )
732  PCL_PRECONDITION( power > 0 )
733  PCL_PRECONDITION( radius > 0 )
734  PCL_PRECONDITION( smoothing >= 0 && smoothing < 1 )
735 
736  m_Sx.Clear();
737  m_Sy.Clear();
738 
739  m_Sx.SetPower( power );
740  m_Sy.SetPower( power );
741 
742  m_Sx.SetRadius( radius );
743  m_Sy.SetRadius( radius );
744 
745  m_Sx.SetSmoothing( smoothing );
746  m_Sy.SetSmoothing( smoothing );
747 
748  if ( P1.Length() < 3 || P2.Length() < 3 )
749  throw Error( "PointShepardInterpolation::Initialize(): At least three input nodes must be specified." );
750 
751  if ( P2.Length() < P1.Length() )
752  throw Error( "PointShepardInterpolation::Initialize(): Incompatible point array lengths." );
753 
754  DVector X( int( P1.Length() ) ),
755  Y( int( P1.Length() ) ),
756  Zx( int( P1.Length() ) ),
757  Zy( int( P1.Length() ) );
758  for ( int i = 0; i < X.Length(); ++i )
759  {
760  X[i] = P1[i].x;
761  Y[i] = P1[i].y;
762  Zx[i] = P2[i].x;
763  Zy[i] = P2[i].y;
764  }
765  m_Sx.Initialize( X.Begin(), Y.Begin(), Zx.Begin(), X.Length() );
766  m_Sy.Initialize( X.Begin(), Y.Begin(), Zy.Begin(), X.Length() );
767  }
768 
773  void Clear()
774  {
775  m_Sx.Clear();
776  m_Sy.Clear();
777  }
778 
783  bool IsValid() const
784  {
785  return m_Sx.IsValid() && m_Sy.IsValid();
786  }
787 
792  const surface& SurfaceX() const
793  {
794  return m_Sx;
795  }
796 
801  const surface& SurfaceY() const
802  {
803  return m_Sy;
804  }
805 
809  template <typename T>
810  DPoint operator ()( T x, T y ) const
811  {
812  return DPoint( m_Sx( x, y ), m_Sy( x, y ) );
813  }
814 
818  template <typename T>
819  DPoint operator ()( const GenericPoint<T>& p ) const
820  {
821  return operator ()( p.x, p.y );
822  }
823 
824 private:
825 
826  /*
827  * The surface interpolations in the X and Y plane directions.
828  */
829  surface m_Sx, m_Sy;
830 
831  friend class DrizzleData;
832  friend class DrizzleDataDecoder;
833 };
834 
835 // ----------------------------------------------------------------------------
836 
837 } // pcl
838 
839 #endif // __PCL_ShepardInterpolation_h
840 
841 // ----------------------------------------------------------------------------
842 // EOF pcl/ShepardInterpolation.h - Released 2024-06-18T15:48:54Z
bool IsEmpty() const noexcept
Definition: Array.h:312
size_type Length() const noexcept
Definition: Array.h:266
Drizzle integration data parser and generator.
Definition: DrizzleData.h:143
A simple exception with an associated error message.
Definition: Exception.h:239
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:314
Generic vector of arbitrary length.
Definition: Vector.h:107
iterator Begin()
Definition: Vector.h:1900
Vector Shepard interpolation/approximation in two dimensions.
const surface & SurfaceX() const
PointShepardInterpolation(const surface &Sx, const surface &Sy)
PointShepardInterpolation(const point_list &P1, const point_list &P2, int power=__PCL_SHEPARD_DEFAULT_POWER, double radius=__PCL_SHEPARD_DEFAULT_SEARCH_RADIUS)
PointShepardInterpolation(const PointShepardInterpolation &)=default
const surface & SurfaceY() const
void Initialize(const point_list &P1, const point_list &P2, int power=__PCL_SHEPARD_DEFAULT_POWER, double radius=__PCL_SHEPARD_DEFAULT_SEARCH_RADIUS, float smoothing=__PCL_SHEPARD_DEFAULT_REGULARIZATION)
PointShepardInterpolation(PointShepardInterpolation &&)=default
Two-dimensional surface interpolation with the local Shepard method.
ShepardInterpolation(const ShepardInterpolation &)=delete
ShepardInterpolation(ShepardInterpolation &&)=default
void DoInitialize(const search_rect *rect, const T *x, const T *y, const T *z, int n)
typename vector_type::scalar scalar
typename search_tree::rectangle search_rect
void Initialize(const search_rect &rect, const T *x, const T *y, const T *z, int n)
void Initialize(const T *x, const T *y, const T *z, int n)
Complex< T > Sqrt(const Complex< T > &c) noexcept
Definition: Complex.h:674
T Abs(const Complex< T > &c) noexcept
Definition: Complex.h:429
T PowI(T x, int n) noexcept
Definition: Math.h:1755
int TruncInt(T x) noexcept
Definition: Math.h:1132
constexpr const T & Min(const T &a, const T &b) noexcept
Definition: Utility.h:90
PCL root namespace.
Definition: AbstractImage.h:77