PlusLib  2.9.0
Software library for tracked ultrasound image acquisition, calibration, and processing.
EditSequenceFile.cxx
Go to the documentation of this file.
1 /*=Plus=header=begin======================================================
2 Program: Plus
3 Copyright (c) Laboratory for Percutaneous Surgery. All rights reserved.
4 See License.txt for details.
5 =========================================================Plus=header=end*/
6 
7 // Local includes
8 #include "PlusConfigure.h"
9 #include "PlusMath.h"
10 #include "igsioTrackedFrame.h"
11 #include "vtkPlusSequenceIO.h"
12 #include "vtkIGSIOTrackedFrameList.h"
13 #include "vtkIGSIOTransformRepository.h"
14 
15 // VTK includes
16 #include <vtkImageData.h>
17 #include <vtkMatrix4x4.h>
18 #include <vtkSmartPointer.h>
19 #include <vtkTransform.h>
20 #include <vtkXMLDataElement.h>
21 #include <vtkXMLUtilities.h>
22 #include <vtksys/CommandLineArguments.hxx>
23 #include <vtksys/RegularExpression.hxx>
24 
26 {
36  MIX,
42 };
43 
44 class FrameFieldUpdate
45 {
46 public:
47  FrameFieldUpdate()
48  {
49  TrackedFrameList = NULL;
50  FrameScalarStart = 0;
51  FrameScalarIncrement = 0;
52  FrameScalarDecimalDigits = 5;
53  FrameTransformStart = NULL;
54  FrameTransformIncrement = NULL;
55  }
56 
57  std::string FieldName;
58  std::string UpdatedFieldName;
59  std::string UpdatedFieldValue;
60  vtkIGSIOTrackedFrameList* TrackedFrameList;
61  double FrameScalarStart;
62  double FrameScalarIncrement;
63  int FrameScalarDecimalDigits;
64  vtkMatrix4x4* FrameTransformStart;
65  vtkMatrix4x4* FrameTransformIncrement;
66  std::string FrameTransformIndexFieldName;
67 };
68 
69 PlusStatus TrimSequenceFile(vtkIGSIOTrackedFrameList* trackedFrameList, unsigned int firstFrameIndex, unsigned int lastFrameIndex);
70 PlusStatus DecimateSequenceFile(vtkIGSIOTrackedFrameList* trackedFrameList, unsigned int decimationFactor);
71 PlusStatus UpdateFrameFieldValue(FrameFieldUpdate& fieldUpdate);
72 PlusStatus DeleteFrameField(vtkIGSIOTrackedFrameList* trackedFrameList, std::string fieldName);
73 PlusStatus ConvertStringToMatrix(std::string& strMatrix, vtkMatrix4x4* matrix);
74 PlusStatus AddTransform(vtkIGSIOTrackedFrameList* trackedFrameList, std::vector<std::string> transformNamesToAdd, std::string deviceSetConfigurationFileName);
75 PlusStatus FillRectangle(vtkIGSIOTrackedFrameList* trackedFrameList, const std::vector<unsigned int>& fillRectOrigin, const std::vector<unsigned int>& fillRectSize, int fillGrayLevel);
76 PlusStatus CropRectangle(vtkIGSIOTrackedFrameList* trackedFrameList, igsioVideoFrame::FlipInfoType& flipInfo, const std::vector<int>& cropRectOrigin, const std::vector<int>& cropRectSize);
77 
78 namespace
79 {
80  const std::string FIELD_VALUE_FRAME_SCALAR = "{frame-scalar}";
81  const std::string FIELD_VALUE_FRAME_TRANSFORM = "{frame-transform}";
82 }
83 
84 // Fuse all fields in sequence files into the first sequence
85 //----------------------------------------------------------------------------
86 PlusStatus MixTrackedFrameLists(vtkIGSIOTrackedFrameList* trackedFrameList, std::vector<std::string> inputFileNames)
87 {
88  if (inputFileNames.size() == 0)
89  {
90  LOG_ERROR("No --source-seq-files specified");
91  return PLUS_FAIL;
92  }
93 
94  LOG_INFO("Read master sequence file: " << inputFileNames[0]);
95  if (vtkPlusSequenceIO::Read(inputFileNames[0], trackedFrameList) != PLUS_SUCCESS)
96  {
97  LOG_ERROR("Couldn't read sequence file: " << inputFileNames[0]);
98  return PLUS_FAIL;
99  }
100  if (trackedFrameList->GetNumberOfTrackedFrames() == 0)
101  {
102  LOG_ERROR("No frames in sequence file: " << inputFileNames[0]);
103  return PLUS_FAIL;
104  }
105 
106  for (unsigned int i = 1; i < inputFileNames.size(); i++)
107  {
108  LOG_INFO("Read input sequence file: " << inputFileNames[i]);
109  vtkSmartPointer<vtkIGSIOTrackedFrameList> additionalTrackedFrameList = vtkSmartPointer<vtkIGSIOTrackedFrameList>::New();
110  if (vtkPlusSequenceIO::Read(inputFileNames[i], additionalTrackedFrameList) != PLUS_SUCCESS)
111  {
112  LOG_ERROR("Couldn't read sequence file: " << inputFileNames[0]);
113  return PLUS_FAIL;
114  }
115  if (additionalTrackedFrameList->GetNumberOfTrackedFrames() == 0)
116  {
117  continue;
118  }
119 
120  unsigned int additionalFrameIndex = 0;
121  double maxTimestampValueForCurrentAdditionalFrame = additionalTrackedFrameList->GetTrackedFrame(0)->GetTimestamp();
122  if (additionalTrackedFrameList->GetNumberOfTrackedFrames() >= 2)
123  {
124  maxTimestampValueForCurrentAdditionalFrame = (additionalTrackedFrameList->GetTrackedFrame(additionalFrameIndex + 1)->GetTimestamp() + additionalTrackedFrameList->GetTrackedFrame(additionalFrameIndex)->GetTimestamp()) / 2.0;
125  }
126  for (unsigned int f = 0; f < trackedFrameList->GetNumberOfTrackedFrames(); ++f)
127  {
128  igsioTrackedFrame* masterTrackedFrame = trackedFrameList->GetTrackedFrame(f);
129 
130  // Determine which additional frame belongs to this master frame
131  while (masterTrackedFrame->GetTimestamp() > maxTimestampValueForCurrentAdditionalFrame
132  && additionalFrameIndex + 1 < additionalTrackedFrameList->GetNumberOfTrackedFrames())
133  {
134  if (additionalFrameIndex == additionalTrackedFrameList->GetNumberOfTrackedFrames() - 1)
135  {
136  // last frame, all remaining frames are assigned to it
137  break;
138  }
139  additionalFrameIndex++;
140  // use this frame index until timestamp is closest to this frame's timestamp
141  maxTimestampValueForCurrentAdditionalFrame = (additionalTrackedFrameList->GetTrackedFrame(additionalFrameIndex)->GetTimestamp() +
142  additionalTrackedFrameList->GetTrackedFrame(additionalFrameIndex + 1)->GetTimestamp()) / 2.0;
143  }
144 
145  // Copy frame fields
146  igsioTrackedFrame* additionalFrame = additionalTrackedFrameList->GetTrackedFrame(additionalFrameIndex);
147  auto customFrameFields = additionalFrame->GetCustomFields();
148  for (auto fieldIter = customFrameFields.begin(); fieldIter != customFrameFields.end(); ++fieldIter)
149  {
150  if (!fieldIter->first.compare("FrameNumber") ||
151  !fieldIter->first.compare("Timestamp") ||
152  !fieldIter->first.compare("UnfilteredTimestamp") ||
153  !fieldIter->first.compare("ImageStatus"))
154  {
155  // Timing and image information is taken from the first sequence
156  continue;
157  }
158  masterTrackedFrame->SetFrameField(fieldIter->first, fieldIter->second.second, fieldIter->second.first);
159  }
160  }
161  }
162  return PLUS_SUCCESS;
163 }
164 
165 //----------------------------------------------------------------------------
166 // Append tracked frame list (one after the other)
167 PlusStatus AppendTrackedFrameLists(vtkIGSIOTrackedFrameList* trackedFrameList, std::vector<std::string> inputFileNames, bool incrementTimestamps, std::vector<std::string> customHeaderFields)
168 {
169  double lastTimestamp = 0;
170  for (unsigned int i = 0; i < inputFileNames.size(); i++)
171  {
172  LOG_INFO("Read input sequence file: " << inputFileNames[i]);
173  vtkSmartPointer<vtkIGSIOTrackedFrameList> timestampFrameList = vtkSmartPointer<vtkIGSIOTrackedFrameList>::New();
174  if (vtkPlusSequenceIO::Read(inputFileNames[i], timestampFrameList) != PLUS_SUCCESS)
175  {
176  LOG_ERROR("Couldn't read sequence file: " << inputFileNames[0]);
177  return PLUS_FAIL;
178  }
179 
180  if (incrementTimestamps)
181  {
182  vtkIGSIOTrackedFrameList* tfList = timestampFrameList;
183  for (unsigned int f = 0; f < tfList->GetNumberOfTrackedFrames(); ++f)
184  {
185  igsioTrackedFrame* tf = tfList->GetTrackedFrame(f);
186  tf->SetTimestamp(lastTimestamp + tf->GetTimestamp());
187  }
188 
189  lastTimestamp = tfList->GetTrackedFrame(tfList->GetNumberOfTrackedFrames() - 1)->GetTimestamp();
190  }
191 
192  if (!customHeaderFields.empty())
193  {
194  std::string fieldValue;
195  for (unsigned int i = 0; i < customHeaderFields.size(); ++i)
196  {
197  fieldValue = timestampFrameList->GetCustomString(customHeaderFields[i]);
198  trackedFrameList->SetCustomString(customHeaderFields[i], fieldValue);
199  }
200  }
201 
202  if (trackedFrameList->AddTrackedFrameList(timestampFrameList) != PLUS_SUCCESS)
203  {
204  LOG_ERROR("Failed to append tracked frame list!");
205  return PLUS_FAIL;
206  }
207  }
208  return PLUS_SUCCESS;
209 }
210 
211 //----------------------------------------------------------------------------
212 int main(int argc, char** argv)
213 {
214  // Parse command-line arguments
215  bool printHelp = false;
216  int verboseLevel(vtkPlusLogger::LOG_LEVEL_UNDEFINED);
217  vtksys::CommandLineArguments args;
218 
219  std::string inputFileName; // Sequence file name with path to edit
220  std::vector<std::string> inputFileNames; // Sequence file name list with path to edit
221  std::string outputFileName; // Sequence file name with path to save the result
222  std::string strOperation;
223  OperationType operation;
224  bool useCompression = false;
225  bool incrementTimestamps = false;
226 
227  int firstFrameIndex = -1; // First frame index used for trimming the sequence file.
228  int lastFrameIndex = -1; // Last frame index used for trimming the sequence file.
229 
230  std::string fieldName; // Field name to edit
231  std::string updatedFieldName; // Updated field name after edit
232  std::string updatedFieldValue; // Updated field value after edit
233 
234  std::vector<std::string> customHeaderFieldsToMaintain; // Custom header field list to maintain from input to output
235 
236  int frameScalarDecimalDigits = 5; // Number of digits saved for frame field value into sequence file (Default: 5)
237 
238  double frameScalarStart = 0.0; // Frame scalar field value starting index (Default: 0.0)
239  double frameScalarIncrement = 1.0; // Frame scalar field value increment (Default: 1.0)
240 
241  int decimationFactor = 2; // Keep every 2nd frame by default
242 
243  std::string strFrameTransformStart; // Frame transform field starting 4x4 transform matrix (Default: identity)
244  vtkSmartPointer<vtkMatrix4x4> frameTransformStart = vtkSmartPointer<vtkMatrix4x4>::New(); // Frame transform field starting 4x4 transform matrix (Default: identity)
245 
246  std::string strFrameTransformIncrement; // Frame transform increment 4x4 transform matrix
247  vtkSmartPointer<vtkMatrix4x4> frameTransformIncrement = vtkSmartPointer<vtkMatrix4x4>::New(); // Frame transform increment 4x4 transform matrix
248 
249  std::string strFrameTransformIndexFieldName;
250 
251  std::string strUpdatedReferenceTransformName;
252 
253  std::string transformNamesToAdd; // Name of the transform to add to each frame
254  std::string deviceSetConfigurationFileName; // Used device set configuration file path and name
255 
256  std::vector<int> rectOriginPix; // Fill/crop rectangle top-left corner position in MF coordinate frame, in pixels
257  std::vector<int> rectSizePix; // Fill/crop rectangle size in MF coordinate frame, in pixels
258  int fillGrayLevel = 0; // Rectangle fill color
259 
260  bool flipX(false);
261  bool flipY(false);
262  bool flipZ(false);
263 
264  args.Initialize(argc, argv);
265  args.AddArgument("--help", vtksys::CommandLineArguments::NO_ARGUMENT, &printHelp, "Print this help.");
266  args.AddArgument("--verbose", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &verboseLevel, "Verbose level (1=error only, 2=warning, 3=info, 4=debug, 5=trace)");
267 
268  args.AddArgument("--source-seq-file", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &inputFileName, "Input sequence file name with path to edit");
269  args.AddArgument("--source-seq-files", vtksys::CommandLineArguments::MULTI_ARGUMENT, &inputFileNames, "Input sequence file name list with path to edit");
270  args.AddArgument("--output-seq-file", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &outputFileName, "Output sequence file name with path to save the result");
271 
272  args.AddArgument("--operation", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &strOperation, "Operation to modify sequence file. See available operations below.");
273 
274  // Trimming parameters
275  args.AddArgument("--first-frame-index", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &firstFrameIndex, "First frame index used for trimming the sequence file. Index of the first frame of the sequence is 0.");
276  args.AddArgument("--last-frame-index", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &lastFrameIndex, "Last frame index used for trimming the sequence file.");
277 
278  // Decimation parameters
279  args.AddArgument("--decimation-factor", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &decimationFactor, "Used for DECIMATE operation, where every N-th frame is kept. This parameter specifies N (Default: 2)");
280 
281  args.AddArgument("--field-name", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &fieldName, "Field name to edit");
282  args.AddArgument("--updated-field-name", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &updatedFieldName, "Updated field name after edit");
283  args.AddArgument("--updated-field-value", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &updatedFieldValue, "Updated field value after edit");
284 
285  args.AddArgument("--maintain-custom-headers", vtksys::CommandLineArguments::MULTI_ARGUMENT, &customHeaderFieldsToMaintain, "List of custom header fields to pass through to output file.");
286 
287  args.AddArgument("--frame-scalar-start", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &frameScalarStart, "Frame scalar field value starting index (Default: 0.0)");
288  args.AddArgument("--frame-scalar-increment", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &frameScalarIncrement, "Frame scalar field value increment (Default: 1.0)");
289  args.AddArgument("--frame-scalar-decimal-digits", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &frameScalarDecimalDigits, "Number of digits saved for frame scalar field value into sequence file (Default: 5)");
290 
291  args.AddArgument("--frame-transform-start", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &strFrameTransformStart, "Frame transform field starting 4x4 transform matrix (Default: identity)");
292  args.AddArgument("--frame-transform-increment", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &strFrameTransformIncrement, "Frame transform increment 4x4 transform matrix (Default: identity)");
293  args.AddArgument("--frame-transform-index-field-name", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &strFrameTransformIndexFieldName, "If specified then increment is applied as many times as the value of this field");
294 
295  args.AddArgument("--update-reference-transform", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &strUpdatedReferenceTransformName, "Set the reference transform name to update old files by changing all ToolToReference transforms to ToolToTracker transform.");
296 
297  args.AddArgument("--use-compression", vtksys::CommandLineArguments::NO_ARGUMENT, &useCompression, "Compress sequence file images.");
298  args.AddArgument("--increment-timestamps", vtksys::CommandLineArguments::NO_ARGUMENT, &incrementTimestamps, "Increment timestamps in the order of the input-file-names");
299 
300  args.AddArgument("--add-transform", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &transformNamesToAdd, "Name of the transform to add to each frame (e.g., StylusTipToTracker); multiple transforms can be added separated by a comma (e.g., StylusTipToReference,ProbeToReference)");
301  args.AddArgument("--config-file", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &deviceSetConfigurationFileName, "Used device set configuration file path and name");
302 
303  // Clip and transform arguments
304  args.AddArgument("--rect-origin", vtksys::CommandLineArguments::MULTI_ARGUMENT, &rectOriginPix, "Fill or crop rectangle top-left corner position in MF coordinate frame, in pixels, separated by space (e.g., --rect-origin 12 34).");
305  args.AddArgument("--rect-size", vtksys::CommandLineArguments::MULTI_ARGUMENT, &rectSizePix, "Fill or crop rectangle size in MF coordinate frame, in pixels, separated by space (e.g., --rect-size 56 78).");
306  args.AddArgument("--flipX", vtksys::CommandLineArguments::NO_ARGUMENT, &flipX, "Flip image along X axis.");
307  args.AddArgument("--flipY", vtksys::CommandLineArguments::NO_ARGUMENT, &flipY, "Flip image along Y axis.");
308  args.AddArgument("--flipZ", vtksys::CommandLineArguments::NO_ARGUMENT, &flipZ, "Flip image along Z axis.");
309  args.AddArgument("--fill-gray-level", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &fillGrayLevel, "Rectangle fill gray level. 0 = black, 255 = white. (Default: 0)");
310 
311  if (!args.Parse())
312  {
313  std::cerr << "Problem parsing arguments" << std::endl;
314  std::cout << "Help: " << args.GetHelp() << std::endl;
315  exit(EXIT_FAILURE);
316  }
317 
319  if (argc == 1 || printHelp)
320  {
321  std::cout << args.GetHelp() << std::endl;
322  std::cout << std::endl << "Operations: " << std::endl << std::endl;
323 
324  std::cout << "- UPDATE_FRAME_FIELD_NAME: update per-frame field names for each frame. If field does not exist then it is added." << std::endl;
325  std::cout << " Requires --field-name and --updated-field-name." << std::endl;
326  std::cout << "- UPDATE_FRAME_FIELD_VALUE: update per-frame field values for each frame, if not exists add it." << std::endl;
327  std::cout << " Uses --field-name, --updated-field-name, --updated-field-value, --frame-scalar-*, --frame-transform-*" << std::endl;
328  std::cout << "- DELETE_FRAME_FIELD: delete per-frame field (field values specified for each frame)." << std::endl;
329  std::cout << " Requires --field-name." << std::endl;
330 
331  std::cout << "- UPDATE_FIELD_NAME: update field name, if not exists add it." << std::endl;
332  std::cout << " Requires --field-name and --updated-field-name." << std::endl;
333  std::cout << "- UPDATE_FIELD_VALUE: update field value, if not exists add it." << std::endl;
334  std::cout << " Requires --field-name and --updated-field-value." << std::endl;
335  std::cout << "- DELETE_FIELD: delete field with name specified." << std::endl;
336  std::cout << " Requires --field-name." << std::endl;
337  std::cout << "- ADD_TRANSFORM: add specified transform." << std::endl;
338  std::cout << " Requires --add-transform." << std::endl;
339 
340  std::cout << "- TRIM: Trim sequence file." << std::endl;
341  std::cout << " Requires --first-frame-index and --last-frame-index." << std::endl;
342  std::cout << "- DECIMATE: Keep every N-th frame of the sequence file." << std::endl;
343  std::cout << " Requires --decimation-factor." << std::endl;
344  std::cout << "- APPEND: Append multiple sequence files (one after the other)." << std::endl;
345  std::cout << " Set input files with the --source-seq-files parameter." << std::endl;
346  std::cout << "- MIX: Merge fields stored in multiple sequence files." << std::endl;
347  std::cout << " Timepoints are defined by the first sequence. Image data is taken from the first sequence." << std::endl;
348  std::cout << " No interpolation is performed, fields are copied from the frame with the closest timestamp." << std::endl;
349  std::cout << " Set input files with the --source-seq-files parameter." << std::endl;
350 
351  std::cout << "- FILL_IMAGE_RECTANGLE: Fill a rectangle in the image (useful for removing patient data from sequences)." << std::endl;
352  std::cout << " Requires --rect-origin, --rect-size, and --fill-gray-level. E.g., --rect-origin 12 34 --rect-size 56 78)" << std::endl;
353  std::cout << "- CROP: Crop a rectangle in the image (useful for cropping b-mode image from the data obtained via frame-grabber)." << std::endl;
354  std::cout << " Requires --rect-origin and --rect-size. (e.g., --rect-size 56 78). Optional: --flip*." << std::endl;
355 
356  std::cout << "- REMOVE_IMAGE_DATA: Remove image data from a meta file that has both image and tracker data, and keep only the tracker data." << std::endl;
357 
358  return EXIT_SUCCESS;
359  }
360 
361  vtkPlusLogger::Instance()->SetLogLevel(verboseLevel);
362 
363  // Check command line arguments
364  if (inputFileName.empty() && inputFileNames.empty())
365  {
366  LOG_ERROR("At least one input file name is mandantory!");
367  return EXIT_FAILURE;
368  }
369 
370  if (outputFileName.empty())
371  {
372  LOG_ERROR("Please set output file name!");
373  return EXIT_FAILURE;
374  }
375 
376  // Set operation
377  if (strOperation.empty())
378  {
379  operation = NO_OPERATION;
380  LOG_INFO("No modification operation has been specified (specify --operation parameter to change the input sequence).");
381  }
382  else if (igsioCommon::IsEqualInsensitive(strOperation, "UPDATE_FRAME_FIELD_NAME"))
383  {
384  operation = UPDATE_FRAME_FIELD_NAME;
385  }
386  else if (igsioCommon::IsEqualInsensitive(strOperation, "UPDATE_FRAME_FIELD_VALUE"))
387  {
388  operation = UPDATE_FRAME_FIELD_VALUE;
389  }
390  else if (igsioCommon::IsEqualInsensitive(strOperation, "DELETE_FRAME_FIELD"))
391  {
392  operation = DELETE_FRAME_FIELD;
393  }
394  else if (igsioCommon::IsEqualInsensitive(strOperation, "UPDATE_FIELD_NAME"))
395  {
396  operation = UPDATE_FIELD_NAME;
397  }
398  else if (igsioCommon::IsEqualInsensitive(strOperation, "UPDATE_FIELD_VALUE"))
399  {
400  operation = UPDATE_FIELD_VALUE;
401  }
402  else if (igsioCommon::IsEqualInsensitive(strOperation, "DELETE_FIELD"))
403  {
404  operation = DELETE_FIELD;
405  }
406  else if (igsioCommon::IsEqualInsensitive(strOperation, "ADD_TRANSFORM"))
407  {
408  operation = ADD_TRANSFORM;
409  }
410  else if (igsioCommon::IsEqualInsensitive(strOperation, "TRIM"))
411  {
412  operation = TRIM;
413  }
414  else if (igsioCommon::IsEqualInsensitive(strOperation, "DECIMATE"))
415  {
416  operation = DECIMATE;
417  }
418  else if (igsioCommon::IsEqualInsensitive(strOperation, "APPEND"))
419  {
420  operation = APPEND;
421  }
422  else if (igsioCommon::IsEqualInsensitive(strOperation, "MERGE"))
423  {
424  LOG_WARNING("MERGE operation name is deprecated. Use APPEND instead.")
425  operation = APPEND;
426  }
427  else if (igsioCommon::IsEqualInsensitive(strOperation, "MIX"))
428  {
429  operation = MIX;
430  }
431  else if (igsioCommon::IsEqualInsensitive(strOperation, "FILL_IMAGE_RECTANGLE"))
432  {
433  operation = FILL_IMAGE_RECTANGLE;
434  }
435  else if (igsioCommon::IsEqualInsensitive(strOperation, "CROP"))
436  {
437  if (rectOriginPix.size() != 2 && rectOriginPix.size() != 3 &&
438  rectSizePix.size() != 2 && rectSizePix.size() != 3)
439  {
440  LOG_ERROR("--rect-origin and --rect-size must be of the form --rect-origin X Y <Z> and --rect-size I J <K>")
441  return EXIT_FAILURE;
442  }
443  operation = CROP;
444  }
445  else if (igsioCommon::IsEqualInsensitive(strOperation, "REMOVE_IMAGE_DATA"))
446  {
447  operation = REMOVE_IMAGE_DATA;
448  }
449  else
450  {
451  LOG_ERROR("Invalid operation selected: " << strOperation);
452  return EXIT_FAILURE;
453  }
454 
455  // Convert strings transforms to vtkMatrix
456  if (ConvertStringToMatrix(strFrameTransformStart, frameTransformStart) != PLUS_SUCCESS)
457  {
458  LOG_ERROR("String to matrix conversion failed for transform start matrix!");
459  return EXIT_FAILURE;
460  }
461 
462  if (ConvertStringToMatrix(strFrameTransformIncrement, frameTransformIncrement) != PLUS_SUCCESS)
463  {
464  LOG_ERROR("String to matrix conversion failed for transform increment matrix!");
465  return EXIT_FAILURE;
466  }
467 
469  // Read input files
470 
471  vtkSmartPointer<vtkIGSIOTrackedFrameList> trackedFrameList = vtkSmartPointer<vtkIGSIOTrackedFrameList>::New();
472 
473  if (!inputFileName.empty())
474  {
475  // Insert file name to the beginning of the list
476  inputFileNames.insert(inputFileNames.begin(), inputFileName);
477  }
478 
479  // Multiple input files are appended unless sequences are mixed
480  PlusStatus status = PLUS_SUCCESS;
481  if (operation == MIX)
482  {
483  status = MixTrackedFrameLists(trackedFrameList, inputFileNames);
484  }
485  else
486  {
487  status = AppendTrackedFrameLists(trackedFrameList, inputFileNames, incrementTimestamps, customHeaderFieldsToMaintain);
488  }
489  if (status == PLUS_FAIL)
490  {
491  return EXIT_FAILURE;
492  }
493 
495  // Make the operation
496 
497  switch (operation)
498  {
499  case NO_OPERATION:
500  case APPEND:
501  case MIX:
502  {
503  // No need to do anything just save into output file
504  }
505  break;
506  case TRIM:
507  {
508  if (firstFrameIndex < 0)
509  {
510  firstFrameIndex = 0;
511  }
512  if (lastFrameIndex < 0)
513  {
514  lastFrameIndex = 0;
515  }
516  unsigned int firstFrameIndexUint = static_cast<unsigned int>(firstFrameIndex);
517  unsigned int lastFrameIndexUint = static_cast<unsigned int>(lastFrameIndex);
518  if (TrimSequenceFile(trackedFrameList, firstFrameIndexUint, lastFrameIndexUint) != PLUS_SUCCESS)
519  {
520  LOG_ERROR("Failed to trim sequence file");
521  return EXIT_FAILURE;
522  }
523  }
524  break;
525  case DECIMATE:
526  {
527  if (DecimateSequenceFile(trackedFrameList, decimationFactor) != PLUS_SUCCESS)
528  {
529  LOG_ERROR("Failed to decimate sequence file");
530  return EXIT_FAILURE;
531  }
532  }
533  break;
535  {
536  FrameFieldUpdate fieldUpdate;
537  fieldUpdate.TrackedFrameList = trackedFrameList;
538  fieldUpdate.FieldName = fieldName;
539  fieldUpdate.UpdatedFieldName = updatedFieldName;
540 
541  if (UpdateFrameFieldValue(fieldUpdate) != PLUS_SUCCESS)
542  {
543  LOG_ERROR("Failed to update frame field name '" << fieldName << "' to '" << updatedFieldName << "'");
544  return EXIT_FAILURE;
545  }
546  }
547  break;
549  {
550  FrameFieldUpdate fieldUpdate;
551  fieldUpdate.TrackedFrameList = trackedFrameList;
552  fieldUpdate.FieldName = fieldName;
553  fieldUpdate.UpdatedFieldName = updatedFieldName;
554  fieldUpdate.UpdatedFieldValue = updatedFieldValue;
555  fieldUpdate.FrameScalarDecimalDigits = frameScalarDecimalDigits;
556  fieldUpdate.FrameScalarIncrement = frameScalarIncrement;
557  fieldUpdate.FrameScalarStart = frameScalarStart;
558  fieldUpdate.FrameTransformStart = frameTransformStart;
559  fieldUpdate.FrameTransformIncrement = frameTransformIncrement;
560  fieldUpdate.FrameTransformIndexFieldName = strFrameTransformIndexFieldName;
561 
562  if (UpdateFrameFieldValue(fieldUpdate) != PLUS_SUCCESS)
563  {
564  LOG_ERROR("Failed to update frame field value");
565  return EXIT_FAILURE;
566  }
567  }
568  break;
569  case DELETE_FRAME_FIELD:
570  {
571  if (DeleteFrameField(trackedFrameList, fieldName) != PLUS_SUCCESS)
572  {
573  LOG_ERROR("Failed to delete frame field");
574  return EXIT_FAILURE;
575  }
576  }
577  break;
578  case DELETE_FIELD:
579  {
580  // Delete field
581  LOG_INFO("Delete field: " << fieldName);
582  if (trackedFrameList->SetCustomString(fieldName.c_str(), NULL) != PLUS_SUCCESS)
583  {
584  LOG_ERROR("Failed to delete field: " << fieldName);
585  return EXIT_FAILURE;
586  }
587  }
588  break;
589  case UPDATE_FIELD_NAME:
590  {
591  // Update field name
592  LOG_INFO("Update field name '" << fieldName << "' to '" << updatedFieldName << "'");
593  const char* fieldValue = trackedFrameList->GetCustomString(fieldName.c_str());
594  if (fieldValue != NULL)
595  {
596  // Delete field
597  if (trackedFrameList->SetCustomString(fieldName.c_str(), NULL) != PLUS_SUCCESS)
598  {
599  LOG_ERROR("Failed to delete field: " << fieldName);
600  return EXIT_FAILURE;
601  }
602 
603  // Add new field
604  if (trackedFrameList->SetCustomString(updatedFieldName.c_str(), fieldValue) != PLUS_SUCCESS)
605  {
606  LOG_ERROR("Failed to update field '" << updatedFieldName << "' with value '" << fieldValue << "'");
607  return EXIT_FAILURE;
608  }
609  }
610  }
611  break;
612  case UPDATE_FIELD_VALUE:
613  {
614  // Update field value
615  LOG_INFO("Update field '" << fieldName << "' with value '" << updatedFieldValue << "'");
616  if (trackedFrameList->SetCustomString(fieldName.c_str(), updatedFieldValue.c_str()) != PLUS_SUCCESS)
617  {
618  LOG_ERROR("Failed to update field '" << fieldName << "' with value '" << updatedFieldValue << "'");
619  return EXIT_FAILURE;
620  }
621  }
622  break;
623  case ADD_TRANSFORM:
624  {
625  // Add transform
626  LOG_INFO("Add transform '" << transformNamesToAdd << "' using device set configuration file '" << deviceSetConfigurationFileName << "'");
627  std::vector<std::string> transformNamesList;
628  igsioCommon::SplitStringIntoTokens(transformNamesToAdd, ',', transformNamesList);
629  if (AddTransform(trackedFrameList, transformNamesList, deviceSetConfigurationFileName) != PLUS_SUCCESS)
630  {
631  LOG_ERROR("Failed to add transform '" << transformNamesToAdd << "' using device set configuration file '" << deviceSetConfigurationFileName << "'");
632  return EXIT_FAILURE;
633  }
634  }
635  break;
637  {
638  if (rectOriginPix.size() != 2 || rectSizePix.size() != 2)
639  {
640  LOG_ERROR("Incorrect size of vector for rectangle origin or size. Aborting.");
641  return PLUS_FAIL;
642  }
643  if (rectOriginPix[0] < 0 || rectOriginPix[1] < 0 || rectSizePix[0] < 0 || rectSizePix[1] < 0)
644  {
645  LOG_ERROR("Negative value for rectangle origin or size entered. Aborting.");
646  return PLUS_FAIL;
647  }
648  std::vector<unsigned int> rectOriginPixUint(rectOriginPix.begin(), rectOriginPix.end());
649  std::vector<unsigned int> rectSizePixUint(rectSizePix.begin(), rectSizePix.end());
650  // Fill a rectangular region in the image with a solid color
651  if (FillRectangle(trackedFrameList, rectOriginPixUint, rectSizePixUint, fillGrayLevel) != PLUS_SUCCESS)
652  {
653  LOG_ERROR("Failed to fill rectangle");
654  return EXIT_FAILURE;
655  }
656  }
657  break;
658  case CROP:
659  {
660  // Crop a rectangular region from the image
661  igsioVideoFrame::FlipInfoType flipInfo;
662  flipInfo.hFlip = flipX;
663  flipInfo.vFlip = flipY;
664  flipInfo.eFlip = flipZ;
665  if (CropRectangle(trackedFrameList, flipInfo, rectOriginPix, rectSizePix) != PLUS_SUCCESS)
666  {
667  LOG_ERROR("Failed to fill rectangle");
668  return EXIT_FAILURE;
669  }
670  }
671  break;
672  case REMOVE_IMAGE_DATA:
673  // No processing is needed, image data is removed when writing the output
674  break;
675  default:
676  {
677  LOG_WARNING("Unknown operation is specified: " << strOperation);
678  return EXIT_FAILURE;
679  }
680  }
681 
682 
684  // Convert files to the new file format
685 
686  if (!strUpdatedReferenceTransformName.empty())
687  {
688  igsioTransformName referenceTransformName;
689  if (referenceTransformName.SetTransformName(strUpdatedReferenceTransformName.c_str()) != PLUS_SUCCESS)
690  {
691  LOG_ERROR("Reference transform name is invalid: " << strUpdatedReferenceTransformName);
692  return EXIT_FAILURE;
693  }
694 
695  for (unsigned int i = 0; i < trackedFrameList->GetNumberOfTrackedFrames(); ++i)
696  {
697  igsioTrackedFrame* trackedFrame = trackedFrameList->GetTrackedFrame(i);
698 
699  vtkSmartPointer<vtkMatrix4x4> referenceToTrackerMatrix = vtkSmartPointer<vtkMatrix4x4>::New();
700  if (trackedFrame->GetFrameTransform(referenceTransformName, referenceToTrackerMatrix) != PLUS_SUCCESS)
701  {
702  LOG_WARNING("Couldn't get reference transform with name: " << strUpdatedReferenceTransformName);
703  continue;
704  }
705 
706  std::vector<igsioTransformName> transformNameList;
707  trackedFrame->GetFrameTransformNameList(transformNameList);
708 
709  vtkSmartPointer<vtkTransform> toolToTrackerTransform = vtkSmartPointer<vtkTransform>::New();
710  vtkSmartPointer<vtkMatrix4x4> toolToReferenceMatrix = vtkSmartPointer<vtkMatrix4x4>::New();
711  for (unsigned int n = 0; n < transformNameList.size(); ++n)
712  {
713  // No need to change the reference transform
714  if (transformNameList[n] == referenceTransformName)
715  {
716  continue;
717  }
718 
719  ToolStatus status = TOOL_INVALID;
720  if (trackedFrame->GetFrameTransform(transformNameList[n], toolToReferenceMatrix) != PLUS_SUCCESS)
721  {
722  std::string strTransformName;
723  transformNameList[i].GetTransformName(strTransformName);
724  LOG_ERROR("Failed to get frame transform: " << strTransformName);
725  continue;
726  }
727 
728  if (trackedFrame->GetFrameTransformStatus(transformNameList[n], status) != PLUS_SUCCESS)
729  {
730  std::string strTransformName;
731  transformNameList[i].GetTransformName(strTransformName);
732  LOG_ERROR("Failed to get frame transform status: " << strTransformName);
733  continue;
734  }
735 
736  // Compute ToolToTracker transform from ToolToReference
737  toolToTrackerTransform->Identity();
738  toolToTrackerTransform->Concatenate(referenceToTrackerMatrix);
739  toolToTrackerTransform->Concatenate(toolToReferenceMatrix);
740 
741  // Update the name to ToolToTracker
742  igsioTransformName toolToTracker(transformNameList[n].From().c_str(), "Tracker");
743  // Set the new custom transform
744  if (trackedFrame->SetFrameTransform(toolToTracker, toolToTrackerTransform->GetMatrix()) != PLUS_SUCCESS)
745  {
746  std::string strTransformName;
747  transformNameList[i].GetTransformName(strTransformName);
748  LOG_ERROR("Failed to set frame transform: " << strTransformName);
749  continue;
750  }
751 
752  // Use the same status as it was before
753  if (trackedFrame->SetFrameTransformStatus(toolToTracker, status) != PLUS_SUCCESS)
754  {
755  std::string strTransformName;
756  transformNameList[i].GetTransformName(strTransformName);
757  LOG_ERROR("Failed to set frame transform status: " << strTransformName);
758  continue;
759  }
760 
761  // Delete old transform and status fields
762  std::string oldTransformName, oldTransformStatus;
763  transformNameList[n].GetTransformName(oldTransformName);
764  // Append Transform to the end of the transform name
765  vtksys::RegularExpression isTransform("Transform$");
766  if (!isTransform.find(oldTransformName))
767  {
768  oldTransformName.append("Transform");
769  }
770  oldTransformStatus = oldTransformName;
771  oldTransformStatus.append("Status");
772  trackedFrame->DeleteFrameField(oldTransformName.c_str());
773  trackedFrame->DeleteFrameField(oldTransformStatus.c_str());
774 
775  }
776  }
777  }
778 
780  // Save output file to file
781 
782  LOG_INFO("Save output sequence file to: " << outputFileName);
783  if (vtkPlusSequenceIO::Write(outputFileName, trackedFrameList, trackedFrameList->GetImageOrientation(), useCompression, operation != REMOVE_IMAGE_DATA) != PLUS_SUCCESS)
784  {
785  LOG_ERROR("Couldn't write sequence file: " << outputFileName);
786  return EXIT_FAILURE;
787  }
788 
789  LOG_INFO("Sequence file editing was successful!");
790  return EXIT_SUCCESS;
791 }
792 
793 //-------------------------------------------------------
794 PlusStatus TrimSequenceFile(vtkIGSIOTrackedFrameList* aTrackedFrameList, unsigned int aFirstFrameIndex, unsigned int aLastFrameIndex)
795 {
796  LOG_INFO("Trim sequence file from frame #: " << aFirstFrameIndex << " to frame #" << aLastFrameIndex);
797  if (aLastFrameIndex >= aTrackedFrameList->GetNumberOfTrackedFrames() || aFirstFrameIndex > aLastFrameIndex)
798  {
799  LOG_ERROR("Invalid input range: (" << aFirstFrameIndex << ", " << aLastFrameIndex << ")" << " Permitted range within (0, " << aTrackedFrameList->GetNumberOfTrackedFrames() - 1 << ")");
800  return PLUS_FAIL;
801  }
802 
803  if (aLastFrameIndex != aTrackedFrameList->GetNumberOfTrackedFrames() - 1)
804  {
805  aTrackedFrameList->RemoveTrackedFrameRange(aLastFrameIndex + 1, aTrackedFrameList->GetNumberOfTrackedFrames() - 1);
806  }
807 
808  if (aFirstFrameIndex != 0)
809  {
810  aTrackedFrameList->RemoveTrackedFrameRange(0, aFirstFrameIndex - 1);
811  }
812 
813  return PLUS_SUCCESS;
814 }
815 
816 //-------------------------------------------------------
817 PlusStatus DecimateSequenceFile(vtkIGSIOTrackedFrameList* aTrackedFrameList, unsigned int decimationFactor)
818 {
819  LOG_INFO("Decimate sequence file: keep 1 frame out of every " << decimationFactor << " frames");
820  if (decimationFactor < 2)
821  {
822  LOG_ERROR("Invalid decimation factor: " << decimationFactor << ". It must be an integer larger or equal than 2.");
823  return PLUS_FAIL;
824  }
825  for (unsigned int i = 0; i < aTrackedFrameList->GetNumberOfTrackedFrames() - 1; i++)
826  {
827  unsigned int removeFirstFrameIndex = i + 1;
828  unsigned int removeLastFrameIndex = i + decimationFactor - 1;
829  if (removeLastFrameIndex >= aTrackedFrameList->GetNumberOfTrackedFrames())
830  {
831  removeLastFrameIndex = aTrackedFrameList->GetNumberOfTrackedFrames() - 1;
832  if (removeLastFrameIndex < removeFirstFrameIndex)
833  {
834  removeLastFrameIndex = removeFirstFrameIndex;
835  }
836  }
837  aTrackedFrameList->RemoveTrackedFrameRange(removeFirstFrameIndex, removeLastFrameIndex);
838  }
839  return PLUS_SUCCESS;
840 }
841 
842 //-------------------------------------------------------
843 PlusStatus DeleteFrameField(vtkIGSIOTrackedFrameList* trackedFrameList, std::string fieldName)
844 {
845  if (trackedFrameList == NULL)
846  {
847  LOG_ERROR("Tracked frame list is NULL!");
848  return PLUS_FAIL;
849  }
850 
851  if (fieldName.empty())
852  {
853  LOG_ERROR("Field name is empty!");
854  return PLUS_FAIL;
855  }
856 
857  LOG_INFO("Delete frame field: " << fieldName);
858  int numberOfErrors(0);
859  for (unsigned int i = 0; i < trackedFrameList->GetNumberOfTrackedFrames(); ++i)
860  {
861  igsioTrackedFrame* trackedFrame = trackedFrameList->GetTrackedFrame(i);
862 
864  // Delete field name
865  std::string fieldValue = trackedFrame->GetFrameField(fieldName);
866  if (!fieldValue.empty() && trackedFrame->DeleteFrameField(fieldName) != PLUS_SUCCESS)
867  {
868  LOG_ERROR("Failed to delete frame field '" << fieldName << "' for frame #" << i);
869  numberOfErrors++;
870  }
871  }
872 
873  return (numberOfErrors == 0 ? PLUS_SUCCESS : PLUS_FAIL);
874 }
875 
876 
877 //-------------------------------------------------------
878 PlusStatus UpdateFrameFieldValue(FrameFieldUpdate& fieldUpdate)
879 {
880  LOG_INFO("Update frame field");
881  int numberOfErrors(0);
882 
883  // Set the start scalar value
884  double scalarVariable = fieldUpdate.FrameScalarStart;
885 
886  // Set the start transform matrix
887  vtkSmartPointer<vtkTransform> frameTransform = vtkSmartPointer<vtkTransform>::New();
888  if (fieldUpdate.FrameTransformStart != NULL)
889  {
890  frameTransform->SetMatrix(fieldUpdate.FrameTransformStart);
891  }
892 
893  for (unsigned int i = 0; i < fieldUpdate.TrackedFrameList->GetNumberOfTrackedFrames(); ++i)
894  {
895  igsioTrackedFrame* trackedFrame = fieldUpdate.TrackedFrameList->GetTrackedFrame(i);
896 
898  // Update field name
899  if (!fieldUpdate.FieldName.empty() && !fieldUpdate.UpdatedFieldName.empty())
900  {
901  std::string fieldValue = trackedFrame->GetFrameField(fieldUpdate.FieldName);
902  if (!fieldValue.empty())
903  {
904  std::string copyOfFieldValue(fieldValue);
905  trackedFrame->DeleteFrameField(fieldUpdate.FieldName);
906  trackedFrame->SetFrameField(fieldUpdate.UpdatedFieldName, copyOfFieldValue);
907  }
908  }
909 
910  std::string fieldName = fieldUpdate.FieldName;
911  if (!fieldUpdate.UpdatedFieldName.empty())
912  {
913  fieldName = fieldUpdate.UpdatedFieldName;
914  }
915 
917  // Update field value
918  if (!fieldName.empty() && !fieldUpdate.UpdatedFieldValue.empty())
919  {
920  if (igsioCommon::IsEqualInsensitive(fieldUpdate.UpdatedFieldValue, FIELD_VALUE_FRAME_SCALAR))
921  {
922  // Update it as a scalar variable
923 
924  std::ostringstream fieldValue;
925  fieldValue << std::fixed << std::setprecision(fieldUpdate.FrameScalarDecimalDigits) << scalarVariable;
926 
927  trackedFrame->SetFrameField(fieldName.c_str(), fieldValue.str().c_str());
928  scalarVariable += fieldUpdate.FrameScalarIncrement;
929 
930  }
931  else if (igsioCommon::IsEqualInsensitive(fieldUpdate.UpdatedFieldValue, FIELD_VALUE_FRAME_TRANSFORM))
932  {
933  // Update it as a transform variable
934 
935  double transformMatrix[16] = { 0 };
936  if (fieldUpdate.FrameTransformIndexFieldName.empty())
937  {
938  vtkMatrix4x4::DeepCopy(transformMatrix, frameTransform->GetMatrix());
939  }
940  else
941  {
942  std::string frameIndexStr = trackedFrame->GetFrameField(fieldUpdate.FrameTransformIndexFieldName);
943  int frameIndex = 0;
944  if (igsioCommon::StringToNumber<int>(frameIndexStr, frameIndex) != PLUS_SUCCESS)
945  {
946  LOG_ERROR("Cannot retrieve frame index from value " << frameIndexStr);
947  }
948  vtkSmartPointer<vtkMatrix4x4> cumulativeTransform = vtkSmartPointer<vtkMatrix4x4>::New();
949  cumulativeTransform->DeepCopy(fieldUpdate.FrameTransformStart);
950  for (int i = 0; i < frameIndex; i++)
951  {
952  vtkMatrix4x4::Multiply4x4(fieldUpdate.FrameTransformIncrement, cumulativeTransform, cumulativeTransform);
953  }
954  vtkMatrix4x4::DeepCopy(transformMatrix, cumulativeTransform);
955 
956  }
957 
958  std::ostringstream strTransform;
959  strTransform << std::fixed << std::setprecision(fieldUpdate.FrameScalarDecimalDigits)
960  << transformMatrix[0] << " " << transformMatrix[1] << " " << transformMatrix[2] << " " << transformMatrix[3] << " "
961  << transformMatrix[4] << " " << transformMatrix[5] << " " << transformMatrix[6] << " " << transformMatrix[7] << " "
962  << transformMatrix[8] << " " << transformMatrix[9] << " " << transformMatrix[10] << " " << transformMatrix[11] << " "
963  << transformMatrix[12] << " " << transformMatrix[13] << " " << transformMatrix[14] << " " << transformMatrix[15] << " ";
964  trackedFrame->SetFrameField(fieldName.c_str(), strTransform.str().c_str());
965 
966  if (fieldUpdate.FrameTransformIndexFieldName.empty())
967  {
968  frameTransform->Concatenate(fieldUpdate.FrameTransformIncrement);
969  }
970 
971  }
972  else // Update only as a string value
973  {
974  trackedFrame->SetFrameField(fieldName.c_str(), fieldUpdate.UpdatedFieldValue.c_str());
975  }
976  }
977 
978  }
979 
980  return (numberOfErrors == 0 ? PLUS_SUCCESS : PLUS_FAIL);
981 }
982 
983 //-------------------------------------------------------
984 PlusStatus ConvertStringToMatrix(std::string& strMatrix, vtkMatrix4x4* matrix)
985 {
986  if (matrix == NULL)
987  {
988  LOG_ERROR("Failed to convert string to matrix - output matrix is NULL!");
989  return PLUS_FAIL;
990  }
991 
992  if (!strMatrix.empty())
993  {
994  double transformMatrix[16] = { 0 };
995  std::istringstream transform(strMatrix);
996  double item;
997  int i = 0;
998  while (transform >> item && i < 16)
999  {
1000  transformMatrix[i++] = item;
1001  }
1002  matrix->DeepCopy(transformMatrix);
1003  }
1004 
1005  return PLUS_SUCCESS;
1006 }
1007 
1008 //-------------------------------------------------------
1009 PlusStatus AddTransform(vtkIGSIOTrackedFrameList* trackedFrameList, std::vector<std::string> transformNamesToAdd, std::string deviceSetConfigurationFileName)
1010 {
1011  if (trackedFrameList == NULL)
1012  {
1013  LOG_ERROR("Tracked frame list is invalid");
1014  return PLUS_FAIL;
1015  }
1016 
1017  if (transformNamesToAdd.empty())
1018  {
1019  LOG_ERROR("No transform names are specified to be added");
1020  return PLUS_FAIL;
1021  }
1022 
1023  if (deviceSetConfigurationFileName.empty())
1024  {
1025  LOG_ERROR("Used device set configuration file name is empty");
1026  return PLUS_FAIL;
1027  }
1028 
1029  // Read configuration
1030  vtkSmartPointer<vtkXMLDataElement> configRootElement = vtkSmartPointer<vtkXMLDataElement>::New();
1031  if (PlusXmlUtils::ReadDeviceSetConfigurationFromFile(configRootElement, deviceSetConfigurationFileName.c_str()) == PLUS_FAIL)
1032  {
1033  LOG_ERROR("Unable to read configuration from file " << deviceSetConfigurationFileName.c_str());
1034  return PLUS_FAIL;
1035  }
1036 
1037  for (unsigned int i = 0; i < trackedFrameList->GetNumberOfTrackedFrames(); ++i)
1038  {
1039  igsioTrackedFrame* trackedFrame = trackedFrameList->GetTrackedFrame(i);
1040 
1041  // Set up transform repository
1042  vtkSmartPointer<vtkIGSIOTransformRepository> transformRepository = vtkSmartPointer<vtkIGSIOTransformRepository>::New();
1043  if (transformRepository->ReadConfiguration(configRootElement) != PLUS_SUCCESS)
1044  {
1045  LOG_ERROR("Unable to set device set configuration to transform repository!");
1046  return PLUS_FAIL;
1047  }
1048  if (transformRepository->SetTransforms(*trackedFrame) != PLUS_SUCCESS)
1049  {
1050  LOG_ERROR("Unable to set transforms from tracked frame " << i << " to transform repository!");
1051  return PLUS_FAIL;
1052  }
1053 
1054  for (std::vector<std::string>::iterator transformNameToAddIt = transformNamesToAdd.begin(); transformNameToAddIt != transformNamesToAdd.end(); ++transformNameToAddIt)
1055  {
1056  // Create transform name
1057  igsioTransformName transformName;
1058  transformName.SetTransformName(transformNameToAddIt->c_str());
1059 
1060  // Get transform matrix
1061  ToolStatus status(TOOL_INVALID);
1062  vtkSmartPointer<vtkMatrix4x4> transformMatrix = vtkSmartPointer<vtkMatrix4x4>::New();
1063  if (transformRepository->GetTransform(transformName, transformMatrix, &status) != PLUS_SUCCESS)
1064  {
1065  LOG_WARNING("Failed to get transform " << (*transformNameToAddIt) << " from tracked frame " << i);
1066  transformMatrix->Identity();
1067  status = TOOL_INVALID;
1068  }
1069  trackedFrame->SetFrameTransform(transformName, transformMatrix);
1070  trackedFrame->SetFrameTransformStatus(transformName, status);
1071  }
1072  }
1073 
1074  return PLUS_SUCCESS;
1075 }
1076 
1077 //-------------------------------------------------------
1078 PlusStatus FillRectangle(vtkIGSIOTrackedFrameList* trackedFrameList, const std::vector<unsigned int>& fillRectOrigin, const std::vector<unsigned int>& fillRectSize, int fillGrayLevel)
1079 {
1080  if (trackedFrameList == NULL)
1081  {
1082  LOG_ERROR("Tracked frame list is NULL!");
1083  return PLUS_FAIL;
1084  }
1085  if (fillRectOrigin.size() != 2 || fillRectSize.size() != 2)
1086  {
1087  LOG_ERROR("Fill rectangle origin or size is not specified correctly");
1088  return PLUS_FAIL;
1089  }
1090 
1091  for (unsigned int i = 0; i < trackedFrameList->GetNumberOfTrackedFrames(); ++i)
1092  {
1093  igsioTrackedFrame* trackedFrame = trackedFrameList->GetTrackedFrame(i);
1094  igsioVideoFrame* videoFrame = trackedFrame->GetImageData();
1095  FrameSizeType frameSize = { 0, 0, 0 };
1096  if (videoFrame == NULL || videoFrame->GetFrameSize(frameSize) != PLUS_SUCCESS)
1097  {
1098  LOG_ERROR("Failed to retrieve pixel data from frame " << i << ". Fill rectangle failed.");
1099  continue;
1100  }
1101  if (fillRectOrigin[0] >= frameSize[0] ||
1102  fillRectOrigin[1] >= frameSize[1])
1103  {
1104  LOG_ERROR("Invalid fill rectangle origin is specified (" << fillRectOrigin[0] << ", " << fillRectOrigin[1] << "). The image size is ("
1105  << frameSize[0] << ", " << frameSize[1] << ").");
1106  continue;
1107  }
1108  if (fillRectSize[0] <= 0 || fillRectOrigin[0] + fillRectSize[0] > frameSize[0] ||
1109  fillRectSize[1] <= 0 || fillRectOrigin[1] + fillRectSize[1] > frameSize[1])
1110  {
1111  LOG_ERROR("Invalid fill rectangle size is specified (" << fillRectSize[0] << ", " << fillRectSize[1] << "). The specified fill rectangle origin is ("
1112  << fillRectOrigin[0] << ", " << fillRectOrigin[1] << ") and the image size is (" << frameSize[0] << ", " << frameSize[1] << ").");
1113  continue;
1114  }
1115  if (videoFrame->GetVTKScalarPixelType() != VTK_UNSIGNED_CHAR)
1116  {
1117  LOG_ERROR("Fill rectangle is supported only for B-mode images (unsigned char type)");
1118  continue;
1119  }
1120  unsigned char fillData = 0;
1121  if (fillGrayLevel < 0)
1122  {
1123  fillData = 0;
1124  }
1125  else if (fillGrayLevel > 255)
1126  {
1127  fillData = 255;
1128  }
1129  else
1130  {
1131  fillData = fillGrayLevel;
1132  }
1133  for (unsigned int y = 0; y < fillRectSize[1]; y++)
1134  {
1135  memset(static_cast<unsigned char*>(videoFrame->GetScalarPointer()) + (fillRectOrigin[1] + y)*frameSize[0] + fillRectOrigin[0], fillData, fillRectSize[0]);
1136  }
1137  }
1138  return PLUS_SUCCESS;
1139 }
1140 
1141 //-------------------------------------------------------
1142 PlusStatus CropRectangle(vtkIGSIOTrackedFrameList* trackedFrameList, igsioVideoFrame::FlipInfoType& flipInfo, const std::vector<int>& cropRectOrigin, const std::vector<int>& cropRectSize)
1143 {
1144  if (trackedFrameList == NULL)
1145  {
1146  LOG_ERROR("Tracked frame list is NULL!");
1147  return PLUS_FAIL;
1148  }
1149  std::array<int, 3> rectOrigin = { cropRectOrigin[0], cropRectOrigin[1], cropRectOrigin.size() == 3 ? cropRectOrigin[2] : 0 };
1150  std::array<int, 3> rectSize = { cropRectSize[0], cropRectSize[1], cropRectSize.size() == 3 ? cropRectSize[2] : 1 };
1151 
1152  vtkSmartPointer<vtkMatrix4x4> tfmMatrix = vtkSmartPointer<vtkMatrix4x4>::New();
1153  tfmMatrix->Identity();
1154  tfmMatrix->SetElement(0, 3, -rectOrigin[0]);
1155  tfmMatrix->SetElement(1, 3, -rectOrigin[1]);
1156  tfmMatrix->SetElement(2, 3, -rectOrigin[2]);
1157  igsioTransformName imageToCroppedImage("Image", "CroppedImage");
1158 
1159  for (unsigned int i = 0; i < trackedFrameList->GetNumberOfTrackedFrames(); ++i)
1160  {
1161  igsioTrackedFrame* trackedFrame = trackedFrameList->GetTrackedFrame(i);
1162  igsioVideoFrame* videoFrame = trackedFrame->GetImageData();
1163 
1164  FrameSizeType frameSize = { 0, 0, 0 };
1165  if (videoFrame == NULL || videoFrame->GetFrameSize(frameSize) != PLUS_SUCCESS)
1166  {
1167  LOG_ERROR("Failed to retrieve pixel data from frame " << i << ". Crop rectangle failed.");
1168  continue;
1169  }
1170 
1171  vtkSmartPointer<vtkImageData> croppedImage = vtkSmartPointer<vtkImageData>::New();
1172 
1173  igsioVideoFrame::FlipClipImage(videoFrame->GetImage(), flipInfo, rectOrigin, rectSize, croppedImage);
1174  videoFrame->DeepCopyFrom(croppedImage);
1175  trackedFrame->SetFrameTransform(imageToCroppedImage, tfmMatrix);
1176  trackedFrame->SetFrameTransformStatus(imageToCroppedImage, TOOL_OK);
1177  }
1178 
1179  return PLUS_SUCCESS;
1180 }
PlusStatus DecimateSequenceFile(vtkIGSIOTrackedFrameList *trackedFrameList, unsigned int decimationFactor)
PlusStatus MixTrackedFrameLists(vtkIGSIOTrackedFrameList *trackedFrameList, std::vector< std::string > inputFileNames)
igsioStatus PlusStatus
Definition: PlusCommon.h:40
PlusStatus TrimSequenceFile(vtkIGSIOTrackedFrameList *trackedFrameList, unsigned int firstFrameIndex, unsigned int lastFrameIndex)
for i
#define PLUS_FAIL
Definition: PlusCommon.h:43
PlusStatus FillRectangle(vtkIGSIOTrackedFrameList *trackedFrameList, const std::vector< unsigned int > &fillRectOrigin, const std::vector< unsigned int > &fillRectSize, int fillGrayLevel)
PlusStatus AppendTrackedFrameLists(vtkIGSIOTrackedFrameList *trackedFrameList, std::vector< std::string > inputFileNames, bool incrementTimestamps, std::vector< std::string > customHeaderFields)
static igsioStatus Write(const std::string &filename, igsioTrackedFrame *frame, US_IMAGE_ORIENTATION orientationInFile=US_IMG_ORIENT_MF, bool useCompression=true, bool EnableImageDataWrite=true)
#define PLUS_SUCCESS
Definition: PlusCommon.h:44
static igsioStatus Read(const std::string &filename, vtkIGSIOTrackedFrameList *frameList)
int main(int argc, char **argv)
PlusStatus ConvertStringToMatrix(std::string &strMatrix, vtkMatrix4x4 *matrix)
PlusStatus CropRectangle(vtkIGSIOTrackedFrameList *trackedFrameList, igsioVideoFrame::FlipInfoType &flipInfo, const std::vector< int > &cropRectOrigin, const std::vector< int > &cropRectSize)
static vtkIGSIOLogger * Instance()
PlusStatus AddTransform(vtkIGSIOTrackedFrameList *trackedFrameList, std::vector< std::string > transformNamesToAdd, std::string deviceSetConfigurationFileName)
PlusStatus UpdateFrameFieldValue(FrameFieldUpdate &fieldUpdate)
Direction vectors of rods y
Definition: algo3.m:15
PlusStatus DeleteFrameField(vtkIGSIOTrackedFrameList *trackedFrameList, std::string fieldName)
static PlusStatus ReadDeviceSetConfigurationFromFile(vtkXMLDataElement *config, const char *filename)
Definition: PlusXmlUtils.h:23