1 /*=========================================================================
2  *
3  *  Copyright Insight Software Consortium
4  *
5  *  Licensed under the Apache License, Version 2.0 (the "License");
6  *  you may not use this file except in compliance with the License.
7  *  You may obtain a copy of the License at
8  *
9  *         http://www.apache.org/licenses/LICENSE-2.0.txt
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  *
17  *=========================================================================*/
18 #ifndef itkNormalVectorDiffusionFunction_h
19 #define itkNormalVectorDiffusionFunction_h
20 
21 #include "itkNormalVectorFunctionBase.h"
22 #include "itkNumericTraits.h"
23 #include <cmath>
24 
25 namespace itk
26 {
27 /**
28  * \class NormalVectorDiffusionFunction
29  *
30  * \brief This class defines all the necessary functionality for performing
31  * isotropic and anisotropic diffusion operations on vector neighborhoods from
32  * a sparse image.
33  *
34  * \par
35  * This class implements the actual computations for performing isotropic and
36  * anisotropic diffusion operations on a neighborhood of unit length
37  * vectors. Moreover, this processing is intrinsic to a manifold as specified
38  * by the ManifoldNormal member variables in the nodes of the sparse image.
39  *
40  * \par
41  * Since the only difference between isotropic and anisotropic diffusion is the
42  * exectution of 1 extra line of code, we have implemented both in this class
43  * and made the choice between the two depend on a parameter (see below).
44 
45  * \par PARAMETERS
46  * The choice between is isotropic/anisotropic diffusion is made by the
47  * parameter NormalProcessType. A value of 0 corresponds to isotropic diffusion
48  * whereas a value of 1 corresponds to anisotropic diffusion. If anisotropic
49  * diffusion is chosen, the parameter ConductanceParameter should be set. This
50  * conductance parameter determines the level of feature preservation.
51  *
52  * \par IMPORTANT
53  * This class works on SparseImage neighborhoods. Before using this class
54  * please read the documentation for SparseImage. Also the documentation for
55  * ImplicitManifoldNormalVectorField class will be helpful in understanding how
56  * to use this class as a function object.
57  * \ingroup ITKLevelSets
58  */
59 template< typename TSparseImageType >
60 class ITK_TEMPLATE_EXPORT NormalVectorDiffusionFunction:
61   public NormalVectorFunctionBase< TSparseImageType >
62 {
63 public:
64   ITK_DISALLOW_COPY_AND_ASSIGN(NormalVectorDiffusionFunction);
65 
66   /** Standard class type alias. */
67   using Self = NormalVectorDiffusionFunction;
68   using Superclass = NormalVectorFunctionBase< TSparseImageType >;
69   using Pointer = SmartPointer< Self >;
70   using ConstPointer = SmartPointer< const Self >;
71 
72   /** Run-time type information (and related methods) */
73   itkTypeMacro(NormalVectorDiffusionFunction, NormalVectorFunctionBase);
74 
75   /** Image dimension derived from the superclass. */
76   static constexpr unsigned int ImageDimension = Superclass::ImageDimension;
77 
78   /** Standard New macro. */
79   itkNewMacro(Self);
80 
81   /** Typedefs from the superclass. */
82   using TimeStepType = typename Superclass::TimeStepType;
83   using RadiusType = typename Superclass::RadiusType;
84   using NeighborhoodType = typename Superclass::NeighborhoodType;
85   using NeighborhoodScalesType = typename Superclass::NeighborhoodScalesType;
86   using FloatOffsetType = typename Superclass::FloatOffsetType;
87   using IndexType = typename Superclass::IndexType;
88   using SparseImageType = typename Superclass::SparseImageType;
89   using NodeType = typename Superclass::NodeType;
90   using NodeValueType = typename Superclass::NodeValueType;
91   using NormalVectorType = typename Superclass::NormalVectorType;
92 
93   /** This method is used to choose between isotropic/anisotropic filtering. A
94       parameter value of 0 indicates isotropic diffusion and is the
95       default. Parameter value 1 is anisotropic diffusion. When using
96       anisotropic diffusion the conductance parameter should also be set. */
SetNormalProcessType(int npt)97   void SetNormalProcessType(int npt)
98   { m_NormalProcessType = npt; }
99 
100   /** This method returns the isotropic/anisotropic filtering parameter. */
GetNormalProcessType()101   int GetNormalProcessType() const
102   { return m_NormalProcessType; }
103 
104   /** This method sets the conductance parameter used in anisotropic
105    * filtering. Useful values for processing 2D and 3D shapes are between
106    *  0.1 and 0.25. Lower values preserve more shape features, higher values
107    *  smooth more. As the conductance parameter large, the processing becomes
108    *  isotropic. Default is 0. */
SetConductanceParameter(NodeValueType cp)109   void SetConductanceParameter(NodeValueType cp)
110   {
111     m_ConductanceParameter = cp + static_cast< NodeValueType >( 0.001 );
112     // we add a minimum conductance to avoid divide by zero
113     // can make this a parameter.
114     m_FluxStopConstant = static_cast< NodeValueType >
115                          ( -1.0 / ( m_ConductanceParameter * m_ConductanceParameter ) );
116   }
117 
118   /** This method returns the conductance parameter. */
GetConductanceParameter()119   NodeValueType GetConductanceParameter() const
120   { return m_ConductanceParameter; }
121 
122   /** This method returns the internal variable FluxStopConstant. */
GetFluxStopConstant()123   NodeValueType GetFluxStopConstant() const
124   { return m_FluxStopConstant; }
125 
126   /** This function is called from LevelSetNormalImageFilter for all of the
127    *  nodes to compute and store the flux vectors (first derivatives of the
128    *  normal vectors. ComputeUpdateNormal then takes derivatives of the flux
129    *  vectors. This way we avoid repeating the same flux computations. */
130   void PrecomputeSparseUpdate(NeighborhoodType & it) const override;
131 
132   /** The actual update rule for the normal vectors. */
133   NormalVectorType ComputeSparseUpdate(NeighborhoodType & neighborhood,
134                                                void *globalData,
135                                                const FloatOffsetType & offset) const override;
136 
137 protected:
138   NormalVectorDiffusionFunction();
139   ~NormalVectorDiffusionFunction() override = default;
140   void PrintSelf(std::ostream & os, Indent indent) const override;
141 
142   /** The method called in anisotropic diffusion to inhibit diffusion across
143       areas with large curvature. */
FluxStopFunction(const NodeValueType v)144   NodeValueType FluxStopFunction(const NodeValueType v) const
145   {
146     // the slow exp function could be replaced with a lookup table
147     if ( v <= 0.0 ) { return NumericTraits< NodeValueType >::OneValue(); }
148     else { return static_cast< NodeValueType >( std::exp(m_FluxStopConstant * v) ); }
149   }
150 
151 private:
152   /** The conductance parameter used for anisotropic diffusion. */
153   NodeValueType m_ConductanceParameter;
154 
155   /** The internal variable used in the FluxStopFunction. It is computed from
156    * ConductanceParameter. */
157   NodeValueType m_FluxStopConstant;
158 
159   /** The isotropic/anisotropic filtering choice parameter. */
160   int m_NormalProcessType;
161 
162 };
163 } // end namespace itk
164 
165 #ifndef ITK_MANUAL_INSTANTIATION
166 #include "itkNormalVectorDiffusionFunction.hxx"
167 #endif
168 
169 #endif
170