13 #include "PlusConfigure.h" 15 #include "igsioTrackedFrame.h" 17 #include "vtkChartXY.h" 18 #include "vtkContextScene.h" 19 #ifdef PLUS_RENDERING_ENABLED 20 #include "vtkContextView.h" 23 #include "vtkDirectory.h" 24 #include "vtkDoubleArray.h" 27 #include "vtkMatrix4x4.h" 28 #include "vtkPNGWriter.h" 34 #include "vtkRenderWindow.h" 35 #include "vtkRenderer.h" 37 #include "vtkIGSIOSequenceIO.h" 38 #include "vtkIGSIOMetaImageSequenceIO.h" 39 #include "vtkSmartPointer.h" 41 #include "vtkIGSIOTrackedFrameList.h" 42 #include "vtkTransform.h" 43 #include "vtkIGSIOTransformRepository.h" 44 #include "vtkWindowToImageFilter.h" 45 #include "vtkXMLDataElement.h" 46 #include "vtkXMLUtilities.h" 47 #include "vtksys/CommandLineArguments.hxx" 48 #include "vtksys/SystemTools.hxx" 62 while (table->GetNumberOfColumns() > 0)
64 table->RemoveColumn(0);
68 vtkSmartPointer<vtkDoubleArray> arrX = vtkSmartPointer<vtkDoubleArray>::New();
69 table->AddColumn(arrX);
72 vtkSmartPointer<vtkDoubleArray> arrY = vtkSmartPointer<vtkDoubleArray>::New();
73 table->AddColumn(arrY);
76 table->SetNumberOfRows(
x.size());
77 for (
unsigned int i = 0;
i <
x.size(); ++
i)
79 table->SetValue(
i, 0,
x.at(
i));
80 table->SetValue(
i, 1,
y.at(
i));
86 void SaveMetricPlot(
const char* filename, vtkTable* stylusRef, vtkTable* stylusTipRef, vtkTable* stylusTipSpeed, std::string& xAxisLabel,
87 std::string& yAxisLabel)
89 #ifdef PLUS_RENDERING_ENABLED 91 vtkSmartPointer<vtkContextView> view = vtkSmartPointer<vtkContextView>::New();
92 view->GetRenderer()->SetBackground(1.0, 1.0, 1.0);
93 vtkSmartPointer<vtkChartXY> chart = vtkSmartPointer<vtkChartXY>::New();
94 view->GetScene()->AddItem(chart);
97 vtkPlot* StylusRefLine = chart->AddPlot(vtkChart::POINTS);
98 StylusRefLine->SetInputData(stylusRef, 0, 1);
99 StylusRefLine->SetColor(0, 0, 1);
100 StylusRefLine->SetWidth(0.3);
102 vtkPlot* StylusTipRefLine = chart->AddPlot(vtkChart::POINTS);
103 StylusTipRefLine->SetInputData(stylusTipRef, 0, 1);
104 StylusTipRefLine->SetColor(0, 1, 0);
105 StylusTipRefLine->SetWidth(0.3);
107 vtkPlot* StylusTipFromLandmarkLine = chart->AddPlot(vtkChart::LINE);
108 StylusTipFromLandmarkLine->SetInputData(stylusTipSpeed, 0, 1);
109 StylusTipFromLandmarkLine->SetColor(1, 0, 0);
110 StylusTipFromLandmarkLine->SetWidth(1.0);
112 chart->SetShowLegend(
true);
117 vtkSmartPointer<vtkRenderWindow> renderWindow = vtkSmartPointer<vtkRenderWindow>::New();
118 renderWindow->AddRenderer(view->GetRenderer());
119 renderWindow->SetSize(1600, 1200);
120 renderWindow->OffScreenRenderingOn();
122 vtkSmartPointer<vtkWindowToImageFilter> windowToImageFilter = vtkSmartPointer<vtkWindowToImageFilter>::New();
123 windowToImageFilter->SetInput(renderWindow);
124 windowToImageFilter->Update();
126 vtkSmartPointer<vtkPNGWriter> writer = vtkSmartPointer<vtkPNGWriter>::New();
127 writer->SetFileName(filename);
128 writer->SetInputData(windowToImageFilter->GetOutput());
131 LOG_ERROR(
"Function not available when VTK_RENDERING_BACKEND is None!");
137 double signalTimeRangeMin = trackedStylusTipFrames->GetTrackedFrame(0)->GetTimestamp();
138 double signalTimeRangeMax = trackedStylusTipFrames->GetTrackedFrame(trackedStylusTipFrames->GetNumberOfTrackedFrames() - 1)->GetTimestamp();
139 std::deque<double> signalTimestamps;
140 std::deque<double> signalValues;
146 LOG_INFO(
"Range [" << signalTimeRangeMin <<
"-" << signalTimeRangeMax <<
"] " << (signalTimeRangeMax - signalTimeRangeMin) <<
"[s]");
147 double frequency = 1 / (trackedStylusTipFrames->GetTrackedFrame(1)->GetTimestamp() - trackedStylusTipFrames->GetTrackedFrame(0)->GetTimestamp());
148 LOG_INFO(
"Frequency first frames = " <<
frequency <<
" Frequency average frame = " << trackedStylusTipFrames->GetNumberOfTrackedFrames() / (signalTimeRangeMax - signalTimeRangeMin));
150 vtkSmartPointer<vtkPlusReadTrackedSignals> trackerDataMetricExtractor = vtkSmartPointer<vtkPlusReadTrackedSignals>::New();
152 trackerDataMetricExtractor->SetTrackerFrames(trackedStylusTipFrames);
153 trackerDataMetricExtractor->SetSignalTimeRange(signalTimeRangeMin, signalTimeRangeMax);
154 trackerDataMetricExtractor->ReadConfiguration(aConfig);
156 if (trackerDataMetricExtractor->Update() !=
PLUS_SUCCESS)
158 LOG_ERROR(
"Failed to get line positions from video frames");
161 trackerDataMetricExtractor->GetTimestamps(signalTimestamps);
162 trackerDataMetricExtractor->GetSignalStylusTipSpeed(signalValues);
163 vtkSmartPointer<vtkTable> stylusTipSpeedTable = vtkSmartPointer<vtkTable>::New();
165 stylusTipSpeedTable->GetColumn(0)->SetName(
"Time [s]");
166 stylusTipSpeedTable->GetColumn(1)->SetName(
"stylusTipSpeed");
168 trackerDataMetricExtractor->GetSignalStylusRef(signalValues);
169 vtkSmartPointer<vtkTable> stylusRefTable = vtkSmartPointer<vtkTable>::New();
171 stylusRefTable->GetColumn(0)->SetName(
"Time [s]");
172 stylusRefTable->GetColumn(1)->SetName(
"stylusRef");
174 trackerDataMetricExtractor->GetSignalStylusTipRef(signalValues);
175 vtkSmartPointer<vtkTable> stylusTipRefTable = vtkSmartPointer<vtkTable>::New();
177 stylusTipRefTable->GetColumn(0)->SetName(
"Time [s]");
178 stylusTipRefTable->GetColumn(1)->SetName(
"stylusTipRef");
180 if (stylusTipSpeedTable->GetNumberOfColumns() != 2)
182 LOG_ERROR(
"Error in constructing the vtk tables that are to hold fixed signal. Table has " <<
183 stylusTipSpeedTable->GetNumberOfColumns() <<
" columns, but should have two columns");
187 std::string filename = intermediateFileOutputDirectory ;
188 std::string xLabel =
"Time [s]";
189 std::string yLabel =
"Position Metric";
191 #ifdef PLUS_RENDERING_ENABLED 192 SaveMetricPlot(filename.c_str(), stylusRefTable, stylusTipRefTable, stylusTipSpeedTable, xLabel, yLabel);
197 int main(
int argc,
char* argv[])
199 std::string inputConfigFileName;
200 std::string inputBaselineFileName;
201 int verboseLevel = vtkPlusLogger::LOG_LEVEL_UNDEFINED;
202 std::string inputTrackedStylusTipSequence;
203 std::string intermediateFileOutputDirectory;
204 bool plotSignal(
false);
205 vtksys::CommandLineArguments cmdargs;
206 double accumulatedError = 0.0;
207 int succesfulDatasets = 0;
209 cmdargs.Initialize(argc, argv);
210 cmdargs.AddArgument(
"--config-file", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &inputConfigFileName,
"Configuration file name");
211 cmdargs.AddArgument(
"--baseline-file", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &inputBaselineFileName,
"Name of file storing baseline calibration results");
212 cmdargs.AddArgument(
"--verbose", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &verboseLevel,
"Verbose level (1=error only, 2=warning, 3=info, 4=debug, 5=trace)");
213 cmdargs.AddArgument(
"--seq-file", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &inputTrackedStylusTipSequence,
"Input tracker sequence metafile name (or directory) with path");
214 cmdargs.AddArgument(
"--intermediate-file-output-dir", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &intermediateFileOutputDirectory,
"Directory into which the intermediate files are written");
215 cmdargs.AddArgument(
"--plot-signal", vtksys::CommandLineArguments::NO_ARGUMENT, &plotSignal,
"Run test without plotting the signal.");
216 if (!cmdargs.Parse())
218 std::cerr <<
"Problem parsing arguments" << std::endl;
219 std::cout <<
"Help: " << cmdargs.GetHelp() << std::endl;
223 LOG_INFO(
"Initialize");
226 vtkSmartPointer<vtkXMLDataElement> configLandmarkDetection = vtkSmartPointer<vtkXMLDataElement>::Take(vtkXMLUtilities::ReadElementFromFile(inputConfigFileName.c_str()));
227 if (configLandmarkDetection == NULL)
229 LOG_ERROR(
"Unable to read LandmarkDetection configuration from file " << inputConfigFileName.c_str());
233 if (intermediateFileOutputDirectory.empty())
240 vtkSmartPointer<vtkPlusDataCollector> dataCollector = vtkSmartPointer<vtkPlusDataCollector>::New();
241 if (dataCollector->ReadConfiguration(configLandmarkDetection) !=
PLUS_SUCCESS)
243 LOG_ERROR(
"Unable to parse configuration from file " << inputConfigFileName.c_str());
248 LOG_ERROR(
"Data collector was unable to connect to devices!");
253 LOG_ERROR(
"Unable to start data collection!");
258 if (dataCollector->GetDevice(aDevice, std::string(
"TrackerDevice")) !=
PLUS_SUCCESS)
260 LOG_ERROR(
"Unable to locate device by ID: \'TrackerDevice\'");
265 LOG_ERROR(
"Unable to locate channel by ID: \'TrackerStream\'");
270 LOG_ERROR(
"Channel \'" << aChannel->
GetChannelId() <<
"\' is not tracking!");
275 LOG_ERROR(
"Data collector is not tracking!");
279 vtkSmartPointer<vtkIGSIOTransformRepository> transformRepository = vtkSmartPointer<vtkIGSIOTransformRepository>::New();
280 if (transformRepository->ReadConfiguration(configLandmarkDetection) !=
PLUS_SUCCESS)
282 LOG_ERROR(
"Failed to read CoordinateDefinitions!");
285 if (transformRepository->ReadConfiguration(configLandmarkDetection) !=
PLUS_SUCCESS)
287 LOG_ERROR(
"Failed to read CoordinateDefinitions!");
293 if (trackerDevice == NULL)
295 LOG_ERROR(
"Invalid tracker object!");
299 std::string extension = vtksys::SystemTools::GetFilenameExtension(inputTrackedStylusTipSequence);
301 vtkSmartPointer<vtkDirectory> myDir = vtkSmartPointer<vtkDirectory>::New();
303 if (vtkIGSIOMetaImageSequenceIO::CanReadFile(inputTrackedStylusTipSequence))
305 LOG_INFO(
"Only one sequence" << extension);
310 myDir->Open(inputTrackedStylusTipSequence.c_str());
311 numberFiles = myDir->GetNumberOfFiles();
314 for (
int i = 0;
i < numberFiles;
i++)
316 std::string fileString;
317 if (numberFiles == 1)
319 fileString = inputTrackedStylusTipSequence;
323 fileString = inputTrackedStylusTipSequence;
325 fileString += myDir->GetFile(
i);
328 if (vtkIGSIOMetaImageSequenceIO::CanReadFile(fileString))
330 vtkSmartPointer<vtkIGSIOTrackedFrameList> trackedStylusTipFrames = vtkSmartPointer<vtkIGSIOTrackedFrameList>::New();
331 if (!fileString.empty())
333 trackedStylusTipFrames->SetValidationRequirements(REQUIRE_UNIQUE_TIMESTAMP | REQUIRE_TRACKING_OK);
334 LOG_INFO(
"Read stylus tracker data from " << fileString);
335 if (vtkIGSIOSequenceIO::Read(fileString, trackedStylusTipFrames) !=
PLUS_SUCCESS)
337 LOG_ERROR(
"Failed to read stylus data from sequence metafile: " << fileString <<
". Exiting...");
340 trackedStylusTipFrames->Register(NULL);
344 LOG_ERROR(
"Empty file name to read sequence metafile: ");
348 fileString = intermediateFileOutputDirectory;
350 if (numberFiles == 1)
352 fileString += vtksys::SystemTools::GetFilenameWithoutLastExtension(inputTrackedStylusTipSequence);
353 fileString +=
".png";
357 fileString += vtksys::SystemTools::GetFilenameWithoutLastExtension(myDir->GetFile(
i));
358 fileString +=
".png";
367 vtkSmartPointer<vtkPlusPhantomLandmarkRegistrationAlgo> phantomRegistration = vtkSmartPointer<vtkPlusPhantomLandmarkRegistrationAlgo>::New();
368 if (phantomRegistration == NULL)
370 LOG_ERROR(
"Unable to instantiate phantom registration algorithm class!");
373 if (phantomRegistration->ReadConfiguration(configLandmarkDetection) !=
PLUS_SUCCESS)
375 LOG_ERROR(
"Unable to read phantom definition!");
378 int numberOfExpectedLandmarks = phantomRegistration->GetDefinedLandmarks_Phantom()->GetNumberOfPoints();
379 if (numberOfExpectedLandmarks != 8)
381 LOG_ERROR(
"Number of defined landmarks should be 8 instead of " << numberOfExpectedLandmarks <<
"!");
385 vtkSmartPointer<vtkPlusLandmarkDetectionAlgo> landmarkDetection = vtkSmartPointer<vtkPlusLandmarkDetectionAlgo>::New();
386 if (landmarkDetection == NULL)
388 LOG_ERROR(
"Unable to instantiate landmark detection algorithm class!");
391 landmarkDetection->SetMinimumDistanceBetweenLandmarksMm(phantomRegistration->GetMinimunDistanceBetweenTwoLandmarksMm());
392 landmarkDetection->SetAcquisitionRate(1 / (trackedStylusTipFrames->GetTrackedFrame(1)->GetTimestamp() - trackedStylusTipFrames->GetTrackedFrame(0)->GetTimestamp()));
393 if (landmarkDetection->ReadConfiguration(configLandmarkDetection) !=
PLUS_SUCCESS)
395 LOG_ERROR(
"Unable to read Landmark detection configuration!");
400 igsioTransformName stylusTipToReferenceTransformName(phantomRegistration->GetStylusTipCoordinateFrame(), phantomRegistration->GetReferenceCoordinateFrame());
401 igsioTransformName stylusToReferenceTransformName(
"Stylus", phantomRegistration->GetReferenceCoordinateFrame());
402 igsioTransformName stylusTipToStylusTransformName(phantomRegistration->GetStylusTipCoordinateFrame(),
"Stylus");
404 vtkSmartPointer<vtkMatrix4x4> stylusTipToStylusTransform = vtkSmartPointer<vtkMatrix4x4>::New();
405 ToolStatus status(TOOL_INVALID);
406 transformRepository->GetTransform(stylusTipToStylusTransformName, stylusTipToStylusTransform, &status);
407 double landmarkFound[3] = {0, 0, 0};
409 if (status == TOOL_OK)
412 igsioTrackedFrame trackedFrame;
413 vtkSmartPointer<vtkMatrix4x4> stylusToReferenceMatrix = vtkSmartPointer<vtkMatrix4x4>::New();
414 for (
unsigned int j = 0; j < trackedStylusTipFrames->GetNumberOfTrackedFrames(); ++j)
419 if (transformRepository->SetTransforms(*(trackedStylusTipFrames->GetTrackedFrame(j))) !=
PLUS_SUCCESS)
421 LOG_ERROR(
"Failed to update transforms in repository with tracked frame!");
425 status = TOOL_INVALID;
426 if (transformRepository->GetTransform(stylusToReferenceTransformName, stylusToReferenceMatrix, &status) !=
PLUS_SUCCESS || status != TOOL_OK)
431 LOG_INFO(
"There is no available transform for this frame; skip frame " << trackedStylusTipFrames->GetTrackedFrame(j)->GetTimestamp() <<
" [s]")
435 vtkSmartPointer<vtkMatrix4x4> stylusTipToReferenceTransformMatrix = vtkMatrix4x4::New();
436 vtkMatrix4x4::Multiply4x4(stylusToReferenceMatrix, stylusTipToStylusTransform, stylusTipToReferenceTransformMatrix);
437 int newLandmarkDetected = -1;
438 landmarkDetection->InsertNextStylusTipToReferenceTransform(stylusTipToReferenceTransformMatrix, newLandmarkDetected);
439 if (newLandmarkDetected > 0)
441 landmarkDetection->GetDetectedLandmarkPoints_Reference()->GetPoint(landmarkDetection->GetDetectedLandmarkPoints_Reference()->GetNumberOfPoints() - 1, landmarkFound);
443 phantomRegistration->GetRecordedLandmarks_Reference()->InsertPoint(landmarkDetection->GetDetectedLandmarkPoints_Reference()->GetNumberOfPoints() - 1, landmarkFound);
444 phantomRegistration->GetRecordedLandmarks_Reference()->Modified();
445 LOG_INFO(
"\nLandmark found (" << landmarkFound[0] <<
", " << landmarkFound[1] <<
", " << landmarkFound[2] <<
") at " << trackedStylusTipFrames->GetTrackedFrame(j)->GetTimestamp() <<
"[ms]" <<
"\nNumber of landmarks in phantonReg " << phantomRegistration->GetRecordedLandmarks_Reference()->GetNumberOfPoints());
446 vtkPlusLogger::PrintProgressbar((100.0 * newLandmarkDetected - 1) / numberOfExpectedLandmarks);
448 if (newLandmarkDetected == numberOfExpectedLandmarks)
460 LOG_ERROR(
"No valid transform found between stylus to stylus tip!");
463 LOG_INFO(landmarkDetection->GetDetectedLandmarksString());
465 if (phantomRegistration->LandmarkRegister(transformRepository) !=
PLUS_SUCCESS)
467 LOG_ERROR(
"Phantom registration failed!");
471 phantomRegistration->PrintRecordedLandmarks_Phantom();
473 LOG_INFO(
"Registration error = " << phantomRegistration->GetRegistrationErrorMm());
474 accumulatedError += phantomRegistration->GetRegistrationErrorMm();
475 vtkPlusLogger::PrintProgressbar(100);
477 if (numberFiles == 1)
480 if (transformRepository->WriteConfiguration(configLandmarkDetection) !=
PLUS_SUCCESS)
482 LOG_ERROR(
"Failed to write phantom registration result to configuration element!");
486 std::string registrationResultFileName =
"PhantomRegistrationAutoDetectLandmarkTest.xml";
487 vtksys::SystemTools::RemoveFile(registrationResultFileName.c_str());
488 igsioCommon::XML::PrintXML(registrationResultFileName.c_str(), configLandmarkDetection);
492 LOG_ERROR(
"Comparison of calibration data to baseline failed");
493 std::cout <<
"Exit failure!!!" << std::endl;
499 LOG_INFO(
"AccumulatedError = " << accumulatedError <<
" Number of succesful registrations/datasets = " << succesfulDatasets <<
"/" << numberFiles - 2);
500 LOG_INFO(
"Exit success!!!");
509 if (baselineFileName == NULL)
511 LOG_ERROR(
"Unable to read the baseline configuration file - filename is NULL");
515 if (currentResultFileName == NULL)
517 LOG_ERROR(
"Unable to read the current configuration file - filename is NULL");
521 igsioTransformName tnPhantomToPhantomReference(phantomCoordinateFrame, referenceCoordinateFrame);
524 vtkSmartPointer<vtkXMLDataElement> currentRootElem = vtkSmartPointer<vtkXMLDataElement>::Take(
525 vtkXMLUtilities::ReadElementFromFile(currentResultFileName));
526 if (currentRootElem == NULL)
528 LOG_ERROR(
"Unable to read the current configuration file: " << currentResultFileName);
532 vtkSmartPointer<vtkIGSIOTransformRepository> currentTransformRepository = vtkSmartPointer<vtkIGSIOTransformRepository>::New();
533 if (currentTransformRepository->ReadConfiguration(currentRootElem) !=
PLUS_SUCCESS)
535 LOG_ERROR(
"Unable to read the current CoordinateDefinitions from configuration file: " << currentResultFileName);
539 vtkSmartPointer<vtkMatrix4x4> currentMatrix = vtkSmartPointer<vtkMatrix4x4>::New();
540 if (currentTransformRepository->GetTransform(tnPhantomToPhantomReference, currentMatrix) !=
PLUS_SUCCESS)
542 std::string strTransformName;
543 tnPhantomToPhantomReference.GetTransformName(strTransformName);
544 LOG_ERROR(
"Unable to get '" << strTransformName <<
"' coordinate definition from configuration file: " << currentResultFileName);
549 vtkSmartPointer<vtkXMLDataElement> baselineRootElem = vtkSmartPointer<vtkXMLDataElement>::Take(
550 vtkXMLUtilities::ReadElementFromFile(baselineFileName));
551 if (baselineFileName == NULL)
553 LOG_ERROR(
"Unable to read the baseline configuration file: " << baselineFileName);
557 vtkSmartPointer<vtkIGSIOTransformRepository> baselineTransformRepository = vtkSmartPointer<vtkIGSIOTransformRepository>::New();
558 if (baselineTransformRepository->ReadConfiguration(baselineRootElem) !=
PLUS_SUCCESS)
560 LOG_ERROR(
"Unable to read the baseline CoordinateDefinitions from configuration file: " << baselineFileName);
564 vtkSmartPointer<vtkMatrix4x4> baselineMatrix = vtkSmartPointer<vtkMatrix4x4>::New();
565 if (baselineTransformRepository->GetTransform(tnPhantomToPhantomReference, baselineMatrix) !=
PLUS_SUCCESS)
567 std::string strTransformName;
568 tnPhantomToPhantomReference.GetTransformName(strTransformName);
569 LOG_ERROR(
"Unable to get '" << strTransformName <<
"' coordinate definition from configuration file: " << baselineFileName);
574 double posDiff = igsioMath::GetPositionDifference(currentMatrix, baselineMatrix);
575 double orientDiff = igsioMath::GetOrientationDifference(currentMatrix, baselineMatrix);
579 LOG_ERROR(
"Transform mismatch (position difference: " << posDiff <<
" orientation difference: " << orientDiff);
582 LOG_INFO(
"Transform difference is acceptable (position difference: " << posDiff <<
" orientation difference: " << orientDiff);
Abstract interface for tracker and video devices.
int main(int argc, char *argv[])
void SaveMetricPlot(const char *filename, vtkTable *stylusRef, vtkTable *stylusTipRef, vtkTable *stylusTipSpeed, std::string &xAxisLabel, std::string &yAxisLabel)
PlusStatus ConstructSignalPlot(vtkIGSIOTrackedFrameList *trackedStylusTipFrames, std::string intermediateFileOutputDirectory, vtkXMLDataElement *aConfig)
bool GetTrackingDataAvailable()
const double ERROR_THRESHOLD_DEG
const double ERROR_THRESHOLD_MM
PlusStatus CompareRegistrationResultsWithBaseline(const char *baselineFileName, const char *currentResultFileName, const char *phantomCoordinateFrame, const char *referenceCoordinateFrame)
static vtkPlusConfig * GetInstance()
PlusStatus ConstructTableSignal(std::deque< double > &x, std::deque< double > &y, vtkTable *table)
PlusStatus GetOutputChannelByName(vtkPlusChannel *&aChannel, const char *aChannelId)
std::string GetOutputDirectory()
virtual char * GetChannelId()
Class for providing VTK video input interface from sequence fileAttributes:
static vtkIGSIOLogger * Instance()
Contains an optional timestamped circular buffer containing the video images and a number of timestam...
Direction vectors of rods y
void SetDeviceSetConfigurationData(vtkXMLDataElement *deviceSetConfigurationData)