21 #include "PlusConfigure.h" 22 #include "igsioTrackedFrame.h" 23 #include "vtkIGSIOSequenceIO.h" 25 #include "vtkIGSIOTrackedFrameList.h" 29 #include <vtkChartXY.h> 30 #include <vtkContextScene.h> 31 #ifdef PLUS_RENDERING_ENABLED 32 #include <vtkContextView.h> 34 #include <vtkPNGWriter.h> 36 #include <vtkRenderWindow.h> 37 #include <vtkRenderer.h> 39 #include <vtkWindowToImageFilter.h> 40 #include <vtkXMLDataElement.h> 41 #include <vtkXMLUtilities.h> 42 #include <vtksys/CommandLineArguments.hxx> 47 const double MAX_ALLOWED_TIME_LAG_DIFF_SEC = 0.005;
48 const double MAX_ALLOWED_ERROR_DIFF = 1.0;
50 struct TemporalCalibrationResult
56 TemporalCalibrationResult()
65 void SaveMetricPlot(
const char* filename, vtkTable* videoPositionMetric, vtkTable* trackerPositionMetric, std::string& xAxisLabel,
66 std::string& yAxisLabel)
68 #ifdef PLUS_RENDERING_ENABLED 70 vtkSmartPointer<vtkContextView> view = vtkSmartPointer<vtkContextView>::New();
71 view->GetRenderer()->SetBackground(1.0, 1.0, 1.0);
72 vtkSmartPointer<vtkChartXY> chart = vtkSmartPointer<vtkChartXY>::New();
73 view->GetScene()->AddItem(chart);
76 vtkPlot* videoPositionMetricLine = chart->AddPlot(vtkChart::LINE);
77 videoPositionMetricLine->SetInputData(videoPositionMetric, 0, 1);
78 videoPositionMetricLine->SetColor(0, 0, 1);
79 videoPositionMetricLine->SetWidth(1.0);
81 vtkPlot* trackerMetricLine = chart->AddPlot(vtkChart::LINE);
82 trackerMetricLine->SetInputData(trackerPositionMetric, 0, 1);
83 trackerMetricLine->SetColor(0, 1, 0);
84 trackerMetricLine->SetWidth(1.0);
85 chart->SetShowLegend(
true);
90 vtkSmartPointer<vtkRenderWindow> renderWindow = vtkSmartPointer<vtkRenderWindow>::New();
91 renderWindow->AddRenderer(view->GetRenderer());
92 renderWindow->SetSize(1600, 1200);
93 renderWindow->OffScreenRenderingOn();
95 vtkSmartPointer<vtkWindowToImageFilter> windowToImageFilter = vtkSmartPointer<vtkWindowToImageFilter>::New();
96 windowToImageFilter->SetInput(renderWindow);
97 windowToImageFilter->Update();
99 vtkSmartPointer<vtkPNGWriter> writer = vtkSmartPointer<vtkPNGWriter>::New();
100 writer->SetFileName(filename);
101 writer->SetInputData(windowToImageFilter->GetOutput());
104 LOG_ERROR(
"Function not available when VTK_RENDERING_BACKEND is None!");
111 std::ofstream myfile;
112 myfile.open(outputFileName.c_str());
113 myfile <<
"<TemporalCalibrationResults TrackerLagSec=\"" << calibResult.trackerLagSec
114 <<
"\" CalibrationError=\"" << calibResult.calibrationError
115 <<
"\" MaxCalibrationError=\"" << calibResult.maxCalibrationError <<
"\" />";
122 calibResult.calibrationError = 0.0;
123 calibResult.maxCalibrationError = 0.0;
124 calibResult.trackerLagSec = 0.0;
126 if (resultSaveFilename.empty())
128 LOG_ERROR(
"Cannot read line calibration results, filename is empty");
131 vtkSmartPointer<vtkXMLDataElement> resultsElem = vtkSmartPointer<vtkXMLDataElement>::Take(vtkXMLUtilities::ReadElementFromFile(resultSaveFilename.c_str()));
132 if (resultsElem == NULL)
134 LOG_ERROR(
"Failed to read baseline file: " << resultSaveFilename);
137 if (resultsElem->GetName() == NULL || STRCASECMP(resultsElem->GetName(),
"TemporalCalibrationResults") != 0)
139 LOG_ERROR(
"Unable to find TemporalCalibrationResults XML data element in baseline: " << resultSaveFilename);
143 if (!resultsElem->GetScalarAttribute(
"CalibrationError", calibResult.calibrationError))
145 LOG_ERROR(
"Unable to find CalibrationError attribute in TemporalCalibrationResults element");
148 if (!resultsElem->GetScalarAttribute(
"MaxCalibrationError", calibResult.maxCalibrationError))
150 LOG_ERROR(
"Unable to find MaxCalibrationError attribute in TemporalCalibrationResults element");
153 if (!resultsElem->GetScalarAttribute(
"TrackerLagSec", calibResult.trackerLagSec))
155 LOG_ERROR(
"Unable to find TrackerLagSec attribute in TemporalCalibrationResults element");
164 int numberOfFailures = 0;
166 if (fabs(calibResult.trackerLagSec - baselineCalibResult.trackerLagSec) > MAX_ALLOWED_TIME_LAG_DIFF_SEC)
168 LOG_ERROR(
"TrackerLagSec comparison error: current=" << calibResult.trackerLagSec <<
", baseline=" << baselineCalibResult.trackerLagSec);
171 if (fabs(calibResult.calibrationError - baselineCalibResult.calibrationError) > MAX_ALLOWED_ERROR_DIFF)
173 LOG_ERROR(
"CalibrationError comparison error: current=" << calibResult.calibrationError <<
", baseline=" << baselineCalibResult.calibrationError);
176 if (fabs(calibResult.maxCalibrationError - baselineCalibResult.maxCalibrationError) > MAX_ALLOWED_ERROR_DIFF)
178 LOG_ERROR(
"MaxCalibrationError comparison error: current=" << calibResult.maxCalibrationError <<
", baseline=" << baselineCalibResult.maxCalibrationError);
182 return numberOfFailures;
186 int main(
int argc,
char** argv)
188 bool printHelp(
false);
189 bool plotResults(
false);
190 bool saveIntermediateImages(
false);
191 int verboseLevel = vtkPlusLogger::LOG_LEVEL_UNDEFINED;
192 std::string inputMovingSequenceMetafile(
"");
193 std::string inputFixedSequenceMetafile(
"");
194 std::string intermediateFileOutputDirectory;
195 double samplingResolutionSec = 0.001;
196 std::string fixedProbeToReferenceTransformNameStr(
"");
197 std::string movingProbeToReferenceTransformNameStr(
"");
198 double maxTimeOffsetSec = 0.5;
199 std::vector<int> clipRectOrigin;
200 std::vector<int> clipRectSize;
201 std::string inputBaselineFileName;
203 vtksys::CommandLineArguments args;
204 args.Initialize(argc, argv);
206 args.AddArgument(
"--help", vtksys::CommandLineArguments::NO_ARGUMENT, &printHelp,
"Print this help.");
207 args.AddArgument(
"--fixed-seq-file", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &inputFixedSequenceMetafile,
"Input US image sequence metafile name with path");
208 args.AddArgument(
"--moving-seq-file", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &inputMovingSequenceMetafile,
"Input tracker sequence metafile name with path");
209 args.AddArgument(
"--fixed-probe-to-reference-transform", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &fixedProbeToReferenceTransformNameStr,
"If specified then it indicates that the fixed sequence contains tracking data and it specifies the transform name that describes the probe pose relative to a static reference (for example, ProbeToReference). If not specified then video data is used from the fixed sequence.");
210 args.AddArgument(
"--moving-probe-to-reference-transform", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &movingProbeToReferenceTransformNameStr,
"If specified then it indicates that the moving sequence contains tracking data and it specifies the transform name that describes the probe pose relative to a static reference (for example, ProbeToReference). If not specified then video data is used from the moving sequence.");
211 args.AddArgument(
"--max-time-offset-sec", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &maxTimeOffsetSec,
"Maximum time offset between the signals, in seconds (default: 2 seconds)");
212 args.AddArgument(
"--plot-results", vtksys::CommandLineArguments::NO_ARGUMENT, &plotResults,
"Plot results (display position vs. time plots without and with temporal calibration)");
213 args.AddArgument(
"--verbose", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &verboseLevel,
"Verbose level (1=error only, 2=warning, 3=info, 4=debug, 5=trace)");
214 args.AddArgument(
"--sampling-resolution-sec", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &samplingResolutionSec,
"Sampling resolution (in seconds, default is 0.001)");
215 args.AddArgument(
"--save-intermediate-images", vtksys::CommandLineArguments::NO_ARGUMENT, &saveIntermediateImages,
"Save images of intermediate steps (scanlines used, and detected lines)");
216 args.AddArgument(
"--intermediate-file-output-dir", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &intermediateFileOutputDirectory,
"Directory into which the intermediate files are written");
217 args.AddArgument(
"--clip-rect-origin", vtksys::CommandLineArguments::MULTI_ARGUMENT, &clipRectOrigin,
"Origin of the clipping rectangle");
218 args.AddArgument(
"--clip-rect-size", vtksys::CommandLineArguments::MULTI_ARGUMENT, &clipRectSize,
"Size of the clipping rectangle");
219 args.AddArgument(
"--baseline-file", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &inputBaselineFileName,
"Input xml baseline file name with path");
223 std::cerr <<
"Problem parsing arguments" << std::endl;
224 std::cout <<
"Help: " << args.GetHelp() << std::endl;
230 std::cout << args.GetHelp() << std::endl;
236 if (inputMovingSequenceMetafile.empty())
238 std::cerr <<
"input-tracker-sequence-metafile required argument!" << std::endl;
239 std::cout <<
"Help: " << args.GetHelp() << std::endl;
243 if (inputFixedSequenceMetafile.empty())
245 std::cerr <<
"input-video-sequence-metafile required argument!" << std::endl;
246 std::cout <<
"Help: " << args.GetHelp() << std::endl;
253 if (intermediateFileOutputDirectory.empty())
261 vtkSmartPointer<vtkIGSIOTrackedFrameList> movingFrames = vtkSmartPointer<vtkIGSIOTrackedFrameList>::New();
262 vtkSmartPointer<vtkIGSIOTrackedFrameList> fixedFrames = vtkSmartPointer<vtkIGSIOTrackedFrameList>::New();
263 if (!fixedProbeToReferenceTransformNameStr.empty())
265 fixedFrames->SetValidationRequirements(REQUIRE_UNIQUE_TIMESTAMP | REQUIRE_TRACKING_OK);
268 LOG_DEBUG(
"Fixed data type: tracking");
272 fixedFrames->SetValidationRequirements(REQUIRE_UNIQUE_TIMESTAMP);
274 LOG_DEBUG(
"Fixed data type: video");
277 if (!movingProbeToReferenceTransformNameStr.empty())
279 movingFrames->SetValidationRequirements(REQUIRE_UNIQUE_TIMESTAMP | REQUIRE_TRACKING_OK);
282 LOG_DEBUG(
"Moving data type: video");
287 movingFrames->SetValidationRequirements(REQUIRE_UNIQUE_TIMESTAMP);
289 LOG_DEBUG(
"Moving data type: tracking");
293 LOG_DEBUG(
"Read fixed data from " << inputFixedSequenceMetafile);
294 if (vtkIGSIOSequenceIO::Read(inputFixedSequenceMetafile, fixedFrames) !=
PLUS_SUCCESS)
296 LOG_ERROR(
"Failed to read fixed data from sequence metafile: " << inputFixedSequenceMetafile <<
". Exiting...");
299 testTemporalCalibrationObject->
SetFixedFrames(fixedFrames, fixedType);
302 LOG_DEBUG(
"Read moving data from " << inputMovingSequenceMetafile);
303 if (vtkIGSIOSequenceIO::Read(inputMovingSequenceMetafile, movingFrames) !=
PLUS_SUCCESS)
305 LOG_ERROR(
"Failed to read moving data from sequence metafile: " << inputMovingSequenceMetafile <<
". Exiting...");
308 testTemporalCalibrationObject->
SetMovingFrames(movingFrames, movingType);
316 if (clipRectOrigin.size() > 0 || clipRectSize.size() > 0)
318 if (clipRectOrigin.size() != 2 || clipRectSize.size() != 2)
320 LOG_ERROR(
"Invalid clip rectangle origin and/or size");
323 int clipRectOriginIntVec[2] = { clipRectOrigin[0], clipRectOrigin[1] };
324 int clipRectSizeIntVec[2] = { clipRectSize[0], clipRectSize[1] };
333 LOG_ERROR(
"Cannot determine tracker lag, temporal calibration failed");
338 TemporalCalibrationResult calibResult;
341 LOG_ERROR(
"Cannot determine tracker lag, temporal calibration failed");
344 LOG_INFO(
"Tracker lag: " << calibResult.trackerLagSec <<
" sec (>0 if the tracker data lags)");
347 LOG_ERROR(
"Cannot determine calibration error, temporal calibration failed");
350 LOG_INFO(
"Calibration error: " << calibResult.calibrationError);
353 LOG_ERROR(
"Cannot determine max calibration error, temporal calibration failed");
356 LOG_INFO(
"Max calibration error: " << calibResult.maxCalibrationError);
359 std::ostringstream trackerLagOutputFilename;
360 trackerLagOutputFilename << intermediateFileOutputDirectory <<
"/TemporalCalibrationResults.xml" << std::ends;
365 vtkSmartPointer<vtkTable> videoPositionMetric = vtkSmartPointer<vtkTable>::New();
369 vtkSmartPointer<vtkTable> uncalibratedTrackerPositionMetric = vtkSmartPointer<vtkTable>::New();
371 std::string filename = intermediateFileOutputDirectory +
"/MetricPlotUncalibrated.png";
373 std::string xLabel =
"Time [s]";
374 std::string yLabel =
"Position Metric";
375 SaveMetricPlot(filename.c_str(), videoPositionMetric, uncalibratedTrackerPositionMetric, xLabel, yLabel);
378 vtkSmartPointer<vtkTable> calibratedTrackerPositionMetric = vtkSmartPointer<vtkTable>::New();
380 filename = intermediateFileOutputDirectory +
"/MetricPlotCalibrated.png";
381 SaveMetricPlot(filename.c_str(), videoPositionMetric, calibratedTrackerPositionMetric, xLabel, yLabel);
384 vtkSmartPointer<vtkTable> correlationSignal = vtkSmartPointer<vtkTable>::New();
386 vtkSmartPointer<vtkTable> correlationSignalFine = vtkSmartPointer<vtkTable>::New();
388 filename = intermediateFileOutputDirectory +
"/CorrelationSignal.png";
389 xLabel =
"Tracker Offset [s]";
390 yLabel =
"Correlation Value";
391 SaveMetricPlot(filename.c_str(), correlationSignal, correlationSignalFine, xLabel, yLabel);
395 if (!inputBaselineFileName.empty())
397 LOG_INFO(
"Comparing result with baseline...");
398 TemporalCalibrationResult baselineCalibResult;
401 LOG_ERROR(
"Failed to read baseline data file");
405 if (numberOfFailures > 0)
407 LOG_ERROR(
"Number of differences compared to baseline: " << numberOfFailures <<
". Test failed!");
410 LOG_INFO(
"Baseline comparison completed successfully");
413 testTemporalCalibrationObject->Delete();
PlusStatus GetCorrelationSignalFine(vtkTable *correlationSignal)
PlusStatus GetUncalibratedMovingPositionSignal(vtkTable *unCalibratedMovingPositionSignal)
static vtkPlusTemporalCalibrationAlgo * New()
int main(int argc, char **argv)
void SetMaximumMovingLagSec(double maxLagSec)
void SetFixedFrames(vtkIGSIOTrackedFrameList *frameList, FRAME_TYPE frameType)
PlusStatus GetCalibrationError(double &error)
void SetVideoClipRectangle(int *clipRectOriginIntVec, int *clipRectSizeIntVec)
int CompareCalibrationResults(const TemporalCalibrationResult &calibResult, const TemporalCalibrationResult &baselineCalibResult)
void SaveMetricPlot(const char *filename, vtkTable *videoPositionMetric, vtkTable *trackerPositionMetric, std::string &xAxisLabel, std::string &yAxisLabel)
PlusStatus ReadCalibrationResultsFromFile(const std::string &resultSaveFilename, TemporalCalibrationResult &calibResult)
void SetSaveIntermediateImages(bool saveIntermediateImages)
void SetFixedProbeToReferenceTransformName(const std::string &probeToReferenceTransformName)
static vtkPlusConfig * GetInstance()
PlusStatus Update(TEMPORAL_CALIBRATION_ERROR &error)
void SetMovingProbeToReferenceTransformName(const std::string &probeToReferenceTransformName)
std::string GetOutputDirectory()
PlusStatus GetCorrelationSignal(vtkTable *correlationSignal)
PlusStatus GetCalibratedMovingPositionSignal(vtkTable *calibratedMovingPositionSignal)
Computes the time lag between tracking streams or between a tracking and an ultrasound image stream.
void WriteCalibrationResultToFile(const std::string &outputFileName, const TemporalCalibrationResult &calibResult)
void SetMovingFrames(vtkIGSIOTrackedFrameList *frameList, FRAME_TYPE frameType)
void SetSamplingResolutionSec(double samplingResolutionSec)
TEMPORAL_CALIBRATION_ERROR
void SetIntermediateFilesOutputDirectory(const std::string &outputDirectory)
static vtkIGSIOLogger * Instance()
PlusStatus GetMaxCalibrationError(double &maxCalibrationError)
PlusStatus GetMovingLagSec(double &lag)
PlusStatus GetFixedPositionSignal(vtkTable *fixedPositionSignal)