Shapeworks Studio  2.1
Shape analysis software suite
itkPSMCommandLineClass.hxx
1 /*=========================================================================
2  *
3  * Copyright Insight Software Consortium
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0.txt
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  *=========================================================================*/
18 
19 #ifndef ____itkPSMCommandLineClass__hxx
20 #define ____itkPSMCommandLineClass__hxx
21 
22 #include "itkPSMCommandLineClass.h"
23 #include <fstream>
24 #include <vector>
25 
26 namespace itk
27 {
28  //template class PSMCommandLineClass<2>;
29  //template class PSMCommandLineClass<3>;
30 
31  template <unsigned int VDimension>
32  PSMCommandLineClass<VDimension>
33  ::PSMCommandLineClass()
34  {
35  m_ProcrustesCounter = 0;
36  m_ReportInterval = 10;
37  m_ProjectReader = ProjectReaderType::New();
38  m_Project = ProjectType::New();
39  m_ProcrustesRegistration = ProcrustesRegistrationType::New();
40  m_PSMFilter = EntropyModelFilterType::New();
41 
42 
43  }
44 
45  template <unsigned int VDimension>
47  ::IterateCallback(itk::Object *caller, const itk::EventObject &)
48  {
50  static_cast<EntropyModelFilterType *>(caller);
51 
52  // Print every 10 iterations
53  if (o->GetNumberOfElapsedIterations() % m_ReportInterval != 0) { return; }
54 
55  std::cout << "Iteration # " << o->GetNumberOfElapsedIterations() << std::endl;
56  std::cout << " Eigenmode variances: ";
57  for (unsigned int i = 0; i < o->GetShapePCAVariances().size(); i++)
58  {
59  std::cout << o->GetShapePCAVariances()[i] << " ";
60  }
61  std::cout << std::endl;
62  std::cout << " Regularization = " << o->GetRegularizationConstant() << std::endl;
63 
64  // Check if optimization is run using scales. Get Procrustes
65  // interval for the current scale.
66  int interval = 0;
67  std::cout << "Optimization Scale " << (o->GetCurrentScale() + 1) << "/"
68  << o->GetNumberOfScales() << std::endl;
69 
70  if (o->GetNumberOfScales() > 1)
71  {
72  interval = m_ProcrustesInterval[o->GetCurrentScale()];
73  std::cout << "Interval = " << interval << std::endl;
74  } else // Use the default interval
75  {
76  interval = m_ProcrustesRegistration->GetProcrustesInterval();
77  }
78 
79  // Check if the Procrustes interval is set to a value other than 0
80  if (interval > 0)
81  {
82  this->m_ProcrustesCounter++;
83 
84  // If the counter is greater than the interval value, run
85  // Procrustes registration
86  if (this->m_ProcrustesCounter >= interval)
87  {
88  // Reset the counter
89  this->m_ProcrustesCounter = 0;
90  this->m_ProcrustesRegistration->RunRegistration();
91  std::cout << "Ran Procrustes Registration" << std::endl;
92  }
93  }
94  }
95 
96  template <unsigned int VDimension>
98  ::ReadDistanceTransforms(std::string input_path_prefix)
99  {
100  if (!m_Project->HasDomains())
101  {
102  throw itk::ExceptionObject("Domains must be defined in the project file.");
103  }
104 
105  // Compile the list of distance transforms
106  std::vector<std::string> domain_names = m_Project->GetDomainNames();
107  std::vector<std::string> dt_files;
108  for (unsigned int i = 0; i < domain_names.size(); i++)
109  {
110  std::string s = (m_Project->GetDomainDistanceTransform(domain_names[i]))[0];
111  dt_files.push_back(s);
112 
113 
114  this->m_PSMFilter->SetDomainName(domain_names[i], i);
115  }
116 
117  // Read in the distance transforms
118  // const std::vector<std::string> &dt_files = this->m_Project->GetDistanceTransforms();
119  std::cout << "Reading distance transforms ..." << std::endl;
120  for (unsigned int i = 0; i < dt_files.size(); i++)
121  {
122  typename itk::ImageFileReader<PSMCommandLineClass::ImageType>::Pointer reader =
123  itk::ImageFileReader<PSMCommandLineClass::ImageType>::New();
124  reader->SetFileName(input_path_prefix + dt_files[i]);
125  //reader->SetFileName(dt_files[i]);
126  reader->Update();
127 
128  std::cout << domain_names[i] << ": " << dt_files[i] << std::endl;
129 
130  this->m_PSMFilter->SetInput(i, reader->GetOutput());
131 
132  }
133  std::cout << "Done!" << std::endl;
134  }
135 
136  template <unsigned int VDimension>
139  {
140  if (!m_Project->HasDomains())
141  {
142  itkExceptionMacro("Domains must be defined in the project file.");
143  }
144 
145  // Compile the list of domains
146  std::vector<std::string> domain_names = m_Project->GetDomainNames();
147 
148  for (unsigned int i = 0; i < domain_names.size(); i++)
149  {
150  if (m_Project->HasDomainCuttingPlanes(domain_names[i]))
151  {
152  // Cutting planes are returned as a list of vnl_vectors.
153  std::vector<vnl_vector_fixed<double, 3> > planes
154  = m_Project->GetDomainCuttingPlanes(domain_names[i]);
155 
156  // The list should have size that is multiple of 3, since
157  // every plane is defined by exactly 3 points.
158  if (planes.size() % 3 != 0)
159  {
160  itkExceptionMacro("Something is wrong with the cutting plane data for domain " + domain_names[i] + ". Correct number of points was not found.");
161  }
162 
163  // Traverse the list and pull out sets of 3 points. Each
164  // set of 3 defines a cutting plane.
165  vnl_vector_fixed<double, 3> x;
166  vnl_vector_fixed<double, 3> y;
167  vnl_vector_fixed<double, 3> z;
168  std::vector<vnl_vector_fixed<double, 3> >::iterator it =
169  planes.begin();
170 
171  while (it != planes.end())
172  {
173  x = *it; it++;
174  y = *it; it++;
175  z = *it; it++;
176 
177  std::cout << "Found cutting plane for domain " << domain_names[i] <<
178  ": " << std::endl;
179  std::cout << x[0] << " " << x[1] << " " << x[2] << std::endl;
180  std::cout << y[0] << " " << y[1] << " " << y[2] << std::endl;
181  std::cout << z[0] << " " << z[1] << " " << z[2] << std::endl;
182 
183  // Add cutting plane to the PSM filter by domain name
184 
185  m_PSMFilter->AddCuttingPlane(x, y, z, domain_names[i]);
186 
187  // int d = m_PSMFilter->GetDomainIndexByName(domain_names[i]);
188  // m_PSMFilter->GetDomain(d)->SetCuttingPlane(x,y,z);
189 
190  }
191 
192 
193  } // end has cutting planes
194  } // end traverse domains
195 
196  }
197 
198  template<unsigned int VDimension>
201  {
202  // double value1, value2, value3;
203  // unsigned int numOfPoints;
204  // typename PSMCommandLineClass::EntropyModelFilterType::PointType pt;
205 
206  // // Check if model initialization is supplied. If not, then optimization scales will need to be
207  // // used later.
208  // if(this->m_Project->HasModel(std::string("initialization")))
209  // {
210  // const std::vector<std::string> &pt_files = this->m_Project->GetModel(std::string("initialization"));
211  // std::cout << "Reading the initial model correspondences ..." << std::endl;
212  // for (unsigned int i = 0; i < pt_files.size(); i++)
213  // {
214  // // Read the points for this file and add as a list
215  // typename std::vector<typename PSMCommandLineClass::EntropyModelFilterType::PointType> init_points;
216  // numOfPoints = 0;
217  // // Open the ascii file.
218  // std::ifstream in( (input_path_prefix + pt_files[i]).c_str() );
219  // if ( !in )
220  // {
221  // std::cerr << "Could not open point file for input." << std::endl;
222  // break;
223  // }
224 
225  // // Read all of the points, one point per line.
226  // while (in)
227  // {
228  // in>>value1>>value2>>value3;
229  // pt[0] = value1;
230  // pt[1] = value2;
231  // if(VDimension == 3)
232  // {
233  // pt[2] = value3;
234  // }
235  // init_points.push_back(pt);
236  // numOfPoints++;
237  // }
238  // // this algorithm pushes the last point twice
239  // init_points.pop_back();
240  // std::cout << "Read " << numOfPoints-1 << " points. " << std::endl;
241  // in.close();
242  // this->m_PSMFilter->SetInputCorrespondencePoints(i,init_points);
243  // std::cout << " " << pt_files[i] << std::endl;
244  // }
245  // std::cout << "Done!" << std::endl;
246 
247 
248  // }
249  // else
250  // {
251  // // Check if scales have been supplied
252  // if(this->m_Project->HasOptimizationAttribute("number_of_scales")
253  // &&
254  // (this->m_Project->GetNumberOfOptimizationScales()) > 0)
255  // {
256  // // Read the input optimization scales from the project file.
257  // this->ReadOptimizationScales();
258  // }
259  // else
260  // {
261  // // REQUIRE THAT WE HAVE SCALES DEFINED. If there is only a single
262  // // optimization scale, it should be defined as scale 0, but for now
263  // // we are enforcing that scales be defined in the parameter file.
264 
265  // throw itk::ExceptionObject("The number of optimization scales and their parameters were not defined in the parameter file.");
266 
267  // //this->SetDefaultScales();
268  // }
269  // }
270  }
271 
272  template <unsigned int VDimension>
274  ::ReadProjectFile(const char *fn)
275  {
276  // Read the project parameter file
277  m_ProjectFileName = fn;
278  this->m_ProjectReader->SetFileName(fn);
279  this->m_ProjectReader->Update();
280 
281  // Store the project parameters
282  this->m_Project = m_ProjectReader->GetOutput();
283  }
284 
285  template <unsigned int VDimension>
288  {
289  // Read the optimization parameters and set their corresponding
290  // parameters in the PSM optimization filter. All of the following
291  // parameters are required to be present in the project file.
292  std::cout << "Reading optimization parameters ... " << std::endl;
293  unsigned int number_of_scales = this->m_Project->GetNumberOfOptimizationScales();
294  std::cout << " Optimization scales = " << number_of_scales << std::endl;
295 
296  if (number_of_scales > 0) {
297  // We need vectors of parameters, one for each scale.
298  std::vector<double> regularization_initial(number_of_scales);
299  std::vector<double> regularization_final(number_of_scales);
300  std::vector<double> regularization_decayspan(number_of_scales);
301  std::vector<double> tolerance(number_of_scales);
302  std::vector<unsigned int> maximum_iterations(number_of_scales);
303  m_ProcrustesInterval.resize(number_of_scales);
304 
305  // Read parameters for each scale
306  for (unsigned int i = 0; i < number_of_scales; i++) {
307  std::cout << "Optimization parameters for scale " << i << ": " << std::endl;
308  if (this->m_Project->HasOptimizationAttribute("regularization_initial", i))
309  {
310  regularization_initial[i] = this->m_Project->GetOptimizationAttribute("regularization_initial", i);
311  } else { throw itk::ExceptionObject("Missing parameter: regularization_initial"); }
312  std::cout << " regularization_initial = " << regularization_initial[i] << std::endl;
313 
314  if (this->m_Project->HasOptimizationAttribute("regularization_final", i))
315  {
316  regularization_final[i] = this->m_Project->GetOptimizationAttribute("regularization_final", i);
317  } else { throw itk::ExceptionObject("Missing parameter: regularization_final"); }
318  std::cout << " regularization_final = " << regularization_final[i] << std::endl;
319 
320  if (this->m_Project->HasOptimizationAttribute("regularization_decayspan", i))
321  {
322  regularization_decayspan[i]
323  = this->m_Project->GetOptimizationAttribute("regularization_decayspan", i);
324  } else { throw itk::ExceptionObject("Missing parameter: regularization_decayspan"); }
325  std::cout << " regularization_decayspan = " << regularization_decayspan[i] << std::endl;
326 
327  if (this->m_Project->HasOptimizationAttribute("tolerance", i))
328  {
329  tolerance[i] = this->m_Project->GetOptimizationAttribute("tolerance", i);
330  } else { throw itk::ExceptionObject("Missing parameter: tolerance"); }
331  std::cout << " tolerance = " << tolerance[i] << std::endl;
332 
333  if (this->m_Project->HasOptimizationAttribute("maximum_iterations", i))
334  {
335  maximum_iterations[i]
336  = static_cast<unsigned int>(this->m_Project->GetOptimizationAttribute("maximum_iterations", i));
337  } else { throw itk::ExceptionObject("Missing parameter: maximum_iterations"); }
338  std::cout << " maximum_iterations = " << maximum_iterations[i] << std::endl;
339 
340  // if(this->m_Project->HasProcrustes() == true)
341  // {
342  if (this->m_Project->HasOptimizationAttribute("procrustes_interval", i))
343  {
344  m_ProcrustesInterval[i]
345  = static_cast<unsigned int>(this->m_Project->GetOptimizationAttribute("procrustes_interval", i));
346  } else { throw itk::ExceptionObject("Missing parameter: procrustes_interval"); }
347  std::cout << " procrustes_interval = " << m_ProcrustesInterval[i] << std::endl;
348  //}
349  }
350 
351  // Now set the parameters in the filter
352  this->m_PSMFilter->SetNumberOfScales(number_of_scales);
353  this->m_PSMFilter->SetRegularizationInitial(regularization_initial);
354  this->m_PSMFilter->SetRegularizationFinal(regularization_final);
355  this->m_PSMFilter->SetRegularizationDecaySpan(regularization_decayspan);
356  this->m_PSMFilter->SetTolerance(tolerance);
357  this->m_PSMFilter->SetMaximumNumberOfIterations(maximum_iterations);
358  } else
359  {
360  throw itk::ExceptionObject("Project must define at least one optimization scale");
361  }
362  }
363 
364  template <unsigned int VDimension>
366  ::WriteOutputs(std::string output_path)
367  {
368  const std::vector<std::string> &out_files = this->m_Project->GetModel(std::string("optimized"));
369  if (out_files.size() != this->m_PSMFilter->GetParticleSystem()->GetNumberOfDomains())
370  {
371  std::cerr << "Number of output files does not match the number of particle domains (inputs)." << std::endl;
372  } else
373  {
374  for (unsigned int d = 0; d < this->m_PSMFilter->GetParticleSystem()->GetNumberOfDomains(); d++)
375  {
376  // Open the output file.
377  std::string fname = output_path + out_files[d];
378  std::ofstream out(fname.c_str());
379  if (!out)
380  {
381  std::cerr << "Could not open point file for output: " << std::endl;
382  break;
383  } else
384  {
385  for (unsigned int j = 0; j < this->m_PSMFilter->GetParticleSystem()->GetNumberOfParticles(d); j++)
386  {
387  for (unsigned int i = 0; i < VDimension; i++)
388  {
389  out << this->m_PSMFilter->GetParticleSystem()->GetPosition(j, d)[i] << " ";
390  }
391  if (VDimension == 2)
392  {
393  out << "0.0"; // printed for display in ShapeWorksViewer2
394  }
395  out << std::endl;
396  }
397  }
398  } // end for number of domains
399  }
400  }
401 
402  template <unsigned int VDimension>
404  ::Run(const char *fname, std::string input_path_prefix,
405  std::string output_path)
406  {
407  // Read the project parameter files. Also sets the project
408  // filename (m_ProjectFileName)
409  this->ReadProjectFile(fname);
410 
411  // Set the optimization parameters. This also checks to be sure
412  // everything is there. If not, this method throws an exception.
413  this->SetPSMFilterParameters();
414 
415  // Read the distance transform files
416  this->ReadDistanceTransforms(input_path_prefix);
417 
418  // Read any geometric contraints (cutting planes)
419  this->ReadCuttingPlanes();
420 
421  // Create the callback function to run Procrustes and report
422  // progress.
423  m_IterateCmd = itk::MemberCommand<PSMCommandLineClass>::New();
424  m_IterateCmd->SetCallbackFunction(this, &PSMCommandLineClass::IterateCallback);
425  this->m_PSMFilter->AddObserver(itk::IterationEvent(), m_IterateCmd);
426 
427  // Set ParticleSystem for ProcrustesRegistration
428  this->m_ProcrustesRegistration->SetPSMParticleSystem(m_PSMFilter->GetParticleSystem());
429 
430  // Run the optimization filter
431  this->m_PSMFilter->Update();
432  // TODO: Is this error message really required?
433  if (this->m_PSMFilter->GetNumberOfElapsedIterations() >= this->m_PSMFilter->GetMaximumNumberOfIterations())
434  {
435  std::cerr << "Optimization did not converge based on tolerance criteria." << std::endl;
436  }
437 
438  std::cout << "Optimization done " << std::endl;
439 
440  // Write out the optimized point sets
441  this->WriteOutputs(output_path);
442  }
443 
444  // template <unsigned int VDimension>
445  // void PSMCommandLineClass<VDimension>
446  // ::SetDefaultScales()
447  // {
448  // // Setting scales to 10 produces 512 points at the output.
449  // unsigned int number_of_scales = 10;
450  // // Set default parameters for the optimization scales
451  // std::vector<double> regularization_initial(number_of_scales);
452  // std::vector<double> regularization_final(number_of_scales);
453  // std::vector<double> regularization_decayspan(number_of_scales);
454  // std::vector<double> tolerance(number_of_scales);
455  // std::vector<unsigned int> maximum_iterations(number_of_scales);
456 
457  // std::cout << "Setting default optimization parameters for scales..." << std::endl;
458  // for (unsigned int i = 0; i < number_of_scales; i++)
459  // {
460  // std::cout << "Scale #" << i << " :" << std::endl;
461  // regularization_initial[i] = 10.0;
462  // std::cout << " regularization_initial = " << regularization_initial[i] << std::endl;
463  // regularization_final[i] = 2.0;
464  // std::cout << " regularization_final = " << regularization_final[i] << std::endl;
465  // regularization_decayspan[i] = 5000;
466  // std::cout << " regularization_decayspan = " << regularization_decayspan[i] << std::endl;
467  // tolerance[i] = 0.01;
468  // std::cout << " tolerance = " << tolerance[i] << std::endl;
469  // maximum_iterations[i] = 1000;
470  // std::cout << " maximum_iterations = " << maximum_iterations[i] << std::endl;
471  // // if(this->m_Project->HasProcrustes() == true)
472  // // {
473  // // Set the Procrustes interval for each scale
474  // unsigned int procrustes_interval = 0;
475  // m_ProcrustesInterval[i] = procrustes_interval;
476  // std::cout << " procrustes_interval = " << procrustes_interval << std::endl;
477  // // }
478  // }
479 
480  // // Set the parameters in the filter
481  // this->m_PSMFilter->SetNumberOfScales(number_of_scales);
482  // this->m_PSMFilter->SetRegularizationInitial(regularization_initial);
483  // this->m_PSMFilter->SetRegularizationFinal(regularization_final);
484  // this->m_PSMFilter->SetRegularizationDecaySpan(regularization_decayspan);
485  // this->m_PSMFilter->SetTolerance(tolerance);
486  // this->m_PSMFilter->SetMaximumNumberOfIterations(maximum_iterations);
487  // }
488 
489 
490 } // end namespace itk
491 
492 #endif
unsigned int GetNumberOfElapsedIterations() const
const std::vector< double > & GetShapePCAVariances() const
This the most basic of all PSM model optimization filters. This filter assembles all of the necessary...
void IterateCallback(itk::Object *, const itk::EventObject &)
This class provides a command line tool to run the Particle Shape Modeling. It runs the optimization ...