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 itkRegionBasedLevelSetFunction_hxx
19 #define itkRegionBasedLevelSetFunction_hxx
20 
21 #include "itkRegionBasedLevelSetFunction.h"
22 #include "itkImageRegionIteratorWithIndex.h"
23 
24 namespace itk
25 {
26 template< typename TInput,
27           typename TFeature,
28           typename TSharedData >
29 double
30 RegionBasedLevelSetFunction< TInput, TFeature, TSharedData >
31 ::m_WaveDT = 1.0 / ( 2.0 * ImageDimension );
32 
33 template< typename TInput,
34           typename TFeature,
35           typename TSharedData >
36 double
37 RegionBasedLevelSetFunction< TInput, TFeature, TSharedData >
38 ::m_DT     = 1.0 / ( 2.0 * ImageDimension );
39 
40 template< typename TInput,
41           typename TFeature,
42           typename TSharedData >
43 RegionBasedLevelSetFunction< TInput,
44                              TFeature,
45                              TSharedData >
RegionBasedLevelSetFunction()46 ::RegionBasedLevelSetFunction()
47 {
48   m_Lambda1 = NumericTraits< ScalarValueType >::OneValue();
49   m_Lambda2 = NumericTraits< ScalarValueType >::OneValue();
50 
51   m_OverlapPenaltyWeight = NumericTraits< ScalarValueType >::ZeroValue();
52   m_AreaWeight = NumericTraits< ScalarValueType >::ZeroValue();
53   m_VolumeMatchingWeight = NumericTraits< ScalarValueType >::ZeroValue();
54   m_ReinitializationSmoothingWeight = NumericTraits< ScalarValueType >::ZeroValue();
55   m_CurvatureWeight = m_AdvectionWeight = NumericTraits< ScalarValueType >::ZeroValue();
56   m_Volume = NumericTraits< ScalarValueType >::ZeroValue();
57 
58   m_FunctionId = 0;
59 
60   m_SharedData = nullptr;
61   m_InitialImage = nullptr;
62   m_FeatureImage = nullptr;
63   m_UpdateC = false;
64 
65   for ( unsigned int i = 0; i < ImageDimension; i++ )
66     {
67     m_InvSpacing[i] = 1;
68     }
69 }
70 
71 template< typename TInput, typename TFeature, typename TSharedData >
72 typename RegionBasedLevelSetFunction< TInput, TFeature, TSharedData >::VectorType
73 RegionBasedLevelSetFunction< TInput, TFeature, TSharedData >
InitializeZeroVectorConstant()74 ::InitializeZeroVectorConstant()
75 {
76   VectorType ans;
77 
78   for ( unsigned int i = 0; i < ImageDimension; ++i )
79     {
80     ans[i] = NumericTraits< ScalarValueType >::ZeroValue();
81     }
82 
83   return ans;
84 }
85 
86 template< typename TInput, typename TFeature, typename TSharedData >
87 typename RegionBasedLevelSetFunction< TInput, TFeature, TSharedData >::VectorType
88 RegionBasedLevelSetFunction< TInput, TFeature, TSharedData >
89 ::m_ZeroVectorConstant =
90   RegionBasedLevelSetFunction< TInput, TFeature, TSharedData >::InitializeZeroVectorConstant();
91 
92 /* Computes the Heaviside function and stores it in
93   m_HeavisideFunctionOfLevelSetImage */
94 template< typename TInput,
95           typename TFeature,
96           typename TSharedData >
97 void RegionBasedLevelSetFunction< TInput, TFeature, TSharedData >
ComputeHImage()98 ::ComputeHImage()
99 {
100   // The phi function
101   InputImageConstPointer contourImage = this->m_InitialImage;
102   InputImagePointer      hBuffer =
103     this->m_SharedData->m_LevelSetDataPointerVector[this->m_FunctionId]->m_HeavisideFunctionOfLevelSetImage;
104 
105   // Iterator for the phi function
106   using ConstImageIteratorType = ImageRegionConstIteratorWithIndex< InputImageType >;
107   ConstImageIteratorType constIt( contourImage, contourImage->GetRequestedRegion() );
108 
109   using ImageIteratorType = ImageRegionIteratorWithIndex< InputImageType >;
110   ImageIteratorType It( hBuffer, hBuffer->GetRequestedRegion() );
111 
112   It.GoToBegin(),
113     constIt.GoToBegin();
114 
115   while ( !constIt.IsAtEnd() )
116     {
117     // Convention is inside of level-set function is negative
118     ScalarValueType hVal = m_DomainFunction->Evaluate( -constIt.Get() );
119     It.Set(hVal);
120     ++It;
121     ++constIt;
122     }
123 }
124 
125 template< typename TInput,
126           typename TFeature,
127           typename TSharedData >
128 void
129 RegionBasedLevelSetFunction< TInput, TFeature, TSharedData >
UpdateSharedData(bool forceUpdate)130 ::UpdateSharedData(bool forceUpdate)
131 {
132   if ( forceUpdate )
133     {
134     // Must update all H before updating C
135     this->ComputeHImage();
136     this->m_UpdateC = false;
137     }
138   else
139     {
140     if ( !this->m_UpdateC )
141       {
142       this->ComputeParameters();
143       this->m_UpdateC = true;
144       }
145     this->UpdateSharedDataParameters();
146     }
147 }
148 
149 template< typename TInput,
150           typename TFeature,
151           typename TSharedData >
152 typename RegionBasedLevelSetFunction< TInput, TFeature, TSharedData >::TimeStepType
153 RegionBasedLevelSetFunction< TInput, TFeature, TSharedData >
ComputeGlobalTimeStep(void * GlobalData) const154 ::ComputeGlobalTimeStep(void *GlobalData) const
155 {
156 /* Computing the time-step for stable curve evolution */
157 
158   TimeStepType dt = 0.0;
159 
160   auto * d = (GlobalDataStruct *)GlobalData;
161 
162   if ( itk::Math::abs(d->m_MaxCurvatureChange) > itk::Math::eps )
163     {
164     if ( d->m_MaxAdvectionChange > itk::Math::eps )
165       {
166       dt = std::min( ( m_WaveDT / d->m_MaxAdvectionChange ),
167                          ( this->m_DT / d->m_MaxCurvatureChange ) );
168       }
169     else
170       {
171       dt = this->m_DT / d->m_MaxCurvatureChange;
172       }
173     }
174   else
175     {
176     if ( d->m_MaxAdvectionChange > itk::Math::eps )
177       {
178       //NOTE: What's the difference between this->m_WaveDT and this->m_DT?
179       dt = this->m_WaveDT / d->m_MaxAdvectionChange;
180       }
181     }
182 
183   // Reset the values
184   d->m_MaxCurvatureChange   = NumericTraits< ScalarValueType >::ZeroValue();
185   d->m_MaxGlobalChange      = NumericTraits< ScalarValueType >::ZeroValue();
186   d->m_MaxAdvectionChange   = NumericTraits< ScalarValueType >::ZeroValue();
187 
188   return dt;
189 }
190 
191 template< typename TInput,
192           typename TFeature, typename TSharedData >
193 typename RegionBasedLevelSetFunction< TInput,
194                                       TFeature, TSharedData >::
195 ScalarValueType
196 RegionBasedLevelSetFunction< TInput,
ComputeCurvature(const NeighborhoodType & itkNotUsed (it),const FloatOffsetType & itkNotUsed (offset),GlobalDataStruct * gd)197                              TFeature, TSharedData >::ComputeCurvature(
198   const NeighborhoodType & itkNotUsed(it),
199   const FloatOffsetType & itkNotUsed(offset), GlobalDataStruct *gd)
200 {
201   // Calculate the mean curvature
202   ScalarValueType curvature = NumericTraits< ScalarValueType >::ZeroValue();
203 
204   unsigned int i, j;
205 
206   for ( i = 0; i < ImageDimension; i++ )
207     {
208     for ( j = 0; j < ImageDimension; j++ )
209       {
210       if ( j != i )
211         {
212         curvature -= gd->m_dx[i] * gd->m_dx[j] * gd->m_dxy[i][j];
213         curvature += gd->m_dxy[j][j] * gd->m_dx[i] * gd->m_dx[i];
214         }
215       }
216     }
217 
218   if ( gd->m_GradMag > itk::Math::eps )
219     {
220     curvature /= gd->m_GradMag * gd->m_GradMag * gd->m_GradMag;
221     }
222   else
223     {
224     curvature /= 1 + gd->m_GradMagSqr;
225     }
226 
227   return curvature;
228 }
229 
230 // Compute the Hessian matrix and various other derivatives.  Some of these
231 // derivatives may be used by overloaded virtual functions.
232 template< typename TInput,
233           typename TFeature,
234           typename TSharedData >
235 void
236 RegionBasedLevelSetFunction< TInput, TFeature, TSharedData >
ComputeHessian(const NeighborhoodType & it,GlobalDataStruct * gd)237 ::ComputeHessian(const NeighborhoodType & it, GlobalDataStruct *gd)
238 {
239   const ScalarValueType inputValue = it.GetCenterPixel();
240 
241   gd->m_GradMagSqr = 0.;
242   gd->m_GradMag = 0.;
243   unsigned int i, j;
244 
245   for ( i = 0; i < ImageDimension; i++ )
246     {
247     const auto positionA = static_cast< unsigned int >( this->m_Center + this->m_xStride[i] );
248     const auto positionB = static_cast< unsigned int >( this->m_Center - this->m_xStride[i] );
249 
250     gd->m_dx[i] = 0.5 * ( this->m_InvSpacing[i] )
251                   * ( it.GetPixel(positionA) - it.GetPixel(positionB) );
252     gd->m_dx_forward[i]  = ( this->m_InvSpacing[i] )
253                            * ( it.GetPixel(positionA) - inputValue );
254     gd->m_dx_backward[i] = ( this->m_InvSpacing[i] )
255                            * ( inputValue - it.GetPixel(positionB) );
256 
257     gd->m_GradMagSqr += gd->m_dx[i] * gd->m_dx[i];
258 
259     gd->m_dxy[i][i] = ( this->m_InvSpacing[i] )
260                       * ( gd->m_dx_forward[i] - gd->m_dx_backward[i] );
261 
262     for ( j = i + 1; j < ImageDimension; j++ )
263       {
264       const auto positionAa = static_cast< unsigned int >(
265         this->m_Center - this->m_xStride[i] - this->m_xStride[j] );
266       const auto positionBa = static_cast< unsigned int >(
267         this->m_Center - this->m_xStride[i] + this->m_xStride[j] );
268       const auto positionCa = static_cast< unsigned int >(
269         this->m_Center + this->m_xStride[i] - this->m_xStride[j] );
270       const auto positionDa = static_cast< unsigned int >(
271         this->m_Center + this->m_xStride[i] + this->m_xStride[j] );
272 
273       gd->m_dxy[i][j] = gd->m_dxy[j][i] = 0.25
274                                           * ( this->m_InvSpacing[i] ) * ( this->m_InvSpacing[j] )
275                                           * ( it.GetPixel(positionAa) - it.GetPixel(positionBa)
276                                               + it.GetPixel(positionDa) - it.GetPixel(positionCa) );
277       }
278     }
279   gd->m_GradMag = std::sqrt(gd->m_GradMagSqr);
280 }
281 
282 template< typename TInput,
283           typename TFeature,
284           typename TSharedData >
285 typename RegionBasedLevelSetFunction< TInput, TFeature, TSharedData >::PixelType
286 RegionBasedLevelSetFunction< TInput, TFeature, TSharedData >
ComputeUpdate(const NeighborhoodType & it,void * globalData,const FloatOffsetType & offset)287 ::ComputeUpdate(const NeighborhoodType & it, void *globalData,
288                 const FloatOffsetType & offset)
289 {
290   // Access the neighborhood center pixel of phi
291   const ScalarValueType inputValue = it.GetCenterPixel();
292 
293   ScalarValueType laplacian_term = NumericTraits< ScalarValueType >::ZeroValue();
294   ScalarValueType curvature_term = NumericTraits< ScalarValueType >::ZeroValue();
295   ScalarValueType curvature = NumericTraits< ScalarValueType >::ZeroValue();
296   ScalarValueType globalTerm = NumericTraits< ScalarValueType >::ZeroValue();
297   VectorType      advection_field;
298   ScalarValueType x_energy, advection_term = NumericTraits< ScalarValueType >::ZeroValue();
299 
300   // Access the global data structure
301   auto * gd = (GlobalDataStruct *)globalData;
302 
303   ComputeHessian(it, gd);
304 
305   ScalarValueType dh = m_DomainFunction->EvaluateDerivative(-inputValue);
306 
307   // Computing the curvature term
308   // Used to regularized using the length of contour
309   if ( ( dh != 0. )
310        && ( this->m_CurvatureWeight != NumericTraits< ScalarValueType >::ZeroValue() ) )
311     {
312     curvature = this->ComputeCurvature(it, offset, gd);
313     curvature_term = this->m_CurvatureWeight * curvature
314                      * this->CurvatureSpeed(it, offset, gd) * dh;
315 
316     gd->m_MaxCurvatureChange =
317       std::max( gd->m_MaxCurvatureChange, itk::Math::abs(curvature_term) );
318     }
319 
320   // Computing the laplacian term
321   // Used in maintaining squared distance function
322   if ( this->m_ReinitializationSmoothingWeight != NumericTraits< ScalarValueType >::ZeroValue() )
323     {
324     laplacian_term = this->ComputeLaplacian(gd) - curvature;
325 
326     laplacian_term *= this->m_ReinitializationSmoothingWeight
327                       * this->LaplacianSmoothingSpeed(it, offset, gd);
328     }
329 
330   if ( ( dh != 0. ) && ( m_AdvectionWeight != NumericTraits< ScalarValueType >::ZeroValue() ) )
331     {
332     advection_field = this->AdvectionField(it, offset, gd);
333 
334     for ( unsigned int i = 0; i < ImageDimension; i++ )
335       {
336       x_energy = m_AdvectionWeight * advection_field[i];
337 
338       // TODO: Is this condition right ?
339       if ( x_energy > NumericTraits< ScalarValueType >::ZeroValue() )
340         {
341         advection_term += advection_field[i] * gd->m_dx_backward[i];
342         }
343       else
344         {
345         advection_term += advection_field[i] * gd->m_dx_forward[i];
346         }
347 
348       gd->m_MaxAdvectionChange =
349         std::max( gd->m_MaxAdvectionChange, itk::Math::abs(x_energy) );
350       }
351     advection_term *= m_AdvectionWeight * dh;
352     }
353 
354   /* Compute the globalTerm - rms difference of image with c_0 or c_1*/
355   if ( dh != 0. )
356     {
357     globalTerm = dh * this->ComputeGlobalTerm( inputValue, it.GetIndex() );
358     }
359 
360   /* Final update value is the local terms of curvature lengths and laplacian
361   squared distances - global terms of rms differences of image and piecewise
362   constant regions*/
363   auto updateVal = static_cast< PixelType >( curvature_term + laplacian_term + globalTerm + advection_term );
364 
365   /* If MaxGlobalChange recorded is lower than the current globalTerm */
366   if ( itk::Math::abs(gd->m_MaxGlobalChange) < itk::Math::abs(globalTerm) )
367     {
368     gd->m_MaxGlobalChange = globalTerm;
369     }
370 
371   return updateVal;
372 }
373 
374 template< typename TInput, typename TFeature, typename TSharedData >
375 typename RegionBasedLevelSetFunction< TInput, TFeature, TSharedData >
376 ::ScalarValueType
377 RegionBasedLevelSetFunction< TInput, TFeature, TSharedData >
ComputeLaplacian(GlobalDataStruct * gd)378 ::ComputeLaplacian(GlobalDataStruct *gd)
379 {
380   ScalarValueType laplacian = 0.;
381 
382   // Compute the laplacian using the existing second derivative values
383   for ( unsigned int i = 0; i < ImageDimension; i++ )
384     {
385     laplacian += gd->m_dxy[i][i];
386     }
387 
388   return laplacian;
389 }
390 
391 template< typename TInput, typename TFeature, typename TSharedData >
392 typename RegionBasedLevelSetFunction< TInput, TFeature, TSharedData >
393 ::ScalarValueType
394 RegionBasedLevelSetFunction< TInput, TFeature, TSharedData >
ComputeVolumeRegularizationTerm()395 ::ComputeVolumeRegularizationTerm()
396 {
397   return 2
398          * ( this->m_SharedData->m_LevelSetDataPointerVector[this->m_FunctionId]->
399              m_WeightedNumberOfPixelsInsideLevelSet
400              - this->m_Volume );
401 }
402 
403 /* Computes the fidelity term (eg: (intensity - mean)2 ).
404 Most of the code is concerned with using the appropriate combination
405 of Heaviside and dirac delta for each part of the fidelity term.
406 - the final dH is the dirac delta term corresponding to the current
407 level set we are updating. */
408 template< typename TInput, typename TFeature, typename TSharedData >
409 typename RegionBasedLevelSetFunction< TInput, TFeature, TSharedData >
410 ::ScalarValueType
411 RegionBasedLevelSetFunction< TInput, TFeature, TSharedData >
ComputeGlobalTerm(const ScalarValueType & itkNotUsed (inputPixel),const InputIndexType & inputIndex)412 ::ComputeGlobalTerm(
413   const ScalarValueType & itkNotUsed(inputPixel),
414   const InputIndexType & inputIndex)
415 {
416   // computes if it belongs to background
417   ScalarValueType product = 1;
418 
419   // Assuming only 1 level set function to be present
420   auto featIndex = static_cast< FeatureIndexType >( inputIndex );
421 
422   const FeaturePixelType featureVal =
423     this->m_FeatureImage->GetPixel (inputIndex);
424 
425   ScalarValueType overlapTerm = 0.;
426 
427   // This conditional statement computes the amount of overlap s
428   // and the presence of background pr
429   if ( this->m_SharedData->m_FunctionCount > 1 )
430     {
431     featIndex = this->m_SharedData->m_LevelSetDataPointerVector[this->m_FunctionId]->GetFeatureIndex(inputIndex);
432     overlapTerm = this->m_OverlapPenaltyWeight *
433                   ComputeOverlapParameters(featIndex, product);
434     }
435 
436   ScalarValueType interim = this->m_Lambda1 * this->ComputeInternalTerm(featureVal, featIndex);
437   ScalarValueType outTerm = this->m_Lambda2 * product * this->ComputeExternalTerm(featureVal, featIndex);
438 
439   ScalarValueType regularizationTerm = this->m_VolumeMatchingWeight *
440                                        ComputeVolumeRegularizationTerm() - this->m_AreaWeight;
441 
442   ScalarValueType globalTerm = +interim - outTerm + overlapTerm + regularizationTerm;
443 
444   return globalTerm;
445 }
446 } // end namespace
447 
448 #endif
449