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 itkUnsharpMaskImageFilter_h
19 #define itkUnsharpMaskImageFilter_h
20 
21 #include "itkImageToImageFilter.h"
22 #include "itkSmoothingRecursiveGaussianImageFilter.h"
23 
24 namespace itk
25 {
26 /**
27  * \class UnsharpMaskImageFilter
28  * \brief Edge enhancement filter.
29  *
30  * This filter subtracts a smoothed version of the image from the image
31  * to achieve the edge enhancing effect.
32  * https://en.wikipedia.org/w/index.php?title=Unsharp_masking&oldid=750486803#Photographic_unsharp_masking
33  *
34  * It has configurable amount, radius (sigma) and threshold,
35  * and whether to clamp the resulting values to the range of output type.
36  *
37  * Formula: sharpened=original+[abs(original-blurred)-threshold]*amount
38  *
39  * If clamping is turned off (it is on by default),
40  * casting to output pixel format is done using C++ defaults,
41  * meaning that values are not clamped but rather wrap around
42  * e.g. 260 -> 4 (unsigned char).
43  *
44  * \sa ImageToImageFilter
45  * \sa SmoothingRecursiveGaussianImageFilter
46  * \sa RescaleIntensityImageFilter
47  *
48  * \ingroup ImageFeatureExtraction
49  *
50  * \ingroup ITKImageFeature
51  *
52  */
53 
54 template< typename TInputImage, typename TOutputImage = TInputImage, typename TInternalPrecision = float >
55 class ITK_TEMPLATE_EXPORT UnsharpMaskImageFilter:
56   public ImageToImageFilter< TInputImage, TOutputImage >
57 {
58 public:
59   ITK_DISALLOW_COPY_AND_ASSIGN(UnsharpMaskImageFilter);
60 
61   /**
62    * Standard "Self" & Superclass type alias.
63    */
64   using Self = UnsharpMaskImageFilter;
65   using Superclass = ImageToImageFilter< TInputImage, TOutputImage >;
66 
67   /**
68    * Extract some information from the image types.
69    */
70   using OutputPixelType = typename TOutputImage::PixelType;
71   using OutputInternalPixelType = typename TOutputImage::InternalPixelType;
72   using InputPixelType = typename TInputImage::PixelType;
73   using InputInternalPixelType = typename TInputImage::InternalPixelType;
74   using OutputImageRegionType = typename TOutputImage::RegionType;
75   using InputImageRegionType = typename TInputImage::RegionType;
76   static constexpr unsigned int ImageDimension = TOutputImage::ImageDimension;
77   static constexpr unsigned int InputImageDimension = TInputImage::ImageDimension;
78 
79   /**
80    * Image type alias support
81    */
82   using InputImageType = TInputImage;
83   using OutputImageType = TOutputImage;
84   using InputImagePointer = typename InputImageType::Pointer;
85 
86   using InternalPrecisionType = TInternalPrecision;
87 
88   /**
89    * Smart pointer type alias support
90    */
91   using Pointer = SmartPointer< Self >;
92   using ConstPointer = SmartPointer< const Self >;
93 
94   /**
95    * Run-time type information (and related methods)
96    */
97   itkTypeMacro(UnsharpMaskImageFilter, ImageToImageFilter);
98 
99   /**
100    * Method for creation through the object factory.
101    */
102   itkNewMacro(Self);
103 
104 #ifdef ITK_USE_CONCEPT_CHECKING
105   itkConceptMacro( SameDimensionCheck,
106     ( Concept::SameDimension< InputImageDimension, ImageDimension > ) );
107   itkConceptMacro( InputHasNumericTraitsCheck,
108     ( Concept::HasNumericTraits< OutputPixelType > ) );
109   itkConceptMacro( InternalTypeIsFloatingPoint,
110     ( Concept::IsFloatingPoint< TInternalPrecision > ) );
111 #endif
112 
113   using GaussianType = SmoothingRecursiveGaussianImageFilter<
114     TInputImage, Image<TInternalPrecision, TOutputImage::ImageDimension> >;
115 
116   using SigmaArrayType = typename GaussianType::SigmaArrayType;
117 
118   /** Set/Get Sigma values measured in the units of image spacing. Default: 1.0. */
119   itkSetMacro(Sigmas, SigmaArrayType);
120   itkGetConstMacro(Sigmas, SigmaArrayType);
121 
122   /** Convenience method for setting all dimensional parameters
123   * to the same values. */
SetSigma(const typename SigmaArrayType::ValueType sigma)124   void SetSigma(const typename SigmaArrayType::ValueType sigma)
125   {
126     SigmaArrayType sigmas;
127     sigmas.Fill(sigma);
128     this->SetSigmas(sigmas); //checks whether it is actually modified
129   }
130 
131   /** Set/Get amount of enhancement. Usual range: 0.1 to 2.0. Default: 0.5. */
132   itkSetMacro(Amount, TInternalPrecision);
133   itkGetConstMacro(Amount, TInternalPrecision);
134 
135 
136   /** Set/Get threshold for enhancement. Default: 0. */
137   itkSetMacro(Threshold, TInternalPrecision);
138   itkGetConstMacro(Threshold, TInternalPrecision);
139 
140   /** Set/Get whether to clamp values to supported
141   * range of output type. Default: On. */
142   itkSetMacro(Clamp, bool);
143   itkGetConstMacro(Clamp, bool);
144   itkBooleanMacro(Clamp);
145 
146 protected:
147   UnsharpMaskImageFilter();
148   ~UnsharpMaskImageFilter() override = default;
149 
150   /**
151   * UnsharpMaskImageFilter needs a larger input requested region than
152   * the output requested region (larger by the size of the
153   * Gaussian kernel).  As such, UnsharpMaskImageFilter needs to
154   * provide an implementation for GenerateInputRequestedRegion() in
155   * order to inform the pipeline execution model.
156   * \sa ImageToImageFilter::GenerateInputRequestedRegion() */
157   void GenerateInputRequestedRegion() override;
158 
159   void VerifyPreconditions() ITKv5_CONST override;
160   void GenerateData() override;
161 
162   void PrintSelf(std::ostream & os, Indent indent) const override;
163 
164 private:
165   /** The edge amplification amount */
166   TInternalPrecision m_Amount;
167   TInternalPrecision m_Threshold;
168   SigmaArrayType     m_Sigmas;
169   bool               m_Clamp;
170 
171   template<typename InPixelType, typename FunctorRealType = TInternalPrecision, typename OutPixelType = InPixelType>
172   class UnsharpMaskingFunctor
173   {
174   private:
175     FunctorRealType m_Amount;
176     FunctorRealType m_Threshold;
177     bool m_Clamp{false};
178 
179   public:
UnsharpMaskingFunctor()180     UnsharpMaskingFunctor()
181       : m_Amount(0.5),
182       m_Threshold(0.0)
183 
184     {
185     }
186 
UnsharpMaskingFunctor(FunctorRealType amount,FunctorRealType threshold,bool clamp)187     UnsharpMaskingFunctor(FunctorRealType amount, FunctorRealType threshold, bool clamp)
188       : m_Amount(amount),
189       m_Threshold(threshold),
190       m_Clamp(clamp)
191     {
192       assert(m_Threshold >= 0.0);
193     }
194 
195     bool operator==(const UnsharpMaskingFunctor & other)
196     {
197       return (m_Amount == other.m_Amount)
198              && (m_Threshold == other.m_Threshold)
199              && (m_Clamp == other.m_Clamp);
200     }
201 
202     bool operator!= (const UnsharpMaskingFunctor & other)
203     {
204       return !(*this == other);
205     }
206 
operator()207     inline OutPixelType operator()(const InPixelType & v, const FunctorRealType & s) const
208     {
209       FunctorRealType diff = v - s;
210       FunctorRealType result;
211       if (diff > m_Threshold)
212         {
213         result = v + (diff - m_Threshold)*m_Amount;
214         }
215       else if (-diff > m_Threshold)
216         {
217         result = v + (diff + m_Threshold)*m_Amount;
218         }
219       else
220         {
221         result = v;
222         }
223 
224       if (m_Clamp)
225         {
226         if (result < itk::NumericTraits< OutPixelType >::NonpositiveMin())
227           {
228           return itk::NumericTraits< OutPixelType >::NonpositiveMin();
229           }
230         else if (result > itk::NumericTraits< OutPixelType >::max())
231           {
232           return itk::NumericTraits< OutPixelType >::max();
233           }
234         }
235 
236       return static_cast<OutPixelType>(result);
237     }
238   }; //end UnsharpMaskingFunctor
239 }; //end UnsharpMaskImageFilter
240 } // end namespace itk
241 
242 #ifndef ITK_MANUAL_INSTANTIATION
243 #include "itkUnsharpMaskImageFilter.hxx"
244 #endif
245 
246 #endif
247