CASToR  3.0
Tomographic Reconstruction (PET/SPECT/CT)
iPenaltyMarkovRandomField.cc
Go to the documentation of this file.
1 /*
2 This file is part of CASToR.
3 
4  CASToR is free software: you can redistribute it and/or modify it under the
5  terms of the GNU General Public License as published by the Free Software
6  Foundation, either version 3 of the License, or (at your option) any later
7  version.
8 
9  CASToR is distributed in the hope that it will be useful, but WITHOUT ANY
10  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11  FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12  details.
13 
14  You should have received a copy of the GNU General Public License along with
15  CASToR (in file GNU_GPL.TXT). If not, see <http://www.gnu.org/licenses/>.
16 
17 Copyright 2017-2019 all CASToR contributors listed below:
18 
19  --> Didier BENOIT, Claude COMTAT, Marina FILIPOVIC, Thibaut MERLIN, Mael MILLARDET, Simon STUTE, Valentin VIELZEUF
20 
21 This is CASToR version 3.0.
22 */
23 
31 
32 // =====================================================================
33 // ---------------------------------------------------------------------
34 // ---------------------------------------------------------------------
35 // =====================================================================
36 
38 {
39  // --------------------------
40  // Specific member parameters
41  // --------------------------
42  m_penaltyID = "MRF";
51  mp_proximityKernel = NULL;
55  m2p_similarityFactors = NULL;
60  m_neighborhoodBoxExcludeCorners = 0; // Keep them by default
64  // The derivative order depends on the potential function that we do not know yet, so we let it by default to 0
66 }
67 
68 // =====================================================================
69 // ---------------------------------------------------------------------
70 // ---------------------------------------------------------------------
71 // =====================================================================
72 
74 {
75  // Destroy mp_neighborhoodNbVoxels
77  {
79  }
80  // Destroy m2p_neighborhoodIndices
82  {
84  {
86  }
88  }
89  // Destroy m2p_similarityFactors
91  {
93  {
95  }
97  }
98  // Destroy m2p_neighborhoodKernel
100  {
101  for (INTNB v=0; v<m_neighborhoodMaxNbVoxels; v++)
102  {
104  }
106  }
107  // Destroy mp_proximityKernel
108  if (mp_proximityKernel)
109  {
110  free(mp_proximityKernel);
111  }
112 }
113 
114 // =====================================================================
115 // ---------------------------------------------------------------------
116 // ---------------------------------------------------------------------
117 // =====================================================================
118 
120 {
121  cout << "The Markov Random Field (MRF) penalty is implemented for several types of neighborhood, potential functions," << endl;
122  cout << "similarity and proximity factors. All these parameters are described below, and a template configuration file" << endl;
123  cout << "for parameters selection can be found in the configuration folder (config/optimizer/MRF.conf). The configuration" << endl;
124  cout << "of this penalty can only be done through a configuration file. It is not possible to parameterize it directly" << endl;
125  cout << "from the command line. The MRF penalty has the following expression:" << endl;
126  cout << "penalty = beta * sum_on_voxels * sum_on_neighborhood * proximity_factor * similarity_factor * potential_function" << endl;
127  cout << "------------" << endl;
128  cout << "Neighborhood" << endl;
129  cout << "------------" << endl;
130  cout << "The neighborhood is set by the 'neighborhood shape' keyword and can be described using one of the following setting:" << endl;
131  cout << "[6-nearest]: Consider the 6 nearest neighbors, i.e. 2 along each dimension. In 2D, only 4 in the plane are used." << endl;
132  cout << "[box]: Consider all voxels included in a box centered on the voxel of interest. The size of the box is parameterized" << endl;
133  cout << " using the 'box neighborhood order' keyword. The side of the box is equal to '2*order+1'. The 8 corner voxels" << endl;
134  cout << " can also be excluded from the neighborhood by setting the 'exclude box neighborhood corners' keyword to 1." << endl;
135  cout << "[sphere]: Consider all voxels whose center is included in a sphere centered on the voxel of interest and of radius" << endl;
136  cout << " provided by the 'sphere neighborhood radius (mm)' keyword." << endl;
137  cout << "-----------------" << endl;
138  cout << "Proximity factors" << endl;
139  cout << "-----------------" << endl;
140  cout << "The proximity factors are used to weight the contribution of each neighbor to the global penalty, based on proximity to the" << endl;
141  cout << "voxel of interest. These factors are always normalized so that their sum is 1. They can be set using the 'proximity factor'" << endl;
142  cout << "keyword based on one of the following setting:" << endl;
143  cout << "[none]: Consider uniform proximity factors." << endl;
144  cout << "[voxel]: Consider factors inversely proportional to the distance in voxels from the voxel of interest (i.e. the unit" << endl;
145  cout << " is voxels, whatever the axis and the voxel dimensions)." << endl;
146  cout << "[euclidian]: Consider factors inversely proportional to the euclidian distance from the voxel of interest (i.e. the unit" << endl;
147  cout << " is mm)." << endl;
148  cout << endl;
149  cout << "------------------" << endl;
150  cout << "Similarity factors" << endl;
151  cout << "------------------" << endl;
152  cout << "The similarity factors are used to weight the contribution of each neighbor to the global penalty, based on similarity to the" << endl;
153  cout << "voxel of interest. These factors are not normalized by default. They can be set using the 'similarity factor' keyword based on" << endl;
154  cout << "one of the following setting:" << endl;
155  cout << "[none]: Consider uniform similarity factors, i.e. the value of 1 for each voxel in the neighborhood." << endl;
156  cout << "[aBowsher]: Consider similarity factors based on the asymmetrical Bowsher's method and an additional image. The additional" << endl;
157  cout << " image must be provided using the '-multimodal' option in the command line. Based on additional image values," << endl;
158  cout << " voxels of the neighborhood most similar to the voxel of interest will have a similarity factor of 1, and the" << endl;
159  cout << " other voxels will have a similarity factor of 0. The number of most similar voxels is parameterized by a percentage" << endl;
160  cout << " of the number of voxels in the neighborhood, provided with the 'similarity threshold Bowsher (%)' keyword." << endl;
161  cout << " For an explanation of asymmetrical Bowsher, see Schramm et al, IEEE Trans. Med. Imaging, vol. 37, pp. 590, 2018." << endl;
162  cout << "-------------------" << endl;
163  cout << "Potential functions" << endl;
164  cout << "-------------------" << endl;
165  cout << "The potential function actually penalizes the difference between the voxel of interest and a neighbor. It can be set using the" << endl;
166  cout << "'potential function' keyword based on one of the following setting:" << endl;
167  cout << "[quadratic]: p(u,v) = 0.5*(u-v)^2" << endl;
168  cout << " Reference: Geman and Geman, IEEE Trans. Pattern Anal. Machine Intell., vol. PAMI-6, pp. 721-741, 1984." << endl;
169  cout << "[geman mcclure]: p(u,v,d) = (u-v)^2 / (d^2+(u-v)^2)" << endl;
170  cout << " The parameter 'd' can be set using the 'deltaGMC' keyword." << endl;
171  cout << " Reference: Geman and McClure, Proc. Amer. Statist. Assoc., 1985." << endl;
172  cout << "[hebert leahy]: p(u,v,m) = log(1+(u-v)^2/m^2)" << endl;
173  cout << " The parameter 'm' can be set using the 'muHL' keyword." << endl;
174  cout << " Reference: Hebert and Leahy, IEEE Trans. Med. Imaging, vol. 8, pp. 194-202, 1989." << endl;
175  cout << "[green logcosh]: p(u,v,d) = log(cosh((u-v)/d))" << endl;
176  cout << " The parameter 'd' can be set using the 'deltaLogCosh' keyword." << endl;
177  cout << " Reference: Green, IEEE Trans. Med. Imaging, vol. 9, pp. 84-93, 1990." << endl;
178  cout << "[huber piecewise]: " << endl;
179  cout << " p(u,v,d) = d*abs(u-v)-0.5*d^2 if abs(u-v) > d" << endl;
180  cout << " = 0.5*(u-v)^2 if abs(u-v) <= d" << endl;
181  cout << " The parameter 'd' can be set using the 'deltaHuber' keyword." << endl;
182  cout << " Reference: e.g. Mumcuoglu et al, Phys. Med. Biol., vol. 41, pp. 1777-1807, 1996." << endl;
183  cout << "[nuyts relative]: p(u,v,g) = (u-v)^2 / (u+v+g*abs(u-v))" << endl;
184  cout << " The parameter 'g' can be set using the 'gammaRD' keyword." << endl;
185  cout << " Reference: Nuyts et al, IEEE Trans. Nucl. Sci., vol. 49, pp. 56-60, 2002." << endl;
186 }
187 
188 // =====================================================================
189 // ---------------------------------------------------------------------
190 // ---------------------------------------------------------------------
191 // =====================================================================
192 
193 int iPenaltyMarkovRandomField::ReadConfigurationFile(const string& a_configurationFile)
194 {
195  string buffer = "";
196  string key_word = "";
197 
198  // -------------------------------------------------------------------
199  // Read the type of neighborhood
200  // -------------------------------------------------------------------
201  key_word = "neighborhood shape";
202  if (ReadDataASCIIFile(a_configurationFile, key_word, &buffer, 1, KEYWORD_MANDATORY))
203  {
204  Cerr("***** iPenaltyMarkovRandomField::ReadConfigurationFile() -> Failed to get the '" << key_word << "' keyword !" << endl);
205  return 1;
206  }
207  // Sphere neighborhood
208  if (buffer == "sphere")
209  {
211  // Radius of the sphere
212  key_word = "sphere neighborhood radius (mm)";
213  if (ReadDataASCIIFile(a_configurationFile, key_word, &m_neighborhoodSphereRadius, 1, KEYWORD_MANDATORY))
214  {
215  Cerr("***** iPenaltyMarkovRandomField::ReadConfigurationFile() -> Failed to get the '" << key_word << "' keyword !" << endl);
216  return 1;
217  }
218  }
219  // Box neighborhood
220  else if (buffer == "box")
221  {
223  // Order of the box (number of voxels which will be included in each direction)
224  key_word = "box neighborhood order";
225  if (ReadDataASCIIFile(a_configurationFile, key_word, &m_neighborhoodBoxOrder, 1, KEYWORD_MANDATORY))
226  {
227  Cerr("***** iPenaltyMarkovRandomField::ReadConfigurationFile() -> Failed to get the '" << key_word << "' keyword !" << endl);
228  return 1;
229  }
230  // Include corners of the box or not (some people do that) (not mandatory as we keep them by default)
231  key_word = "exclude box neighborhood corners";
233  {
234  Cerr("***** iPenaltyMarkovRandomField::ReadConfigurationFile() -> Failed to get the '" << key_word << "' keyword !" << endl);
235  return 1;
236  }
237  }
238  // 6-nearest
239  else if (buffer == "6-nearest")
240  {
242  }
243  // Unknown
244  else
245  {
246  Cerr("***** iPenaltyMarkovRandomField::ReadConfigurationFile() -> Unknown neighborhood type '" << buffer << "' !" << endl);
247  return 1;
248  }
249 
250  // -------------------------------------------------------------------
251  // Read and check the type of the proximity factor
252  // -------------------------------------------------------------------
253  key_word = "proximity factor";
254  if (ReadDataASCIIFile(a_configurationFile, key_word, &buffer, 1, KEYWORD_MANDATORY))
255  {
256  Cerr("***** iPenaltyMarkovRandomField::ReadConfigurationFile() -> Failed to get the '" << key_word << "' keyword !" << endl);
257  return 1;
258  }
259  // Inverse of euclidian distance
260  if (buffer == "euclidian")
261  {
263  }
264  // Inverse of voxel distance (in numbers of voxels)
265  else if (buffer == "voxel")
266  {
268  }
269  // Exponential distance squared
270  /*
271  else if (buffer == "exponential distance squared")
272  {
273  m_proximityType = MRF_PROXIMITY_EXP_DIST_SQUARED;
274  key_word = "proximity characteristic distance (mm)";
275  if (ReadDataASCIIFile(a_configurationFile, key_word, &m_proximityCharacteristicDistance, 1, KEYWORD_MANDATORY))
276  {
277  Cerr("***** iPenaltyMarkovRandomField::ReadConfigurationFile() -> Failed to get the '" << key_word << "' keyword !" << endl);
278  return 1;
279  }
280  }
281  */
282  // No proximity factor
283  else if (buffer == "none")
284  {
286  }
287  // Unknown
288  else
289  {
290  Cerr("***** iPenaltyMarkovRandomField::ReadConfigurationFile() -> Unknown proximity factor type '" << buffer << "' !" << endl);
291  return 1;
292  }
293 
294  // -------------------------------------------------------------------
295  // Read and check the type of the similarity factor
296  // -------------------------------------------------------------------
297  key_word = "similarity factor";
298  if (ReadDataASCIIFile(a_configurationFile, key_word, &buffer, 1, KEYWORD_MANDATORY))
299  {
300  Cerr("***** iPenaltyMarkovRandomField::ReadConfigurationFile() -> Failed to get the '" << key_word << "' keyword !" << endl);
301  return 1;
302  }
303  // No similarity factors
304  if (buffer == "none")
305  {
307  }
308  // Bowsher's similarity factors
309  else if (buffer == "aBowsher")
310  {
312  // Read the Bowsher threshold
313  key_word = "similarity threshold Bowsher (%)";
314  if (ReadDataASCIIFile(a_configurationFile, key_word, &m_similarityBowsherThreshold, 1, KEYWORD_MANDATORY))
315  {
316  Cerr("***** iPenaltyMarkovRandomField::ReadConfigurationFile() -> Failed to get the '" << key_word << "' keyword !" << endl);
317  return 1;
318  }
319  }
320  // Unknown
321  else
322  {
323  Cerr("***** iPenaltyMarkovRandomField::ReadConfigurationFile() -> Unknown similarity factor type '" << buffer << "' !" << endl);
324  return 1;
325  }
326 
327  // -------------------------------------------------------------------
328  // Read and check the type of the potential energy function
329  // -------------------------------------------------------------------
330  key_word = "potential function";
331  if (ReadDataASCIIFile(a_configurationFile, key_word, &buffer, 1, KEYWORD_MANDATORY))
332  {
333  Cerr("***** iPenaltyMarkovRandomField::ReadConfigurationFile() -> Failed to get the '" << key_word << "' keyword !" << endl);
334  return 1;
335  }
336  // Quadratic potential function
337  if (buffer == "quadratic")
338  {
340  m_penaltyDerivativesOrder = INT_MAX;
341  }
342  // Relative differences potential function
343  else if (buffer == "nuyts relative")
344  {
346  m_penaltyDerivativesOrder = INT_MAX;
347  key_word = "gammaRD";
348  if (ReadDataASCIIFile(a_configurationFile, key_word, &m_potentialRelativeDifferenceGamma, 1, KEYWORD_MANDATORY))
349  {
350  Cerr("***** iPenaltyMarkovRandomField::ReadConfigurationFile() -> Failed to get the '" << key_word << "' keyword !" << endl);
351  return 1;
352  }
353  }
354  // Geman and McClure function
355  else if (buffer == "geman mcclure")
356  {
358  m_penaltyDerivativesOrder = INT_MAX;
359  key_word = "deltaGMC";
360  if (ReadDataASCIIFile(a_configurationFile, key_word, &m_potentialGemanMcClureDelta, 1, KEYWORD_MANDATORY))
361  {
362  Cerr("***** iPenaltyMarkovRandomField::ReadConfigurationFile() -> Failed to get the '" << key_word << "' keyword !" << endl);
363  return 1;
364  }
365  }
366  // Green's logcosh
367  else if (buffer == "green logcosh")
368  {
370  m_penaltyDerivativesOrder = INT_MAX;
371  key_word = "deltaLogCosh";
372  if (ReadDataASCIIFile(a_configurationFile, key_word, &m_potentialGreenLogCoshDelta, 1, KEYWORD_MANDATORY))
373  {
374  Cerr("***** iPenaltyMarkovRandomField::ReadConfigurationFile() -> Failed to get the '" << key_word << "' keyword !" << endl);
375  return 1;
376  }
377  }
378  // Hebert and Leahy function
379  else if (buffer == "hebert leahy")
380  {
382  m_penaltyDerivativesOrder = INT_MAX;
383  key_word = "muHL";
384  if (ReadDataASCIIFile(a_configurationFile, key_word, &m_potentialHebertLeahyMu, 1, KEYWORD_MANDATORY))
385  {
386  Cerr("***** iPenaltyMarkovRandomField::ReadConfigurationFile() -> Failed to get the '" << key_word << "' keyword !" << endl);
387  return 1;
388  }
389  }
390  // Huber function
391  else if (buffer == "huber piecewise")
392  {
394  m_penaltyDerivativesOrder = INT_MAX;
395  key_word = "deltaHuber";
396  if (ReadDataASCIIFile(a_configurationFile, key_word, &m_potentialHuberDelta, 1, KEYWORD_MANDATORY))
397  {
398  Cerr("***** iPenaltyMarkovRandomField::ReadConfigurationFile() -> Failed to get the '" << key_word << "' keyword !" << endl);
399  return 1;
400  }
401  }
402  // Unknown
403  else
404  {
405  Cerr("***** iPenaltyMarkovRandomField::ReadConfigurationFile() -> Unknown potential function '" << buffer << "' !" << endl);
406  return 1;
407  }
408 
409  // Normal end
410  return 0;
411 }
412 
413 // =====================================================================
414 // ---------------------------------------------------------------------
415 // ---------------------------------------------------------------------
416 // =====================================================================
417 
418 int iPenaltyMarkovRandomField::ReadOptionsList(const string& a_optionsList)
419 {
420  // Too complicated to do it that way
421  Cerr("***** iPenaltyMarkovRandomField::ReadOptionsList() -> Options can be specified only using a configuration file !" << endl);
422  return 1;
423 }
424 
425 // =====================================================================
426 // ---------------------------------------------------------------------
427 // ---------------------------------------------------------------------
428 // =====================================================================
429 
431 {
432  // Check potential function type
434  {
435  Cerr("***** iPenaltyMarkovRandomField::CheckSpecificParameters() -> Should provide a potential function type !" << endl);
436  return 1;
437  }
438  // Check proximity factor type
440  {
441  Cerr("***** iPenaltyMarkovRandomField::CheckSpecificParameters() -> Should provide a proximity factor type !" << endl);
442  return 1;
443  }
444  // Check similarity factor type
446  {
447  Cerr("***** iPenaltyMarkovRandomField::CheckSpecificParameters() -> Should provide a similarity factor type !" << endl);
448  return 1;
449  }
450  // Check neighborhood type
452  {
453  Cerr("***** iPenaltyMarkovRandomField::CheckSpecificParameters() -> Should provide a neighborhood type !" << endl);
454  return 1;
455  }
456  /*
457  // check parameters of the proximity factors
458  if (m_proximityType == MRF_PROXIMITY_EXP_DIST_SQUARED && m_proximityCharacteristicDistance <=0.)
459  {
460  Cerr("***** iPenaltyMarkovRandomField::CheckSpecificParameters() -> Provided proximity characteristic distance (" << m_proximityCharacteristicDistance << ") is non-positive" << endl);
461  return 1;
462  }
463  */
464  // Check parameters of the similarity factors
466  {
467  if (m_similarityBowsherThreshold<0. || m_similarityBowsherThreshold>100.)
468  {
469  Cerr("***** iPenaltyMarkovRandomField::CheckSpecificParameters() -> Provided threshold parameter for the Bowsher similarity " << m_similarityBowsherThreshold << " does not fall into [0,100]% !" << endl);
470  return 1;
471  }
473  {
474  Cerr("***** iPenaltyMarkovRandomField::CheckSpecificParameters() -> Bowsher similarity requires a multimodal image !"<< endl);
475  return 1;
476  }
478  {
479  Cout("***** iPenaltyMarkovRandomField::CheckSpecificParameters() -> Warning : More than one multimodal image, Bowsher similarity will use only the first one !"<< endl);
480  }
481  }
482  // Check some parameters of the neighborhood
484  {
485  Cerr("***** iPenaltyMarkovRandomField::CheckSpecificParameters() -> Provided radius of the sphere neighborhood (" << m_neighborhoodSphereRadius << ") is negative" << endl);
486  return 1;
487  }
489  {
490  Cerr("***** iPenaltyMarkovRandomField::CheckSpecificParameters() -> Provided order of the box neighborhood (" << m_neighborhoodBoxOrder << ") is negative" << endl);
491  return 1;
492  }
493  // Check for gamma value of the relative distance potential function
495  {
496  Cerr("***** iPenaltyMarkovRandomField::CheckSpecificParameters() -> Provided gamma parameter of relative differences potential function must be positive or null !" << endl);
497  return 1;
498  }
499  // Check for delta value of the Geman and McClure potential function
501  {
502  Cerr("***** iPenaltyMarkovRandomField::CheckSpecificParameters() -> Provided delta parameter of Geman and McClure's potential function must be strictly positive !" << endl);
503  return 1;
504  }
505  // Check for delta value of the Green's log-cosh potential function
507  {
508  Cerr("***** iPenaltyMarkovRandomField::CheckSpecificParameters() -> Provided delta parameter of Green's log-cosh potential function must be strictly positive !" << endl);
509  return 1;
510  }
511  // Check for mu value of the Hebert and Leahy potential function
513  {
514  Cerr("***** iPenaltyMarkovRandomField::CheckSpecificParameters() -> Provided mu parameter of Hebert and Leahy's potential function must be strictly positive !" << endl);
515  return 1;
516  }
517  // Check for delta value of the Huber potential function
519  {
520  Cerr("***** iPenaltyMarkovRandomField::CheckSpecificParameters() -> Provided delta parameter of Huber's piecewise potential function must be strictly positive !" << endl);
521  return 1;
522  }
523  // Normal end
524  return 0;
525 }
526 
527 // =====================================================================
528 // ---------------------------------------------------------------------
529 // ---------------------------------------------------------------------
530 // =====================================================================
531 
533 {
534  // Build the neighborhood kernel that will be used to facilitate the computation of the neighborhood of each voxel later on
536  {
537  Cerr("***** iPenaltyMarkovRandomField::InitializeSpecific() -> Failed to build the neighborhood !" << endl);
538  return 1;
539  }
540  // Build the proximity factors of the neighborhood that remain constant for any voxel
541  if (BuildProximityFactors())
542  {
543  Cerr("***** iPenaltyMarkovRandomField::InitializeSpecific() -> Failed to build the proximity factors !" << endl);
544  return 1;
545  }
546  // Some allocations for the specific neighborhood of each voxel used during computations
551  {
554  // Initialize the similarity factors to 1, in case they stay uniform for the reconstruction
555  for (INTNB v=0; v<m_neighborhoodMaxNbVoxels; v++) m2p_similarityFactors[th][v] = 1.;
556  }
557  // Verbose
559  {
560  Cout("iPenaltyMarkovRandomField::InitializeSpecific() -> Use the MRF penalty" << endl);
562  {
563  // Type of neighborhood
565  {
566  Cout(" --> shape of neighborhood: sphere" << endl);
567  Cout(" --> radius of the sphere neighborhood: " << m_neighborhoodSphereRadius << " mm " << endl);
568  }
570  {
571  Cout(" --> shape of neighborhood: box" << endl);
572  Cout(" --> order of the box (number of voxels in each direction): " << m_neighborhoodBoxOrder << endl);
573  if (m_neighborhoodBoxExcludeCorners) Cout(" --> exclude corner voxels" << endl);
574  }
576  {
577  Cout(" --> shape of neighborhood: 6-nearest" << endl);
578  }
579  }
580  Cout(" --> Max number of voxels in the neighborhood: " << m_neighborhoodMaxNbVoxels << endl);
582  {
583  // Proximity factors
585  {
586  Cout(" --> proximity factor: uniform" << endl);
587  }
589  {
590  Cout(" --> proximity factor: inverse of euclidian distance" << endl);
591  }
593  {
594  Cout(" --> proximity factor: inverse of voxel distance (i.e. distance in numbers of voxels)" << endl);
595  }
596 /*
597  else if (m_proximityType == MRF_PROXIMITY_EXP_DIST_SQUARED)
598  {
599  Cout(" --> proximity factor: exponential square distances"<< endl);
600  Cout(" --> characteristic distance for the proximity factors: "<< m_proximityCharacteristicDistance << endl);
601  }
602 */
603  }
604  // Similarity factors
606  {
607  Cout(" --> similarity factor: uniform" << endl);
608  }
610  {
611  Cout(" --> similarity factor: asymmetric Bowsher" << endl);
613  {
614  Cout(" --> Bowsher's threshold: " << m_similarityBowsherThreshold << " %" << endl);
615  Cout(" --> Bowsher's most similar voxels kept: " << m_similarityBowsherNbVoxels << endl);
616  }
617  }
618  // Potential function
620  {
621  Cout(" --> potential function: quadratic" << endl);
622  }
624  {
625  Cout(" --> potential function: Nuyts relative differences 2002" << endl);
626  if (m_verbose>=VERBOSE_DETAIL) Cout(" --> gamma: " << m_potentialRelativeDifferenceGamma << endl);
627  }
629  {
630  Cout(" --> potential function: Geman and McClure 1985" << endl);
631  if (m_verbose>=VERBOSE_DETAIL) Cout(" --> delta: " << m_potentialGemanMcClureDelta << endl);
632  }
634  {
635  Cout(" --> potential function: Green log-cosh 1990" << endl);
636  if (m_verbose>=VERBOSE_DETAIL) Cout(" --> delta: " << m_potentialGreenLogCoshDelta << endl);
637  }
639  {
640  Cout(" --> potential function: Hebert and Leahy 1989" << endl);
641  if (m_verbose>=VERBOSE_DETAIL) Cout(" --> mu: " << m_potentialHebertLeahyMu << endl);
642  }
644  {
645  Cout(" --> potential function: Huber piecewise linear-quadratic" << endl);
646  if (m_verbose>=VERBOSE_DETAIL) Cout(" --> delta: " << m_potentialHuberDelta << endl);
647  }
648  if (m_verbose>=VERBOSE_DETAIL) Cout(" --> penalty energy function derivatives order: " << m_penaltyDerivativesOrder << endl);
649  }
650  // Normal end
651  return 0;
652 }
653 
654 // =====================================================================
655 // ---------------------------------------------------------------------
656 // ---------------------------------------------------------------------
657 // =====================================================================
658 
660 {
661  // Check if the neighborhood has already been created
663  {
664  Cerr("***** iPenaltyMarkovRandomField::BuildNeighborhoodKernel() -> Neighborhood kernel has already been created !" << endl);
665  return 1;
666  }
667  // Get voxel sizes locally
671  // ========================================================================================================
672  // Case where the neighborhood is defined by a sphere with radius provided in mm
673  // ========================================================================================================
675  {
676  // Compute the size of the box including the sphere (minimum of 1 voxel radius)
677  INTNB radius_vox_x = max( (INTNB)1 , ((INTNB)(m_neighborhoodSphereRadius/vox_size_x)) );
678  INTNB radius_vox_y = max( (INTNB)1 , ((INTNB)(m_neighborhoodSphereRadius/vox_size_y)) );
679  INTNB radius_vox_z = max( (INTNB)1 , ((INTNB)(m_neighborhoodSphereRadius/vox_size_z)) );
680  // Particular case for 2D reconstruction
681  if (mp_ImageDimensionsAndQuantification->GetNbVoxZ()==1) radius_vox_z = 0;
682  // Precompute the maximum number of voxels in the neighborhood as the box including the sphere
683  m_neighborhoodMaxNbVoxels = (radius_vox_x*2+1) * (radius_vox_y*2+1) * (radius_vox_z*2+1);
684  // Allocate the neighborhood kernel
686  for (INTNB v = 0; v<m_neighborhoodMaxNbVoxels; v++) m2p_neighborhoodKernel[v] = (INTNB*)malloc(3*sizeof(INTNB));
687  // Counter for the number of voxels in the neighborhood
688  INTNB nb_voxels_in_neighborhood = 0;
689  // Loop on increments of voxels indices in box neighborhood
690  for (INTNB vox_z = -radius_vox_z; vox_z<=radius_vox_z; vox_z++)
691  for (INTNB vox_y = -radius_vox_y; vox_y<=radius_vox_y; vox_y++)
692  for (INTNB vox_x = -radius_vox_x; vox_x<=radius_vox_x; vox_x++)
693  // Exclude the voxel itself
694  if (vox_x!=0 || vox_y!=0 || vox_z!=0)
695  {
696  // Compute the square distance between the two voxels
697  FLTNB squareDistance = ((FLTNB)(vox_x*vox_x))*vox_size_x*vox_size_x
698  + ((FLTNB)(vox_y*vox_y))*vox_size_y*vox_size_y
699  + ((FLTNB)(vox_z*vox_z))*vox_size_z*vox_size_z;
700  // Include voxel if the distance from the central voxel is less than the radius
702  {
703  m2p_neighborhoodKernel[nb_voxels_in_neighborhood][MRF_NEIGHBOR_X] = vox_x;
704  m2p_neighborhoodKernel[nb_voxels_in_neighborhood][MRF_NEIGHBOR_Y] = vox_y;
705  m2p_neighborhoodKernel[nb_voxels_in_neighborhood][MRF_NEIGHBOR_Z] = vox_z;
706  nb_voxels_in_neighborhood++;
707  }
708  }
709  // Update the maximum number of voxels in the neighborhood by the exact value
710  m_neighborhoodMaxNbVoxels = nb_voxels_in_neighborhood;
711  m2p_neighborhoodKernel = (INTNB**)realloc(m2p_neighborhoodKernel,m_neighborhoodMaxNbVoxels*sizeof(INTNB*));
712  }
713  // ========================================================================================================
714  // Case where the neighborhood is defined by a box as described in many papers
715  // ========================================================================================================
717  {
718  // Particular case for 2D reconstruction
719  INTNB neighborhood_box_order_z = m_neighborhoodBoxOrder;
720  if (mp_ImageDimensionsAndQuantification->GetNbVoxZ()==1) neighborhood_box_order_z = 0;
721  // Maximum number of voxels in the neighborhood of a voxel with no image boundary constraints
723  // Allocate the neighborhood kernel
725  for (INTNB v = 0; v<m_neighborhoodMaxNbVoxels; v++) m2p_neighborhoodKernel[v] = (INTNB*)malloc(3*sizeof(INTNB));
726  // Counter for the number of voxels in the neighborhood
727  INTNB nb_voxels_in_neighborhood = 0;
728  // Loop on increments of voxels indices in the box neighborhood
729  for (INTNB vox_z = -neighborhood_box_order_z; vox_z<=neighborhood_box_order_z; vox_z++)
730  for (INTNB vox_y = -m_neighborhoodBoxOrder; vox_y<=m_neighborhoodBoxOrder; vox_y++)
731  for (INTNB vox_x = -m_neighborhoodBoxOrder; vox_x<=m_neighborhoodBoxOrder; vox_x++)
732  // Exclude the voxel itself
733  if (vox_x!=0 || vox_y!=0 || vox_z!=0)
734  {
735  // Exclude corner voxels if requested
737  || (vox_y!=-m_neighborhoodBoxOrder && vox_y!=m_neighborhoodBoxOrder)
738  || (vox_z!=-m_neighborhoodBoxOrder && vox_z!=m_neighborhoodBoxOrder))
739  {
740  m2p_neighborhoodKernel[nb_voxels_in_neighborhood][MRF_NEIGHBOR_X] = vox_x;
741  m2p_neighborhoodKernel[nb_voxels_in_neighborhood][MRF_NEIGHBOR_Y] = vox_y;
742  m2p_neighborhoodKernel[nb_voxels_in_neighborhood][MRF_NEIGHBOR_Z] = vox_z;
743  nb_voxels_in_neighborhood++;
744  }
745  }
746  // Update the maximum number of voxels in the neighborhood by the exact value
747  m_neighborhoodMaxNbVoxels = nb_voxels_in_neighborhood;
748  m2p_neighborhoodKernel = (INTNB**)realloc(m2p_neighborhoodKernel,m_neighborhoodMaxNbVoxels*sizeof(INTNB*));
749  }
750  // ========================================================================================================
751  // Case where the neighborhood is defined simply by the 6-nearest neighbors
752  // ========================================================================================================
754  {
755  // Maximum number of voxels in the neighborhood is 6
757  // Particular case for 2D reconstruction
759  // Allocate the neighborhood kernel
761  for (INTNB v = 0; v<m_neighborhoodMaxNbVoxels; v++) m2p_neighborhoodKernel[v] = (INTNB*)malloc(3*sizeof(INTNB));
762  // The 6 voxels
763  for (INTNB v=0; v<m_neighborhoodMaxNbVoxels; v++) for (INTNB dim=0; dim<3; dim++) m2p_neighborhoodKernel[v][dim] = 0;
769  {
772  }
773  }
774  // ========================================================================================================
775  // Unknown neighborhood type
776  // ========================================================================================================
777  else
778  {
779  Cerr("***** iPenaltyMarkovRandomField::BuildNeighborhoodKernel() -> The provided shape of neighborhood (" << m_neighborhoodShape << ") is unknown !" << endl);
780  return 1;
781  }
782  // Check that there are some voxels in the neighborhood
784  {
785  Cerr("***** iPenaltyMarkovRandomField::BuildNeighborhoodKernel() -> There is no voxel in the neighborhood ! Check your neighborhood definition." << endl);
786  return 1;
787  }
788  // ========================================================================================================
789  // With Bowsher's similarity factors, we compute the number of voxels that will be kept in the penalty
790  // ========================================================================================================
792  {
794  }
795  // Normal end
796  return 0;
797 }
798 
799 // =====================================================================
800 // ---------------------------------------------------------------------
801 // ---------------------------------------------------------------------
802 // =====================================================================
803 
805 {
806  // Check for allocations
807  if (mp_proximityKernel)
808  {
809  Cerr("***** iPenaltyMarkovRandomField::BuildProximityFactors() -> Proximity kernel has already been created somewhere else !" << endl);
810  return 1;
811  }
813  {
814  Cerr("***** iPenaltyMarkovRandomField::BuildProximityFactors() -> Neighborhood kernel must have been created and initialized before !" << endl);
815  return 1;
816  }
817  // Compute the sum of proximity factors for normalization
818  FLTNB proximity_factor_sum = 0.;
819  // Allocate the proximity kernel
821  // Loop on the maximal neighborhood
822  for (INTNB neigh=0; neigh<m_neighborhoodMaxNbVoxels; neigh++)
823  {
824  // Proximity factors based on inverse of euclidian distance
826  {
829  * fabs(m2p_neighborhoodKernel[neigh][MRF_NEIGHBOR_X])
833  * fabs(m2p_neighborhoodKernel[neigh][MRF_NEIGHBOR_Y])
837  * fabs(m2p_neighborhoodKernel[neigh][MRF_NEIGHBOR_Z])
839  mp_proximityKernel[neigh] = 1./sqrt(mp_proximityKernel[neigh]);
840  }
841  // Proximity factors based on inverse of voxel distance (as described in many papers)
843  {
845  * fabs(m2p_neighborhoodKernel[neigh][MRF_NEIGHBOR_X]);
847  * fabs(m2p_neighborhoodKernel[neigh][MRF_NEIGHBOR_Y]);
849  * fabs(m2p_neighborhoodKernel[neigh][MRF_NEIGHBOR_Z]);
850  mp_proximityKernel[neigh] = 1./sqrt(mp_proximityKernel[neigh]);
851  }
852  /*
853  // Proximity factors based on decreasing exponential of squared distance
854  else if (m_proximityType == MRF_PROXIMITY_EXP_DIST_SQUARED)
855  {
856  FLTNB squareDistance = abs(m2p_neighborhoodKernel[neigh][MRF_NEIGHBOR_X])
857  * mp_ImageDimensionsAndQuantification->GetVoxSizeX()
858  * abs(m2p_neighborhoodKernel[neigh][MRF_NEIGHBOR_X])
859  * mp_ImageDimensionsAndQuantification->GetVoxSizeX();
860  squareDistance += abs(m2p_neighborhoodKernel[neigh][MRF_NEIGHBOR_Y])
861  * mp_ImageDimensionsAndQuantification->GetVoxSizeY()
862  * abs(m2p_neighborhoodKernel[neigh][MRF_NEIGHBOR_Y])
863  * mp_ImageDimensionsAndQuantification->GetVoxSizeY();
864  squareDistance += abs(m2p_neighborhoodKernel[neigh][MRF_NEIGHBOR_Z])
865  * mp_ImageDimensionsAndQuantification->GetVoxSizeZ()
866  * abs(m2p_neighborhoodKernel[neigh][MRF_NEIGHBOR_Z])
867  * mp_ImageDimensionsAndQuantification->GetVoxSizeZ();
868  mp_proximityKernel[neigh] = exp(-squareDistance/(m_proximityCharacteristicDistance*m_proximityCharacteristicDistance))/squareDistance;
869  }
870  */
871  // Uniform proximity factors
873  {
874  mp_proximityKernel[neigh] = 1.;
875  }
876  else
877  {
878  Cerr("***** iPenaltyMarkovRandomField::BuildProximityFactors() -> The provided type of proximity factor (" << m_proximityType << ") is unknown !" << endl);
879  return 1;
880  }
881  // Add to the sum
882  proximity_factor_sum += mp_proximityKernel[neigh];
883  }
884  // Normalize the proximity factors
885  for (INTNB neigh=0; neigh<m_neighborhoodMaxNbVoxels; neigh++) mp_proximityKernel[neigh] /= proximity_factor_sum;
886  // Normal end
887  return 0;
888 }
889 
890 // =====================================================================
891 // ---------------------------------------------------------------------
892 // ---------------------------------------------------------------------
893 // =====================================================================
894 
896 {
897  // Get number of voxels locally for code readability
902  // Compute the X, Y and Z components of the current voxel
903  INTNB index_x = a_voxel % nb_vox_x;
904  INTNB index_y = (a_voxel/nb_vox_x) % nb_vox_y;
905  INTNB index_z = a_voxel / nb_vox_xy;
906  // Count the number of valid neighbors
907  mp_neighborhoodNbVoxels[a_th] = 0;
908  // Loop on all possible neighbors in the kernel
909  for (INTNB neigh = 0; neigh<m_neighborhoodMaxNbVoxels; neigh++)
910  {
911  // Compute X, Y and Z indices of this potential neighbor
912  INTNB neighbor_x = index_x + m2p_neighborhoodKernel[neigh][MRF_NEIGHBOR_X];
913  INTNB neighbor_y = index_y + m2p_neighborhoodKernel[neigh][MRF_NEIGHBOR_Y];
914  INTNB neighbor_z = index_z + m2p_neighborhoodKernel[neigh][MRF_NEIGHBOR_Z];
915  // Check if the neighbour is inside the image
916  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 )
917  {
918  // Add the global index of this neighbor to the neighborhood list
919  m2p_neighborhoodIndices[a_th][neigh] = neighbor_x + neighbor_y*nb_vox_x + neighbor_z*nb_vox_xy;
920  // One more neighbor
921  mp_neighborhoodNbVoxels[a_th]++;
922  }
923  // Otherwise, discard it by setting -1
924  else
925  {
926  m2p_neighborhoodIndices[a_th][neigh] = -1;
927  }
928  }
929  // Normal end
930  return 0;
931 }
932 
933 // =====================================================================
934 // ---------------------------------------------------------------------
935 // ---------------------------------------------------------------------
936 // =====================================================================
937 
938 int iPenaltyMarkovRandomField::ComputeSimilarityFactors(int a_tbf, int a_rbf, int a_cbf, INTNB a_voxel, int a_th)
939 {
940  // No similarity factor (uniform)
942  {
943  // We just return as the factors were set to one after allocation
944  return 0;
945  }
946  // Bowsher's similarity factors
948  {
949  // Apply Bowsher's similarity only if there are more voxels in the neighborhood of this voxel than Bowsher's voxels
951  {
952  // Sort the neighborhood indices according to ascending absolute intensity difference from the voxel v
954  [a_voxel,a_tbf,a_rbf,a_cbf,a_th,this](INTNB i1, INTNB i2)
955  {
956  // This two lines pushes the -1 indices (voxels out of image) at the end
957  if (i1 == -1) return false;
958  if (i2 == -1) return true;
959  return (abs(mp_ImageSpace-> m2p_multiModalImage[0][i1] - mp_ImageSpace-> m2p_multiModalImage[0][a_voxel]))
960  < (abs(mp_ImageSpace-> m2p_multiModalImage[0][i2] - mp_ImageSpace-> m2p_multiModalImage[0][a_voxel]));
961  });
962  // Consider the most similar neighbors as defined by the number of Bowsher's voxels
963  for (INTNB n=0; n<m_similarityBowsherNbVoxels; n++) m2p_similarityFactors[a_th][n] = 1.;
964  // And discard all the others
965  for (INTNB n=m_similarityBowsherNbVoxels; n<mp_neighborhoodNbVoxels[a_th]; n++) m2p_similarityFactors[a_th][n] = 0.;
966  }
967  }
968  // Unknown
969  else
970  {
971  Cerr("***** iPenaltyMarkovRandomField::ComputeSimilarityFactors() -> Unknown similarity type provided !" << endl);
972  return 1;
973  }
974  // Normal end
975  return 0;
976 }
977 
978 // =====================================================================
979 // ---------------------------------------------------------------------
980 // ---------------------------------------------------------------------
981 // =====================================================================
982 
983 int iPenaltyMarkovRandomField::LocalPreProcessingStep(int a_tbf, int a_rbf, int a_cbf, INTNB a_voxel, int a_th)
984 {
985  // First build the list of neighbours of this voxel
986  if (BuildSpecificNeighborhood(a_voxel,a_th))
987  {
988  Cerr("***** iPenaltyMarkovRandomField::LocalPreProcessingStep() -> A problem occurred while building specific neighborhood of voxel " << a_voxel << " !" << endl);
989  return 1;
990  }
991  // Then compute the similarity factors
992  if (ComputeSimilarityFactors(a_tbf,a_rbf,a_cbf,a_voxel,a_th))
993  {
994  Cerr("***** iPenaltyMarkovRandomField::LocalPreProcessingStep() -> A problem occurred while computing the similirity factors of the neighborhood of voxel " << a_voxel << " !" << endl);
995  return 1;
996  }
997  // Normal end
998  return 0;
999 }
1000 
1001 // =====================================================================
1002 // ---------------------------------------------------------------------
1003 // ---------------------------------------------------------------------
1004 // =====================================================================
1005 
1006 FLTNB iPenaltyMarkovRandomField::ComputePenaltyValue(int a_tbf, int a_rbf, int a_cbf, INTNB a_voxel, int a_th)
1007 {
1008  // Compute the value of the penalty
1009  FLTNB result = 0.;
1010  // Sum on the contribution of the different neighbors
1011  for (INTNB n=0; n<m_neighborhoodMaxNbVoxels; n++)
1012  {
1013  // Get the neighbor index
1014  INTNB neighbor = m2p_neighborhoodIndices[a_th][n];
1015  // If the voxel is not in the neighborhood, skip it
1016  if (neighbor==-1) continue;
1017  else
1018  {
1019  // Pointer to the image
1020  FLTNB* p_image = mp_ImageSpace->m4p_image[a_tbf][a_rbf][a_cbf];
1021  // Proximity factor
1022  FLTNB proximity_factor = mp_proximityKernel[n];
1023  // Similarity factor
1024  FLTNB similarity_factor = m2p_similarityFactors[a_th][n];
1025  // Precompute the difference
1026  FLTNB difference = p_image[a_voxel] - p_image[neighbor];
1027  // Compute value of the potential function (consider the potential function to be symmetric)
1028  FLTNB value = 0.;
1029  switch(m_potentialType)
1030  {
1031  // Quadratic
1033  {
1034  value = 0.5 * difference * difference;
1035  break;
1036  }
1037  // Relative differences
1039  {
1040  FLTNB denominator = p_image[a_voxel] + p_image[neighbor] + m_potentialRelativeDifferenceGamma * abs(difference);
1041  if (denominator==0.) value = 0.;
1042  else value = difference * difference / denominator;
1043  break;
1044  }
1045  // Geman and McClure + 1 (we add 1 to the original definition to make it positive)
1047  {
1048  FLTNB squared_difference = difference * difference;
1049  value = squared_difference / (m_potentialGemanMcClureDelta*m_potentialGemanMcClureDelta + squared_difference);
1050  break;
1051  }
1052  // Green logcosh
1053  case MRF_POTENTIAL_GREEN:
1054  {
1055  value = log(cosh( difference / m_potentialGreenLogCoshDelta));
1056  break;
1057  }
1058  // Hebert and Leahy
1060  {
1061  value = log( 1. + difference * difference / (m_potentialHebertLeahyMu * m_potentialHebertLeahyMu));
1062  break;
1063  }
1064  // Huber piecewise
1065  case MRF_POTENTIAL_HUBER:
1066  {
1067  FLTNB abs_difference = fabs(difference);
1068  if (abs_difference>m_potentialHuberDelta) value = m_potentialHuberDelta*abs_difference - 0.5*m_potentialHuberDelta*m_potentialHuberDelta;
1069  else value = 0.5 * abs_difference * abs_difference;
1070  break;
1071  }
1072  }
1073  // Check that the value is a normal number
1074  if (fpclassify(value) != FP_NORMAL) value = 0.;
1075  // Add the final contribution to the penalty value
1076  else result += proximity_factor * similarity_factor * value;
1077  }
1078  }
1079  // Apply the penalty strength
1080  result *= m_penaltyStrength;
1081  // Check for Inf, Nan, etc
1082  if (fpclassify(result) != FP_NORMAL) result = 0.;
1083  // Return result
1084  return result;
1085 }
1086 
1087 // =====================================================================
1088 // ---------------------------------------------------------------------
1089 // ---------------------------------------------------------------------
1090 // =====================================================================
1091 
1092 FLTNB iPenaltyMarkovRandomField::ComputeFirstDerivative(int a_tbf, int a_rbf, int a_cbf, INTNB a_voxel, int a_th)
1093 {
1094  // Compute the first derivative of the penalty
1095  FLTNB result = 0.;
1096  // Sum on the contribution of the different neighbors
1097  for (INTNB n=0; n<m_neighborhoodMaxNbVoxels; n++)
1098  {
1099  // Get the neighbor index
1100  INTNB neighbor = m2p_neighborhoodIndices[a_th][n];
1101  // If the voxel is not in the neighborhood, skip it
1102  if (neighbor==-1) continue;
1103  else
1104  {
1105  // Pointer to the image
1106  FLTNB* p_image = mp_ImageSpace->m4p_image[a_tbf][a_rbf][a_cbf];
1107  // Proximity factor
1108  FLTNB proximity_factor = mp_proximityKernel[n];
1109  // Similarity factor
1110  FLTNB similarity_factor = m2p_similarityFactors[a_th][n];
1111  // Precompute the difference
1112  FLTNB difference = p_image[a_voxel] - p_image[neighbor];
1113  // Compute first derivative of the potential function (consider the potential function to be symmetric)
1114  FLTNB first_derivative = 0.;
1115  switch(m_potentialType)
1116  {
1117  // Quadratic
1119  {
1120  first_derivative = difference;
1121  break;
1122  }
1123  // Relative differences
1125  {
1126  FLTNB term = p_image[a_voxel] + p_image[neighbor] + m_potentialRelativeDifferenceGamma * fabs(difference);
1127  FLTNB term_square = term * term;
1128  if (term_square==0.) first_derivative = 0.;
1129  else first_derivative = difference * (term + 2.*p_image[neighbor]) / (term_square);
1130  break;
1131  }
1132  // Geman and McClure
1134  {
1135  FLTNB difference = p_image[a_voxel] - p_image[neighbor];
1137  first_derivative = 2. * delta_square * difference
1138  / pow( difference*difference + delta_square , 2. );
1139  break;
1140  }
1141  // Green logcosh
1142  case MRF_POTENTIAL_GREEN:
1143  {
1144  FLTNB rel_diff = difference / m_potentialGreenLogCoshDelta;
1145  first_derivative = tanh(rel_diff) / m_potentialGreenLogCoshDelta;
1146  break;
1147  }
1148  // Hebert and Leahy
1150  {
1151  first_derivative = 2. * difference / (difference*difference + m_potentialHebertLeahyMu*m_potentialHebertLeahyMu);
1152  break;
1153  }
1154  // Huber piecewise
1155  case MRF_POTENTIAL_HUBER:
1156  {
1157  if (difference==0.) first_derivative = 0.;
1158  else
1159  {
1160  FLTNB abs_difference = fabs(difference);
1161  if (abs_difference>m_potentialHuberDelta) first_derivative = m_potentialHuberDelta * difference / abs_difference;
1162  else first_derivative = difference;
1163  }
1164  break;
1165  }
1166  }
1167  // Check that the derivative is a normal number
1168  if (fpclassify(first_derivative) != FP_NORMAL) first_derivative = 0.;
1169  // Add the final contribution to the derivative value
1170  else result += proximity_factor * similarity_factor * first_derivative;
1171  }
1172  }
1173  // Apply the penalty strength
1174  result *= m_penaltyStrength;
1175  // Check for Inf, Nan, etc
1176  if (fpclassify(result) != FP_NORMAL) result = 0.;
1177  // Return result
1178  return result;
1179 }
1180 
1181 
1182 // =====================================================================
1183 // ---------------------------------------------------------------------
1184 // ---------------------------------------------------------------------
1185 // =====================================================================
1186 
1187 FLTNB iPenaltyMarkovRandomField::ComputeSecondDerivative(int a_tbf, int a_rbf, int a_cbf, INTNB a_voxel, int a_th)
1188 {
1189  // Compute the second derivative of the penalty
1190  FLTNB result = 0.;
1191  // Sum on the contribution of the different neighbors
1192  for (INTNB n=0; n<m_neighborhoodMaxNbVoxels; n++)
1193  {
1194  // Get the neighbor index
1195  INTNB neighbor = m2p_neighborhoodIndices[a_th][n];
1196  // If the voxel is not in the neighborhood, skip it
1197  if (neighbor==-1) continue;
1198  else
1199  {
1200  // Pointer to the image
1201  FLTNB* p_image = mp_ImageSpace->m4p_image[a_tbf][a_rbf][a_cbf];
1202  // Proximity factor
1203  FLTNB proximity_factor = mp_proximityKernel[n];
1204  // Similarity factor
1205  FLTNB similarity_factor = m2p_similarityFactors[a_th][n];
1206  // Compute second derivative of the potential function (consider the potential function to be symmetric)
1207  FLTNB second_derivative = 0.;
1208  switch(m_potentialType)
1209  {
1210  // Quadratic
1212  {
1213  second_derivative = 1.;
1214  break;
1215  }
1216  // Relative differences
1218  {
1219  FLTNB term = p_image[a_voxel] + p_image[neighbor] + m_potentialRelativeDifferenceGamma * fabs(p_image[a_voxel] - p_image[neighbor]);
1220  FLTNB term_cube = term * term * term;
1221  if (term_cube==0.) second_derivative = 0.;
1222  else second_derivative = (8.*p_image[neighbor]*p_image[neighbor])/(term_cube);
1223  break;
1224  }
1225  // Geman and McClure
1227  {
1228  FLTNB difference = p_image[a_voxel] - p_image[neighbor];
1229  FLTNB difference_square = difference * difference;
1231  second_derivative = -2. * delta_square * (3.*difference_square - delta_square)
1232  / pow( difference_square + delta_square , 3. );
1233  break;
1234  }
1235  // Green logcosh
1236  case MRF_POTENTIAL_GREEN:
1237  {
1238  second_derivative = 1. / (m_potentialGreenLogCoshDelta * cosh((p_image[a_voxel]-p_image[neighbor])/m_potentialGreenLogCoshDelta));
1239  second_derivative *= second_derivative;
1240  break;
1241  }
1242  // Hebert and Leahy
1244  {
1245  FLTNB difference = p_image[a_voxel] - p_image[neighbor];
1246  second_derivative = - 2. * (difference+m_potentialHebertLeahyMu) * (difference-m_potentialHebertLeahyMu)
1247  / pow( difference*difference + m_potentialHebertLeahyMu*m_potentialHebertLeahyMu , 2. );
1248  break;
1249  }
1250  // Huber piecewise
1251  case MRF_POTENTIAL_HUBER:
1252  {
1253  // Assume null derivative in all domain
1254  second_derivative = 0.;
1255  break;
1256  }
1257  }
1258  // Check that the derivative is a normal number
1259  if (fpclassify(second_derivative) != FP_NORMAL) second_derivative = 0.;
1260  // Add the final contribution to the derivative value
1261  else result += proximity_factor * similarity_factor * second_derivative;
1262  }
1263  }
1264  // Apply the penalty strength
1265  result *= m_penaltyStrength;
1266  // Check for Inf, Nan, etc
1267  if (fpclassify(result) != FP_NORMAL) result = 0.;
1268  // Return result
1269  return result;
1270 }
#define MRF_PROXIMITY_EUCLIDIAN
int BuildSpecificNeighborhood(INTNB a_voxel, int a_th)
Computes the specific neighborhood of a voxel, m2p_neighborhoodIndices, as well as mp_neighborhoodNbV...
int m_penaltyDerivativesOrder
Definition: vPenalty.hh:293
#define MRF_POTENTIAL_HEBERT_LEAHY
#define FLTNB
Definition: gVariables.hh:81
oImageSpace * mp_ImageSpace
Definition: vPenalty.hh:289
FLTNB GetVoxSizeX()
Get the voxel&#39;s size along the X axis, in mm.
#define MRF_PROXIMITY_NONE
FLTNB GetVoxSizeZ()
Get the voxel&#39;s size along the Z axis, in mm.
#define VERBOSE_DETAIL
FLTNB m_penaltyStrength
Definition: vPenalty.hh:294
int GetNbMultiModalImages()
Get the number of additional multimodal images.
int BuildProximityFactors()
A function used to build the kernel of the proximity factors.
string m_penaltyID
Definition: vPenalty.hh:285
int LocalPreProcessingStep(int a_tbf, int a_rbf, int a_cbf, INTNB a_voxel, int a_th)
A public function computing a local pre-processing step for the penalty.
FLTNB ComputeSecondDerivative(int a_tbf, int a_rbf, int a_cbf, INTNB a_voxel, int a_th)
Implementation of the pure virtual vPenalty::ComputeSecondDerivative()
int ReadConfigurationFile(const string &a_configurationFile)
A function used to read options from a configuration file.
#define MRF_NEIGHBOR_Y
FLTNB ComputeFirstDerivative(int a_tbf, int a_rbf, int a_cbf, INTNB a_voxel, int a_th)
Implementation of the pure virtual vPenalty::ComputeFirstDerivative()
#define MRF_NEIGHBORHOOD_SPHERE
#define MRF_POTENTIAL_HUBER
int m_verbose
Definition: vPenalty.hh:291
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.
#define Cerr(MESSAGE)
#define MRF_SIMILARITY_NONE
FLTNB **** m4p_image
Definition: oImageSpace.hh:80
#define MRF_POTENTIAL_RELATIVE_DIFFERENCE
oImageDimensionsAndQuantification * mp_ImageDimensionsAndQuantification
Definition: vPenalty.hh:288
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...
Definition: gOptions.cc:129
#define MRF_POTENTIAL_GEMAN_MCCLURE
iPenaltyMarkovRandomField()
The constructor of iPenaltyMarkovRandomField.
#define MRF_NOT_DEFINED
INTNB GetNbVoxXY()
Get the number of voxels in a slice.
This class is designed to generically described any penalty applied to MAP algorithms.
Definition: vPenalty.hh:48
#define VERBOSE_NORMAL
Declaration of class iPenaltyMarkovRandomField.
#define MRF_NEIGHBOR_Z
#define KEYWORD_MANDATORY
Definition: gOptions.hh:47
#define KEYWORD_OPTIONAL
Definition: gOptions.hh:49
#define MRF_PROXIMITY_VOXEL
int ReadOptionsList(const string &a_optionsList)
A function used to read options from a list of options.
#define MRF_POTENTIAL_QUADRATIC
#define INTNB
Definition: gVariables.hh:92
FLTNB ComputePenaltyValue(int a_tbf, int a_rbf, int a_cbf, INTNB a_voxel, int a_th)
Implementation of the pure virtual vPenalty::ComputePenaltyValue()
int ComputeSimilarityFactors(int a_tbf, int a_rbf, int a_cbf, INTNB a_voxel, int a_th)
Computes the similarity factors of this specific voxel with respect to the similarity type used...
#define MRF_NEIGHBOR_X
int GetNbThreadsForImageComputation()
Get the number of threads used for image operations.
~iPenaltyMarkovRandomField()
The destructor of iPenaltyMarkovRandomField.
#define MRF_NEIGHBORHOOD_BOX
INTNB GetNbVoxX()
Get the number of voxels along the X axis.
INTNB GetNbVoxZ()
Get the number of voxels along the Z axis.
#define MRF_POTENTIAL_GREEN
#define Cout(MESSAGE)
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.
#define MRF_SIMILARITY_BOWSHER
void ShowHelpSpecific()
A function used to show help about the child penalty.
#define MRF_NEIGHBORHOOD_6_NEAREST
INTNB GetNbVoxY()
Get the number of voxels along the Y axis.
#define KEYWORD_OPTIONAL_ERROR
Definition: gOptions.hh:55