CASToR  3.2
Tomographic Reconstruction (PET/SPECT/CT)
code/src/optimizer/iPenaltyMarkovRandomField.cc
Go to the documentation of this file.
1 
8 #include "iPenaltyMarkovRandomField.hh"
9 
10 // =====================================================================
11 // ---------------------------------------------------------------------
12 // ---------------------------------------------------------------------
13 // =====================================================================
14 
16 {
17  // --------------------------
18  // Specific member parameters
19  // --------------------------
20  m_penaltyID = "MRF";
29  mp_proximityKernel = NULL;
33  m2p_similarityFactors = NULL;
38  m_neighborhoodBoxExcludeCorners = 0; // Keep them by default
42  // The derivative order depends on the potential function that we do not know yet, so we let it by default to 0
44 }
45 
46 // =====================================================================
47 // ---------------------------------------------------------------------
48 // ---------------------------------------------------------------------
49 // =====================================================================
50 
52 {
53  // Destroy mp_neighborhoodNbVoxels
55  {
57  }
58  // Destroy m2p_neighborhoodIndices
60  {
62  {
64  }
66  }
67  // Destroy m2p_similarityFactors
69  {
71  {
73  }
75  }
76  // Destroy m2p_neighborhoodKernel
78  {
79  for (INTNB v=0; v<m_neighborhoodMaxNbVoxels; v++)
80  {
82  }
84  }
85  // Destroy mp_proximityKernel
87  {
88  free(mp_proximityKernel);
89  }
90 }
91 
92 // =====================================================================
93 // ---------------------------------------------------------------------
94 // ---------------------------------------------------------------------
95 // =====================================================================
96 
98 {
99  cout << "The Markov Random Field (MRF) penalty is implemented for several types of neighborhood, potential functions," << endl;
100  cout << "similarity and proximity factors. All these parameters are described below, and a template configuration file" << endl;
101  cout << "for parameters selection can be found in the configuration folder (config/optimizer/MRF.conf). The configuration" << endl;
102  cout << "of this penalty can only be done through a configuration file. It is not possible to parameterize it directly" << endl;
103  cout << "from the command line. The MRF penalty has the following expression:" << endl;
104  cout << "penalty = beta * sum_on_voxels * sum_on_neighborhood * proximity_factor * similarity_factor * potential_function" << endl;
105  cout << "------------" << endl;
106  cout << "Neighborhood" << endl;
107  cout << "------------" << endl;
108  cout << "The neighborhood is set by the 'neighborhood shape' keyword and can be described using one of the following setting:" << endl;
109  cout << "[6-nearest]: Consider the 6 nearest neighbors, i.e. 2 along each dimension. In 2D, only 4 in the plane are used." << endl;
110  cout << "[box]: Consider all voxels included in a box centered on the voxel of interest. The size of the box is parameterized" << endl;
111  cout << " using the 'box neighborhood order' keyword. The side of the box is equal to '2*order+1'. The 8 corner voxels" << endl;
112  cout << " can also be excluded from the neighborhood by setting the 'exclude box neighborhood corners' keyword to 1." << endl;
113  cout << "[sphere]: Consider all voxels whose center is included in a sphere centered on the voxel of interest and of radius" << endl;
114  cout << " provided by the 'sphere neighborhood radius (mm)' keyword." << endl;
115  cout << "-----------------" << endl;
116  cout << "Proximity factors" << endl;
117  cout << "-----------------" << endl;
118  cout << "The proximity factors are used to weight the contribution of each neighbor to the global penalty, based on proximity to the" << endl;
119  cout << "voxel of interest. These factors are always normalized so that their sum is 1. They can be set using the 'proximity factor'" << endl;
120  cout << "keyword based on one of the following setting:" << endl;
121  cout << "[none]: Consider uniform proximity factors." << endl;
122  cout << "[voxel]: Consider factors inversely proportional to the distance in voxels from the voxel of interest (i.e. the unit" << endl;
123  cout << " is voxels, whatever the axis and the voxel dimensions)." << endl;
124  cout << "[euclidian]: Consider factors inversely proportional to the euclidian distance from the voxel of interest (i.e. the unit" << endl;
125  cout << " is mm)." << endl;
126  cout << endl;
127  cout << "------------------" << endl;
128  cout << "Similarity factors" << endl;
129  cout << "------------------" << endl;
130  cout << "The similarity factors are used to weight the contribution of each neighbor to the global penalty, based on similarity to the" << endl;
131  cout << "voxel of interest. These factors are not normalized by default. They can be set using the 'similarity factor' keyword based on" << endl;
132  cout << "one of the following setting:" << endl;
133  cout << "[none]: Consider uniform similarity factors, i.e. the value of 1 for each voxel in the neighborhood." << endl;
134  cout << "[aBowsher]: Consider similarity factors based on the asymmetrical Bowsher's method and an additional image. The additional" << endl;
135  cout << " image must be provided using the '-multimodal' option in the command line. Based on additional image values," << endl;
136  cout << " voxels of the neighborhood most similar to the voxel of interest will have a similarity factor of 1, and the" << endl;
137  cout << " other voxels will have a similarity factor of 0. The number of most similar voxels is parameterized by a percentage" << endl;
138  cout << " of the number of voxels in the neighborhood, provided with the 'similarity threshold Bowsher (%)' keyword." << endl;
139  cout << " For an explanation of asymmetrical Bowsher, see Schramm et al, IEEE Trans. Med. Imaging, vol. 37, pp. 590, 2018." << endl;
140  cout << "-------------------" << endl;
141  cout << "Potential functions" << endl;
142  cout << "-------------------" << endl;
143  cout << "The potential function actually penalizes the difference between the voxel of interest and a neighbor. It can be set using the" << endl;
144  cout << "'potential function' keyword based on one of the following setting:" << endl;
145  cout << "[quadratic]: p(u,v) = 0.5*(u-v)^2" << endl;
146  cout << " Reference: Geman and Geman, IEEE Trans. Pattern Anal. Machine Intell., vol. PAMI-6, pp. 721-741, 1984." << endl;
147  cout << "[geman mcclure]: p(u,v,d) = (u-v)^2 / (d^2+(u-v)^2)" << endl;
148  cout << " The parameter 'd' can be set using the 'deltaGMC' keyword." << endl;
149  cout << " Reference: Geman and McClure, Proc. Amer. Statist. Assoc., 1985." << endl;
150  cout << "[hebert leahy]: p(u,v,m) = log(1+(u-v)^2/m^2)" << endl;
151  cout << " The parameter 'm' can be set using the 'muHL' keyword." << endl;
152  cout << " Reference: Hebert and Leahy, IEEE Trans. Med. Imaging, vol. 8, pp. 194-202, 1989." << endl;
153  cout << "[green logcosh]: p(u,v,d) = log(cosh((u-v)/d))" << endl;
154  cout << " The parameter 'd' can be set using the 'deltaLogCosh' keyword." << endl;
155  cout << " Reference: Green, IEEE Trans. Med. Imaging, vol. 9, pp. 84-93, 1990." << endl;
156  cout << "[huber piecewise]: " << endl;
157  cout << " p(u,v,d) = d*abs(u-v)-0.5*d^2 if abs(u-v) > d" << endl;
158  cout << " = 0.5*(u-v)^2 if abs(u-v) <= d" << endl;
159  cout << " The parameter 'd' can be set using the 'deltaHuber' keyword." << endl;
160  cout << " Reference: e.g. Mumcuoglu et al, Phys. Med. Biol., vol. 41, pp. 1777-1807, 1996." << endl;
161  cout << "[nuyts relative]: p(u,v,g) = (u-v)^2 / (u+v+g*abs(u-v))" << endl;
162  cout << " The parameter 'g' can be set using the 'gammaRD' keyword." << endl;
163  cout << " Reference: Nuyts et al, IEEE Trans. Nucl. Sci., vol. 49, pp. 56-60, 2002." << endl;
164 }
165 
166 // =====================================================================
167 // ---------------------------------------------------------------------
168 // ---------------------------------------------------------------------
169 // =====================================================================
170 
171 int iPenaltyMarkovRandomField::ReadConfigurationFile(const string& a_configurationFile)
172 {
173  string buffer = "";
174  string key_word = "";
175 
176  // -------------------------------------------------------------------
177  // Read the type of neighborhood
178  // -------------------------------------------------------------------
179  key_word = "neighborhood shape";
180  if (ReadDataASCIIFile(a_configurationFile, key_word, &buffer, 1, KEYWORD_MANDATORY))
181  {
182  Cerr("***** iPenaltyMarkovRandomField::ReadConfigurationFile() -> Failed to get the '" << key_word << "' keyword !" << endl);
183  return 1;
184  }
185  // Sphere neighborhood
186  if (buffer == "sphere")
187  {
189  // Radius of the sphere
190  key_word = "sphere neighborhood radius (mm)";
191  if (ReadDataASCIIFile(a_configurationFile, key_word, &m_neighborhoodSphereRadius, 1, KEYWORD_MANDATORY))
192  {
193  Cerr("***** iPenaltyMarkovRandomField::ReadConfigurationFile() -> Failed to get the '" << key_word << "' keyword !" << endl);
194  return 1;
195  }
196  }
197  // Box neighborhood
198  else if (buffer == "box")
199  {
201  // Order of the box (number of voxels which will be included in each direction)
202  key_word = "box neighborhood order";
203  if (ReadDataASCIIFile(a_configurationFile, key_word, &m_neighborhoodBoxOrder, 1, KEYWORD_MANDATORY))
204  {
205  Cerr("***** iPenaltyMarkovRandomField::ReadConfigurationFile() -> Failed to get the '" << key_word << "' keyword !" << endl);
206  return 1;
207  }
208  // Include corners of the box or not (some people do that) (not mandatory as we keep them by default)
209  key_word = "exclude box neighborhood corners";
211  {
212  Cerr("***** iPenaltyMarkovRandomField::ReadConfigurationFile() -> Failed to get the '" << key_word << "' keyword !" << endl);
213  return 1;
214  }
215  }
216  // 6-nearest
217  else if (buffer == "6-nearest")
218  {
220  }
221  // Unknown
222  else
223  {
224  Cerr("***** iPenaltyMarkovRandomField::ReadConfigurationFile() -> Unknown neighborhood type '" << buffer << "' !" << endl);
225  return 1;
226  }
227 
228  // -------------------------------------------------------------------
229  // Read and check the type of the proximity factor
230  // -------------------------------------------------------------------
231  key_word = "proximity factor";
232  if (ReadDataASCIIFile(a_configurationFile, key_word, &buffer, 1, KEYWORD_MANDATORY))
233  {
234  Cerr("***** iPenaltyMarkovRandomField::ReadConfigurationFile() -> Failed to get the '" << key_word << "' keyword !" << endl);
235  return 1;
236  }
237  // Inverse of euclidian distance
238  if (buffer == "euclidian")
239  {
241  }
242  // Inverse of voxel distance (in numbers of voxels)
243  else if (buffer == "voxel")
244  {
246  }
247  // Exponential distance squared
248  /*
249  else if (buffer == "exponential distance squared")
250  {
251  m_proximityType = MRF_PROXIMITY_EXP_DIST_SQUARED;
252  key_word = "proximity characteristic distance (mm)";
253  if (ReadDataASCIIFile(a_configurationFile, key_word, &m_proximityCharacteristicDistance, 1, KEYWORD_MANDATORY))
254  {
255  Cerr("***** iPenaltyMarkovRandomField::ReadConfigurationFile() -> Failed to get the '" << key_word << "' keyword !" << endl);
256  return 1;
257  }
258  }
259  */
260  // No proximity factor
261  else if (buffer == "none")
262  {
264  }
265  // Unknown
266  else
267  {
268  Cerr("***** iPenaltyMarkovRandomField::ReadConfigurationFile() -> Unknown proximity factor type '" << buffer << "' !" << endl);
269  return 1;
270  }
271 
272  // -------------------------------------------------------------------
273  // Read and check the type of the similarity factor
274  // -------------------------------------------------------------------
275  key_word = "similarity factor";
276  if (ReadDataASCIIFile(a_configurationFile, key_word, &buffer, 1, KEYWORD_MANDATORY))
277  {
278  Cerr("***** iPenaltyMarkovRandomField::ReadConfigurationFile() -> Failed to get the '" << key_word << "' keyword !" << endl);
279  return 1;
280  }
281  // No similarity factors
282  if (buffer == "none")
283  {
285  }
286  // Bowsher's similarity factors
287  else if (buffer == "aBowsher")
288  {
290  // Read the Bowsher threshold
291  key_word = "similarity threshold Bowsher (%)";
292  if (ReadDataASCIIFile(a_configurationFile, key_word, &m_similarityBowsherThreshold, 1, KEYWORD_MANDATORY))
293  {
294  Cerr("***** iPenaltyMarkovRandomField::ReadConfigurationFile() -> Failed to get the '" << key_word << "' keyword !" << endl);
295  return 1;
296  }
297  }
298  // Unknown
299  else
300  {
301  Cerr("***** iPenaltyMarkovRandomField::ReadConfigurationFile() -> Unknown similarity factor type '" << buffer << "' !" << endl);
302  return 1;
303  }
304 
305  // -------------------------------------------------------------------
306  // Read and check the type of the potential energy function
307  // -------------------------------------------------------------------
308  key_word = "potential function";
309  if (ReadDataASCIIFile(a_configurationFile, key_word, &buffer, 1, KEYWORD_MANDATORY))
310  {
311  Cerr("***** iPenaltyMarkovRandomField::ReadConfigurationFile() -> Failed to get the '" << key_word << "' keyword !" << endl);
312  return 1;
313  }
314  // Quadratic potential function
315  if (buffer == "quadratic")
316  {
318  m_penaltyDerivativesOrder = INT_MAX;
319  }
320  // Relative differences potential function
321  else if (buffer == "nuyts relative")
322  {
324  m_penaltyDerivativesOrder = INT_MAX;
325  key_word = "gammaRD";
326  if (ReadDataASCIIFile(a_configurationFile, key_word, &m_potentialRelativeDifferenceGamma, 1, KEYWORD_MANDATORY))
327  {
328  Cerr("***** iPenaltyMarkovRandomField::ReadConfigurationFile() -> Failed to get the '" << key_word << "' keyword !" << endl);
329  return 1;
330  }
331  }
332  // Geman and McClure function
333  else if (buffer == "geman mcclure")
334  {
336  m_penaltyDerivativesOrder = INT_MAX;
337  key_word = "deltaGMC";
338  if (ReadDataASCIIFile(a_configurationFile, key_word, &m_potentialGemanMcClureDelta, 1, KEYWORD_MANDATORY))
339  {
340  Cerr("***** iPenaltyMarkovRandomField::ReadConfigurationFile() -> Failed to get the '" << key_word << "' keyword !" << endl);
341  return 1;
342  }
343  }
344  // Green's logcosh
345  else if (buffer == "green logcosh")
346  {
348  m_penaltyDerivativesOrder = INT_MAX;
349  key_word = "deltaLogCosh";
350  if (ReadDataASCIIFile(a_configurationFile, key_word, &m_potentialGreenLogCoshDelta, 1, KEYWORD_MANDATORY))
351  {
352  Cerr("***** iPenaltyMarkovRandomField::ReadConfigurationFile() -> Failed to get the '" << key_word << "' keyword !" << endl);
353  return 1;
354  }
355  }
356  // Hebert and Leahy function
357  else if (buffer == "hebert leahy")
358  {
360  m_penaltyDerivativesOrder = INT_MAX;
361  key_word = "muHL";
362  if (ReadDataASCIIFile(a_configurationFile, key_word, &m_potentialHebertLeahyMu, 1, KEYWORD_MANDATORY))
363  {
364  Cerr("***** iPenaltyMarkovRandomField::ReadConfigurationFile() -> Failed to get the '" << key_word << "' keyword !" << endl);
365  return 1;
366  }
367  }
368  // Huber function
369  else if (buffer == "huber piecewise")
370  {
372  m_penaltyDerivativesOrder = INT_MAX;
373  key_word = "deltaHuber";
374  if (ReadDataASCIIFile(a_configurationFile, key_word, &m_potentialHuberDelta, 1, KEYWORD_MANDATORY))
375  {
376  Cerr("***** iPenaltyMarkovRandomField::ReadConfigurationFile() -> Failed to get the '" << key_word << "' keyword !" << endl);
377  return 1;
378  }
379  }
380  // Unknown
381  else
382  {
383  Cerr("***** iPenaltyMarkovRandomField::ReadConfigurationFile() -> Unknown potential function '" << buffer << "' !" << endl);
384  return 1;
385  }
386 
387  // Normal end
388  return 0;
389 }
390 
391 // =====================================================================
392 // ---------------------------------------------------------------------
393 // ---------------------------------------------------------------------
394 // =====================================================================
395 
396 int iPenaltyMarkovRandomField::ReadOptionsList(const string& a_optionsList)
397 {
398  // Too complicated to do it that way
399  Cerr("***** iPenaltyMarkovRandomField::ReadOptionsList() -> Options can be specified only using a configuration file !" << endl);
400  return 1;
401 }
402 
403 // =====================================================================
404 // ---------------------------------------------------------------------
405 // ---------------------------------------------------------------------
406 // =====================================================================
407 
409 {
410  // Check potential function type
412  {
413  Cerr("***** iPenaltyMarkovRandomField::CheckSpecificParameters() -> Should provide a potential function type !" << endl);
414  return 1;
415  }
416  // Check proximity factor type
418  {
419  Cerr("***** iPenaltyMarkovRandomField::CheckSpecificParameters() -> Should provide a proximity factor type !" << endl);
420  return 1;
421  }
422  // Check similarity factor type
424  {
425  Cerr("***** iPenaltyMarkovRandomField::CheckSpecificParameters() -> Should provide a similarity factor type !" << endl);
426  return 1;
427  }
428  // Check neighborhood type
430  {
431  Cerr("***** iPenaltyMarkovRandomField::CheckSpecificParameters() -> Should provide a neighborhood type !" << endl);
432  return 1;
433  }
434  /*
435  // check parameters of the proximity factors
436  if (m_proximityType == MRF_PROXIMITY_EXP_DIST_SQUARED && m_proximityCharacteristicDistance <=0.)
437  {
438  Cerr("***** iPenaltyMarkovRandomField::CheckSpecificParameters() -> Provided proximity characteristic distance (" << m_proximityCharacteristicDistance << ") is non-positive" << endl);
439  return 1;
440  }
441  */
442  // Check parameters of the similarity factors
444  {
445  if (m_similarityBowsherThreshold<0. || m_similarityBowsherThreshold>100.)
446  {
447  Cerr("***** iPenaltyMarkovRandomField::CheckSpecificParameters() -> Provided threshold parameter for the Bowsher similarity " << m_similarityBowsherThreshold << " does not fall into [0,100]% !" << endl);
448  return 1;
449  }
451  {
452  Cerr("***** iPenaltyMarkovRandomField::CheckSpecificParameters() -> Bowsher similarity requires a multimodal image !"<< endl);
453  return 1;
454  }
456  {
457  Cout("***** iPenaltyMarkovRandomField::CheckSpecificParameters() -> Warning : More than one multimodal image, Bowsher similarity will use only the first one !"<< endl);
458  }
459  }
460  // Check some parameters of the neighborhood
462  {
463  Cerr("***** iPenaltyMarkovRandomField::CheckSpecificParameters() -> Provided radius of the sphere neighborhood (" << m_neighborhoodSphereRadius << ") is negative" << endl);
464  return 1;
465  }
467  {
468  Cerr("***** iPenaltyMarkovRandomField::CheckSpecificParameters() -> Provided order of the box neighborhood (" << m_neighborhoodBoxOrder << ") is negative" << endl);
469  return 1;
470  }
471  // Check for gamma value of the relative distance potential function
473  {
474  Cerr("***** iPenaltyMarkovRandomField::CheckSpecificParameters() -> Provided gamma parameter of relative differences potential function must be positive or null !" << endl);
475  return 1;
476  }
477  // Check for delta value of the Geman and McClure potential function
479  {
480  Cerr("***** iPenaltyMarkovRandomField::CheckSpecificParameters() -> Provided delta parameter of Geman and McClure's potential function must be strictly positive !" << endl);
481  return 1;
482  }
483  // Check for delta value of the Green's log-cosh potential function
485  {
486  Cerr("***** iPenaltyMarkovRandomField::CheckSpecificParameters() -> Provided delta parameter of Green's log-cosh potential function must be strictly positive !" << endl);
487  return 1;
488  }
489  // Check for mu value of the Hebert and Leahy potential function
491  {
492  Cerr("***** iPenaltyMarkovRandomField::CheckSpecificParameters() -> Provided mu parameter of Hebert and Leahy's potential function must be strictly positive !" << endl);
493  return 1;
494  }
495  // Check for delta value of the Huber potential function
497  {
498  Cerr("***** iPenaltyMarkovRandomField::CheckSpecificParameters() -> Provided delta parameter of Huber's piecewise potential function must be strictly positive !" << endl);
499  return 1;
500  }
501  // Normal end
502  return 0;
503 }
504 
505 // =====================================================================
506 // ---------------------------------------------------------------------
507 // ---------------------------------------------------------------------
508 // =====================================================================
509 
511 {
512  // Build the neighborhood kernel that will be used to facilitate the computation of the neighborhood of each voxel later on
514  {
515  Cerr("***** iPenaltyMarkovRandomField::InitializeSpecific() -> Failed to build the neighborhood !" << endl);
516  return 1;
517  }
518  // Build the proximity factors of the neighborhood that remain constant for any voxel
519  if (BuildProximityFactors())
520  {
521  Cerr("***** iPenaltyMarkovRandomField::InitializeSpecific() -> Failed to build the proximity factors !" << endl);
522  return 1;
523  }
524  // Some allocations for the specific neighborhood of each voxel used during computations
529  {
532  // Initialize the similarity factors to 1, in case they stay uniform for the reconstruction
533  for (INTNB v=0; v<m_neighborhoodMaxNbVoxels; v++) m2p_similarityFactors[th][v] = 1.;
534  }
535  // Verbose
537  {
538  Cout("iPenaltyMarkovRandomField::InitializeSpecific() -> Use the MRF penalty" << endl);
540  {
541  // Type of neighborhood
543  {
544  Cout(" --> shape of neighborhood: sphere" << endl);
545  Cout(" --> radius of the sphere neighborhood: " << m_neighborhoodSphereRadius << " mm " << endl);
546  }
548  {
549  Cout(" --> shape of neighborhood: box" << endl);
550  Cout(" --> order of the box (number of voxels in each direction): " << m_neighborhoodBoxOrder << endl);
551  if (m_neighborhoodBoxExcludeCorners) Cout(" --> exclude corner voxels" << endl);
552  }
554  {
555  Cout(" --> shape of neighborhood: 6-nearest" << endl);
556  }
557  }
558  Cout(" --> Max number of voxels in the neighborhood: " << m_neighborhoodMaxNbVoxels << endl);
560  {
561  // Proximity factors
563  {
564  Cout(" --> proximity factor: uniform" << endl);
565  }
567  {
568  Cout(" --> proximity factor: inverse of euclidian distance" << endl);
569  }
571  {
572  Cout(" --> proximity factor: inverse of voxel distance (i.e. distance in numbers of voxels)" << endl);
573  }
574 /*
575  else if (m_proximityType == MRF_PROXIMITY_EXP_DIST_SQUARED)
576  {
577  Cout(" --> proximity factor: exponential square distances"<< endl);
578  Cout(" --> characteristic distance for the proximity factors: "<< m_proximityCharacteristicDistance << endl);
579  }
580 */
581  }
582  // Similarity factors
584  {
585  Cout(" --> similarity factor: uniform" << endl);
586  }
588  {
589  Cout(" --> similarity factor: asymmetric Bowsher" << endl);
591  {
592  Cout(" --> Bowsher's threshold: " << m_similarityBowsherThreshold << " %" << endl);
593  Cout(" --> Bowsher's most similar voxels kept: " << m_similarityBowsherNbVoxels << endl);
594  }
595  }
596  // Potential function
598  {
599  Cout(" --> potential function: quadratic" << endl);
600  }
602  {
603  Cout(" --> potential function: Nuyts relative differences 2002" << endl);
604  if (m_verbose>=VERBOSE_DETAIL) Cout(" --> gamma: " << m_potentialRelativeDifferenceGamma << endl);
605  }
607  {
608  Cout(" --> potential function: Geman and McClure 1985" << endl);
609  if (m_verbose>=VERBOSE_DETAIL) Cout(" --> delta: " << m_potentialGemanMcClureDelta << endl);
610  }
612  {
613  Cout(" --> potential function: Green log-cosh 1990" << endl);
614  if (m_verbose>=VERBOSE_DETAIL) Cout(" --> delta: " << m_potentialGreenLogCoshDelta << endl);
615  }
617  {
618  Cout(" --> potential function: Hebert and Leahy 1989" << endl);
619  if (m_verbose>=VERBOSE_DETAIL) Cout(" --> mu: " << m_potentialHebertLeahyMu << endl);
620  }
622  {
623  Cout(" --> potential function: Huber piecewise linear-quadratic" << endl);
624  if (m_verbose>=VERBOSE_DETAIL) Cout(" --> delta: " << m_potentialHuberDelta << endl);
625  }
626  if (m_verbose>=VERBOSE_DETAIL) Cout(" --> penalty energy function derivatives order: " << m_penaltyDerivativesOrder << endl);
627  }
628  // Normal end
629  return 0;
630 }
631 
632 // =====================================================================
633 // ---------------------------------------------------------------------
634 // ---------------------------------------------------------------------
635 // =====================================================================
636 
638 {
639  // Check if the neighborhood has already been created
641  {
642  Cerr("***** iPenaltyMarkovRandomField::BuildNeighborhoodKernel() -> Neighborhood kernel has already been created !" << endl);
643  return 1;
644  }
645  // Get voxel sizes locally
649  // ========================================================================================================
650  // Case where the neighborhood is defined by a sphere with radius provided in mm
651  // ========================================================================================================
653  {
654  // Compute the size of the box including the sphere (minimum of 1 voxel radius)
655  INTNB radius_vox_x = max( (INTNB)1 , ((INTNB)(m_neighborhoodSphereRadius/vox_size_x)) );
656  INTNB radius_vox_y = max( (INTNB)1 , ((INTNB)(m_neighborhoodSphereRadius/vox_size_y)) );
657  INTNB radius_vox_z = max( (INTNB)1 , ((INTNB)(m_neighborhoodSphereRadius/vox_size_z)) );
658  // Particular case for 2D reconstruction
659  if (mp_ImageDimensionsAndQuantification->GetNbVoxZ()==1) radius_vox_z = 0;
660  // Precompute the maximum number of voxels in the neighborhood as the box including the sphere
661  m_neighborhoodMaxNbVoxels = (radius_vox_x*2+1) * (radius_vox_y*2+1) * (radius_vox_z*2+1);
662  // Allocate the neighborhood kernel
664  for (INTNB v = 0; v<m_neighborhoodMaxNbVoxels; v++) m2p_neighborhoodKernel[v] = (INTNB*)malloc(3*sizeof(INTNB));
665  // Counter for the number of voxels in the neighborhood
666  INTNB nb_voxels_in_neighborhood = 0;
667  // Loop on increments of voxels indices in box neighborhood
668  for (INTNB vox_z = -radius_vox_z; vox_z<=radius_vox_z; vox_z++)
669  for (INTNB vox_y = -radius_vox_y; vox_y<=radius_vox_y; vox_y++)
670  for (INTNB vox_x = -radius_vox_x; vox_x<=radius_vox_x; vox_x++)
671  // Exclude the voxel itself
672  if (vox_x!=0 || vox_y!=0 || vox_z!=0)
673  {
674  // Compute the square distance between the two voxels
675  FLTNB squareDistance = ((FLTNB)(vox_x*vox_x))*vox_size_x*vox_size_x
676  + ((FLTNB)(vox_y*vox_y))*vox_size_y*vox_size_y
677  + ((FLTNB)(vox_z*vox_z))*vox_size_z*vox_size_z;
678  // Include voxel if the distance from the central voxel is less than the radius
680  {
681  m2p_neighborhoodKernel[nb_voxels_in_neighborhood][MRF_NEIGHBOR_X] = vox_x;
682  m2p_neighborhoodKernel[nb_voxels_in_neighborhood][MRF_NEIGHBOR_Y] = vox_y;
683  m2p_neighborhoodKernel[nb_voxels_in_neighborhood][MRF_NEIGHBOR_Z] = vox_z;
684  nb_voxels_in_neighborhood++;
685  }
686  }
687  // Update the maximum number of voxels in the neighborhood by the exact value
688  m_neighborhoodMaxNbVoxels = nb_voxels_in_neighborhood;
689  m2p_neighborhoodKernel = (INTNB**)realloc(m2p_neighborhoodKernel,m_neighborhoodMaxNbVoxels*sizeof(INTNB*));
690  }
691  // ========================================================================================================
692  // Case where the neighborhood is defined by a box as described in many papers
693  // ========================================================================================================
695  {
696  // Particular case for 2D reconstruction
697  INTNB neighborhood_box_order_z = m_neighborhoodBoxOrder;
698  if (mp_ImageDimensionsAndQuantification->GetNbVoxZ()==1) neighborhood_box_order_z = 0;
699  // Maximum number of voxels in the neighborhood of a voxel with no image boundary constraints
701  // Allocate the neighborhood kernel
703  for (INTNB v = 0; v<m_neighborhoodMaxNbVoxels; v++) m2p_neighborhoodKernel[v] = (INTNB*)malloc(3*sizeof(INTNB));
704  // Counter for the number of voxels in the neighborhood
705  INTNB nb_voxels_in_neighborhood = 0;
706  // Loop on increments of voxels indices in the box neighborhood
707  for (INTNB vox_z = -neighborhood_box_order_z; vox_z<=neighborhood_box_order_z; vox_z++)
708  for (INTNB vox_y = -m_neighborhoodBoxOrder; vox_y<=m_neighborhoodBoxOrder; vox_y++)
709  for (INTNB vox_x = -m_neighborhoodBoxOrder; vox_x<=m_neighborhoodBoxOrder; vox_x++)
710  // Exclude the voxel itself
711  if (vox_x!=0 || vox_y!=0 || vox_z!=0)
712  {
713  // Exclude corner voxels if requested
715  || (vox_y!=-m_neighborhoodBoxOrder && vox_y!=m_neighborhoodBoxOrder)
716  || (vox_z!=-m_neighborhoodBoxOrder && vox_z!=m_neighborhoodBoxOrder))
717  {
718  m2p_neighborhoodKernel[nb_voxels_in_neighborhood][MRF_NEIGHBOR_X] = vox_x;
719  m2p_neighborhoodKernel[nb_voxels_in_neighborhood][MRF_NEIGHBOR_Y] = vox_y;
720  m2p_neighborhoodKernel[nb_voxels_in_neighborhood][MRF_NEIGHBOR_Z] = vox_z;
721  nb_voxels_in_neighborhood++;
722  }
723  }
724  // Update the maximum number of voxels in the neighborhood by the exact value
725  m_neighborhoodMaxNbVoxels = nb_voxels_in_neighborhood;
726  m2p_neighborhoodKernel = (INTNB**)realloc(m2p_neighborhoodKernel,m_neighborhoodMaxNbVoxels*sizeof(INTNB*));
727  }
728  // ========================================================================================================
729  // Case where the neighborhood is defined simply by the 6-nearest neighbors
730  // ========================================================================================================
732  {
733  // Maximum number of voxels in the neighborhood is 6
735  // Particular case for 2D reconstruction
737  // Allocate the neighborhood kernel
739  for (INTNB v = 0; v<m_neighborhoodMaxNbVoxels; v++) m2p_neighborhoodKernel[v] = (INTNB*)malloc(3*sizeof(INTNB));
740  // The 6 voxels
741  for (INTNB v=0; v<m_neighborhoodMaxNbVoxels; v++) for (INTNB dim=0; dim<3; dim++) m2p_neighborhoodKernel[v][dim] = 0;
747  {
750  }
751  }
752  // ========================================================================================================
753  // Unknown neighborhood type
754  // ========================================================================================================
755  else
756  {
757  Cerr("***** iPenaltyMarkovRandomField::BuildNeighborhoodKernel() -> The provided shape of neighborhood (" << m_neighborhoodShape << ") is unknown !" << endl);
758  return 1;
759  }
760  // Check that there are some voxels in the neighborhood
762  {
763  Cerr("***** iPenaltyMarkovRandomField::BuildNeighborhoodKernel() -> There is no voxel in the neighborhood ! Check your neighborhood definition." << endl);
764  return 1;
765  }
766  // ========================================================================================================
767  // With Bowsher's similarity factors, we compute the number of voxels that will be kept in the penalty
768  // ========================================================================================================
770  {
772  }
773  // Normal end
774  return 0;
775 }
776 
777 // =====================================================================
778 // ---------------------------------------------------------------------
779 // ---------------------------------------------------------------------
780 // =====================================================================
781 
783 {
784  // Check for allocations
785  if (mp_proximityKernel)
786  {
787  Cerr("***** iPenaltyMarkovRandomField::BuildProximityFactors() -> Proximity kernel has already been created somewhere else !" << endl);
788  return 1;
789  }
791  {
792  Cerr("***** iPenaltyMarkovRandomField::BuildProximityFactors() -> Neighborhood kernel must have been created and initialized before !" << endl);
793  return 1;
794  }
795  // Compute the sum of proximity factors for normalization
796  FLTNB proximity_factor_sum = 0.;
797  // Allocate the proximity kernel
799  // Loop on the maximal neighborhood
800  for (INTNB neigh=0; neigh<m_neighborhoodMaxNbVoxels; neigh++)
801  {
802  // Proximity factors based on inverse of euclidian distance
804  {
807  * fabs(m2p_neighborhoodKernel[neigh][MRF_NEIGHBOR_X])
811  * fabs(m2p_neighborhoodKernel[neigh][MRF_NEIGHBOR_Y])
815  * fabs(m2p_neighborhoodKernel[neigh][MRF_NEIGHBOR_Z])
817  mp_proximityKernel[neigh] = 1./sqrt(mp_proximityKernel[neigh]);
818  }
819  // Proximity factors based on inverse of voxel distance (as described in many papers)
821  {
823  * fabs(m2p_neighborhoodKernel[neigh][MRF_NEIGHBOR_X]);
825  * fabs(m2p_neighborhoodKernel[neigh][MRF_NEIGHBOR_Y]);
827  * fabs(m2p_neighborhoodKernel[neigh][MRF_NEIGHBOR_Z]);
828  mp_proximityKernel[neigh] = 1./sqrt(mp_proximityKernel[neigh]);
829  }
830  /*
831  // Proximity factors based on decreasing exponential of squared distance
832  else if (m_proximityType == MRF_PROXIMITY_EXP_DIST_SQUARED)
833  {
834  FLTNB squareDistance = abs(m2p_neighborhoodKernel[neigh][MRF_NEIGHBOR_X])
835  * mp_ImageDimensionsAndQuantification->GetVoxSizeX()
836  * abs(m2p_neighborhoodKernel[neigh][MRF_NEIGHBOR_X])
837  * mp_ImageDimensionsAndQuantification->GetVoxSizeX();
838  squareDistance += abs(m2p_neighborhoodKernel[neigh][MRF_NEIGHBOR_Y])
839  * mp_ImageDimensionsAndQuantification->GetVoxSizeY()
840  * abs(m2p_neighborhoodKernel[neigh][MRF_NEIGHBOR_Y])
841  * mp_ImageDimensionsAndQuantification->GetVoxSizeY();
842  squareDistance += abs(m2p_neighborhoodKernel[neigh][MRF_NEIGHBOR_Z])
843  * mp_ImageDimensionsAndQuantification->GetVoxSizeZ()
844  * abs(m2p_neighborhoodKernel[neigh][MRF_NEIGHBOR_Z])
845  * mp_ImageDimensionsAndQuantification->GetVoxSizeZ();
846  mp_proximityKernel[neigh] = exp(-squareDistance/(m_proximityCharacteristicDistance*m_proximityCharacteristicDistance))/squareDistance;
847  }
848  */
849  // Uniform proximity factors
851  {
852  mp_proximityKernel[neigh] = 1.;
853  }
854  else
855  {
856  Cerr("***** iPenaltyMarkovRandomField::BuildProximityFactors() -> The provided type of proximity factor (" << m_proximityType << ") is unknown !" << endl);
857  return 1;
858  }
859  // Add to the sum
860  proximity_factor_sum += mp_proximityKernel[neigh];
861  }
862  // Normalize the proximity factors
863  for (INTNB neigh=0; neigh<m_neighborhoodMaxNbVoxels; neigh++) mp_proximityKernel[neigh] /= proximity_factor_sum;
864  // Normal end
865  return 0;
866 }
867 
868 // =====================================================================
869 // ---------------------------------------------------------------------
870 // ---------------------------------------------------------------------
871 // =====================================================================
872 
874 {
875  // Get number of voxels locally for code readability
880  // Compute the X, Y and Z components of the current voxel
881  INTNB index_x = a_voxel % nb_vox_x;
882  INTNB index_y = (a_voxel/nb_vox_x) % nb_vox_y;
883  INTNB index_z = a_voxel / nb_vox_xy;
884  // Count the number of valid neighbors
885  mp_neighborhoodNbVoxels[a_th] = 0;
886  // Loop on all possible neighbors in the kernel
887  for (INTNB neigh = 0; neigh<m_neighborhoodMaxNbVoxels; neigh++)
888  {
889  // Compute X, Y and Z indices of this potential neighbor
890  INTNB neighbor_x = index_x + m2p_neighborhoodKernel[neigh][MRF_NEIGHBOR_X];
891  INTNB neighbor_y = index_y + m2p_neighborhoodKernel[neigh][MRF_NEIGHBOR_Y];
892  INTNB neighbor_z = index_z + m2p_neighborhoodKernel[neigh][MRF_NEIGHBOR_Z];
893  INTNB neighbor_xyz = neighbor_x + neighbor_y*nb_vox_x + neighbor_z*nb_vox_xy;
894  // Check if the neighbour is inside the image
895  if ( neighbor_x>=0 && neighbor_x<nb_vox_x && neighbor_y>=0 && neighbor_y<nb_vox_y && neighbor_z>=0 && neighbor_z<nb_vox_z && !mp_ImageDimensionsAndQuantification->IsVoxelMasked(neighbor_xyz) )
896  {
897  // Add the global index of this neighbor to the neighborhood list
898  m2p_neighborhoodIndices[a_th][neigh] = neighbor_xyz;
899  // One more neighbor
900  mp_neighborhoodNbVoxels[a_th]++;
901  }
902  // Otherwise, discard it by setting -1
903  else
904  {
905  m2p_neighborhoodIndices[a_th][neigh] = -1;
906  }
907  }
908  // Normal end
909  return 0;
910 }
911 
912 // =====================================================================
913 // ---------------------------------------------------------------------
914 // ---------------------------------------------------------------------
915 // =====================================================================
916 
917 int iPenaltyMarkovRandomField::ComputeSimilarityFactors(int a_tbf, int a_rbf, int a_cbf, INTNB a_voxel, int a_th)
918 {
919  // No similarity factor (uniform)
921  {
922  // We just return as the factors were set to one after allocation
923  return 0;
924  }
925  // Bowsher's similarity factors
927  {
928  // Apply Bowsher's similarity only if there are more voxels in the neighborhood of this voxel than Bowsher's voxels
930  {
931  // Sort the neighborhood indices according to ascending absolute intensity difference from the voxel v
933  [a_voxel,a_tbf,a_rbf,a_cbf,a_th,this](INTNB i1, INTNB i2)
934  {
935  // This two lines pushes the -1 indices (voxels out of image) at the end
936  if (i1 == -1) return false;
937  if (i2 == -1) return true;
938  return (abs(mp_ImageSpace-> m2p_multiModalImage[0][i1] - mp_ImageSpace-> m2p_multiModalImage[0][a_voxel]))
939  < (abs(mp_ImageSpace-> m2p_multiModalImage[0][i2] - mp_ImageSpace-> m2p_multiModalImage[0][a_voxel]));
940  });
941  // Consider the most similar neighbors as defined by the number of Bowsher's voxels
942  for (INTNB n=0; n<m_similarityBowsherNbVoxels; n++) m2p_similarityFactors[a_th][n] = 1.;
943  // And discard all the others
944  for (INTNB n=m_similarityBowsherNbVoxels; n<mp_neighborhoodNbVoxels[a_th]; n++) m2p_similarityFactors[a_th][n] = 0.;
945  }
946  }
947  // Unknown
948  else
949  {
950  Cerr("***** iPenaltyMarkovRandomField::ComputeSimilarityFactors() -> Unknown similarity type provided !" << endl);
951  return 1;
952  }
953  // Normal end
954  return 0;
955 }
956 
957 // =====================================================================
958 // ---------------------------------------------------------------------
959 // ---------------------------------------------------------------------
960 // =====================================================================
961 
962 int iPenaltyMarkovRandomField::LocalPreProcessingStep(int a_tbf, int a_rbf, int a_cbf, INTNB a_voxel, int a_th)
963 {
964  // First build the list of neighbours of this voxel
965  if (BuildSpecificNeighborhood(a_voxel,a_th))
966  {
967  Cerr("***** iPenaltyMarkovRandomField::LocalPreProcessingStep() -> A problem occurred while building specific neighborhood of voxel " << a_voxel << " !" << endl);
968  return 1;
969  }
970  // Then compute the similarity factors
971  if (ComputeSimilarityFactors(a_tbf,a_rbf,a_cbf,a_voxel,a_th))
972  {
973  Cerr("***** iPenaltyMarkovRandomField::LocalPreProcessingStep() -> A problem occurred while computing the similirity factors of the neighborhood of voxel " << a_voxel << " !" << endl);
974  return 1;
975  }
976  // Normal end
977  return 0;
978 }
979 
980 // =====================================================================
981 // ---------------------------------------------------------------------
982 // ---------------------------------------------------------------------
983 // =====================================================================
984 
985 FLTNB iPenaltyMarkovRandomField::ComputePenaltyValue(int a_tbf, int a_rbf, int a_cbf, INTNB a_voxel, int a_th)
986 {
987  // Compute the value of the penalty
988  FLTNB result = 0.;
989  // Sum on the contribution of the different neighbors
990  for (INTNB n=0; n<m_neighborhoodMaxNbVoxels; n++)
991  {
992  // Get the neighbor index
993  INTNB neighbor = m2p_neighborhoodIndices[a_th][n];
994  // If the voxel is not in the neighborhood, skip it
995  if (neighbor==-1) continue;
996  else
997  {
998  // Pointer to the image
999  FLTNB* p_image = mp_ImageSpace->m4p_image[a_tbf][a_rbf][a_cbf];
1000  // Proximity factor
1001  FLTNB proximity_factor = mp_proximityKernel[n];
1002  // Similarity factor
1003  FLTNB similarity_factor = m2p_similarityFactors[a_th][n];
1004  // Precompute the difference
1005  FLTNB difference = p_image[a_voxel] - p_image[neighbor];
1006  // Compute value of the potential function (consider the potential function to be symmetric)
1007  FLTNB value = 0.;
1008  switch(m_potentialType)
1009  {
1010  // Quadratic
1012  {
1013  value = 0.5 * difference * difference;
1014  break;
1015  }
1016  // Relative differences
1018  {
1019  FLTNB denominator = p_image[a_voxel] + p_image[neighbor] + m_potentialRelativeDifferenceGamma * abs(difference);
1020  if (denominator==0.) value = 0.;
1021  else value = difference * difference / denominator;
1022  break;
1023  }
1024  // Geman and McClure + 1 (we add 1 to the original definition to make it positive)
1026  {
1027  FLTNB squared_difference = difference * difference;
1028  value = squared_difference / (m_potentialGemanMcClureDelta*m_potentialGemanMcClureDelta + squared_difference);
1029  break;
1030  }
1031  // Green logcosh
1032  case MRF_POTENTIAL_GREEN:
1033  {
1034  value = log(cosh( difference / m_potentialGreenLogCoshDelta));
1035  break;
1036  }
1037  // Hebert and Leahy
1039  {
1040  value = log( 1. + difference * difference / (m_potentialHebertLeahyMu * m_potentialHebertLeahyMu));
1041  break;
1042  }
1043  // Huber piecewise
1044  case MRF_POTENTIAL_HUBER:
1045  {
1046  FLTNB abs_difference = fabs(difference);
1047  if (abs_difference>m_potentialHuberDelta) value = m_potentialHuberDelta*abs_difference - 0.5*m_potentialHuberDelta*m_potentialHuberDelta;
1048  else value = 0.5 * abs_difference * abs_difference;
1049  break;
1050  }
1051  }
1052  // Check that the value is a normal number
1053  if (fpclassify(value) != FP_NORMAL) value = 0.;
1054  // Add the final contribution to the penalty value
1055  else result += proximity_factor * similarity_factor * value;
1056  }
1057  }
1058  // Apply the penalty strength
1059  result *= m_penaltyStrength;
1060  // Check for Inf, Nan, etc
1061  if (fpclassify(result) != FP_NORMAL) result = 0.;
1062  // Return result
1063  return result;
1064 }
1065 
1066 // =====================================================================
1067 // ---------------------------------------------------------------------
1068 // ---------------------------------------------------------------------
1069 // =====================================================================
1070 
1071 FLTNB iPenaltyMarkovRandomField::ComputeFirstDerivative(int a_tbf, int a_rbf, int a_cbf, INTNB a_voxel, int a_th)
1072 {
1073  // Compute the first derivative of the penalty
1074  FLTNB result = 0.;
1075  // Sum on the contribution of the different neighbors
1076  for (INTNB n=0; n<m_neighborhoodMaxNbVoxels; n++)
1077  {
1078  // Get the neighbor index
1079  INTNB neighbor = m2p_neighborhoodIndices[a_th][n];
1080  // If the voxel is not in the neighborhood, skip it
1081  if (neighbor==-1) continue;
1082  else
1083  {
1084  // Pointer to the image
1085  FLTNB* p_image = mp_ImageSpace->m4p_image[a_tbf][a_rbf][a_cbf];
1086  // Proximity factor
1087  FLTNB proximity_factor = mp_proximityKernel[n];
1088  // Similarity factor
1089  FLTNB similarity_factor = m2p_similarityFactors[a_th][n];
1090  // Precompute the difference
1091  FLTNB difference = p_image[a_voxel] - p_image[neighbor];
1092  // Compute first derivative of the potential function (consider the potential function to be symmetric)
1093  FLTNB first_derivative = 0.;
1094  switch(m_potentialType)
1095  {
1096  // Quadratic
1098  {
1099  first_derivative = difference;
1100  break;
1101  }
1102  // Relative differences
1104  {
1105  FLTNB term = p_image[a_voxel] + p_image[neighbor] + m_potentialRelativeDifferenceGamma * fabs(difference);
1106  FLTNB term_square = term * term;
1107  if (term_square==0.) first_derivative = 0.;
1108  else first_derivative = difference * (term + 2.*p_image[neighbor]) / (term_square);
1109  break;
1110  }
1111  // Geman and McClure
1113  {
1114  FLTNB difference = p_image[a_voxel] - p_image[neighbor];
1116  first_derivative = 2. * delta_square * difference
1117  / pow( difference*difference + delta_square , 2. );
1118  break;
1119  }
1120  // Green logcosh
1121  case MRF_POTENTIAL_GREEN:
1122  {
1123  FLTNB rel_diff = difference / m_potentialGreenLogCoshDelta;
1124  first_derivative = tanh(rel_diff) / m_potentialGreenLogCoshDelta;
1125  break;
1126  }
1127  // Hebert and Leahy
1129  {
1130  first_derivative = 2. * difference / (difference*difference + m_potentialHebertLeahyMu*m_potentialHebertLeahyMu);
1131  break;
1132  }
1133  // Huber piecewise
1134  case MRF_POTENTIAL_HUBER:
1135  {
1136  if (difference==0.) first_derivative = 0.;
1137  else
1138  {
1139  FLTNB abs_difference = fabs(difference);
1140  if (abs_difference>m_potentialHuberDelta) first_derivative = m_potentialHuberDelta * difference / abs_difference;
1141  else first_derivative = difference;
1142  }
1143  break;
1144  }
1145  }
1146  // Check that the derivative is a normal number
1147  if (fpclassify(first_derivative) != FP_NORMAL) first_derivative = 0.;
1148  // Add the final contribution to the derivative value
1149  else result += proximity_factor * similarity_factor * first_derivative;
1150  }
1151  }
1152  // Apply the penalty strength
1153  result *= m_penaltyStrength;
1154  // Check for Inf, Nan, etc
1155  if (fpclassify(result) != FP_NORMAL) result = 0.;
1156  // Return result
1157  return result;
1158 }
1159 
1160 
1161 // =====================================================================
1162 // ---------------------------------------------------------------------
1163 // ---------------------------------------------------------------------
1164 // =====================================================================
1165 
1166 FLTNB iPenaltyMarkovRandomField::ComputeSecondDerivative(int a_tbf, int a_rbf, int a_cbf, INTNB a_voxel, int a_th)
1167 {
1168  // Compute the second derivative of the penalty
1169  FLTNB result = 0.;
1170  // Sum on the contribution of the different neighbors
1171  for (INTNB n=0; n<m_neighborhoodMaxNbVoxels; n++)
1172  {
1173  // Get the neighbor index
1174  INTNB neighbor = m2p_neighborhoodIndices[a_th][n];
1175  // If the voxel is not in the neighborhood, skip it
1176  if (neighbor==-1) continue;
1177  else
1178  {
1179  // Pointer to the image
1180  FLTNB* p_image = mp_ImageSpace->m4p_image[a_tbf][a_rbf][a_cbf];
1181  // Proximity factor
1182  FLTNB proximity_factor = mp_proximityKernel[n];
1183  // Similarity factor
1184  FLTNB similarity_factor = m2p_similarityFactors[a_th][n];
1185  // Compute second derivative of the potential function (consider the potential function to be symmetric)
1186  FLTNB second_derivative = 0.;
1187  switch(m_potentialType)
1188  {
1189  // Quadratic
1191  {
1192  second_derivative = 1.;
1193  break;
1194  }
1195  // Relative differences
1197  {
1198  FLTNB term = p_image[a_voxel] + p_image[neighbor] + m_potentialRelativeDifferenceGamma * fabs(p_image[a_voxel] - p_image[neighbor]);
1199  FLTNB term_cube = term * term * term;
1200  if (term_cube==0.) second_derivative = 0.;
1201  else second_derivative = (8.*p_image[neighbor]*p_image[neighbor])/(term_cube);
1202  break;
1203  }
1204  // Geman and McClure
1206  {
1207  FLTNB difference = p_image[a_voxel] - p_image[neighbor];
1208  FLTNB difference_square = difference * difference;
1210  second_derivative = -2. * delta_square * (3.*difference_square - delta_square)
1211  / pow( difference_square + delta_square , 3. );
1212  break;
1213  }
1214  // Green logcosh
1215  case MRF_POTENTIAL_GREEN:
1216  {
1217  second_derivative = 1. / (m_potentialGreenLogCoshDelta * cosh((p_image[a_voxel]-p_image[neighbor])/m_potentialGreenLogCoshDelta));
1218  second_derivative *= second_derivative;
1219  break;
1220  }
1221  // Hebert and Leahy
1223  {
1224  FLTNB difference = p_image[a_voxel] - p_image[neighbor];
1225  second_derivative = - 2. * (difference+m_potentialHebertLeahyMu) * (difference-m_potentialHebertLeahyMu)
1226  / pow( difference*difference + m_potentialHebertLeahyMu*m_potentialHebertLeahyMu , 2. );
1227  break;
1228  }
1229  // Huber piecewise
1230  case MRF_POTENTIAL_HUBER:
1231  {
1232  // Assume null derivative in all domain
1233  second_derivative = 0.;
1234  break;
1235  }
1236  }
1237  // Check that the derivative is a normal number
1238  if (fpclassify(second_derivative) != FP_NORMAL) second_derivative = 0.;
1239  // Add the final contribution to the derivative value
1240  else result += proximity_factor * similarity_factor * second_derivative;
1241  }
1242  }
1243  // Apply the penalty strength
1244  result *= m_penaltyStrength;
1245  // Check for Inf, Nan, etc
1246  if (fpclassify(result) != FP_NORMAL) result = 0.;
1247  // Return result
1248  return result;
1249 }
FLTNB GetVoxSizeX()
Get the voxel&#39;s size along the X axis, in mm.
#define Cerr(MESSAGE)
FLTNB GetVoxSizeZ()
Get the voxel&#39;s size along the Z axis, in mm.
int GetNbMultiModalImages()
Get the number of additional multimodal images.
int BuildProximityFactors()
A function used to build the kernel of the proximity factors.
int LocalPreProcessingStep(int a_tbf, int a_rbf, int a_cbf, INTNB a_voxel, int a_th)
FLTNB ComputeSecondDerivative(int a_tbf, int a_rbf, int a_cbf, INTNB a_voxel, int a_th)
int ReadConfigurationFile(const string &a_configurationFile)
FLTNB ComputeFirstDerivative(int a_tbf, int a_rbf, int a_cbf, INTNB a_voxel, int a_th)
int InitializeSpecific()
This function is used to initialize specific stuff to the child penalty.
FLTNB GetVoxSizeY()
Get the voxel&#39;s size along the Y axis, in mm.
iPenaltyMarkovRandomField()
The constructor of iPenaltyMarkovRandomField.
This class is designed to generically described any penalty applied to MAP algorithms.
#define KEYWORD_MANDATORY
#define KEYWORD_OPTIONAL
FLTNB ComputePenaltyValue(int a_tbf, int a_rbf, int a_cbf, INTNB a_voxel, int a_th)
int ComputeSimilarityFactors(int a_tbf, int a_rbf, int a_cbf, INTNB a_voxel, int a_th)
int GetNbThreadsForImageComputation()
Get the number of threads used for image operations.
~iPenaltyMarkovRandomField()
The destructor of iPenaltyMarkovRandomField.
INTNB GetNbVoxX()
Get the number of voxels along the X axis.
INTNB GetNbVoxZ()
Get the number of voxels along the Z axis.
int CheckSpecificParameters()
A private function used to check the parameters settings specific to the child penalty.
int BuildNeighborhoodKernel()
A function used to build the neighborhood kernel.
int ReadDataASCIIFile(const string &a_file, const string &a_keyword, T *ap_return, int a_nbElts, bool a_mandatoryFlag)
Look for "a_nbElts" elts in the "a_file" file matching the "a_keyword" string passed as parameter a...
void ShowHelpSpecific()
A function used to show help about the child penalty.
#define Cout(MESSAGE)
oImageDimensionsAndQuantification * mp_ImageDimensionsAndQuantification
INTNB GetNbVoxY()
Get the number of voxels along the Y axis.
#define KEYWORD_OPTIONAL_ERROR