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