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