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 itkKLMRegionGrowImageFilter_h 19 #define itkKLMRegionGrowImageFilter_h 20 21 #include "itkImage.h" 22 #include "itkRegionGrowImageFilter.h" 23 #include "itkKLMSegmentationBorder.h" 24 #include "itkImageRegionIterator.h" 25 #include "itkConceptChecking.h" 26 #include <algorithm> 27 #include <functional> 28 29 namespace itk 30 { 31 /** \class KLMRegionGrowImageFilter 32 * \brief Base class for a region growing object that performs energy-based 33 * region growing for multiband images. 34 * 35 * itkKLMRegionGrowImageFilter is the base class for the KLMRegionGrowImageFilter objects. 36 * This object performs energy-based region growing for multiband images. 37 * Since this is based on G. Koepfler,C. Lopez and J. M. Morel's work 38 * described below, the acronym KLM is added at the end of the object name. 39 * 40 * The ApplyRegionGrowImageFilter() function implements the segmentation algorithm 41 * that partitions the input image into non-overlapping regions 42 * by minimizing an energy functional which trades off the similarity 43 * of regions against the length of their shared boundary. The heart of the 44 * process relies on the MergeRegion() method that calls a private function 45 * to perform the merging of region based on the piecewise constant KLM 46 * algorithm for region merging. For extensibility purposes, the MergeRegion() 47 * function is made virtual. Extensions can be made possible using 48 * function overloading or overriding the virtual function in a derived 49 * class. It starts by breaking the image into many small regions and fitting 50 * the regions to a polynomial model. The algorithm iteratively merges into 51 * one region the two adjoining regions which are most alike in terms 52 * of the specified polynomial model given the length of the border 53 * between the two regions. Internally, the energy functional is 54 * evaluated using a Lagrangian parameter called lambda which is also 55 * called the scale parameter as it controls the coarseness of the 56 * segmentation where a small value of lambda corresponds to a finer 57 * segmentation with more regions and a large value corresponds to a 58 * coarse segmentation with fewer regions. Since the algorithm grows 59 * regions by merging like regions, the internal value of lambda 60 * increases as the number of regions decreases. 61 * 62 * The user can stop the merging of regions using the 63 * SetMaximumNumberOfRegions() and SetMaximumLambda() functions. 64 * The SetMaximumNumberOfRegions() function is publicly 65 * inherited from its base class and internally sets the m_MaximumNumberOfRegions 66 * parameter. The SetMaximumLambda() function sets the m_MaximumLambda 67 * parameter. If the 68 * number of regions in the image is equal to m_MaximumNumberOfRegions or if the 69 * internal energy functional becomes greater than m_MaximumLambda, then the 70 * merging iterations will stop. Note that a larger energy function 71 * value for m_MaximumLambda 72 * will result in fewer boundaries and fewer regions, while a smaller value 73 * for m_MaximumLambda will result in more boundaries and more regions. To have 74 * m_MaximumNumberOfRegions control exactly the number of output regions, m_MaximumLamda 75 * should be set to a very large number. To have m_MaximumLambda control exactly 76 * the number of output regions, m_MaximumNumberOfRegions should be set to 2. As a 77 * default value, m_MaximumLambda is set to 1000 and m_MaximumNumberOfRegions 78 * is set to 2. 79 * 80 * Currently implementation puts equal weight to the multichannel values. 81 * In future improvements we plan to allow the user to control the weights 82 * associated with each individual channels. 83 * 84 * It is templated over the type of input and output image. This object 85 * supports data handling of multiband images. The object accepts images 86 * in vector format, where each pixel is a vector and each element of the 87 * vector corresponds to an entry from 1 particular band of a multiband 88 * dataset. We expect the user to provide the input to the routine in vector 89 * format. A single band image is treated as a vector image with a single 90 * element for every vector. 91 * 92 * This algorithm implementation takes a multiband image stored in vector 93 * format as input and produces two outputs. Using the ImageToImageFilter, 94 * the piecewise constant approximation image is the output calculated 95 * using the process update mechanism. The second output, i.e., the 96 * image with the region labels (segmentation image) is returned at 97 * users request by calling GetLabelledImage() function. This function 98 * returns a reference to the labelled image determined using the KLM 99 * algorithm. The algorithm supports 2D and 3D data sets only. The input 100 * image dimensions must be exact multiples of the user specified gridsizes. 101 * Appropriate padding must be performed by the user if any image which 102 * are not multiples of the gridsizes are used. 103 * 104 * For more information about the algorithm, see G. Koepfler, C. Lopez 105 * and J. M. Morel, ``A Multiscale Algorithm for Image Segmentation by 106 * Variational Method,'' {\em SIAM Journal of Numerical Analysis}, 107 * vol. 31, pp. 282-299, 1994. 108 * 109 * Algorithm details: 110 * 111 * This function segments a two-dimensional input image into 112 * non-overlapping atomic regions \f$ O_i, i=1,2,\ldots,N \f$, where 113 * \f$ N \f$ is the 114 * total number of region, by minimizing the following energy functional 115 * (also known as the simplified Mumford and Shah functional): 116 * \f$ 117 * E(u,K)=\int_{\Omega-K}||u(r,c)-g(r,c)||^2{d{\Omega}}+\lambda\cdot{L(K)} 118 * \f$, 119 * where \f$ \Omega \f$ denotes the domain of an image, \f$ g(r,c) \f$ 120 * is the input image, and \f$ u(r,c) \f$ is an approximation of 121 * \f$ g(r,c) \f$. Furthermore, \f$ u(r,c) \f$ is defined to be 122 * piecewise constant in regions \f$ O_i \f$. If 123 * \f$ \partial O_i \f$ represents the boundary of the region, 124 * \f$ K=\bigcup_{i=1}^N\partial{O_i} \f$ denotes the set of all region 125 * boundaries and \f$ L(K) \f$ is the total length of the boundaries. The 126 * parameter \f$ \lambda \f$ controls the coarseness of the segmentation 127 * (i.e. a larger \f$ \lambda \f$ will result in fewer boundaries). 128 * 129 * Starting with small, piecewise-constant initial regions the algorithm 130 * iteratively merges the two adjacent regions \f$ O_i \f$ and 131 * \f$ O_j \f$ which most 132 * decrease the energy functional. In other words, the merging criterion 133 * is based on the difference between the current energy 134 * \f$ E(u,K) \f$ and the 135 * energy that would result after a merge, 136 * \f$ E(\hat{u},K-\partial(O_i,O_j)) \f$, 137 * where \f$ \hat{u} \f$ is the piecewise constant approximation of the 138 * input image \f$ g \f$, and \f$ \partial(O_i,O_j) \f$ is the common boundary 139 * between region \f$ O_i \f$ and \f$ O_j \f$. It can be shown that 140 * \f$ E(u,K)-E(\hat{u},K-\partial(O_i,O_j))= 141 * \lambda\cdot{L(\partial(O_i,O_j))}- 142 * {\frac{(|O_i| \cdot |O_j|)}{(|O_i|+|O_j|)}} \|c_i-c_j\|^2 \f$. 143 * 144 * Once two regions are merged the following update equations are used 145 * to calculated the constant approximation of the new region: 146 * 147 * \f$ c_{i,j} = \frac{(c_i |O_i| + c_j |O_j|)}{(|O_i| + |O_j|)} \f$. 148 * 149 * Again, the merging of regions continues until the desired number of 150 * regions has been reached or until the desired coarseness (specified 151 * by the scale parameter \f$ \lambda \f$) has been reached. 152 * 153 * The two outputs are possible to derive from the object: 154 * (1) u, the piecewise constant approximation (mean of the regions) 155 * to the input image set; This is currently generated by the 156 * process object pipeline and the 157 * (2) the labelled regions in the input image set is generated by the 158 * GetLabelledImage() function. 159 * 160 * \ingroup RegionGrowingSegmentation 161 * \ingroup ITKKLMRegionGrowing 162 */ 163 164 template< typename TInputImage, typename TOutputImage > 165 class ITK_TEMPLATE_EXPORT KLMRegionGrowImageFilter:public RegionGrowImageFilter< TInputImage, TOutputImage > 166 { 167 public: 168 ITK_DISALLOW_COPY_AND_ASSIGN(KLMRegionGrowImageFilter); 169 170 /** Standard class type aliases. */ 171 using Self = KLMRegionGrowImageFilter; 172 using Superclass = RegionGrowImageFilter< TInputImage, TOutputImage >; 173 using Pointer = SmartPointer< Self >; 174 using ConstPointer = SmartPointer< const Self >; 175 176 /** Method for creation through the object factory. */ 177 itkNewMacro(Self); 178 179 /** Run-time type information (and related methods). */ 180 itkTypeMacro(KLMRegionGrowImageFilter, RegionGrowImageFilter); 181 182 /** Type definition for the input image. */ 183 using InputImageType = TInputImage; 184 using InputImagePointer = typename TInputImage::Pointer; 185 using InputImageConstPointer = typename TInputImage::ConstPointer; 186 187 /** Type definition for the input image pixel type. */ 188 using InputImagePixelType = typename TInputImage::PixelType; 189 190 /** Type definition for the input image pixel vector type. */ 191 using InputImageVectorType = typename TInputImage::PixelType::VectorType; 192 193 /** InputImageVectorDimension enumeration. */ 194 static constexpr unsigned int InputImageVectorDimension = InputImagePixelType::Dimension; 195 196 /** Type definition for the input image index type. */ 197 using InputImageIndexType = typename TInputImage::IndexType; 198 199 /** Type definition for the image iterators to be used. */ 200 using InputImageIterator = ImageRegionIterator< TInputImage >; 201 using InputImageConstIterator = ImageRegionConstIterator< TInputImage >; 202 203 /** Type definition for the image region type. */ 204 using InputRegionType = typename TInputImage::RegionType; 205 206 /** Type definition for the input grid size type used to create 207 * initial atomic regions. */ 208 using GridSizeType = typename Superclass::GridSizeType; 209 210 /** Type definition for the output image. */ 211 using OutputImageType = TOutputImage; 212 using OutputImagePointer = typename TOutputImage::Pointer; 213 214 /** InputImageDimension enumeration. */ 215 static constexpr unsigned int InputImageDimension = TInputImage::ImageDimension; 216 217 /** OutputImageDimension enumeration. */ 218 static constexpr unsigned int OutputImageDimension = TOutputImage::ImageDimension; 219 220 /** Type definition for the output image pixel type. */ 221 using OutputImagePixelType = typename TOutputImage::PixelType; 222 223 /** Type definition for the output image pixel vector type. */ 224 using OutputImageVectorType = typename TOutputImage::PixelType::VectorType; 225 226 /** OutputImageVectorDimension enumeration. */ 227 static constexpr unsigned int OutputImageVectorDimension = OutputImagePixelType::Dimension; 228 229 /** Type definition for the output image index type. */ 230 using OutputImageIndexType = typename TOutputImage::IndexType; 231 232 /** Type definition for the output image iterators. */ 233 using OutputImageIterator = ImageRegionIterator< TOutputImage >; 234 235 /** type definition for the region label type. */ 236 using RegionLabelType = typename KLMSegmentationRegion::RegionLabelType; 237 238 /** The dimension of the labelled image. */ 239 static constexpr RegionLabelType LabelImageDimension = InputImageDimension; 240 241 /** Type definition for the labelled image pixel type. */ 242 using LabelImageType = Image< RegionLabelType, Self::LabelImageDimension >; 243 244 /** Type definition for the labelled image pointer. */ 245 using LabelImagePointer = typename LabelImageType::Pointer; 246 247 /** Type definition for the labelled image pixel type. */ 248 using LabelImagePixelType = typename LabelImageType::PixelType; 249 250 /** Type definition for the labelled image index type. */ 251 using LabelImageIndexType = typename LabelImageType::IndexType; 252 253 /** Type definition for the labelled image iterators. */ 254 using LabelImageIterator = ImageRegionIterator< LabelImageType >; 255 256 /** Storage type for the mean region intensity. */ 257 using MeanRegionIntensityType = vnl_vector< double >; 258 259 /** Type definition for the smart border type. */ 260 using BorderType = KLMSegmentationBorder; 261 262 /** Type definition for the smart border pointers object. */ 263 using KLMSegmentationBorderArrayPtr = KLMDynamicBorderArray< BorderType >; 264 265 /** Set/Get the desired threshold parameter for lambda. See 266 * itkSegmentationBorder documentation for details regarding this 267 * parameter. */ 268 itkSetMacro(MaximumLambda, double); 269 itkGetConstReferenceMacro(MaximumLambda, double); 270 271 /** Set/Get the desired number of regions. */ 272 itkSetMacro(NumberOfRegions, unsigned int); 273 itkGetConstReferenceMacro(NumberOfRegions, unsigned int); 274 275 /** Generate labelled image. */ 276 LabelImagePointer GetLabelledImage(); 277 278 /** Function that prints all the region information. */ 279 void PrintAlgorithmRegionStats(); 280 281 /** Function that prints all the border information. */ 282 void PrintAlgorithmBorderStats(); 283 284 #ifdef ITK_USE_CONCEPT_CHECKING 285 // Begin concept checking 286 itkConceptMacro( InputHasNumericTraitsCheck, 287 ( Concept::HasNumericTraits< typename InputImagePixelType::ValueType > ) ); 288 itkConceptMacro( SameDimension, 289 ( Concept::SameDimension< Self::InputImageDimension, 290 Self::OutputImageDimension > ) ); 291 #if defined(THIS_CONCEPT_FAILS_ON_GCC) 292 /** The input pixel type must be the same as that of the output image. */ 293 itkConceptMacro( SameVectorDimension, 294 ( Concept::SameDimension< Self::InputImageVectorDimension, 295 Self::OutputImageVectorDimension > ) ); 296 #endif 297 // End concept checking 298 #endif 299 300 protected: 301 KLMRegionGrowImageFilter(); 302 ~KLMRegionGrowImageFilter() override = default; 303 void PrintSelf(std::ostream & os, Indent indent) const override; 304 305 /** 306 * Standard pipeline method. 307 */ 308 void GenerateData() override; 309 310 /** KLMRegionGrowImageFilter needs the entire input. Therefore 311 * it must provide an implementation GenerateInputRequestedRegion(). 312 * \sa ProcessObject::GenerateInputRequestedRegion(). */ 313 void GenerateInputRequestedRegion() override; 314 315 /** KLMRegionGrowImageFilter will produce all of the output. 316 * Therefore it must provide an implementation of 317 * EnlargeOutputRequestedRegion(). 318 * \sa ProcessObject::EnlargeOutputRequestedRegion() */ 319 void EnlargeOutputRequestedRegion(DataObject *) override; 320 321 /** This is the interface function that calls the specific algorithm 322 * implementation of region growing. */ 323 void ApplyRegionGrowImageFilter() override; 324 325 /** Function to merge two regions. 326 * The smaller label is always assigned to the new region. This is 327 * consistent with the connected components algorithm. */ 328 void MergeRegions() override; 329 330 /** Generate output approximated image. */ 331 virtual void GenerateOutputImage(); 332 333 /** Function that calls the KLM region growing algorithm. */ 334 void ApplyKLM(); 335 336 /** Initialize the RegionGrowImageFilter algorithm. */ 337 void InitializeKLM(); 338 339 /** Generate the labelled image. */ 340 LabelImagePointer GenerateLabelledImage(LabelImageType *labelImagePtr); 341 342 /** Calculate the statistics representing the region. In this 343 * case we compute the mean region intensity and the area of the 344 * initial N-dimensional rectangular area. This is the function that 345 * can be overriden in order to enable a different statistical 346 * representation for region initialization. */ 347 virtual void InitializeRegionParameters(InputRegionType region); 348 349 /** Function to resolve the region labels to be consecutively ordered. 350 * Each initial atomic region is given a new label and the aggregrate 351 * region area and mean intensity. */ 352 virtual void ResolveRegions(); 353 354 private: 355 using InputImageSizeType = typename TInputImage::SizeType; 356 using KLMSegmentationRegionPtr = typename KLMSegmentationRegion::Pointer; 357 using KLMSegmentationBorderPtr = typename KLMSegmentationBorder::Pointer; 358 359 double m_MaximumLambda{1000}; 360 unsigned int m_NumberOfRegions{0}; 361 362 /** Local variables. */ 363 364 double m_InternalLambda{0}; 365 unsigned int m_InitialNumberOfRegions{0}; 366 double m_TotalBorderLength{0.0}; 367 368 std::vector< KLMSegmentationRegionPtr > m_RegionsPointer; 369 std::vector< KLMSegmentationBorderPtr > m_BordersPointer; 370 std::vector< KLMSegmentationBorderArrayPtr > m_BordersDynamicPointer; 371 KLMSegmentationBorderArrayPtr * m_BorderCandidate{nullptr}; 372 373 MeanRegionIntensityType m_InitialRegionMean; 374 double m_InitialRegionArea{0}; 375 }; // class KLMRegionGrowImageFilter 376 } // namespace itk 377 378 #ifndef ITK_MANUAL_INSTANTIATION 379 #include "itkKLMRegionGrowImageFilter.hxx" 380 #endif 381 382 #endif 383