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