PlusLib  2.9.0
Software library for tracked ultrasound image acquisition, calibration, and processing.
VolumeReconstructor.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 #include "PlusConfigure.h"
8 #include "igsioTrackedFrame.h"
9 #include "vtkImageData.h"
10 #include "vtkMatrix4x4.h"
11 #include "vtkIGSIOSequenceIO.h"
12 #include "vtkIGSIOTrackedFrameList.h"
13 #include "vtkIGSIOTransformRepository.h"
15 #include "vtkXMLUtilities.h"
16 #include "vtksys/CommandLineArguments.hxx"
17 
18 int main(int argc, char* argv[])
19 {
20  bool printHelp(false);
21  // Parse command line arguments.
22 
23  std::string inputImgSeqFileName;
24  std::string inputConfigFileName;
25  std::string outputVolumeFileName;
26  std::string outputVolumeAlphaFileNameDeprecated;
27  std::string outputVolumeAccumulationFileName;
28  std::string outputFrameFileName;
29  std::string importanceMaskFileName;
30  std::string inputImageToReferenceTransformName;
31 
32  // Deprecated arguments (2013-07-29, #800)
33  std::string inputImageToReferenceTransformNameDeprecated;
34  std::string inputImgSeqFileNameDeprecated;
35 
36  int verboseLevel = vtkPlusLogger::LOG_LEVEL_UNDEFINED;
37 
38  bool disableCompression = false;
39 
40  std::vector<std::string> customHeaderFieldsToSave;
41  std::vector<std::string> customHeaderValuesToSave;
42 
43  vtksys::CommandLineArguments cmdargs;
44  cmdargs.Initialize(argc, argv);
45 
46  cmdargs.AddArgument("--image-to-reference-transform", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &inputImageToReferenceTransformName, "Name of the transform to define the image slice pose relative to the reference coordinate system (e.g., ImageToReference). Note that this parameter is optional, if it is defined then it overrides the ImageCoordinateFrame and ReferenceCoordinateFrame attribute values in the configuration file.");
47  cmdargs.AddArgument("--source-seq-file", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &inputImgSeqFileName, "Input sequence file filename (.mha/.nrrd)");
48  cmdargs.AddArgument("--config-file", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &inputConfigFileName, "Input configuration file name (.xml)");
49  cmdargs.AddArgument("--output-volume-file", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &outputVolumeFileName, "Output file name of the reconstructed volume (must have .mha, .mhd, .nrrd or .nhdr extension)");
50  cmdargs.AddArgument("--output-volume-accumulation-file", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &outputVolumeAccumulationFileName, "Output file name of the accumulation of the reconstructed volume (.mha/.nrrd)");
51  cmdargs.AddArgument("--output-frame-file", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &outputFrameFileName, "A filename that will be used for storing the tracked image frames. Each frame will be exported individually, with the proper position and orientation in the reference coordinate system");
52  cmdargs.AddArgument("--help", vtksys::CommandLineArguments::NO_ARGUMENT, &printHelp, "Print this help.");
53  cmdargs.AddArgument("--disable-compression", vtksys::CommandLineArguments::NO_ARGUMENT, &disableCompression, "Do not compress output image files.");
54  cmdargs.AddArgument("--save-custom-headers", vtksys::CommandLineArguments::MULTI_ARGUMENT, &customHeaderFieldsToSave, "List of custom header fields to pass into the output file.");
55  cmdargs.AddArgument("--verbose", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &verboseLevel, "Verbose level (1=error only, 2=warning, 3=info, 4=debug, 5=trace)");
56  cmdargs.AddArgument("--importance-mask-file", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &importanceMaskFileName, "The file to use as the importance mask.");
57 
58  // Deprecated arguments (2013-07-29, #800)
59  cmdargs.AddArgument("--transform", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &inputImageToReferenceTransformNameDeprecated, "Image to reference transform name used for the reconstruction. DEPRECATED, use --image-to-reference-transform argument instead");
60  cmdargs.AddArgument("--img-seq-file", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &inputImgSeqFileNameDeprecated, "Input sequence file filename (.mha/.nrrd). DEPRECATED: use --source-seq-file argument instead");
61  // Deprecated argument (2014-08-15, #923)
62  cmdargs.AddArgument("--output-volume-alpha-file", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &outputVolumeAlphaFileNameDeprecated, "Output file name of the alpha channel of the reconstructed volume (.mha/.nrrd). DEPRECATED: use --output-volume-accumulation-file argument instead");
63 
64  if (!cmdargs.Parse())
65  {
66  std::cerr << "Problem parsing arguments" << std::endl;
67  std::cout << "Help: " << cmdargs.GetHelp() << std::endl;
68  exit(EXIT_FAILURE);
69  }
70 
71  if (printHelp)
72  {
73  std::cout << cmdargs.GetHelp() << std::endl;
74  exit(EXIT_SUCCESS);
75  }
76 
77  // Set the log level
78  vtkPlusLogger::Instance()->SetLogLevel(verboseLevel);
79 
80  // Deprecated arguments (2013-07-29, #800)
81  if (!inputImageToReferenceTransformNameDeprecated.empty())
82  {
83  LOG_WARNING("The --transform argument is deprecated. Use --image-to-reference-transform instead.");
84  if (inputImageToReferenceTransformName.empty())
85  {
86  inputImageToReferenceTransformName = inputImageToReferenceTransformNameDeprecated;
87  }
88  }
89  if (!inputImgSeqFileNameDeprecated.empty())
90  {
91  LOG_WARNING("The --img-seq-file argument is deprecated. Use --source-seq-file instead.");
92  if (inputImgSeqFileName.empty())
93  {
94  inputImgSeqFileName = inputImgSeqFileNameDeprecated;
95  }
96  }
97  // Deprecated argument (2014-08-15, #923)
98  if (!outputVolumeAlphaFileNameDeprecated.empty())
99  {
100  LOG_WARNING("The --output-volume-alpha-file argument is deprecated. Use --output-volume-accumulation-file instead.");
101  if (outputVolumeAccumulationFileName.empty())
102  {
103  outputVolumeAccumulationFileName = outputVolumeAlphaFileNameDeprecated;
104  }
105  }
106 
107  if (inputConfigFileName.empty())
108  {
109  std::cout << "ERROR: Input config file name is missing!" << std::endl;
110  std::cout << "Help: " << cmdargs.GetHelp() << std::endl;
111  exit(EXIT_FAILURE);
112  }
113 
114  vtkSmartPointer<vtkPlusVolumeReconstructor> reconstructor = vtkSmartPointer<vtkPlusVolumeReconstructor>::New();
115 
116  LOG_INFO("Reading configuration file:" << inputConfigFileName);
117  vtkSmartPointer<vtkXMLDataElement> configRootElement = vtkSmartPointer<vtkXMLDataElement>::New();
118  if (PlusXmlUtils::ReadDeviceSetConfigurationFromFile(configRootElement, inputConfigFileName.c_str()) == PLUS_FAIL)
119  {
120  LOG_ERROR("Unable to read configuration from file " << inputConfigFileName.c_str());
121  return EXIT_FAILURE;
122  }
123 
124  if (reconstructor->ReadConfiguration(configRootElement) != PLUS_SUCCESS)
125  {
126  LOG_ERROR("Failed to read configuration from " << inputConfigFileName.c_str());
127  return EXIT_FAILURE;
128  }
129 
130  if (!importanceMaskFileName.empty())
131  {
132  reconstructor->SetImportanceMaskFilename(importanceMaskFileName);
133  }
134 
135  vtkSmartPointer<vtkIGSIOTransformRepository> transformRepository = vtkSmartPointer<vtkIGSIOTransformRepository>::New();
136  if (configRootElement->FindNestedElementWithName("CoordinateDefinitions") != NULL)
137  {
138  if (transformRepository->ReadConfiguration(configRootElement) != PLUS_SUCCESS)
139  {
140  LOG_ERROR("Failed to read transforms from CoordinateDefinitions");
141  return EXIT_FAILURE;
142  }
143  }
144  else
145  {
146  LOG_DEBUG("No transforms were found in CoordinateDefinitions. Only the transforms defined in the input image will be available.");
147  }
148 
149  // Print calibration transform
150  std::ostringstream osTransformRepo;
151  transformRepository->Print(osTransformRepo);
152  LOG_DEBUG("Transform repository: \n" << osTransformRepo.str());
153 
154  // Read image sequence
155  LOG_INFO("Reading image sequence " << inputImgSeqFileName);
156  vtkSmartPointer<vtkIGSIOTrackedFrameList> trackedFrameList = vtkSmartPointer<vtkIGSIOTrackedFrameList>::New();
157  if (vtkIGSIOSequenceIO::Read(inputImgSeqFileName, trackedFrameList) != PLUS_SUCCESS)
158  {
159  LOG_ERROR("Unable to load input sequences file.");
160  exit(EXIT_FAILURE);
161  }
162 
163  // Reconstruct volume
164  igsioTransformName imageToReferenceTransformName;
165  if (!inputImageToReferenceTransformName.empty())
166  {
167  // image to reference transform is specified at the command-line
168  if (imageToReferenceTransformName.SetTransformName(inputImageToReferenceTransformName.c_str()) != PLUS_SUCCESS)
169  {
170  LOG_ERROR("Invalid image to reference transform name: " << inputImageToReferenceTransformName);
171  return EXIT_FAILURE;
172  }
173  reconstructor->SetImageCoordinateFrame(imageToReferenceTransformName.From());
174  reconstructor->SetReferenceCoordinateFrame(imageToReferenceTransformName.To());
175  }
176 
177  LOG_INFO("Set volume output extent...");
178  std::string errorDetail;
179  if (reconstructor->SetOutputExtentFromFrameList(trackedFrameList, transformRepository, errorDetail) != PLUS_SUCCESS)
180  {
181  LOG_ERROR("Failed to set output extent of volume!");
182  return EXIT_FAILURE;
183  }
184 
185  LOG_INFO("Reconstruct volume...");
186  const int numberOfFrames = trackedFrameList->GetNumberOfTrackedFrames();
187  int numberOfFramesAddedToVolume = 0;
188 
189  for (int frameIndex = 0; frameIndex < numberOfFrames; frameIndex += reconstructor->GetSkipInterval())
190  {
191  LOG_DEBUG("Frame: " << frameIndex);
192  vtkPlusLogger::PrintProgressbar((100.0 * frameIndex) / numberOfFrames);
193 
194  igsioTrackedFrame* frame = trackedFrameList->GetTrackedFrame(frameIndex);
195 
196  if (transformRepository->SetTransforms(*frame) != PLUS_SUCCESS)
197  {
198  LOG_ERROR("Failed to update transform repository with frame #" << frameIndex);
199  continue;
200  }
201 
202  // Insert slice for reconstruction
203  bool insertedIntoVolume = false;
204  bool isFirst = frameIndex == 0;
205  bool isLast = frameIndex + reconstructor->GetSkipInterval() >= numberOfFrames;
206  if (reconstructor->AddTrackedFrame(frame, transformRepository, isFirst, isLast, &insertedIntoVolume) != PLUS_SUCCESS)
207  {
208  LOG_ERROR("Failed to add tracked frame to volume with frame #" << frameIndex);
209  continue;
210  }
211 
212  if (insertedIntoVolume)
213  {
214  numberOfFramesAddedToVolume++;
215  }
216 
217  // Write an ITK image with the image pose in the reference coordinate system
218  if (!outputFrameFileName.empty())
219  {
220  vtkSmartPointer<vtkMatrix4x4> imageToReferenceTransformMatrix = vtkSmartPointer<vtkMatrix4x4>::New();
221  if (transformRepository->GetTransform(imageToReferenceTransformName, imageToReferenceTransformMatrix) != PLUS_SUCCESS)
222  {
223  std::string strImageToReferenceTransformName;
224  imageToReferenceTransformName.GetTransformName(strImageToReferenceTransformName);
225  LOG_ERROR("Failed to get transform '" << strImageToReferenceTransformName << "' from transform repository!");
226  continue;
227  }
228 
229  // Print the image to reference transform
230  std::ostringstream os;
231  imageToReferenceTransformMatrix->Print(os);
232  LOG_TRACE("Image to reference transform: \n" << os.str());
233 
234  // Insert frame index before the file extension (image.mha => image001.mha)
235  std::ostringstream ss;
236  size_t found;
237  found = outputFrameFileName.find_last_of(".");
238  ss << outputFrameFileName.substr(0, found);
239  ss.width(3);
240  ss.fill('0');
241  ss << frameIndex;
242  ss << outputFrameFileName.substr(found);
243 
244  PlusCommon::WriteToFile(frame, ss.str(), imageToReferenceTransformMatrix);
245  }
246  }
247 
248  vtkPlusLogger::PrintProgressbar(100);
249 
250  if (!customHeaderFieldsToSave.empty())
251  {
252  std::string fieldValue;
253  for (unsigned int i = 0; i < customHeaderFieldsToSave.size(); ++i)
254  {
255  fieldValue = trackedFrameList->GetCustomString(customHeaderFieldsToSave[i]);
256  customHeaderValuesToSave.push_back(fieldValue);
257  }
258  }
259 
260  trackedFrameList->Clear();
261 
262  LOG_INFO("Number of frames added to the volume: " << numberOfFramesAddedToVolume << " out of " << numberOfFrames);
263 
264  LOG_INFO("Saving volume to file...");
265  if (!customHeaderValuesToSave.empty())
266  {
267  reconstructor->SaveReconstructedVolumeToFile(outputVolumeFileName, false, !disableCompression, &customHeaderFieldsToSave, &customHeaderValuesToSave);
268  }
269  else{
270  reconstructor->SaveReconstructedVolumeToFile(outputVolumeFileName, false, !disableCompression, nullptr, nullptr);
271  }
272 
273  if (!outputVolumeAccumulationFileName.empty())
274  {
275  reconstructor->SaveReconstructedVolumeToFile(outputVolumeAccumulationFileName, true, !disableCompression, &customHeaderFieldsToSave, &customHeaderValuesToSave);
276  }
277 
278  return EXIT_SUCCESS;
279 }
const char char * errorDetail
Definition: phidget22.h:1271
vtkPlusCommonExport PlusStatus WriteToFile(igsioTrackedFrame *frame, const std::string &filename, vtkMatrix4x4 *imageToTracker)
Definition: PlusCommon.cxx:50
for i
int main(int argc, char *argv[])
#define PLUS_FAIL
Definition: PlusCommon.h:43
#define PLUS_SUCCESS
Definition: PlusCommon.h:44
static vtkIGSIOLogger * Instance()
static PlusStatus ReadDeviceSetConfigurationFromFile(vtkXMLDataElement *config, const char *filename)
Definition: PlusXmlUtils.h:23