PCL
DisplayFunction.h
Go to the documentation of this file.
1 // ____ ______ __
2 // / __ \ / ____// /
3 // / /_/ // / / /
4 // / ____// /___ / /___ PixInsight Class Library
5 // /_/ \____//_____/ PCL 2.6.5
6 // ----------------------------------------------------------------------------
7 // pcl/DisplayFunction.h - Released 2024-01-13T15:47:58Z
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_DisplayFunction_h
53 #define __PCL_DisplayFunction_h
54 
56 
57 #include <pcl/Defs.h>
58 #include <pcl/Diagnostics.h>
59 
61 #include <pcl/Vector.h>
62 
63 namespace pcl
64 {
65 
66 // ----------------------------------------------------------------------------
67 
68 /*
69  * Default clipping increment in sigma units.
70  */
71 #define __PCL_AUTOSTRETCH_DEFAULT_CLIP -2.80
72 
73 /*
74  * Default target mean background in the [0,1] range.
75  */
76 #define __PCL_AUTOSTRETCH_DEFAULT_TBGD 0.25
77 
78 /*
79  * Whether to apply a single transformation to nominal RGB channels, or
80  * separate per-channel transformations.
81  */
82 #define __PCL_AUTOSTRETCH_DEFAULT_LINK false
83 
84 // ----------------------------------------------------------------------------
85 
93 class PCL_CLASS DisplayFunction : public ImageTransformation
94 {
95 public:
96 
101  : m_m( 4 ), m_s( 4 ), m_h( 4 ), m_l( 4 ), m_r( 4 )
102  {
103  Reset();
104  }
105 
124  template <class V>
125  DisplayFunction( const V& m, const V& s, const V& h, const V& l = V(), const V& r = V() )
126  : m_m( 4 ), m_s( 4 ), m_h( 4 ), m_l( 4 ), m_r( 4 )
127  {
128  Reset();
129  for ( int i = 0; i < 4 && i < int( m.Length() ); ++i ) m_m[i] = Range( double( m[i] ), 0.0, 1.0 );
130  for ( int i = 0; i < 4 && i < int( s.Length() ); ++i ) m_s[i] = Range( double( s[i] ), 0.0, 1.0 );
131  for ( int i = 0; i < 4 && i < int( h.Length() ); ++i ) m_h[i] = Range( double( h[i] ), 0.0, 1.0 );
132  for ( int i = 0; i < 4 && i < int( l.Length() ); ++i ) m_l[i] = Max( 0.0, double( l[i] ) );
133  for ( int i = 0; i < 4 && i < int( r.Length() ); ++i ) m_r[i] = Min( 1.0, double( r[i] ) );
134  for ( int i = 0; i < 4; ++i )
135  if ( m_h[i] < m_s[i] )
136  pcl::Swap( m_s[i], m_h[i] );
137  }
138 
142  DisplayFunction( const DisplayFunction& ) = default;
143 
147  DisplayFunction( DisplayFunction&& ) = default;
148 
152  DisplayFunction& operator =( const DisplayFunction& ) = default;
153 
157  DisplayFunction& operator =( DisplayFunction&& ) = default;
158 
162  ~DisplayFunction() override
163  {
164  }
165 
174  HistogramTransformation operator []( int i ) const
175  {
176  PCL_PRECONDITION( 0 <= i && i < 4 )
177  PCL_CHECK( m_m.Length() == 4 && m_s.Length() == 4 && m_h.Length() == 4 && m_l.Length() == 4 && m_r.Length() == 4 )
178  if ( i >= 0 && i < 4 )
179  return HistogramTransformation( m_m[i], m_s[i], m_h[i], m_l[i], m_r[i] );
180  return HistogramTransformation();
181  }
182 
191  {
192  return Array<HistogramTransformation>() << operator[]( 0 )
193  << operator[]( 1 )
194  << operator[]( 2 )
195  << operator[]( 3 );
196  }
197 
211  template <class V>
212  void GetDisplayFunctionParameters( V& m, V& s, V& h, V& l, V& r ) const
213  {
214  m = m_m; s = m_s; h = m_h; l = m_l; r = m_r;
215  }
216 
224  bool IsIdentityTransformation( int i ) const
225  {
226  PCL_PRECONDITION( 0 <= i && i < 4 )
227  PCL_CHECK( m_m.Length() == 4 && m_s.Length() == 4 && m_h.Length() == 4 && m_l.Length() == 4 && m_r.Length() == 4 )
228  return i >= 0 && i < 4 && m_m[i] == 0.5 && m_s[i] == 0 && m_h[i] == 1 && m_l[i] == 0 && m_r[i] == 1;
229  }
230 
239  {
240  return IsIdentityTransformation( 0 )
241  && IsIdentityTransformation( 1 )
242  && IsIdentityTransformation( 2 )
243  && IsIdentityTransformation( 3 );
244  }
245 
250  double ClippingPoint() const
251  {
252  return m_clip;
253  }
254 
259  void SetClippingPoint( double clip )
260  {
261  m_clip = clip;
262  }
263 
268  double TargetBackground() const
269  {
270  return m_tbkg;
271  }
272 
273  /*
274  * Sets the target background parameter of this transformation in the
275  * normalized [0,1] range. The default target background is 0.25.
276  */
277  void SetTargetBackground( double tbkg )
278  {
279  m_tbkg = Range( tbkg, 0.0, 1.0 );
280  }
281 
288  bool LinkedRGB() const
289  {
290  return m_link;
291  }
292 
300  void SetLinkedRGB( bool link = true )
301  {
302  m_link = link;
303  }
304 
326  template <class V>
327  void ComputeAutoStretch( const V& sigma, const V& center )
328  {
329  PCL_CHECK( m_m.Length() == 4 && m_s.Length() == 4 && m_h.Length() == 4 && m_l.Length() == 4 && m_r.Length() == 4 )
330 
331  if ( sigma.IsEmpty() || center.IsEmpty() )
332  {
333  Reset();
334  return;
335  }
336 
337  int n = (sigma.Length() < 3 || center.Length() < 3) ? 1 : 3;
338 
339  if ( m_link )
340  {
341  /*
342  * Try to find out how many channels look like channels of an inverted
343  * image.
344  *
345  * We know a channel is inverted because the main histogram peak is
346  * located over the right-hand half of the histogram. Seems simplistic
347  * but this is consistent with most real-world images.
348  */
349  int invertedChannels = 0;
350  for ( int i = 0; i < n; ++i )
351  if ( center[i] > 0.5 )
352  ++invertedChannels;
353 
354  double c = 0, m = 0;
355  if ( invertedChannels < n )
356  {
357  // Noninverted image
358  for ( int i = 0; i < n; ++i )
359  {
360  if ( 1 + sigma[i] != 1 )
361  c += center[i] + m_clip * sigma[i];
362  m += center[i];
363  }
364  m_s[0] = m_s[1] = m_s[2] = Range( c/n, 0.0, 1.0 );
365  m_m[0] = m_m[1] = m_m[2] = HistogramTransformation::MTF( m_tbkg, m/n - m_s[0] );
366  m_l[0] = m_l[1] = m_l[2] = 0.0;
367  m_h[0] = m_h[1] = m_h[2] = m_r[0] = m_r[1] = m_r[2] = 1.0;
368  }
369  else
370  {
371  // Inverted image
372  for ( int i = 0; i < n; ++i )
373  {
374  c += (1 + sigma[i] != 1) ? center[i] - m_clip * sigma[i] : 1.0;
375  m += center[i];
376  }
377  m_h[0] = m_h[1] = m_h[2] = Range( c/n, 0.0, 1.0 );
378  m_m[0] = m_m[1] = m_m[2] = HistogramTransformation::MTF( m_h[0] - m/n, m_tbkg );
379  m_s[0] = m_s[1] = m_s[2] = m_l[0] = m_l[1] = m_l[2] = 0.0;
380  m_r[0] = m_r[1] = m_r[2] = 1.0;
381  }
382  }
383  else
384  {
385  /*
386  * Unlinked RGB/K channnels: Compute automatic stretch functions for
387  * individual RGB/K channels separately.
388  */
389  for ( int i = 0; i < n; ++i )
390  if ( center[i] < 0.5 )
391  {
392  // Noninverted channel
393  m_s[i] = (1 + sigma[i] != 1) ? Range( center[i] + m_clip * sigma[i], 0.0, 1.0 ) : 0.0;
394  m_m[i] = HistogramTransformation::MTF( m_tbkg, center[i] - m_s[i] );
395  m_l[i] = 0.0;
396  m_h[i] = m_r[i] = 1.0;
397  }
398  else
399  {
400  // Inverted channel
401  m_h[i] = (1 + sigma[i] != 1) ? Range( center[i] - m_clip * sigma[i], 0.0, 1.0 ) : 1.0;
402  m_m[i] = HistogramTransformation::MTF( m_h[i] - center[i], m_tbkg );
403  m_s[i] = m_l[i] = 0.0;
404  m_r[i] = 1.0;
405  }
406  }
407  }
408 
413  void Reset()
414  {
415  m_m = 0.5;
416  m_s = m_l = 0.0;
417  m_h = m_r = 1.0;
418  }
419 
423  friend void Swap( DisplayFunction& x, DisplayFunction& y )
424  {
425  pcl::Swap( x.m_m, y.m_m );
426  pcl::Swap( x.m_s, y.m_s );
427  pcl::Swap( x.m_h, y.m_h );
428  pcl::Swap( x.m_l, y.m_l );
429  pcl::Swap( x.m_r, y.m_r );
430  pcl::Swap( x.m_clip, y.m_clip );
431  pcl::Swap( x.m_tbkg, y.m_tbkg );
432  pcl::Swap( x.m_link, y.m_link );
433  }
434 
435 private:
436 
437  DVector m_m; // midtones balance
438  DVector m_s; // shadows clipping point
439  DVector m_h; // highlights clipping point
440  DVector m_l; // shadows dynamic range expansion
441  DVector m_r; // highlights dynamic range expansion
442  double m_clip = __PCL_AUTOSTRETCH_DEFAULT_CLIP; // auto-stretch clipping point
443  double m_tbkg = __PCL_AUTOSTRETCH_DEFAULT_TBGD; // auto-stretch target background
444  bool m_link = __PCL_AUTOSTRETCH_DEFAULT_LINK; // auto-stretch linked RGB
445 
446  /*
447  * Display function transformation.
448  */
449  void Apply( pcl::Image& ) const override;
450  void Apply( pcl::DImage& ) const override;
451  void Apply( pcl::UInt8Image& ) const override;
452  void Apply( pcl::UInt16Image& ) const override;
453  void Apply( pcl::UInt32Image& ) const override;
454 };
455 
456 // ----------------------------------------------------------------------------
457 
458 } // pcl
459 
460 #endif // __PCL_DisplayFunction_h
461 
462 // ----------------------------------------------------------------------------
463 // EOF pcl/DisplayFunction.h - Released 2024-01-13T15:47:58Z
pcl
PCL root namespace.
Definition: AbstractImage.h:76
pcl::Range
constexpr const T & Range(const T &x, const T &a, const T &b) noexcept
Definition: Utility.h:190
Vector.h
pcl::DisplayFunction::SetLinkedRGB
void SetLinkedRGB(bool link=true)
Definition: DisplayFunction.h:300
pcl::DisplayFunction::TargetBackground
double TargetBackground() const
Definition: DisplayFunction.h:268
pcl::DisplayFunction::SetClippingPoint
void SetClippingPoint(double clip)
Definition: DisplayFunction.h:259
pcl::ImageTransformation
Root base class of all PCL image transformations.
Definition: ImageTransformation.h:90
pcl::DisplayFunction::IsIdentityTransformation
bool IsIdentityTransformation(int i) const
Definition: DisplayFunction.h:224
pcl::Max
constexpr const T & Max(const T &a, const T &b) noexcept
Definition: Utility.h:119
pcl::DisplayFunction::HistogramTransformations
Array< HistogramTransformation > HistogramTransformations() const
Definition: DisplayFunction.h:190
pcl::HistogramTransformation::MTF
static double MTF(double m, double x)
Definition: HistogramTransformation.h:425
pcl::HistogramTransformation
Multiple histogram transformation.
Definition: HistogramTransformation.h:76
pcl::DisplayFunction::~DisplayFunction
~DisplayFunction() override
Definition: DisplayFunction.h:162
pcl::DisplayFunction::Reset
void Reset()
Definition: DisplayFunction.h:413
pcl::DisplayFunction::ClippingPoint
double ClippingPoint() const
Definition: DisplayFunction.h:250
pcl::Apply
void Apply(FI i, FI j, F f) noexcept(noexcept(f))
Definition: Utility.h:249
pcl::DisplayFunction::Swap
friend void Swap(DisplayFunction &x, DisplayFunction &y)
Definition: DisplayFunction.h:423
pcl::Array< HistogramTransformation >
pcl::DisplayFunction::IsIdentityTransformation
bool IsIdentityTransformation() const
Definition: DisplayFunction.h:238
pcl::DisplayFunction::DisplayFunction
DisplayFunction()
Definition: DisplayFunction.h:100
pcl::Swap
void Swap(GenericPoint< T > &p1, GenericPoint< T > &p2) noexcept
Definition: Point.h:1459
pcl::Min
constexpr const T & Min(const T &a, const T &b) noexcept
Definition: Utility.h:90
pcl::DisplayFunction::DisplayFunction
DisplayFunction(const V &m, const V &s, const V &h, const V &l=V(), const V &r=V())
Definition: DisplayFunction.h:125
pcl::DisplayFunction::LinkedRGB
bool LinkedRGB() const
Definition: DisplayFunction.h:288
pcl::DisplayFunction
Adaptive histogram transformations for image visualization.
Definition: DisplayFunction.h:93
pcl::DisplayFunction::ComputeAutoStretch
void ComputeAutoStretch(const V &sigma, const V &center)
Definition: DisplayFunction.h:327
Defs.h
pcl::GenericVector< double >
HistogramTransformation.h
pcl::DisplayFunction::GetDisplayFunctionParameters
void GetDisplayFunctionParameters(V &m, V &s, V &h, V &l, V &r) const
Definition: DisplayFunction.h:212
pcl::GenericImage
Implements a generic, two-dimensional, shared or local image.
Definition: Image.h:277