ANIMA  4.0
animaCropImage.cxx
Go to the documentation of this file.
1 #include <tclap/CmdLine.h>
2 
3 #include <itkImage.h>
4 #include <itkCommand.h>
5 #include <itkExtractImageFilter.h>
6 #include <itkImageRegionIteratorWithIndex.h>
7 
10 
11 //Update progression of the process
12 void eventCallback (itk::Object* caller, const itk::EventObject& event, void* clientData)
13 {
14  itk::ProcessObject * processObject = (itk::ProcessObject*) caller;
15  std::cout<<"\033[K\rProgression: "<<(int)(processObject->GetProgress() * 100)<<"%"<<std::flush;
16 }
17 
18 struct arguments
19 {
21  std::string input, output;
22 };
23 
24 template <class InputImageType, unsigned int OutputDimension>
25 void
26 extract(const arguments &args)
27 {
28  typename InputImageType::Pointer input = anima::readImage<InputImageType>(args.input);
29 
30  typename InputImageType::SizeType inputSize;
31  inputSize = input->GetLargestPossibleRegion().GetSize();
32 
33  typename InputImageType::RegionType extractRegion;
34  typename InputImageType::SizeType extractSize;
35  typename InputImageType::IndexType extractIndex;
36 
37  int indexes[4] = {args.xindex, args.yindex, args.zindex, args.tindex};
38  int sizes[4] = {args.xsize, args.ysize, args.zsize, args.tsize};
39 
40  for (unsigned int d = 0; d < InputImageType::ImageDimension; ++d)
41  {
42  extractIndex[d] = indexes[d];
43  if(sizes[d] == -1 || indexes[d] + sizes[d] > inputSize[d])
44  extractSize[d] = inputSize[d] - extractIndex[d];
45  else
46  extractSize[d] = sizes[d];
47  }
48 
49  extractRegion.SetIndex(extractIndex);
50  extractRegion.SetSize(extractSize);
51 
52  std::cout<< "Input will be crop using ROI of dimensions:" << extractRegion << std::endl;
53 
54  if(input->GetNumberOfComponentsPerPixel() == 1)
55  {
56  typedef itk::Image<typename InputImageType::PixelType, OutputDimension> OutputImageType;
57 
58  typedef itk::ExtractImageFilter<InputImageType, OutputImageType> ExtractFilterType;
59  typename ExtractFilterType::Pointer extractFilter = ExtractFilterType::New();
60  extractFilter->SetExtractionRegion(extractRegion);
61  extractFilter->SetDirectionCollapseToGuess();
62  extractFilter->SetInput(input);
63 
64  itk::CStyleCommand::Pointer callback = itk::CStyleCommand::New();
65  callback->SetCallback(eventCallback);
66  extractFilter->AddObserver(itk::ProgressEvent(), callback);
67  extractFilter->Update();
68 
69  typename OutputImageType::Pointer output = extractFilter->GetOutput();
70 
71  std::cout << "\n\nOutput dimensions: " << output->GetLargestPossibleRegion();
72  anima::writeImage<OutputImageType>(args.output, output);
73  }
74  else
75  {
76  typedef itk::VectorImage<typename InputImageType::InternalPixelType, OutputDimension> OutputImageType;
77 
78  typedef itk::ExtractImageFilter<InputImageType, OutputImageType> ExtractFilterType;
79  typename ExtractFilterType::Pointer extractFilter = ExtractFilterType::New();
80  extractFilter->SetExtractionRegion(extractRegion);
81  extractFilter->SetDirectionCollapseToGuess();
82  extractFilter->SetInput(input);
83 
84  itk::CStyleCommand::Pointer callback = itk::CStyleCommand::New();
85  callback->SetCallback(eventCallback);
86  extractFilter->AddObserver(itk::ProgressEvent(), callback);
87  extractFilter->Update();
88 
89  typename OutputImageType::Pointer output = extractFilter->GetOutput();
90 
91  std::cout << "\n\nOutput dimensions: " << output->GetLargestPossibleRegion();
92  anima::writeImage<OutputImageType>(args.output, output);
93  }
94 
95 
96 }
97 
98 template <class InputImageType>
99 void
101 {
102  unsigned int imageDim = InputImageType::ImageDimension;
103 
104  switch(imageDim)
105  {
106  case 2:
107  {
108  unsigned int outputDim = 2;
109  outputDim -= (args.xsize == 0)? 1 :0;
110  outputDim -= (args.ysize == 0)? 1 :0;
111  switch(outputDim)
112  {
113  case 1:
114  if constexpr (InputImageType::ImageDimension >= 1)
115  extract<InputImageType, 1>(args);
116  break;
117  case 2:
118  if constexpr (InputImageType::ImageDimension >= 2)
119  extract<InputImageType, 2>(args);
120  break;
121  default:
122  std::string msg = "Number of collapsed dimension not supported.";
123  itk::ExceptionObject excp(__FILE__, __LINE__,msg , ITK_LOCATION);
124  throw excp;
125  }
126  break;
127  }
128  case 3:
129  {
130  unsigned int outputDim = 3;
131  outputDim -= (args.xsize == 0)? 1 :0;
132  outputDim -= (args.ysize == 0)? 1 :0;
133  outputDim -= (args.zsize == 0)? 1 :0;
134  switch(outputDim)
135  {
136  case 1:
137  if constexpr (InputImageType::ImageDimension >= 1)
138  extract<InputImageType, 1>(args);
139  break;
140  case 2:
141  if constexpr (InputImageType::ImageDimension >= 2)
142  extract<InputImageType, 2>(args);
143  break;
144  case 3:
145  if constexpr (InputImageType::ImageDimension >= 3)
146  extract<InputImageType, 3>(args);
147  break;
148  default:
149  std::string msg = "Number of collapsed dimension not supported.";
150  itk::ExceptionObject excp(__FILE__, __LINE__,msg , ITK_LOCATION);
151  throw excp;
152  }
153  break;
154  }
155  case 4:
156  {
157  unsigned int outputDim = 4;
158  outputDim -= (args.xsize == 0)? 1 :0;
159  outputDim -= (args.ysize == 0)? 1 :0;
160  outputDim -= (args.zsize == 0)? 1 :0;
161  outputDim -= (args.tsize == 0)? 1 :0;
162  switch(outputDim)
163  {
164  case 1:
165  if constexpr (InputImageType::ImageDimension >= 1)
166  extract<InputImageType, 1>(args);
167  break;
168  case 2:
169  if constexpr (InputImageType::ImageDimension >= 2)
170  extract<InputImageType, 2>(args);
171  break;
172  case 3:
173  if constexpr (InputImageType::ImageDimension >= 3)
174  extract<InputImageType, 3>(args);
175  break;
176  case 4:
177  if constexpr (InputImageType::ImageDimension >= 4)
178  extract<InputImageType, 4>(args);
179  break;
180  default:
181  std::string msg = "Number of collapsed dimension not supported.";
182  itk::ExceptionObject excp(__FILE__, __LINE__,msg , ITK_LOCATION);
183  throw excp;
184  }
185  break;
186  }
187  default:
188  std::string msg = "Number of collapsed dimension not supported.";
189  itk::ExceptionObject excp(__FILE__, __LINE__,msg , ITK_LOCATION);
190  throw excp;
191  }
192 }
193 
194 template <class ComponentType, int dimension>
195 void
196 checkIfComponentsAreVectors(itk::ImageIOBase::Pointer imageIO, const arguments &args)
197 {
198  ANIMA_CHECK_IF_COMPONENTS_ARE_VECTORS(imageIO, ComponentType, dimension, evaluateOutputType, args)
199 }
200 
201 
202 template <class ComponentType >
203 void
204 retrieveNbDimensions(itk::ImageIOBase::Pointer imageIO, const arguments &args)
205 {
206  ANIMA_RETRIEVE_NUMBER_OF_DIMENSIONS(imageIO, ComponentType, checkIfComponentsAreVectors, imageIO, args)
207 }
208 
209 int main(int ac, const char** av)
210 {
211 
212  TCLAP::CmdLine cmd("The animaCropImage uses an itkExtractImage filter to crop "
213  "an image given as input.\n"
214  "The lower case arguments(x<xindex>, y<yindex>, z<zindex>, t<tindex>)"
215  " are the starting indexes of the input region to keep. The default value is 0\n"
216  "The upper case arguments(X<xsize>, Y<ysize>, Z<zsize>, T<tsize>) are the sizes of "
217  "the input region to keep. The default value is the largest possible sizes given the "
218  "corresponding indexes.\nIf you give args size of zero the corresponding dimension will "
219  "be collapsed.\nExample: for args a 4D image 4x4x4x4 the arguments :\n --xindex 1"
220  " --zindex 1 --zsize 2 --tindex 3 --tsize 0\n will result on an image 3x4x2\n"
221  "Where the x dim corresponds to [1,2,3] of the input, y[0,3], zindex[1,2] and tindex is "
222  "collapsed, only the last sequence has been kept."
223  "INRIA / IRISA - VisAGeS/Empenn Team",
224  ' ',
225  ANIMA_VERSION);
226 
227  TCLAP::ValueArg<std::string> inputArg("i",
228  "input",
229  "Input image to crop",
230  true,
231  "",
232  "Input image to crop",
233  cmd);
234 
235  TCLAP::ValueArg<std::string> outputArg("o",
236  "output",
237  "Output cropped image",
238  true,
239  "",
240  "Output cropped image",
241  cmd);
242 
243  TCLAP::ValueArg<std::string> maskArg("m",
244  "mask",
245  "A mask used instead of other arguments to determine a bounding box",
246  false,
247  "",
248  "Bounding box mask",
249  cmd);
250 
251  TCLAP::ValueArg<unsigned int> xArg("x",
252  "xindex",
253  "The resulting croped image will go from xindex to xsize along the xindex axis.",
254  false,
255  0,
256  "Start of ROI for the xindex dimension",
257  cmd);
258 
259  TCLAP::ValueArg<unsigned int> XArg("X",
260  "xsize",
261  "The resulting croped image will go from xindex to xsize along the xindex axis. If 0 the dimension is collapsed.",
262  false,
263  -1,
264  "Size of ROI for the xindex dimension",
265  cmd);
266 
267  TCLAP::ValueArg<unsigned int> yArg("y",
268  "yindex",
269  "The resulting croped image will go from yindex to ysize along the yindex axis.",
270  false,
271  0,
272  "Start of ROI for the yindex dimension",
273  cmd);
274 
275  TCLAP::ValueArg<unsigned int> YArg("Y",
276  "ysize",
277  "The resulting croped image will go from yindex to ysize along the yindex axis. If 0 the dimension is collapsed.",
278  false,
279  -1,
280  "Size of ROI for the yindex dimension",
281  cmd);
282  TCLAP::ValueArg<unsigned int> zArg("z",
283  "zindex",
284  "The resulting croped image will go from yindex to ysize along the zindex axis.",
285  false,
286  0,
287  "Start of ROI for the zindex dimension",
288  cmd);
289 
290  TCLAP::ValueArg<unsigned int> ZArg("Z",
291  "zsize",
292  "The resulting croped image will go from zindex to zsize along the zindex axis. If 0 the dimension is collapsed.",
293  false,
294  -1,
295  "Size of ROI for the zindex dimension",
296  cmd);
297  TCLAP::ValueArg<unsigned int> tArg("t",
298  "tindex",
299  "The resulting croped image will go from tindex to tsize along the tindex axis.",
300  false,
301  0,
302  "Start of ROI for the tindex dimension",
303  cmd);
304 
305  TCLAP::ValueArg<unsigned int> TArg("T",
306  "tsize",
307  "The resulting croped image will go from tindex to tsize along the tindex axis. If 0 the dimension is collapsed.",
308  false,
309  -1,
310  "Size of ROI for the tindex dimension",
311  cmd);
312 
313  try
314  {
315  cmd.parse(ac,av);
316  }
317  catch (TCLAP::ArgException& e)
318  {
319  std::cerr << "Error: " << e.error() << "for argument " << e.argId() << std::endl;
320  return EXIT_FAILURE;
321  }
322 
323  // Find out the type of the image in file
324  itk::ImageIOBase::Pointer imageIO = itk::ImageIOFactory::CreateImageIO(inputArg.getValue().c_str(),
325  itk::ImageIOFactory::ReadMode);
326 
327  if( !imageIO )
328  {
329  std::cerr << "Itk could not find suitable IO factory for the input" << std::endl;
330  return EXIT_FAILURE;
331  }
332 
333  // Now that we found the appropriate ImageIO class, ask it to read the meta data from the image file.
334  imageIO->SetFileName(inputArg.getValue());
335  imageIO->ReadImageInformation();
336 
337  std::cout<<"\npreparing filter...\n";
338 
339  arguments args;
340  args.xindex = xArg.getValue(); args.xsize = XArg.getValue();
341  args.yindex = yArg.getValue(); args.ysize = YArg.getValue();
342  args.zindex = zArg.getValue(); args.zsize = ZArg.getValue();
343  args.tindex = tArg.getValue(); args.tsize = TArg.getValue();
344 
345  if (maskArg.getValue() != "")
346  {
347  typedef itk::Image <unsigned char, 3> MaskImageType;
348  MaskImageType::Pointer maskIm = anima::readImage<MaskImageType>(maskArg.getValue());
349  unsigned int xMax = maskIm->GetLargestPossibleRegion().GetIndex()[0];
350  unsigned int yMax = maskIm->GetLargestPossibleRegion().GetIndex()[1];
351  unsigned int zMax = maskIm->GetLargestPossibleRegion().GetIndex()[2];
352  unsigned int xMin = maskIm->GetLargestPossibleRegion().GetIndex()[0] + maskIm->GetLargestPossibleRegion().GetSize()[0];
353  unsigned int yMin = maskIm->GetLargestPossibleRegion().GetIndex()[1] + maskIm->GetLargestPossibleRegion().GetSize()[1];
354  unsigned int zMin = maskIm->GetLargestPossibleRegion().GetIndex()[2] + maskIm->GetLargestPossibleRegion().GetSize()[2];
355 
356  typedef itk::ImageRegionIteratorWithIndex <MaskImageType> MaskIteratorType;
357  typedef MaskImageType::IndexType IndexType;
358  MaskIteratorType maskIt(maskIm,maskIm->GetLargestPossibleRegion());
359  while (!maskIt.IsAtEnd())
360  {
361  if (maskIt.Get() != 0)
362  {
363  IndexType tmpIndex = maskIt.GetIndex();
364  if (xMax < tmpIndex[0])
365  xMax = tmpIndex[0];
366  if (yMax < tmpIndex[1])
367  yMax = tmpIndex[1];
368  if (zMax < tmpIndex[2])
369  zMax = tmpIndex[2];
370 
371  if (xMin > tmpIndex[0])
372  xMin = tmpIndex[0];
373  if (yMin > tmpIndex[1])
374  yMin = tmpIndex[1];
375  if (zMin > tmpIndex[2])
376  zMin = tmpIndex[2];
377  }
378 
379  ++maskIt;
380  }
381 
382  args.xindex = xMin;
383  args.yindex = yMin;
384  args.zindex = zMin;
385 
386  args.xsize = xMax - xMin + 1;
387  args.ysize = yMax - yMin + 1;
388  args.zsize = zMax - zMin + 1;
389  }
390 
391  args.input = inputArg.getValue(); args.output = outputArg.getValue();
392 
393  try
394  {
395  ANIMA_RETRIEVE_COMPONENT_TYPE(imageIO, retrieveNbDimensions, imageIO, args);
396  }
397  catch ( itk::ExceptionObject & err )
398  {
399  std::cerr << "Itk cannot extract, be sure to use valid arguments..." << std::endl;
400  std::cerr << err << std::endl;
401  return EXIT_FAILURE;
402  }
403 
404  return EXIT_SUCCESS;
405 }
std::string output
void evaluateOutputType(const arguments &args)
void extract(const arguments &args)
void checkIfComponentsAreVectors(itk::ImageIOBase::Pointer imageIO, const arguments &args)
itk::ImageIOBase::Pointer imageIO
int main(int ac, const char **av)
#define ANIMA_RETRIEVE_COMPONENT_TYPE(imageIO, function,...)
void retrieveNbDimensions(itk::ImageIOBase::Pointer imageIO, const arguments &args)
#define ANIMA_RETRIEVE_NUMBER_OF_DIMENSIONS(imageIO, ComponentType, function,...)
void eventCallback(itk::Object *caller, const itk::EventObject &event, void *clientData)
std::string input
#define ANIMA_CHECK_IF_COMPONENTS_ARE_VECTORS(imageIO, ComponentType, Dimension, function,...)