ANIMA  4.0
animaDenseTransformArithmetic.cxx
Go to the documentation of this file.
1 #include <tclap/CmdLine.h>
2 
3 #include <itkImage.h>
4 #include <itkComposeDisplacementFieldsImageFilter.h>
5 #include <itkVectorLinearInterpolateNearestNeighborExtrapolateImageFunction.h>
6 #include <itkMultiplyImageFilter.h>
7 
9 #include <animaVelocityUtils.h>
10 
11 int main(int argc, char **argv)
12 {
13  std::string descriptionMessage = "Performs mathematical operations on dense transformations: exponentiation, composition, BCH composition \n";
14  descriptionMessage += "This software has known limitations: you might have to use it several times in a row or in combination with regular image arithmetic to perform the operation you want,\n";
15  descriptionMessage += "for example taking the power of a log-transform, requires you to multiply the field outside of this tool.\n";
16  descriptionMessage += "The logarithm computation of a field is also not yet implemented.\n";
17  descriptionMessage += "INRIA / IRISA - VisAGeS/Empenn Team";
18 
19  TCLAP::CmdLine cmd(descriptionMessage, ' ',ANIMA_VERSION);
20 
21  TCLAP::ValueArg<std::string> inArg("i","input","Input transformation (SVF or dense)",true,"","input transformation",cmd);
22  TCLAP::ValueArg<std::string> composeArg("c","compose","Composed transformation (SVF or dense)",false,"","composed transformation",cmd);
23  TCLAP::ValueArg<std::string> outArg("o","output","output image",true,"","output image",cmd);
24 
25  TCLAP::SwitchArg expArg("E","exp","Exponentiate inputs (if not selected, composition will be done using BCH)",cmd,false);
26  TCLAP::SwitchArg compositionArg("R","regular-composition","Use regular composition (transformations are taken as dense field (if not selected and no exponentiation is done, BCH will be used)",cmd,false);
27 
28  TCLAP::ValueArg<unsigned int> bchArg("b","bch-order","Order of BCH composition (in between 1 and 4, default: 2)",false,2,"BCH order",cmd);
29  TCLAP::ValueArg<unsigned int> expOrderArg("e","exp-order","Order of field exponentiation approximation (in between 0 and 1, default: 0)",false,0,"exponentiation order",cmd);
30  TCLAP::ValueArg<unsigned int> dividerArg("d","div-order","If BCH composition of order > 1, divide input fields by d (default: 1)",false,1,"BCH field divider",cmd);
31 
32  TCLAP::ValueArg<unsigned int> nbpArg("T","numberofthreads","Number of threads to run on (default : all cores)",false,itk::MultiThreaderBase::GetGlobalDefaultNumberOfThreads(),"number of threads",cmd);
33 
34  try
35  {
36  cmd.parse(argc,argv);
37  }
38  catch (TCLAP::ArgException& e)
39  {
40  std::cerr << "Error: " << e.error() << "for argument " << e.argId() << std::endl;
41  return EXIT_FAILURE;
42  }
43 
44  typedef itk::StationaryVelocityFieldTransform <double,3> SVFTransformType;
45  typedef rpi::DisplacementFieldTransform <double,3> DenseTransformType;
46  typedef SVFTransformType::VectorFieldType FieldType;
47 
48  FieldType::Pointer inputField = anima::readImage <FieldType> (inArg.getValue());
49  FieldType::Pointer composeField = ITK_NULLPTR;
50  if (composeArg.getValue() != "")
51  composeField = anima::readImage <FieldType> (composeArg.getValue());
52 
53  if (expArg.isSet())
54  {
55  std::cout << "Taking transformation(s) exponential" << std::endl;
56  SVFTransformType::Pointer tmpTrsf = SVFTransformType::New();
57  DenseTransformType::Pointer outTrsf = DenseTransformType::New();
58 
59  tmpTrsf->SetParametersAsVectorField(inputField);
60  anima::GetSVFExponential(tmpTrsf.GetPointer(),outTrsf.GetPointer(),expOrderArg.getValue(),nbpArg.getValue(),false);
61 
62  inputField = const_cast <FieldType *> (outTrsf->GetParametersAsVectorField());
63 
64  if (composeField)
65  {
66  tmpTrsf->SetParametersAsVectorField(composeField);
67  anima::GetSVFExponential(tmpTrsf.GetPointer(),outTrsf.GetPointer(),expOrderArg.getValue(),nbpArg.getValue(),false);
68 
69  composeField = const_cast <FieldType *> (outTrsf->GetParametersAsVectorField());
70  }
71  }
72 
73  // Now do the composition
74  if (composeField)
75  {
76  std::cout << "Composing transformations ";
77 
78  if (expArg.isSet() || compositionArg.isSet())
79  {
80  std::cout << "using regular transformation composition" << std::endl;
81  typedef itk::ComposeDisplacementFieldsImageFilter <FieldType,FieldType> ComposeFilterType;
82  typedef FieldType::PixelType VectorType;
83  typedef itk::VectorLinearInterpolateNearestNeighborExtrapolateImageFunction <FieldType,VectorType::ValueType> VectorInterpolateFunctionType;
84 
85  ComposeFilterType::Pointer composeFilter = ComposeFilterType::New();
86  composeFilter->SetWarpingField(composeField);
87  composeFilter->SetDisplacementField(inputField);
88  composeFilter->SetNumberOfWorkUnits(nbpArg.getValue());
89 
90  VectorInterpolateFunctionType::Pointer interpolator = VectorInterpolateFunctionType::New();
91 
92  composeFilter->SetInterpolator(interpolator);
93  composeFilter->Update();
94 
95  inputField = composeFilter->GetOutput();
96  inputField->DisconnectPipeline();
97  }
98  else
99  {
100  unsigned int dividerValue = 1;
101  if (bchArg.getValue() > 1)
102  dividerValue = dividerArg.getValue();
103 
104  std::cout << "using BCH approximation of order " << bchArg.getValue() << ", dividing input fields by " << dividerValue << std::endl;
105 
106  if (dividerValue != 1)
107  {
108  typedef itk::MultiplyImageFilter <FieldType, itk::Image <double, 3>, FieldType> MultiplyConstantFilterType;
109  MultiplyConstantFilterType::Pointer inputFieldDivider = MultiplyConstantFilterType::New();
110  inputFieldDivider->SetInput(inputField);
111  inputFieldDivider->SetConstant(1.0 / dividerValue);
112  inputFieldDivider->SetNumberOfWorkUnits(nbpArg.getValue());
113 
114  inputFieldDivider->Update();
115  inputField = inputFieldDivider->GetOutput();
116  inputField->DisconnectPipeline();
117 
118  MultiplyConstantFilterType::Pointer composeFieldDivider = MultiplyConstantFilterType::New();
119  composeFieldDivider->SetInput(composeField);
120  composeFieldDivider->SetConstant(1.0 / dividerValue);
121  composeFieldDivider->SetNumberOfWorkUnits(nbpArg.getValue());
122 
123  composeFieldDivider->Update();
124  composeField = composeFieldDivider->GetOutput();
125  composeField->DisconnectPipeline();
126  }
127 
128  SVFTransformType::Pointer inputTrsf = SVFTransformType::New();
129  SVFTransformType::Pointer resultTrsf = SVFTransformType::New();
130  SVFTransformType::Pointer composeTrsf = SVFTransformType::New();
131 
132  inputTrsf->SetParametersAsVectorField(inputField);
133  resultTrsf->SetParametersAsVectorField(inputField);
134  composeTrsf->SetParametersAsVectorField(composeField);
135 
136  for (unsigned int i = 0;i < dividerValue;++i)
137  {
138  std::cout << "Iteration " << i+1 << " / " << dividerValue << std::endl;
139  anima::composeSVF(resultTrsf.GetPointer(),composeTrsf.GetPointer(),nbpArg.getValue(),bchArg.getValue());
140 
141  if (i < dividerValue - 1)
142  {
143  anima::composeSVF(inputTrsf.GetPointer(),resultTrsf.GetPointer(),nbpArg.getValue(),bchArg.getValue());
144  resultTrsf->SetParametersAsVectorField(inputTrsf->GetParametersAsVectorField());
145  inputTrsf->SetParametersAsVectorField(inputField);
146  }
147  }
148 
149  inputField = const_cast <FieldType *> (resultTrsf->GetParametersAsVectorField());
150  }
151  }
152 
153  anima::writeImage <FieldType> (outArg.getValue(),inputField);
154 
155  return EXIT_SUCCESS;
156 }
int main(int argc, char **argv)
void GetSVFExponential(itk::StationaryVelocityFieldTransform< ScalarType, NDimensions > *baseTrsf, rpi::DisplacementFieldTransform< ScalarType, NDimensions > *resultTransform, unsigned int exponentiationOrder, unsigned int numThreads, bool invert)
void composeSVF(itk::StationaryVelocityFieldTransform< ScalarType, NDimensions > *baseTrsf, itk::StationaryVelocityFieldTransform< ScalarType, NDimensions > *addonTrsf, unsigned int numThreads, unsigned int bchOrder)