diff --git a/Plugins/org.mitk.gui.qt.igt.app.hummelprotocolmeasurements/src/internal/QmitkIGTTrackingDataEvaluationView.cpp b/Plugins/org.mitk.gui.qt.igt.app.hummelprotocolmeasurements/src/internal/QmitkIGTTrackingDataEvaluationView.cpp index 37a99df5ab..3005cab9a0 100644 --- a/Plugins/org.mitk.gui.qt.igt.app.hummelprotocolmeasurements/src/internal/QmitkIGTTrackingDataEvaluationView.cpp +++ b/Plugins/org.mitk.gui.qt.igt.app.hummelprotocolmeasurements/src/internal/QmitkIGTTrackingDataEvaluationView.cpp @@ -1,1164 +1,1169 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include // Blueberry #include #include // Qmitk #include "QmitkIGTTrackingDataEvaluationView.h" // Qt #include #include #include // MITK #include "mitkNavigationDataCSVSequentialPlayer.h" #include #include #include #include #include //ITK #include //VNL #include //vtk headers #include #include #include const std::string QmitkIGTTrackingDataEvaluationView::VIEW_ID = "org.mitk.views.igttrackingdataevaluation"; QmitkIGTTrackingDataEvaluationView::QmitkIGTTrackingDataEvaluationView() : QmitkFunctionality() , m_Controls(nullptr) , m_MultiWidget(nullptr) , m_scalingfactor(1) { m_CSVtoXMLInputFilenameVector = std::vector(); m_CSVtoXMLOutputFilenameVector = std::vector(); } QmitkIGTTrackingDataEvaluationView::~QmitkIGTTrackingDataEvaluationView() { } void QmitkIGTTrackingDataEvaluationView::CreateQtPartControl(QWidget *parent) { // build up qt view, unless already done if (!m_Controls) { // create GUI widgets from the Qt Designer's .ui file m_Controls = new Ui::QmitkIGTTrackingDataEvaluationViewControls; m_Controls->setupUi(parent); connect(m_Controls->m_LoadInputFileList, SIGNAL(clicked()), this, SLOT(OnLoadFileList())); connect(m_Controls->m_StartEvaluation, SIGNAL(clicked()), this, SLOT(OnEvaluateData())); connect(m_Controls->m_AddToCurrentList, SIGNAL(clicked()), this, SLOT(OnAddToCurrentList())); connect(m_Controls->m_GeneratePointSetOfMeanPositions, SIGNAL(clicked()), this, SLOT(OnGeneratePointSet())); connect(m_Controls->m_GenerateRotationLines, SIGNAL(clicked()), this, SLOT(OnGenerateRotationLines())); connect(m_Controls->m_GeneratePointSet, SIGNAL(clicked()), this, SLOT(OnGenerateGroundTruthPointSet())); connect(m_Controls->m_Convert, SIGNAL(clicked()), this, SLOT(OnConvertCSVtoXMLFile())); connect(m_Controls->m_loadCSVtoXMLInputList, SIGNAL(clicked()), this, SLOT(OnCSVtoXMLLoadInputList())); connect(m_Controls->m_loadCSVtoXMLOutputList, SIGNAL(clicked()), this, SLOT(OnCSVtoXMLLoadOutputList())); connect(m_Controls->m_OrientationCalculationGenerateReference, SIGNAL(clicked()), this, SLOT(OnOrientationCalculation_CalcRef())); connect(m_Controls->m_OrientationCalculationWriteOrientationsToFile, SIGNAL(clicked()), this, SLOT(OnOrientationCalculation_CalcOrientandWriteToFile())); connect(m_Controls->m_GeneratePointSetsOfSinglePositions, SIGNAL(clicked()), this, SLOT(OnGeneratePointSetsOfSinglePositions())); connect(m_Controls->m_StartEvaluationAll, SIGNAL(clicked()), this, SLOT(OnEvaluateDataAll())); connect(m_Controls->m_GridMatching, SIGNAL(clicked()), this, SLOT(OnPerfomGridMatching())); connect(m_Controls->m_ComputeRotation, SIGNAL(clicked()), this, SLOT(OnComputeRotation())); //initialize data storage combo boxes m_Controls->m_ReferencePointSetComboBox->SetDataStorage(this->GetDataStorage()); m_Controls->m_ReferencePointSetComboBox->SetAutoSelectNewItems(true); m_Controls->m_ReferencePointSetComboBox->SetPredicate(mitk::NodePredicateDataType::New("PointSet")); m_Controls->m_MeasurementPointSetComboBox->SetDataStorage(this->GetDataStorage()); m_Controls->m_MeasurementPointSetComboBox->SetAutoSelectNewItems(true); m_Controls->m_MeasurementPointSetComboBox->SetPredicate(mitk::NodePredicateDataType::New("PointSet")); } } void QmitkIGTTrackingDataEvaluationView::OnComputeRotation() { //Get all data from UI auto EvaluationDataCollection = GetAllDataFromUIList(); //Compute mean Quaternions auto OrientationVector = GetMeanOrientationsOfAllData(EvaluationDataCollection); //Compute Rotations itk::Vector rotationVec; //adapt for Aurora 5D tools: [0,0,1000] rotationVec[0] = m_Controls->m_rotVecX->value(); //X rotationVec[1] = m_Controls->m_rotVecY->value(); //Y rotationVec[2] = m_Controls->m_rotVecZ->value(); //Z std::vector allOrientationErrors; for (std::vector::size_type i = 0; i < OrientationVector.size() - 1; ++i) { double AngleBetweenTwoQuaternions = mitk::StaticIGTHelperFunctions::GetAngleBetweenTwoQuaterions(OrientationVector.at(i), OrientationVector.at(i+1), rotationVec); double AngularError = fabs(AngleBetweenTwoQuaternions - 11.25); std::stringstream description; description << "Rotation Error ROT" << (i + 1) << " / ROT" << (i + 2); allOrientationErrors.push_back({ AngularError, description.str() }); MITK_INFO << description.str() << ": " << AngularError; } //compute statistics std::vector orientationErrorStatistics; orientationErrorStatistics = mitk::HummelProtocolEvaluation::ComputeStatistics(allOrientationErrors); MITK_INFO << "## Rotation error statistics: ##"; for (auto stat : orientationErrorStatistics) { MITK_INFO << stat.description << ": " << stat.distanceError; } //write results to file allOrientationErrors.insert(allOrientationErrors.end(), orientationErrorStatistics.begin(), orientationErrorStatistics.end()); allOrientationErrors.push_back({rotationVec[0],"Rot Vector [x]"}); allOrientationErrors.push_back({rotationVec[1], "Rot Vector [y]"}); allOrientationErrors.push_back({rotationVec[2], "Rot Vector [z]"}); std::stringstream filenameOrientationStat; filenameOrientationStat << std::string(m_Controls->m_OutputFilename->text().toUtf8()).c_str() << ".orientationStatistics.csv"; MITK_INFO << "Writing output to file " << filenameOrientationStat.str(); writeToFile(filenameOrientationStat.str(), allOrientationErrors); } void QmitkIGTTrackingDataEvaluationView::OnPerfomGridMatching() { mitk::PointSet::Pointer reference = dynamic_cast(m_Controls->m_ReferencePointSetComboBox->GetSelectedNode()->GetData()); mitk::PointSet::Pointer measurement = dynamic_cast(m_Controls->m_MeasurementPointSetComboBox->GetSelectedNode()->GetData()); //convert point sets to vtk poly data vtkSmartPointer sourcePoints = vtkSmartPointer::New(); vtkSmartPointer targetPoints = vtkSmartPointer::New(); for (int i = 0; iGetSize(); i++) { double point[3] = { reference->GetPoint(i)[0], reference->GetPoint(i)[1], reference->GetPoint(i)[2] }; sourcePoints->InsertNextPoint(point); double point_targets[3] = { measurement->GetPoint(i)[0], measurement->GetPoint(i)[1], measurement->GetPoint(i)[2] }; targetPoints->InsertNextPoint(point_targets); } //compute transform vtkSmartPointer transform = vtkSmartPointer::New(); transform->SetSourceLandmarks(sourcePoints); transform->SetTargetLandmarks(targetPoints); transform->SetModeToRigidBody(); transform->Modified(); transform->Update(); //compute FRE of transform double FRE = mitk::StaticIGTHelperFunctions::ComputeFRE(reference, measurement, transform); MITK_INFO << "FRE after grid matching: " + QString::number(FRE) + " mm"; //convert from vtk to itk data types itk::Matrix rotationFloat = itk::Matrix(); itk::Vector translationFloat = itk::Vector(); itk::Matrix rotationDouble = itk::Matrix(); itk::Vector translationDouble = itk::Vector(); vtkSmartPointer m = transform->GetMatrix(); for (int k = 0; k<3; k++) for (int l = 0; l<3; l++) { rotationFloat[k][l] = m->GetElement(k, l); rotationDouble[k][l] = m->GetElement(k, l); } for (int k = 0; k<3; k++) { translationFloat[k] = m->GetElement(k, 3); translationDouble[k] = m->GetElement(k, 3); } //create affine transform 3D mitk::AffineTransform3D::Pointer mitkTransform = mitk::AffineTransform3D::New(); mitkTransform->SetMatrix(rotationDouble); mitkTransform->SetOffset(translationDouble); mitk::NavigationData::Pointer transformNavigationData = mitk::NavigationData::New(mitkTransform); m_Controls->m_ReferencePointSetComboBox->GetSelectedNode()->GetData()->GetGeometry()->SetIndexToWorldTransform(mitkTransform); m_Controls->m_ReferencePointSetComboBox->GetSelectedNode()->GetData()->GetGeometry()->Modified(); //write to file std::stringstream filename; filename << std::string(m_Controls->m_OutputFilename->text().toUtf8()).c_str() << ".GridMatchingResult.csv"; MITK_INFO << "Writing output to file " << filename.str(); std::vector FRE_Error; FRE_Error.push_back({ FRE, "FRE after grid matching [mm]" }); writeToFile(filename.str(), FRE_Error); } void QmitkIGTTrackingDataEvaluationView::OnOrientationCalculation_CalcRef() { if (m_FilenameVector.size() != 3) { MessageBox("Need exactly three points as reference, aborting!"); return; } //start loop and iterate through all files of list for (std::size_t i = 0; i < m_FilenameVector.size(); ++i) { //create navigation data player mitk::NavigationDataCSVSequentialPlayer::Pointer myPlayer = ConstructNewNavigationDataPlayer(); myPlayer->SetFiletype(mitk::NavigationDataCSVSequentialPlayer::ManualLoggingCSV); myPlayer->SetFileName(m_FilenameVector[i]); //check if the stream is valid and skip file if not //create evaluation filter mitk::NavigationDataEvaluationFilter::Pointer myEvaluationFilter = mitk::NavigationDataEvaluationFilter::New(); //connect pipeline for (unsigned int j = 0; j < myPlayer->GetNumberOfOutputs(); ++j) myEvaluationFilter->SetInput(j, myPlayer->GetOutput(j)); //update pipline until number of samples is reached for (int j = 0; j < m_Controls->m_NumberOfSamples->value(); ++j) myEvaluationFilter->Update(); //store mean position as reference switch (i) { case 0: m_RefPoint1 = myEvaluationFilter->GetPositionMean(0); break; case 1: m_RefPoint2 = myEvaluationFilter->GetPositionMean(0); break; case 2: m_RefPoint3 = myEvaluationFilter->GetPositionMean(0); break; } } MessageBox("Created Reference!"); } void QmitkIGTTrackingDataEvaluationView::OnOrientationCalculation_CalcOrientandWriteToFile() { //start loop and iterate through all files of list for (std::size_t i = 0; i < m_FilenameVector.size(); ++i) { //create navigation data player mitk::NavigationDataCSVSequentialPlayer::Pointer myPlayer = ConstructNewNavigationDataPlayer(); myPlayer->SetFiletype(mitk::NavigationDataCSVSequentialPlayer::ManualLoggingCSV); myPlayer->SetFileName(m_FilenameVector.at(i)); //open file header QString outputname = QString(m_FilenameVector.at(i).c_str()) + "_orientationFile.csv"; m_CurrentWriteFile.open(outputname.toStdString().c_str(), std::ios::out); if (m_CurrentWriteFile.bad()) { MessageBox("Error: Can't open output file!"); return; } //write header to file m_CurrentWriteFile << "Nr;Calypso_Time;Valid_Reference;MeasureTool_Measurement-Tool[x];MeasureTool_Measurement-Tool[y];MeasureTool_Measurement-Tool[z];MeasureTool_Measurement-Tool[qx];MeasureTool_Measurement-Tool[qy];MeasureTool_Measurement-Tool[qz];MeasureTool_Measurement-Tool[qr]\n"; //update pipeline until number of samples is reached int step = 0; mitk::Point3D point1, point2, point3; mitk::Quaternion current_orientation; for (int j = 0; !myPlayer->IsAtEnd(); j++) { myPlayer->Update(); mitk::NavigationData::Pointer currentNavData = myPlayer->GetOutput(0); switch (step) { case 0: step++; point1 = currentNavData->GetPosition(); break; case 1: step++; point2 = currentNavData->GetPosition(); break; case 2: step = 0; point3 = currentNavData->GetPosition(); //compute transform from reference to current points if (point1[0] == 0 && point1[1] == 0 && point1[2] == 0 && point2[0] == 0 && point2[1] == 0 && point2[2] == 0 && point3[0] == 0 && point3[1] == 0 && point3[2] == 0 ) current_orientation.fill(0); else { vtkSmartPointer transform = vtkSmartPointer::New(); vtkSmartPointer sourcePoints = vtkSmartPointer::New(); double sourcepoint1[3] = { point1[0], point1[1], point1[2] }; double sourcepoint2[3] = { point2[0], point2[1], point2[2] }; double sourcepoint3[3] = { point3[0], point3[1], point3[2] }; sourcePoints->InsertNextPoint(sourcepoint1); sourcePoints->InsertNextPoint(sourcepoint2); sourcePoints->InsertNextPoint(sourcepoint3); vtkSmartPointer targetPoints = vtkSmartPointer::New(); double targetpoint1[3] = { m_RefPoint1[0], m_RefPoint1[1], m_RefPoint1[2] }; double targetpoint2[3] = { m_RefPoint2[0], m_RefPoint2[1], m_RefPoint2[2] }; double targetpoint3[3] = { m_RefPoint3[0], m_RefPoint3[1], m_RefPoint3[2] }; targetPoints->InsertNextPoint(targetpoint1); targetPoints->InsertNextPoint(targetpoint2); targetPoints->InsertNextPoint(targetpoint3); transform->SetSourceLandmarks(sourcePoints); transform->SetTargetLandmarks(targetPoints); transform->Modified(); transform->Update(); mitk::Transform::Pointer newTransform = mitk::Transform::New(); newTransform->SetMatrix(transform->GetMatrix()); current_orientation = newTransform->GetOrientation(); //add pointset with the three positions if ((j > 15) && (j < 18)) { mitk::DataNode::Pointer newNode = mitk::DataNode::New(); mitk::PointSet::Pointer newPointSet = mitk::PointSet::New(); newPointSet->InsertPoint(0, point1); newPointSet->InsertPoint(1, point2); newPointSet->InsertPoint(2, point3); QString name = QString(m_FilenameVector.at(i).c_str()); newNode->SetName(name.toStdString().c_str()); newNode->SetData(newPointSet); newNode->SetFloatProperty("pointsize", 0.1); this->GetDataStorage()->Add(newNode); } } break; } m_CurrentWriteFile << i << ";"; m_CurrentWriteFile << currentNavData->GetTimeStamp() << ";"; //IMPORTANT: change to GetIGTTimeStamp in new version! m_CurrentWriteFile << "true;"; m_CurrentWriteFile << currentNavData->GetPosition()[0] << ";"; m_CurrentWriteFile << currentNavData->GetPosition()[1] << ";"; m_CurrentWriteFile << currentNavData->GetPosition()[2] << ";"; m_CurrentWriteFile << current_orientation.x() << ";"; m_CurrentWriteFile << current_orientation.y() << ";"; m_CurrentWriteFile << current_orientation.z() << ";"; m_CurrentWriteFile << current_orientation.r() << ";"; m_CurrentWriteFile << "\n"; } //close output file m_CurrentWriteFile.close(); } MessageBox("Finished!"); } void QmitkIGTTrackingDataEvaluationView::MultiWidgetAvailable(QmitkAbstractMultiWidget &multiWidget) { m_MultiWidget = dynamic_cast(&multiWidget); } void QmitkIGTTrackingDataEvaluationView::MultiWidgetNotAvailable() { m_MultiWidget = nullptr; } void QmitkIGTTrackingDataEvaluationView::OnAddToCurrentList() { //read in files QStringList files = QFileDialog::getOpenFileNames(nullptr, "Select one or more files to open", "/", "CSV (*.csv)"); if (files.isEmpty()) return; for (int i = 0; i < files.size(); i++) { std::string tmp = files.at(i).toStdString().c_str(); m_FilenameVector.push_back(tmp); } //fill list at GUI m_Controls->m_FileList->clear(); for (unsigned int i = 0; i < m_FilenameVector.size(); i++) { new QListWidgetItem(tr(m_FilenameVector.at(i).c_str()), m_Controls->m_FileList); } } void QmitkIGTTrackingDataEvaluationView::OnLoadFileList() { m_FilenameVector = std::vector(); m_FilenameVector.clear(); OnAddToCurrentList(); } void QmitkIGTTrackingDataEvaluationView::OnEvaluateDataAll() { std::vector results5cm, results15cm, results30cm, resultsAccum; mitk::HummelProtocolEvaluation::HummelProtocolMeasurementVolume volume; if (m_Controls->m_standardVolume->isChecked()) { volume = mitk::HummelProtocolEvaluation::standard; mitk::HummelProtocolEvaluation::Evaluate5cmDistances(m_PointSetMeanPositions, volume, results5cm); mitk::HummelProtocolEvaluation::Evaluate15cmDistances(m_PointSetMeanPositions, volume, results15cm); mitk::HummelProtocolEvaluation::Evaluate30cmDistances(m_PointSetMeanPositions, volume, results30cm); mitk::HummelProtocolEvaluation::EvaluateAccumulatedDistances(m_PointSetMeanPositions, volume, resultsAccum); } else if (m_Controls->m_smallVolume->isChecked()) { volume = mitk::HummelProtocolEvaluation::small; mitk::HummelProtocolEvaluation::Evaluate5cmDistances(m_PointSetMeanPositions, volume, results5cm); } else if (m_Controls->m_mediumVolume->isChecked()) { volume = mitk::HummelProtocolEvaluation::medium; mitk::HummelProtocolEvaluation::Evaluate5cmDistances(m_PointSetMeanPositions, volume, results5cm); } + else if (m_Controls->m_widespreadVolume->isChecked()) + { + volume = mitk::HummelProtocolEvaluation::widespread; + mitk::HummelProtocolEvaluation::Evaluate15cmDistances(m_PointSetMeanPositions, volume, results15cm); + } //write results to file std::stringstream filename5cm; filename5cm << std::string(m_Controls->m_OutputFilename->text().toUtf8()).c_str() << ".results5cm.csv"; MITK_INFO << "Writing output to file " << filename5cm.str(); writeToFile(filename5cm.str(), results5cm); std::stringstream filename15cm; filename15cm << std::string(m_Controls->m_OutputFilename->text().toUtf8()).c_str() << ".results15cm.csv"; MITK_INFO << "Writing output to file " << filename15cm.str(); writeToFile(filename15cm.str(), results15cm); std::stringstream filename30cm; filename30cm << std::string(m_Controls->m_OutputFilename->text().toUtf8()).c_str() << ".results30cm.csv"; MITK_INFO << "Writing output to file " << filename30cm.str(); writeToFile(filename30cm.str(), results30cm); std::stringstream filenameAccum; filenameAccum << std::string(m_Controls->m_OutputFilename->text().toUtf8()).c_str() << ".resultsAccumDist.csv"; MITK_INFO << "Writing output to file " << filenameAccum.str(); writeToFile(filenameAccum.str(), resultsAccum); } void QmitkIGTTrackingDataEvaluationView::OnEvaluateData() { //open output file m_CurrentWriteFile.open(std::string(m_Controls->m_OutputFilename->text().toUtf8()).c_str(), std::ios::out); if (m_CurrentWriteFile.bad()) { MessageBox("Error: Can't open output file!"); return; } std::vector jitterValues; //write output file header WriteHeader(); //start loop and iterate through all files of list for (std::size_t i = 0; i < m_FilenameVector.size(); ++i) { //create navigation data player mitk::NavigationDataCSVSequentialPlayer::Pointer myPlayer = ConstructNewNavigationDataPlayer(); myPlayer->SetFiletype(mitk::NavigationDataCSVSequentialPlayer::ManualLoggingCSV); myPlayer->SetFileName(m_FilenameVector.at(i)); //create evaluation filter mitk::NavigationDataEvaluationFilter::Pointer myEvaluationFilter = mitk::NavigationDataEvaluationFilter::New(); //connect pipeline - for (unsigned int j = 0; j < myPlayer->GetNumberOfOutputs(); ++i) { myEvaluationFilter->SetInput(j, myPlayer->GetOutput(j)); } + for (unsigned int j = 0; j < myPlayer->GetNumberOfOutputs(); ++j) { myEvaluationFilter->SetInput(j, myPlayer->GetOutput(j)); } if (myPlayer->GetNumberOfSnapshots() < m_Controls->m_NumberOfSamples->value()) { MITK_WARN << "Number of snapshots (" << myPlayer->GetNumberOfSnapshots() << ") smaller than number of samples to evaluate (" << m_Controls->m_NumberOfSamples->value() << ") ! Cannot proceed!"; return; } //update pipline until number of samples is reached for (int j = 0; j < m_Controls->m_NumberOfSamples->value(); j++) myEvaluationFilter->Update(); //store all jitter values in separate vector for statistics jitterValues.push_back({ myEvaluationFilter->GetPositionErrorRMS(0), "RMS" }); //write result to output file WriteDataSet(myEvaluationFilter, m_FilenameVector.at(i)); } //close output file for single data m_CurrentWriteFile.close(); //compute statistics std::vector jitterStatistics = mitk::HummelProtocolEvaluation::ComputeStatistics(jitterValues); MITK_INFO << "## Jitter (RMS) statistics: ##"; for (auto jitterStat : jitterStatistics) {MITK_INFO << jitterStat.description << ": " << jitterStat.distanceError;} //write statistic results to separate file std::stringstream filenameJitterStat; filenameJitterStat << std::string(m_Controls->m_OutputFilename->text().toUtf8()).c_str() << ".resultsJitterStatistics.csv"; MITK_INFO << "Writing output to file " << filenameJitterStat.str(); writeToFile(filenameJitterStat.str(), jitterStatistics); //calculate angles if option is on if (m_Controls->m_settingDifferenceAngles->isChecked() || m_Controls->m_DifferencesSLERP->isChecked()) CalculateDifferenceAngles(); MessageBox("Finished!"); } void QmitkIGTTrackingDataEvaluationView::OnGeneratePointSetsOfSinglePositions() { m_scalingfactor = m_Controls->m_ScalingFactor->value(); //start loop and iterate through all files of list for (std::size_t i = 0; i < m_FilenameVector.size(); ++i) { //create point set for this file mitk::PointSet::Pointer thisPointSet = mitk::PointSet::New(); //create navigation data player mitk::NavigationDataCSVSequentialPlayer::Pointer myPlayer = ConstructNewNavigationDataPlayer(); myPlayer->SetFiletype(mitk::NavigationDataCSVSequentialPlayer::ManualLoggingCSV); myPlayer->SetFileName(m_FilenameVector.at(i)); //update pipline until number of samlples is reached and store every single point for (int j = 0; j < m_Controls->m_NumberOfSamples->value(); j++) { myPlayer->Update(); mitk::Point3D thisPoint = myPlayer->GetOutput()->GetPosition(); thisPoint[0] *= m_scalingfactor; thisPoint[1] *= m_scalingfactor; thisPoint[2] *= m_scalingfactor; thisPointSet->InsertPoint(j, thisPoint); } //add point set to data storage mitk::DataNode::Pointer newNode = mitk::DataNode::New(); QString name = this->m_Controls->m_prefix->text() + QString("PointSet_of_All_Positions_") + QString::number(i); newNode->SetName(name.toStdString()); newNode->SetData(thisPointSet); this->GetDataStorage()->Add(newNode); } } void QmitkIGTTrackingDataEvaluationView::OnGeneratePointSet() { m_scalingfactor = m_Controls->m_ScalingFactor->value(); mitk::PointSet::Pointer generatedPointSet = mitk::PointSet::New(); //start loop and iterate through all files of list for (std::size_t i = 0; i < m_FilenameVector.size(); ++i) { //create navigation data player mitk::NavigationDataCSVSequentialPlayer::Pointer myPlayer = ConstructNewNavigationDataPlayer(); myPlayer->SetFiletype(mitk::NavigationDataCSVSequentialPlayer::ManualLoggingCSV); myPlayer->SetFileName(m_FilenameVector.at(i)); //create evaluation filter mitk::NavigationDataEvaluationFilter::Pointer myEvaluationFilter = mitk::NavigationDataEvaluationFilter::New(); //connect pipeline for (unsigned int j = 0; j < myPlayer->GetNumberOfOutputs(); ++j) { myEvaluationFilter->SetInput(j, myPlayer->GetOutput(j)); } //update pipline until number of samlples is reached for (int j = 0; j < m_Controls->m_NumberOfSamples->value(); ++j) { myEvaluationFilter->Update(); } //add mean position to point set mitk::Point3D meanPos = myEvaluationFilter->GetPositionMean(0); if (m_scalingfactor != 1) { meanPos[0] *= m_scalingfactor; meanPos[1] *= m_scalingfactor; meanPos[2] *= m_scalingfactor; } generatedPointSet->InsertPoint(i, meanPos); } //add point set to data storage mitk::DataNode::Pointer newNode = mitk::DataNode::New(); QString name = this->m_Controls->m_prefix->text() + "PointSet_of_Mean_Positions"; newNode->SetName(name.toStdString()); newNode->SetData(generatedPointSet); newNode->SetFloatProperty("pointsize", 5); this->GetDataStorage()->Add(newNode); m_PointSetMeanPositions = generatedPointSet; } void QmitkIGTTrackingDataEvaluationView::OnGenerateRotationLines() { m_scalingfactor = m_Controls->m_ScalingFactor->value(); //start loop and iterate through all files of list for (std::size_t i = 0; i < m_FilenameVector.size(); ++i) { //create navigation data player mitk::NavigationDataCSVSequentialPlayer::Pointer myPlayer = ConstructNewNavigationDataPlayer(); myPlayer->SetFiletype(mitk::NavigationDataCSVSequentialPlayer::ManualLoggingCSV); myPlayer->SetFileName(m_FilenameVector.at(i)); //create evaluation filter mitk::NavigationDataEvaluationFilter::Pointer myEvaluationFilter = mitk::NavigationDataEvaluationFilter::New(); //connect pipeline for (unsigned int j = 0; j < myPlayer->GetNumberOfOutputs(); ++j) { myEvaluationFilter->SetInput(j, myPlayer->GetOutput(j)); } //update pipline until number of samlples is reached for (int j = 0; j < m_Controls->m_NumberOfSamples->value(); ++j) myEvaluationFilter->Update(); //create line from mean pos to a second point which lies along the sensor (1,0,0 in tool coordinates for aurora) mitk::Point3D meanPos = myEvaluationFilter->GetPositionMean(0); if (m_scalingfactor != 1) { meanPos[0] *= m_scalingfactor; meanPos[1] *= m_scalingfactor; meanPos[2] *= m_scalingfactor; } mitk::Point3D secondPoint; mitk::Point3D thirdPoint; mitk::Point3D fourthPoint; mitk::FillVector3D(secondPoint, 2, 0, 0); //X vnl_vector secondPointTransformed = myEvaluationFilter->GetQuaternionMean(0).rotation_matrix_transpose().transpose() * secondPoint.GetVnlVector() + meanPos.GetVnlVector(); mitk::Point3D secondPointTransformedMITK; mitk::FillVector3D(secondPointTransformedMITK, secondPointTransformed[0], secondPointTransformed[1], secondPointTransformed[2]); mitk::FillVector3D(thirdPoint, 0, 4, 0); //Y vnl_vector thirdPointTransformed = myEvaluationFilter->GetQuaternionMean(0).rotation_matrix_transpose().transpose() * thirdPoint.GetVnlVector() + meanPos.GetVnlVector(); mitk::Point3D thirdPointTransformedMITK; mitk::FillVector3D(thirdPointTransformedMITK, thirdPointTransformed[0], thirdPointTransformed[1], thirdPointTransformed[2]); mitk::FillVector3D(fourthPoint, 0, 0, 6); //Z vnl_vector fourthPointTransformed = myEvaluationFilter->GetQuaternionMean(0).rotation_matrix_transpose().transpose() * fourthPoint.GetVnlVector() + meanPos.GetVnlVector(); mitk::Point3D fourthPointTransformedMITK; mitk::FillVector3D(fourthPointTransformedMITK, fourthPointTransformed[0], fourthPointTransformed[1], fourthPointTransformed[2]); mitk::PointSet::Pointer rotationLine = mitk::PointSet::New(); rotationLine->InsertPoint(0, secondPointTransformedMITK); rotationLine->InsertPoint(1, meanPos); rotationLine->InsertPoint(2, thirdPointTransformedMITK); rotationLine->InsertPoint(3, meanPos); rotationLine->InsertPoint(4, fourthPointTransformedMITK); mitk::DataNode::Pointer newNode = mitk::DataNode::New(); QString nodeName = this->m_Controls->m_prefix->text() + "RotationLineNumber" + QString::number(i); newNode->SetName(nodeName.toStdString()); newNode->SetData(rotationLine); newNode->SetBoolProperty("show contour", true); newNode->SetFloatProperty("pointsize", 0.5); this->GetDataStorage()->Add(newNode); } } void QmitkIGTTrackingDataEvaluationView::OnGenerateGroundTruthPointSet() { mitk::PointSet::Pointer generatedPointSet = mitk::PointSet::New(); int currentPointID = 0; mitk::Point3D currentPoint; mitk::FillVector3D(currentPoint, 0, 0, 0); for (int i = 0; i < m_Controls->m_PointNumber2->value(); i++) { for (int j = 0; j < m_Controls->m_PointNumber1->value(); j++) { generatedPointSet->InsertPoint(currentPointID, currentPoint); currentPointID++; currentPoint[1] += m_Controls->m_PointDistance->value(); } currentPoint[1] = 0; currentPoint[2] += m_Controls->m_PointDistance->value(); } mitk::DataNode::Pointer newNode = mitk::DataNode::New(); QString nodeName = "GroundTruthPointSet_" + QString::number(m_Controls->m_PointNumber1->value()) + "x" + QString::number(m_Controls->m_PointNumber2->value()) + "_(" + QString::number(m_Controls->m_PointDistance->value()) + "mm)"; newNode->SetName(nodeName.toStdString()); newNode->SetData(generatedPointSet); newNode->SetFloatProperty("pointsize", 5); this->GetDataStorage()->Add(newNode); } void QmitkIGTTrackingDataEvaluationView::OnConvertCSVtoXMLFile() { if (m_Controls->m_ConvertSingleFile->isChecked()) { //convert one file int lines = ConvertOneFile(this->m_Controls->m_InputCSV->text().toStdString(), this->m_Controls->m_OutputXML->text().toStdString()); QString result = "Converted one file with" + QString::number(lines) + " data sets"; MessageBox(result.toStdString()); } else //converte file list { if (m_CSVtoXMLInputFilenameVector.empty() || m_CSVtoXMLOutputFilenameVector.empty()) { MessageBox("Error: one list is not loaded!"); return; } else if (m_CSVtoXMLInputFilenameVector.size() != m_CSVtoXMLOutputFilenameVector.size()) { MessageBox("Error: lists do not have the same number of files!"); return; } for (std::size_t i = 0; i < m_CSVtoXMLInputFilenameVector.size(); ++i) { ConvertOneFile(m_CSVtoXMLInputFilenameVector.at(i), m_CSVtoXMLOutputFilenameVector.at(i)); } QString result = "Converted " + QString::number(m_CSVtoXMLInputFilenameVector.size()) + " files from file list!"; MessageBox(result.toStdString()); } } int QmitkIGTTrackingDataEvaluationView::ConvertOneFile(std::string inputFilename, std::string outputFilename) { std::vector myNavigationDatas = GetNavigationDatasFromFile(inputFilename); mitk::NavigationDataRecorderDeprecated::Pointer myRecorder = mitk::NavigationDataRecorderDeprecated::New(); myRecorder->SetFileName(outputFilename.c_str()); mitk::NavigationData::Pointer input = mitk::NavigationData::New(); if (m_Controls->m_ConvertCSV->isChecked()) myRecorder->SetOutputFormat(mitk::NavigationDataRecorderDeprecated::csv); myRecorder->AddNavigationData(input); myRecorder->StartRecording(); for (std::size_t i = 0; i < myNavigationDatas.size(); ++i) { input->Graft(myNavigationDatas.at(i)); myRecorder->Update(); } myRecorder->StopRecording(); return myNavigationDatas.size(); } void QmitkIGTTrackingDataEvaluationView::OnCSVtoXMLLoadInputList() { //read in filename QString filename = QFileDialog::getOpenFileName(nullptr, tr("Open Measurement Filename List"), "/", tr("All Files (*.*)")); if (filename.isNull()) return; m_CSVtoXMLInputFilenameVector = this->GetFileContentLineByLine(filename.toStdString()); m_Controls->m_labelCSVtoXMLInputList->setText("READY"); } void QmitkIGTTrackingDataEvaluationView::OnCSVtoXMLLoadOutputList() { //read in filename QString filename = QFileDialog::getOpenFileName(nullptr, tr("Open Measurement Filename List"), "/", tr("All Files (*.*)")); if (filename.isNull()) return; m_CSVtoXMLOutputFilenameVector = this->GetFileContentLineByLine(filename.toStdString()); m_Controls->m_labelCSVtoXMLOutputList->setText("READY"); } void QmitkIGTTrackingDataEvaluationView::MessageBox(std::string s) { QMessageBox msgBox; msgBox.setText(s.c_str()); msgBox.exec(); } void QmitkIGTTrackingDataEvaluationView::WriteHeader() { m_CurrentWriteFile << "Filename;"; m_CurrentWriteFile << "N;"; m_CurrentWriteFile << "N_invalid;"; m_CurrentWriteFile << "Percentage_invalid;"; if (m_Controls->m_settingPosMean->isChecked()) { m_CurrentWriteFile << "Position_Mean[x];"; m_CurrentWriteFile << "Position_Mean[y];"; m_CurrentWriteFile << "Position_Mean[z];"; } if (m_Controls->m_settingPosStabw->isChecked()) { m_CurrentWriteFile << "Position_StandDev[x];"; m_CurrentWriteFile << "Position_StandDev[y];"; m_CurrentWriteFile << "Position_StandDev[z];"; } if (m_Controls->m_settingPosSampleStabw->isChecked()) { m_CurrentWriteFile << "Position_SampleStandDev[x];"; m_CurrentWriteFile << "Position_SampleStandDev[y];"; m_CurrentWriteFile << "Position_SampleStandDev[z];"; } if (m_Controls->m_settingQuaternionMean->isChecked()) { m_CurrentWriteFile << "Quaternion_Mean[qx];"; m_CurrentWriteFile << "Quaternion_Mean[qy];"; m_CurrentWriteFile << "Quaternion_Mean[qz];"; m_CurrentWriteFile << "Quaternion_Mean[qr];"; } if (m_Controls->m_settionQuaternionStabw->isChecked()) { m_CurrentWriteFile << "Quaternion_StandDev[qx];"; m_CurrentWriteFile << "Quaternion_StandDev[qy];"; m_CurrentWriteFile << "Quaternion_StandDev[qz];"; m_CurrentWriteFile << "Quaternion_StandDev[qr];"; } if (m_Controls->m_settingPosErrorMean->isChecked()) m_CurrentWriteFile << "PositionError_Mean;"; if (m_Controls->m_settingPosErrorStabw->isChecked()) m_CurrentWriteFile << "PositionError_StandDev;"; if (m_Controls->m_settingPosErrorSampleStabw->isChecked()) m_CurrentWriteFile << "PositionError_SampleStandDev;"; if (m_Controls->m_settingPosErrorRMS->isChecked()) m_CurrentWriteFile << "PositionError_RMS;"; if (m_Controls->m_settingPosErrorMedian->isChecked()) m_CurrentWriteFile << "PositionError_Median;"; if (m_Controls->m_settingPosErrorMinMax->isChecked()) { m_CurrentWriteFile << "PositionError_Max;"; m_CurrentWriteFile << "PositionError_Min;"; } if (m_Controls->m_settingEulerMean->isChecked()) { m_CurrentWriteFile << "Euler_tx;"; m_CurrentWriteFile << "Euler_ty;"; m_CurrentWriteFile << "Euler_tz;"; } if (m_Controls->m_settingEulerRMS->isChecked()) { m_CurrentWriteFile << "EulerErrorRMS (rad);"; m_CurrentWriteFile << "EulerErrorRMS (grad);"; } m_CurrentWriteFile << "\n"; } void QmitkIGTTrackingDataEvaluationView::WriteDataSet(mitk::NavigationDataEvaluationFilter::Pointer myEvaluationFilter, std::string dataSetName) { if (myEvaluationFilter->GetNumberOfOutputs() == 0) m_CurrentWriteFile << "Error: no input \n"; else { m_CurrentWriteFile << dataSetName << ";"; m_CurrentWriteFile << myEvaluationFilter->GetNumberOfAnalysedNavigationData(0) << ";"; m_CurrentWriteFile << myEvaluationFilter->GetNumberOfInvalidSamples(0) << ";"; m_CurrentWriteFile << myEvaluationFilter->GetPercentageOfInvalidSamples(0) << ";"; if (m_Controls->m_settingPosMean->isChecked()) { m_CurrentWriteFile << myEvaluationFilter->GetPositionMean(0)[0] << ";"; m_CurrentWriteFile << myEvaluationFilter->GetPositionMean(0)[1] << ";"; m_CurrentWriteFile << myEvaluationFilter->GetPositionMean(0)[2] << ";"; } if (m_Controls->m_settingPosStabw->isChecked()) { m_CurrentWriteFile << myEvaluationFilter->GetPositionStandardDeviation(0)[0] << ";"; m_CurrentWriteFile << myEvaluationFilter->GetPositionStandardDeviation(0)[1] << ";"; m_CurrentWriteFile << myEvaluationFilter->GetPositionStandardDeviation(0)[2] << ";"; } if (m_Controls->m_settingPosSampleStabw->isChecked()) { m_CurrentWriteFile << myEvaluationFilter->GetPositionSampleStandardDeviation(0)[0] << ";"; m_CurrentWriteFile << myEvaluationFilter->GetPositionSampleStandardDeviation(0)[1] << ";"; m_CurrentWriteFile << myEvaluationFilter->GetPositionSampleStandardDeviation(0)[2] << ";"; } if (m_Controls->m_settingQuaternionMean->isChecked()) { m_CurrentWriteFile << myEvaluationFilter->GetQuaternionMean(0).x() << ";"; m_CurrentWriteFile << myEvaluationFilter->GetQuaternionMean(0).y() << ";"; m_CurrentWriteFile << myEvaluationFilter->GetQuaternionMean(0).z() << ";"; m_CurrentWriteFile << myEvaluationFilter->GetQuaternionMean(0).r() << ";"; } if (m_Controls->m_settionQuaternionStabw->isChecked()) { m_CurrentWriteFile << myEvaluationFilter->GetQuaternionStandardDeviation(0).x() << ";"; m_CurrentWriteFile << myEvaluationFilter->GetQuaternionStandardDeviation(0).y() << ";"; m_CurrentWriteFile << myEvaluationFilter->GetQuaternionStandardDeviation(0).z() << ";"; m_CurrentWriteFile << myEvaluationFilter->GetQuaternionStandardDeviation(0).r() << ";"; } if (m_Controls->m_settingPosErrorMean->isChecked()) m_CurrentWriteFile << myEvaluationFilter->GetPositionErrorMean(0) << ";"; if (m_Controls->m_settingPosErrorStabw->isChecked()) m_CurrentWriteFile << myEvaluationFilter->GetPositionErrorStandardDeviation(0) << ";"; if (m_Controls->m_settingPosErrorSampleStabw->isChecked()) m_CurrentWriteFile << myEvaluationFilter->GetPositionErrorSampleStandardDeviation(0) << ";"; if (m_Controls->m_settingPosErrorRMS->isChecked()) m_CurrentWriteFile << myEvaluationFilter->GetPositionErrorRMS(0) << ";"; if (m_Controls->m_settingPosErrorMedian->isChecked()) m_CurrentWriteFile << myEvaluationFilter->GetPositionErrorMedian(0) << ";"; if (m_Controls->m_settingPosErrorMinMax->isChecked()) { m_CurrentWriteFile << myEvaluationFilter->GetPositionErrorMax(0) << ";"; m_CurrentWriteFile << myEvaluationFilter->GetPositionErrorMin(0) << ";"; } if (m_Controls->m_settingEulerMean->isChecked()) { m_CurrentWriteFile << myEvaluationFilter->GetEulerAnglesMean(0)[0] << ";"; m_CurrentWriteFile << myEvaluationFilter->GetEulerAnglesMean(0)[1] << ";"; m_CurrentWriteFile << myEvaluationFilter->GetEulerAnglesMean(0)[2] << ";"; } if (m_Controls->m_settingEulerRMS->isChecked()) { m_CurrentWriteFile << myEvaluationFilter->GetEulerAnglesRMS(0) << ";"; m_CurrentWriteFile << myEvaluationFilter->GetEulerAnglesRMSDegree(0) << ";"; } m_CurrentWriteFile << "\n"; } } std::vector QmitkIGTTrackingDataEvaluationView::GetMeanOrientationsOfAllData(std::vector allData, bool useSLERP) { std::vector returnValue; for (auto dataSet : allData) { if (useSLERP) returnValue.push_back(GetSLERPAverage(dataSet)); else returnValue.push_back(dataSet->GetQuaternionMean(0)); } return returnValue; } std::vector QmitkIGTTrackingDataEvaluationView::GetAllDataFromUIList() { std::vector EvaluationDataCollection; //start loop and iterate through all files of list: store the evaluation data for (std::size_t i = 0; i < m_FilenameVector.size(); ++i) { //create navigation data player mitk::NavigationDataCSVSequentialPlayer::Pointer myPlayer = ConstructNewNavigationDataPlayer(); myPlayer->SetFiletype(mitk::NavigationDataCSVSequentialPlayer::ManualLoggingCSV); myPlayer->SetFileName(m_FilenameVector.at(i)); //create evaluation filter mitk::NavigationDataEvaluationFilter::Pointer myEvaluationFilter = mitk::NavigationDataEvaluationFilter::New(); //connect pipeline for (unsigned int j = 0; j < myPlayer->GetNumberOfOutputs(); ++j) myEvaluationFilter->SetInput(j, myPlayer->GetOutput(j)); //update pipline until number of samlples is reached for (int j = 0; j < m_Controls->m_NumberOfSamples->value(); ++j) myEvaluationFilter->Update(); myEvaluationFilter->SetInput(nullptr); myPlayer = nullptr; EvaluationDataCollection.push_back(myEvaluationFilter); } return EvaluationDataCollection; } void QmitkIGTTrackingDataEvaluationView::CalculateDifferenceAngles() { //Get all data from UI std::vector EvaluationDataCollection = GetAllDataFromUIList(); //calculation and writing of output data //open output file m_CurrentAngleDifferencesWriteFile.open(std::string((m_Controls->m_OutputFilename->text() + ".angledifferences.csv").toUtf8()).c_str(), std::ios::out); if (m_CurrentAngleDifferencesWriteFile.bad()) { MessageBox("Error: Can't open output file for angle differences calculation!"); return; } //write header WriteDifferenceAnglesHeader(); //compute angle differences QString pos1 = "invalid"; QString pos2 = "invalid"; //now iterate through all evaluation data and calculate the angles for (std::size_t i = 0; i < m_FilenameVector.size(); ++i) { pos1 = QString::fromStdString(itksys::SystemTools::GetFilenameWithoutLastExtension(m_FilenameVector.at(i))); for (std::size_t j = 0; j < m_FilenameVector.size(); ++j) { pos2 = QString::fromStdString(itksys::SystemTools::GetFilenameWithoutLastExtension(m_FilenameVector.at(j))); mitk::Quaternion q1; mitk::Quaternion q2; if (m_Controls->m_DifferencesSLERP->isChecked()) { //compute slerp average q1 = GetSLERPAverage(EvaluationDataCollection.at(i)); q2 = GetSLERPAverage(EvaluationDataCollection.at(j)); } else { //compute arithmetic average q1 = EvaluationDataCollection.at(i)->GetQuaternionMean(0); q2 = EvaluationDataCollection.at(j)->GetQuaternionMean(0); } itk::Vector rotationVec; //adapt for Aurora 5D tools: [0,0,1000] rotationVec[0] = 10000; //X rotationVec[1] = 0; //Y rotationVec[2] = 0; //Z double AngleBetweenTwoQuaternions = mitk::StaticIGTHelperFunctions::GetAngleBetweenTwoQuaterions(q1, q2, rotationVec); //write data set WriteDifferenceAnglesDataSet(pos1.toStdString(), pos2.toStdString(), i, j, AngleBetweenTwoQuaternions); } } //close output file m_CurrentAngleDifferencesWriteFile.close(); } void QmitkIGTTrackingDataEvaluationView::WriteDifferenceAnglesHeader() { m_CurrentAngleDifferencesWriteFile << "Name;Idx1;Idx2;Angle [Degree]\n"; } void QmitkIGTTrackingDataEvaluationView::WriteDifferenceAnglesDataSet(std::string pos1, std::string pos2, int idx1, int idx2, double angle) { m_CurrentAngleDifferencesWriteFile << "Angle between " << pos1 << " and " << pos2 << ";" << idx1 << ";" << idx2 << ";" << angle << "\n"; MITK_INFO << "Angle: " << angle; } std::vector QmitkIGTTrackingDataEvaluationView::GetNavigationDatasFromFile(std::string filename) { std::vector returnValue = std::vector(); std::vector fileContentLineByLine = GetFileContentLineByLine(filename); for (std::size_t i = 1; i < fileContentLineByLine.size(); ++i) //skip header so start at 1 { returnValue.push_back(GetNavigationDataOutOfOneLine(fileContentLineByLine.at(i))); } return returnValue; } std::vector QmitkIGTTrackingDataEvaluationView::GetFileContentLineByLine(std::string filename) { std::vector readData = std::vector(); //save old locale char * oldLocale; oldLocale = setlocale(LC_ALL, nullptr); //define own locale std::locale C("C"); setlocale(LC_ALL, "C"); //read file std::ifstream file; file.open(filename.c_str(), std::ios::in); if (file.good()) { //read out file file.seekg(0L, std::ios::beg); // move to begin of file while (!file.eof()) { std::string buffer; std::getline(file, buffer); // read out file line by line if (buffer.size() > 0) readData.push_back(buffer); } } file.close(); //switch back to old locale setlocale(LC_ALL, oldLocale); return readData; } mitk::NavigationData::Pointer QmitkIGTTrackingDataEvaluationView::GetNavigationDataOutOfOneLine(std::string line) { mitk::NavigationData::Pointer returnValue = mitk::NavigationData::New(); QString myLine = QString(line.c_str()); QStringList myLineList = myLine.split(';'); mitk::Point3D position; mitk::Quaternion orientation; bool valid = false; if (myLineList.at(2).toStdString() == "1") valid = true; position[0] = myLineList.at(3).toDouble(); position[1] = myLineList.at(4).toDouble(); position[2] = myLineList.at(5).toDouble(); orientation[0] = myLineList.at(6).toDouble(); orientation[1] = myLineList.at(7).toDouble(); orientation[2] = myLineList.at(8).toDouble(); orientation[3] = myLineList.at(9).toDouble(); returnValue->SetDataValid(valid); returnValue->SetPosition(position); returnValue->SetOrientation(orientation); return returnValue; } mitk::Quaternion QmitkIGTTrackingDataEvaluationView::GetSLERPAverage(mitk::NavigationDataEvaluationFilter::Pointer evaluationFilter) { mitk::Quaternion average; //build a vector of quaternions from the evaulation filter (caution always takes the first (0) input of the filter std::vector quaternions = std::vector(); for (int i = 0; i < evaluationFilter->GetNumberOfAnalysedNavigationData(0); i++) { mitk::Quaternion currentq = evaluationFilter->GetLoggedOrientation(i, 0); quaternions.push_back(currentq); } //compute the slerp average using the quaternion averaging class mitk::QuaternionAveraging::Pointer myAverager = mitk::QuaternionAveraging::New(); average = myAverager->CalcAverage(quaternions); return average; } void QmitkIGTTrackingDataEvaluationView::writeToFile(std::string filename, std::vector values) { std::fstream currentFile; currentFile.open(filename.c_str(), std::ios::out); if (currentFile.bad()) { MITK_WARN << "Cannot open file, aborting!"; return; } currentFile << "Description" << ";" << "Error[mm]" << "\n"; for (auto currentError : values) { currentFile << currentError.description << ";" << currentError.distanceError << "\n"; } currentFile.close(); } mitk::NavigationDataCSVSequentialPlayer::Pointer QmitkIGTTrackingDataEvaluationView::ConstructNewNavigationDataPlayer() { bool rightHanded = m_Controls->m_RigthHanded->isChecked(); QString separator = m_Controls->m_SeparatorSign->text(); QChar sepaSign = separator.at(0); //char separatorSign; char separatorSign = sepaSign.toLatin1(); //std::string separatorSign = m_Controls->m_SeparatorSign->text().toStdString(); int sampleCount = m_Controls->m_SampleCount->value(); bool headerRow = m_Controls->m_HeaderRow->isChecked(); int xPos = m_Controls->m_XPos->value(); int yPos = m_Controls->m_YPos->value(); int zPos = m_Controls->m_ZPos->value(); bool useQuats = m_Controls->m_UseQuats->isChecked(); int qx = m_Controls->m_Qx->value(); int qy = m_Controls->m_Qy->value(); int qz = m_Controls->m_Qz->value(); int qr = m_Controls->m_Qr->value(); int azimuth = m_Controls->m_Azimuth->value(); int elevation = m_Controls->m_Elevation->value(); int roll = m_Controls->m_Roll->value(); bool eulersInRad = m_Controls->m_Radiants->isChecked(); //need to find the biggest column number to determine the minimal number of columns the .csv file has to have int allInts[] = {xPos, yPos, zPos, qx, qy, qr, azimuth, elevation, roll}; int minNumberOfColumns = (*std::max_element(allInts, allInts+9)+1); //size needs to be +1 because columns start at 0 but size at 1 mitk::NavigationDataCSVSequentialPlayer::Pointer navDataPlayer = mitk::NavigationDataCSVSequentialPlayer::New(); navDataPlayer->SetOptions(rightHanded, separatorSign, sampleCount, headerRow, xPos, yPos, zPos, useQuats, qx, qy, qz, qr, azimuth, elevation, roll, eulersInRad, minNumberOfColumns); return navDataPlayer; } diff --git a/Plugins/org.mitk.gui.qt.igt.app.hummelprotocolmeasurements/src/internal/QmitkIGTTrackingDataEvaluationViewControls.ui b/Plugins/org.mitk.gui.qt.igt.app.hummelprotocolmeasurements/src/internal/QmitkIGTTrackingDataEvaluationViewControls.ui index 2dd08f80f3..ed322e3a99 100644 --- a/Plugins/org.mitk.gui.qt.igt.app.hummelprotocolmeasurements/src/internal/QmitkIGTTrackingDataEvaluationViewControls.ui +++ b/Plugins/org.mitk.gui.qt.igt.app.hummelprotocolmeasurements/src/internal/QmitkIGTTrackingDataEvaluationViewControls.ui @@ -1,1780 +1,1787 @@ QmitkIGTTrackingDataEvaluationViewControls 0 0 448 955 0 0 QmitkTemplate 0 Evaluation Input File List (recorded NavigationData / *.csv): Qt::Horizontal 40 20 120 0 Load New List Qt::Horizontal 40 20 120 0 Add To Current List Qt::Horizontal (1) - VISUALIZATION - of all data sets: Prefix for Data Nodes: Generate PointSet of Mean Positions Generate PointSets of Single Positions Generate Lines for Rotation Qt::Horizontal (3) - JITTER - Evaluation per file / data set: Result CSV Filename: D:/tmp/output.csv Qt::Horizontal 40 20 220 0 200 50 COMPUTE RESULTS PER DATA SET Qt::Horizontal 40 20 Qt::Horizontal (3) - ACCURACY - Evaluation of all data sets: Qt::Horizontal 40 20 220 0 200 50 COMPUTE RESULTS OF ALL DATA Qt::Horizontal 40 20 Qt::Horizontal (4) - GRID MATCHING - Evaluation of all data sets: Reference PointSet: Qt::Horizontal 40 20 150 0 Measurement PointSet: Qt::Horizontal 40 20 150 0 Qt::Horizontal 40 20 220 0 200 50 PERFOM GRID MATCHING Qt::Horizontal 40 20 Qt::Horizontal (5) - ROTATION - Evaluation of all data sets: Qt::Horizontal 40 20 220 0 200 50 COMPUTE ROTATION ERRORS Qt::Horizontal 40 20 Qt::Vertical QSizePolicy::Expanding 20 220 Settings 0 0 - 409 - 768 + 418 + 842 General Scaling Factor for Visualization: Qt::Horizontal 38 20 1.000000000000000 Number of samples to analyze: Qt::Horizontal 60 20 1000000 150 Qt::Horizontal Tracking Volume: Standard Volume (10 X 9 Positions) true Medium Volume (5 X 5 Positions) - Small Volume (3 X 4 Positions) + Small Volume (3 X 4 Positions, 5cm distance) + + + + + + + Widespread Volume (3 X 4 Positions, 15cm distance) Qt::Horizontal Rotation Evaluation: Rotation Vector: Qt::Horizontal 40 20 X 99999 Qt::Horizontal 40 20 Y 99999 Qt::Horizontal 40 20 Z 99999 10000 Qt::Vertical 20 40 0 0 - 286 - 746 + 176 + 530 .csv file input options File Options: Separator in the csv file: Qt::Horizontal 60 20 0 0 40 16777215 ; 1 Use every n-th smaple n: Qt::Horizontal 40 20 1 The csv file has a header row true Type of Coordinate System: Left handed Right handed true Position and Orientation Options: Y 4 Z 5 X 3 Coordinate: Colum number: Use Quaternions for Orientation true Qr 8 Qy 9 7 6 Qx Qz Quaternion component: Column number: Use Euler Angles for Orientation Column number: Azimuth -1 Roll Angle: -1 Elevation -1 Unity for Euler Angles: Radiants true Degrees false 0 0 - 343 - 607 + 212 + 392 Output per data set Position Mean (x,y,z) true Standard Deviation (x,y,z) Sample Standard Deviation (x,y,z) Orientation Quaternion Mean (qx,qy,qz,qr) Quaternion Mean (SLERP) Quaternion Standard Deviation (qx,qy,qz,qr) Euler Mean (tx,ty,tz) Difference Angles to all other Positions Difference Angles to all other Positions (SLERP) Position Error Mean Standard Deviation Sample Standard Deviation RMS true Median Min/Max Orientation Error Euler RMS Tools Point Set Ground Truth Generator Generate 1 999 10 X 1 999 9 Point Set Qt::Horizontal 40 20 Inter Point Distance (in mm): Qt::Horizontal 40 20 1 99999 50 Qt::Horizontal 40 20 Generate Result CSV File to NavigationData Converter Convert Single File true Input CSV Logging File: C:/Tools/test.csv Output Navigation Data File: C:/Tools/testoutput.xml Qt::Horizontal Convert File List <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-style:italic;">(use text files with a complete filename in every line)</span></p></body></html> not loaded Qt::Horizontal 40 20 100 0 Load Input List not loaded Qt::Horizontal 40 20 100 0 Load Output List Qt::Horizontal Qt::Horizontal 40 20 Output Format XML true CSV Qt::Horizontal 40 20 Convert Orientation Calculation (out of three positions) Qt::Horizontal 40 20 Generate Reference From Current List Qt::Horizontal 40 20 Write Orientation Quaternions To File Qt::Vertical 20 632 QmitkDataStorageComboBox QComboBox
QmitkDataStorageComboBox.h
diff --git a/Plugins/org.mitk.gui.qt.igt.app.hummelprotocolmeasurements/src/internal/QmitkIGTTrackingSemiAutomaticMeasurementView.cpp b/Plugins/org.mitk.gui.qt.igt.app.hummelprotocolmeasurements/src/internal/QmitkIGTTrackingSemiAutomaticMeasurementView.cpp index 4c14d4c333..fb254b52a4 100644 --- a/Plugins/org.mitk.gui.qt.igt.app.hummelprotocolmeasurements/src/internal/QmitkIGTTrackingSemiAutomaticMeasurementView.cpp +++ b/Plugins/org.mitk.gui.qt.igt.app.hummelprotocolmeasurements/src/internal/QmitkIGTTrackingSemiAutomaticMeasurementView.cpp @@ -1,618 +1,670 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ // Blueberry #include #include // Qmitk #include "QmitkIGTTrackingSemiAutomaticMeasurementView.h" // Qt +#include #include -#include #include -#include +#include // MITK +#include "mitkHummelProtocolEvaluation.h" +#include #include #include -#include -#include "mitkHummelProtocolEvaluation.h" // POCO #include #include -const std::string QmitkIGTTrackingSemiAutomaticMeasurementView::VIEW_ID = "org.mitk.views.igttrackingsemiautomaticmeasurement"; +const std::string QmitkIGTTrackingSemiAutomaticMeasurementView::VIEW_ID = + "org.mitk.views.igttrackingsemiautomaticmeasurement"; QmitkIGTTrackingSemiAutomaticMeasurementView::QmitkIGTTrackingSemiAutomaticMeasurementView() - : QmitkFunctionality() - , m_Controls(nullptr) - , m_MultiWidget(nullptr) + : QmitkFunctionality(), m_Controls(nullptr), m_MultiWidget(nullptr) { m_NextFile = 0; m_FilenameVector = std::vector(); m_Timer = new QTimer(this); m_logging = false; m_referenceValid = true; m_tracking = false; m_EvaluationFilter = mitk::NavigationDataEvaluationFilter::New(); } -QmitkIGTTrackingSemiAutomaticMeasurementView::~QmitkIGTTrackingSemiAutomaticMeasurementView() -{ -} +QmitkIGTTrackingSemiAutomaticMeasurementView::~QmitkIGTTrackingSemiAutomaticMeasurementView() {} void QmitkIGTTrackingSemiAutomaticMeasurementView::CreateResults() { QString LogFileName = m_Controls->m_OutputPath->text() + "_results.log"; mitk::LoggingBackend::Unregister(); mitk::LoggingBackend::SetLogFile(LogFileName.toStdString().c_str()); mitk::LoggingBackend::Register(); double RMSmean = 0; for (std::size_t i = 0; i < m_RMSValues.size(); ++i) { MITK_INFO << "RMS at " << this->m_FilenameVector.at(i) << ": " << m_RMSValues.at(i); RMSmean += m_RMSValues.at(i); } RMSmean /= m_RMSValues.size(); MITK_INFO << "RMS mean over " << m_RMSValues.size() << " values: " << RMSmean; mitk::DataNode::Pointer newNode = mitk::DataNode::New(); newNode->SetName("Tracking Results"); newNode->SetData(this->m_MeanPoints); this->GetDataStorage()->Add(newNode); - std::vector results5cmDistances; + std::vector results; if (m_Controls->m_mediumVolume->isChecked()) - mitk::HummelProtocolEvaluation::Evaluate5cmDistances(m_MeanPoints, mitk::HummelProtocolEvaluation::medium, results5cmDistances); + mitk::HummelProtocolEvaluation::Evaluate5cmDistances( + m_MeanPoints, mitk::HummelProtocolEvaluation::medium, results); else if (m_Controls->m_smallVolume->isChecked()) - mitk::HummelProtocolEvaluation::Evaluate5cmDistances(m_MeanPoints, mitk::HummelProtocolEvaluation::small, results5cmDistances); + mitk::HummelProtocolEvaluation::Evaluate5cmDistances( + m_MeanPoints, mitk::HummelProtocolEvaluation::small, results); else if (m_Controls->m_standardVolume->isChecked()) - mitk::HummelProtocolEvaluation::Evaluate5cmDistances(m_MeanPoints, mitk::HummelProtocolEvaluation::standard, results5cmDistances); + mitk::HummelProtocolEvaluation::Evaluate5cmDistances( + m_MeanPoints, mitk::HummelProtocolEvaluation::standard, results); + else if (m_Controls->m_widespreadVolume->isChecked()) + mitk::HummelProtocolEvaluation::Evaluate15cmDistances( + m_MeanPoints, mitk::HummelProtocolEvaluation::widespread, results); } void QmitkIGTTrackingSemiAutomaticMeasurementView::CreateQtPartControl(QWidget *parent) { // build up qt view, unless already done if (!m_Controls) { // create GUI widgets from the Qt Designer's .ui file m_Controls = new Ui::QmitkIGTTrackingSemiAutomaticMeasurementViewControls; m_Controls->setupUi(parent); - //buttons + // buttons connect(m_Controls->m_LoadMeasurementToolStorage, SIGNAL(clicked()), this, SLOT(OnLoadMeasurementStorage())); connect(m_Controls->m_LoadReferenceToolStorage, SIGNAL(clicked()), this, SLOT(OnLoadReferenceStorage())); connect(m_Controls->m_StartTracking, SIGNAL(clicked()), this, SLOT(OnStartTracking())); connect(m_Controls->m_LoadList, SIGNAL(clicked()), this, SLOT(OnMeasurementLoadFile())); connect(m_Controls->m_StartNextMeasurement, SIGNAL(clicked()), this, SLOT(StartNextMeasurement())); connect(m_Controls->m_ReapeatLastMeasurement, SIGNAL(clicked()), this, SLOT(RepeatLastMeasurement())); connect(m_Controls->m_SetReference, SIGNAL(clicked()), this, SLOT(OnSetReference())); connect(m_Controls->m_UseReferenceTrackingSystem, SIGNAL(toggled(bool)), this, SLOT(OnUseReferenceToggled(bool))); connect(m_Controls->m_CreateResults, SIGNAL(clicked()), this, SLOT(CreateResults())); - //event filter + // event filter qApp->installEventFilter(this); - //timers + // timers connect(m_Timer, SIGNAL(timeout()), this, SLOT(UpdateTimer())); } - //initialize some view + // initialize some view m_Controls->m_StopTracking->setEnabled(false); } void QmitkIGTTrackingSemiAutomaticMeasurementView::MultiWidgetAvailable(QmitkAbstractMultiWidget &multiWidget) { - m_MultiWidget = dynamic_cast(&multiWidget); + m_MultiWidget = dynamic_cast(&multiWidget); } void QmitkIGTTrackingSemiAutomaticMeasurementView::MultiWidgetNotAvailable() { m_MultiWidget = nullptr; } void QmitkIGTTrackingSemiAutomaticMeasurementView::OnUseReferenceToggled(bool state) { if (state) { m_Controls->m_ReferenceBox->setEnabled(true); m_Controls->m_SetReference->setEnabled(true); } else { m_Controls->m_ReferenceBox->setEnabled(false); m_Controls->m_SetReference->setEnabled(false); } } mitk::NavigationToolStorage::Pointer QmitkIGTTrackingSemiAutomaticMeasurementView::ReadStorage(std::string file) { mitk::NavigationToolStorage::Pointer returnValue; - //initialize tool storage + // initialize tool storage returnValue = mitk::NavigationToolStorage::New(); - //read tool storage from disk - mitk::NavigationToolStorageDeserializer::Pointer myDeserializer = mitk::NavigationToolStorageDeserializer::New(GetDataStorage()); + // read tool storage from disk + mitk::NavigationToolStorageDeserializer::Pointer myDeserializer = + mitk::NavigationToolStorageDeserializer::New(GetDataStorage()); returnValue = myDeserializer->Deserialize(file); if (returnValue.IsNull()) { QMessageBox msgBox; msgBox.setText(myDeserializer->GetErrorMessage().c_str()); msgBox.exec(); returnValue = nullptr; } return returnValue; } void QmitkIGTTrackingSemiAutomaticMeasurementView::OnSetReference() { - //initialize reference + // initialize reference m_ReferenceStartPositions = std::vector(); m_ReferenceTrackingDeviceSource->Update(); QString Label = "Positions At Start: "; for (unsigned int i = 0; i < m_ReferenceTrackingDeviceSource->GetNumberOfOutputs(); ++i) { mitk::Point3D position = m_ReferenceTrackingDeviceSource->GetOutput(i)->GetPosition(); - Label = Label + "Tool" + QString::number(i) + ":[" + QString::number(position[0]) + ":" + QString::number(position[1]) + ":" + QString::number(position[1]) + "] "; + Label = Label + "Tool" + QString::number(i) + ":[" + QString::number(position[0]) + ":" + + QString::number(position[1]) + ":" + QString::number(position[1]) + "] "; m_ReferenceStartPositions.push_back(position); } m_Controls->m_ReferencePosAtStart->setText(Label); } void QmitkIGTTrackingSemiAutomaticMeasurementView::OnLoadMeasurementStorage() { - //read in filename + // read in filename QString filename = QFileDialog::getOpenFileName(nullptr, tr("Open Toolfile"), "/", tr("All Files (*.*)")); - if (filename.isNull()) return; + if (filename.isNull()) + return; m_MeasurementStorage = ReadStorage(filename.toStdString()); - //update label - Poco::Path myPath = Poco::Path(filename.toStdString()); //use this to seperate filename from path - QString toolLabel = QString("Tool Storage: ") + QString::number(m_MeasurementStorage->GetToolCount()) + " Tools from " + myPath.getFileName().c_str(); + // update label + Poco::Path myPath = Poco::Path(filename.toStdString()); // use this to seperate filename from path + QString toolLabel = QString("Tool Storage: ") + QString::number(m_MeasurementStorage->GetToolCount()) + + " Tools from " + myPath.getFileName().c_str(); m_Controls->m_MeasurementToolStorageLabel->setText(toolLabel); - //update status widget + // update status widget m_Controls->m_ToolStatusWidget->RemoveStatusLabels(); m_Controls->m_ToolStatusWidget->PreShowTools(m_MeasurementStorage); } void QmitkIGTTrackingSemiAutomaticMeasurementView::OnLoadReferenceStorage() { - //read in filename + // read in filename static QString oldFile; - if (oldFile.isNull()) oldFile = "/"; + if (oldFile.isNull()) + oldFile = "/"; QString filename = QFileDialog::getOpenFileName(nullptr, tr("Open Toolfile"), oldFile, tr("All Files (*.*)")); - if (filename.isNull()) return; + if (filename.isNull()) + return; oldFile = filename; m_ReferenceStorage = ReadStorage(filename.toStdString()); - //update label - Poco::Path myPath = Poco::Path(filename.toStdString()); //use this to seperate filename from path - QString toolLabel = QString("Tool Storage: ") + QString::number(m_ReferenceStorage->GetToolCount()) + " Tools from " + myPath.getFileName().c_str(); + // update label + Poco::Path myPath = Poco::Path(filename.toStdString()); // use this to seperate filename from path + QString toolLabel = QString("Tool Storage: ") + QString::number(m_ReferenceStorage->GetToolCount()) + " Tools from " + + myPath.getFileName().c_str(); m_Controls->m_ReferenceToolStorageLabel->setText(toolLabel); } void QmitkIGTTrackingSemiAutomaticMeasurementView::OnStartTracking() { - //check if everything is ready to start tracking + // check if everything is ready to start tracking if (m_MeasurementStorage.IsNull()) { MessageBox("Error: No measurement tools loaded yet!"); return; } else if (m_ReferenceStorage.IsNull() && m_Controls->m_UseReferenceTrackingSystem->isChecked()) { MessageBox("Error: No refernce tools loaded yet!"); return; } else if (m_MeasurementStorage->GetToolCount() == 0) { MessageBox("Error: No way to track without tools!"); return; } else if (m_Controls->m_UseReferenceTrackingSystem->isChecked() && (m_ReferenceStorage->GetToolCount() == 0)) { MessageBox("Error: No way to track without tools!"); return; } - //build the first IGT pipeline (MEASUREMENT) - mitk::TrackingDeviceSourceConfigurator::Pointer myTrackingDeviceSourceFactory1 = mitk::TrackingDeviceSourceConfigurator::New(this->m_MeasurementStorage, this->m_Controls->m_MeasurementTrackingDeviceConfigurationWidget->GetTrackingDevice()); - m_MeasurementTrackingDeviceSource = myTrackingDeviceSourceFactory1->CreateTrackingDeviceSource(this->m_MeasurementToolVisualizationFilter); + // build the first IGT pipeline (MEASUREMENT) + mitk::TrackingDeviceSourceConfigurator::Pointer myTrackingDeviceSourceFactory1 = + mitk::TrackingDeviceSourceConfigurator::New( + this->m_MeasurementStorage, + this->m_Controls->m_MeasurementTrackingDeviceConfigurationWidget->GetTrackingDevice()); + m_MeasurementTrackingDeviceSource = + myTrackingDeviceSourceFactory1->CreateTrackingDeviceSource(this->m_MeasurementToolVisualizationFilter); if (m_MeasurementTrackingDeviceSource.IsNull()) { MessageBox(myTrackingDeviceSourceFactory1->GetErrorMessage()); return; } - //connect the tool visualization widget + // connect the tool visualization widget for (unsigned int i = 0; i < m_MeasurementTrackingDeviceSource->GetNumberOfOutputs(); ++i) { m_Controls->m_ToolStatusWidget->AddNavigationData(m_MeasurementTrackingDeviceSource->GetOutput(i)); m_EvaluationFilter->SetInput(i, m_MeasurementTrackingDeviceSource->GetOutput(i)); } m_Controls->m_ToolStatusWidget->ShowStatusLabels(); m_Controls->m_ToolStatusWidget->SetShowPositions(true); m_Controls->m_ToolStatusWidget->SetShowQuaternions(true); - //build the second IGT pipeline (REFERENCE) + // build the second IGT pipeline (REFERENCE) if (m_Controls->m_UseReferenceTrackingSystem->isChecked()) { - mitk::TrackingDeviceSourceConfigurator::Pointer myTrackingDeviceSourceFactory2 = mitk::TrackingDeviceSourceConfigurator::New(this->m_ReferenceStorage, this->m_Controls->m_ReferenceDeviceConfigurationWidget->GetTrackingDevice()); + mitk::TrackingDeviceSourceConfigurator::Pointer myTrackingDeviceSourceFactory2 = + mitk::TrackingDeviceSourceConfigurator::New( + this->m_ReferenceStorage, this->m_Controls->m_ReferenceDeviceConfigurationWidget->GetTrackingDevice()); m_ReferenceTrackingDeviceSource = myTrackingDeviceSourceFactory2->CreateTrackingDeviceSource(); if (m_ReferenceTrackingDeviceSource.IsNull()) { MessageBox(myTrackingDeviceSourceFactory2->GetErrorMessage()); return; } } - //initialize tracking + // initialize tracking try { m_MeasurementTrackingDeviceSource->Connect(); m_MeasurementTrackingDeviceSource->StartTracking(); if (m_Controls->m_UseReferenceTrackingSystem->isChecked()) { m_ReferenceTrackingDeviceSource->Connect(); m_ReferenceTrackingDeviceSource->StartTracking(); } } catch (...) { MessageBox("Error while starting the tracking device!"); return; } - //set reference - if (m_Controls->m_UseReferenceTrackingSystem->isChecked()) OnSetReference(); + // set reference + if (m_Controls->m_UseReferenceTrackingSystem->isChecked()) + OnSetReference(); - //start timer + // start timer m_Timer->start(1000 / (m_Controls->m_SamplingRate->value())); m_Controls->m_StartTracking->setEnabled(false); m_Controls->m_StartTracking->setEnabled(true); m_tracking = true; } void QmitkIGTTrackingSemiAutomaticMeasurementView::OnStopTracking() { - if (this->m_logging) FinishMeasurement(); + if (this->m_logging) + FinishMeasurement(); m_MeasurementTrackingDeviceSource->Disconnect(); m_MeasurementTrackingDeviceSource->StopTracking(); if (m_Controls->m_UseReferenceTrackingSystem->isChecked()) { m_ReferenceTrackingDeviceSource->Disconnect(); m_ReferenceTrackingDeviceSource->StopTracking(); } m_Timer->stop(); m_Controls->m_StartTracking->setEnabled(true); m_Controls->m_StartTracking->setEnabled(false); m_tracking = false; } void QmitkIGTTrackingSemiAutomaticMeasurementView::OnMeasurementLoadFile() { m_FilenameVector = std::vector(); m_FilenameVector.clear(); m_NextFile = 0; - //read in filename - QString filename = QFileDialog::getOpenFileName(nullptr, tr("Open Measurement Filename List"), "/", tr("All Files (*.*)")); - if (filename.isNull()) return; + // read in filename + QString filename = + QFileDialog::getOpenFileName(nullptr, tr("Open Measurement Filename List"), "/", tr("All Files (*.*)")); + if (filename.isNull()) + return; - //define own locale + // define own locale std::locale C("C"); setlocale(LC_ALL, "C"); - //read file + // read file std::ifstream file; file.open(filename.toStdString().c_str(), std::ios::in); if (file.good()) { - //read out file - file.seekg(0L, std::ios::beg); // move to begin of file + // read out file + file.seekg(0L, std::ios::beg); // move to begin of file while (!file.eof()) { std::string buffer; - std::getline(file, buffer); // read out file line by line - if (buffer.size() > 0) m_FilenameVector.push_back(buffer); + std::getline(file, buffer); // read out file line by line + if (buffer.size() > 0) + m_FilenameVector.push_back(buffer); } } - //fill list at GUI + // fill list at GUI m_Controls->m_MeasurementList->clear(); - for (unsigned int i = 0; i < m_FilenameVector.size(); i++) { new QListWidgetItem(tr(m_FilenameVector.at(i).c_str()), m_Controls->m_MeasurementList); } + for (unsigned int i = 0; i < m_FilenameVector.size(); i++) + { + new QListWidgetItem(tr(m_FilenameVector.at(i).c_str()), m_Controls->m_MeasurementList); + } - //update label next measurement + // update label next measurement std::stringstream label; label << "Next Measurement: " << m_FilenameVector.at(0); m_Controls->m_NextMeasurement->setText(label.str().c_str()); - //reset results files + // reset results files m_MeanPoints = mitk::PointSet::New(); m_RMSValues = std::vector(); m_EvaluationFilter = mitk::NavigationDataEvaluationFilter::New(); - if (m_MeasurementToolVisualizationFilter.IsNotNull()) m_EvaluationFilter->SetInput(0, m_MeasurementToolVisualizationFilter->GetOutput(0)); + if (m_MeasurementToolVisualizationFilter.IsNotNull()) + m_EvaluationFilter->SetInput(0, m_MeasurementToolVisualizationFilter->GetOutput(0)); } void QmitkIGTTrackingSemiAutomaticMeasurementView::UpdateTimer() { - if (m_EvaluationFilter.IsNotNull() && m_logging) m_EvaluationFilter->Update(); - else m_MeasurementToolVisualizationFilter->Update(); + if (m_EvaluationFilter.IsNotNull() && m_logging) + m_EvaluationFilter->Update(); + else + m_MeasurementToolVisualizationFilter->Update(); m_Controls->m_ToolStatusWidget->Refresh(); - //update reference + // update reference if (m_Controls->m_UseReferenceTrackingSystem->isChecked()) { m_ReferenceTrackingDeviceSource->Update(); QString Label = "Current Positions: "; bool distanceThresholdExceeded = false; for (unsigned int i = 0; i < m_ReferenceTrackingDeviceSource->GetNumberOfOutputs(); ++i) { mitk::Point3D position = m_ReferenceTrackingDeviceSource->GetOutput(i)->GetPosition(); - Label = Label + "Tool" + QString::number(i) + ":[" + QString::number(position[0]) + ":" + QString::number(position[1]) + ":" + QString::number(position[1]) + "] "; - if (position.EuclideanDistanceTo(m_ReferenceStartPositions.at(i)) > m_Controls->m_ReferenceThreshold->value()) distanceThresholdExceeded = true; + Label = Label + "Tool" + QString::number(i) + ":[" + QString::number(position[0]) + ":" + + QString::number(position[1]) + ":" + QString::number(position[1]) + "] "; + if (position.EuclideanDistanceTo(m_ReferenceStartPositions.at(i)) > m_Controls->m_ReferenceThreshold->value()) + distanceThresholdExceeded = true; } m_Controls->m_ReferenceCurrentPos->setText(Label); if (distanceThresholdExceeded) { - m_Controls->m_ReferenceOK->setText("NOT OK!"); + m_Controls->m_ReferenceOK->setText("NOT OK!"); m_referenceValid = false; } else { - m_Controls->m_ReferenceOK->setText("OK"); + m_Controls->m_ReferenceOK->setText("OK"); m_referenceValid = true; } } - //update logging + // update logging if (m_logging) { - //check for missing objects - if (m_MeasurementLoggingFilterXML.IsNull() || - m_MeasurementLoggingFilterCSV.IsNull() - ) + // check for missing objects + if (m_MeasurementLoggingFilterXML.IsNull() || m_MeasurementLoggingFilterCSV.IsNull()) { return; } - //log/measure + // log/measure m_MeasurementLoggingFilterXML->Update(); m_MeasurementLoggingFilterCSV->Update(); - if (m_Controls->m_UseReferenceTrackingSystem->isChecked() && - m_ReferenceLoggingFilterXML.IsNotNull() && - m_ReferenceLoggingFilterCSV.IsNotNull()) + if (m_Controls->m_UseReferenceTrackingSystem->isChecked() && m_ReferenceLoggingFilterXML.IsNotNull() && + m_ReferenceLoggingFilterCSV.IsNotNull()) { m_ReferenceLoggingFilterXML->Update(); m_ReferenceLoggingFilterCSV->Update(); } m_loggedFrames++; LogAdditionalCSVFile(); - //check if all frames are logged ... if yes finish the measurement - if (m_loggedFrames > m_Controls->m_SamplesPerMeasurement->value()) { FinishMeasurement(); } + // check if all frames are logged ... if yes finish the measurement + if (m_loggedFrames > m_Controls->m_SamplesPerMeasurement->value()) + { + FinishMeasurement(); + } - //update logging label + // update logging label QString loggingLabel = "Collected Samples: " + QString::number(m_loggedFrames); m_Controls->m_CollectedSamples->setText(loggingLabel); } } void QmitkIGTTrackingSemiAutomaticMeasurementView::StartNextMeasurement() { if (this->m_NextFile >= static_cast(m_FilenameVector.size())) { MessageBox("Last Measurement reached!"); return; } m_loggedFrames = 0; m_logging = true; - //check if directory exists, if not create one + // check if directory exists, if not create one Poco::File myPath(std::string(m_Controls->m_OutputPath->text().toUtf8()).c_str()); - if (!myPath.exists()) myPath.createDirectory(); + if (!myPath.exists()) + myPath.createDirectory(); QString LogFileName = m_Controls->m_OutputPath->text() + QString(m_FilenameVector.at(m_NextFile).c_str()) + ".log"; mitk::LoggingBackend::Unregister(); mitk::LoggingBackend::SetLogFile(LogFileName.toStdString().c_str()); mitk::LoggingBackend::Register(); - //initialize logging filters + // initialize logging filters m_MeasurementLoggingFilterXML = mitk::NavigationDataRecorderDeprecated::New(); m_MeasurementLoggingFilterXML->SetRecordingMode(mitk::NavigationDataRecorderDeprecated::NormalFile); m_MeasurementLoggingFilterCSV = mitk::NavigationDataRecorderDeprecated::New(); m_MeasurementLoggingFilterCSV->SetRecordingMode(mitk::NavigationDataRecorderDeprecated::NormalFile); m_MeasurementLoggingFilterXML->SetOutputFormat(mitk::NavigationDataRecorderDeprecated::xml); m_MeasurementLoggingFilterCSV->SetOutputFormat(mitk::NavigationDataRecorderDeprecated::csv); - QString MeasurementFilenameXML = m_Controls->m_OutputPath->text() + QString(m_FilenameVector.at(m_NextFile).c_str()) + ".xml"; - QString MeasurementFilenameCSV = m_Controls->m_OutputPath->text() + QString(m_FilenameVector.at(m_NextFile).c_str()) + ".csv"; + QString MeasurementFilenameXML = + m_Controls->m_OutputPath->text() + QString(m_FilenameVector.at(m_NextFile).c_str()) + ".xml"; + QString MeasurementFilenameCSV = + m_Controls->m_OutputPath->text() + QString(m_FilenameVector.at(m_NextFile).c_str()) + ".csv"; m_MeasurementLoggingFilterXML->SetFileName(MeasurementFilenameXML.toStdString()); m_MeasurementLoggingFilterCSV->SetFileName(MeasurementFilenameCSV.toStdString()); m_MeasurementLoggingFilterXML->SetRecordCountLimit(m_Controls->m_SamplesPerMeasurement->value()); m_MeasurementLoggingFilterCSV->SetRecordCountLimit(m_Controls->m_SamplesPerMeasurement->value()); if (m_Controls->m_UseReferenceTrackingSystem->isChecked()) { m_ReferenceLoggingFilterXML = mitk::NavigationDataRecorderDeprecated::New(); m_ReferenceLoggingFilterXML->SetRecordingMode(mitk::NavigationDataRecorderDeprecated::NormalFile); m_ReferenceLoggingFilterCSV = mitk::NavigationDataRecorderDeprecated::New(); m_ReferenceLoggingFilterCSV->SetRecordingMode(mitk::NavigationDataRecorderDeprecated::NormalFile); m_ReferenceLoggingFilterXML->SetOutputFormat(mitk::NavigationDataRecorderDeprecated::xml); m_ReferenceLoggingFilterCSV->SetOutputFormat(mitk::NavigationDataRecorderDeprecated::csv); - QString ReferenceFilenameXML = m_Controls->m_OutputPath->text() + "Reference_" + QString(m_FilenameVector.at(m_NextFile).c_str()) + ".xml"; - QString ReferenceFilenameCSV = m_Controls->m_OutputPath->text() + "Reference_" + QString(m_FilenameVector.at(m_NextFile).c_str()) + ".csv"; + QString ReferenceFilenameXML = + m_Controls->m_OutputPath->text() + "Reference_" + QString(m_FilenameVector.at(m_NextFile).c_str()) + ".xml"; + QString ReferenceFilenameCSV = + m_Controls->m_OutputPath->text() + "Reference_" + QString(m_FilenameVector.at(m_NextFile).c_str()) + ".csv"; m_ReferenceLoggingFilterXML->SetFileName(ReferenceFilenameXML.toStdString()); m_ReferenceLoggingFilterCSV->SetFileName(ReferenceFilenameCSV.toStdString()); m_ReferenceLoggingFilterXML->SetRecordCountLimit(m_Controls->m_SamplesPerMeasurement->value()); m_ReferenceLoggingFilterCSV->SetRecordCountLimit(m_Controls->m_SamplesPerMeasurement->value()); } - //start additional csv logging + // start additional csv logging StartLoggingAdditionalCSVFile(m_FilenameVector.at(m_NextFile)); - //connect filter + // connect filter for (unsigned int i = 0; i < m_MeasurementToolVisualizationFilter->GetNumberOfOutputs(); ++i) { m_MeasurementLoggingFilterXML->AddNavigationData(m_MeasurementToolVisualizationFilter->GetOutput(i)); m_MeasurementLoggingFilterCSV->AddNavigationData(m_MeasurementToolVisualizationFilter->GetOutput(i)); } - if (m_Controls->m_UseReferenceTrackingSystem->isChecked()) for (unsigned int i = 0; i < m_ReferenceTrackingDeviceSource->GetNumberOfOutputs(); ++i) - { - m_ReferenceLoggingFilterXML->AddNavigationData(m_ReferenceTrackingDeviceSource->GetOutput(i)); - m_ReferenceLoggingFilterCSV->AddNavigationData(m_ReferenceTrackingDeviceSource->GetOutput(i)); - } + if (m_Controls->m_UseReferenceTrackingSystem->isChecked()) + for (unsigned int i = 0; i < m_ReferenceTrackingDeviceSource->GetNumberOfOutputs(); ++i) + { + m_ReferenceLoggingFilterXML->AddNavigationData(m_ReferenceTrackingDeviceSource->GetOutput(i)); + m_ReferenceLoggingFilterCSV->AddNavigationData(m_ReferenceTrackingDeviceSource->GetOutput(i)); + } - //start filter + // start filter m_MeasurementLoggingFilterXML->StartRecording(); m_MeasurementLoggingFilterCSV->StartRecording(); if (m_Controls->m_UseReferenceTrackingSystem->isChecked()) { m_ReferenceLoggingFilterXML->StartRecording(); m_ReferenceLoggingFilterCSV->StartRecording(); } - //disable all buttons + // disable all buttons DisableAllButtons(); - //update label next measurement + // update label next measurement std::stringstream label; - if ((m_NextFile + 1) >= static_cast(m_FilenameVector.size())) label << "Next Measurement: "; - else label << "Next Measurement: " << m_FilenameVector.at(m_NextFile + 1); + if ((m_NextFile + 1) >= static_cast(m_FilenameVector.size())) + label << "Next Measurement: "; + else + label << "Next Measurement: " << m_FilenameVector.at(m_NextFile + 1); m_Controls->m_NextMeasurement->setText(label.str().c_str()); - //update label last measurement + // update label last measurement std::stringstream label2; label2 << "Last Measurement: " << m_FilenameVector.at(m_NextFile); m_Controls->m_LastMeasurement->setText(label2.str().c_str()); m_NextFile++; } void QmitkIGTTrackingSemiAutomaticMeasurementView::RepeatLastMeasurement() { m_NextFile--; StartNextMeasurement(); } void QmitkIGTTrackingSemiAutomaticMeasurementView::MessageBox(std::string s) { QMessageBox msgBox; msgBox.setText(s.c_str()); msgBox.exec(); } void QmitkIGTTrackingSemiAutomaticMeasurementView::DisableAllButtons() { m_Controls->m_LoadList->setEnabled(false); m_Controls->m_StartNextMeasurement->setEnabled(false); m_Controls->m_ReapeatLastMeasurement->setEnabled(false); m_Controls->m_SamplingRate->setEnabled(false); m_Controls->m_SamplesPerMeasurement->setEnabled(false); m_Controls->m_ReferenceThreshold->setEnabled(false); } void QmitkIGTTrackingSemiAutomaticMeasurementView::EnableAllButtons() { m_Controls->m_LoadList->setEnabled(true); m_Controls->m_StartNextMeasurement->setEnabled(true); m_Controls->m_ReapeatLastMeasurement->setEnabled(true); m_Controls->m_SamplingRate->setEnabled(true); m_Controls->m_SamplesPerMeasurement->setEnabled(true); m_Controls->m_ReferenceThreshold->setEnabled(true); } void QmitkIGTTrackingSemiAutomaticMeasurementView::FinishMeasurement() { m_logging = false; m_MeasurementLoggingFilterXML->StopRecording(); m_MeasurementLoggingFilterCSV->StopRecording(); if (m_Controls->m_UseReferenceTrackingSystem->isChecked()) { m_ReferenceLoggingFilterXML->StopRecording(); m_ReferenceLoggingFilterCSV->StopRecording(); } StopLoggingAdditionalCSVFile(); int id = m_NextFile - 1; mitk::Point3D positionMean = m_EvaluationFilter->GetPositionMean(0); MITK_INFO << "Evaluated " << m_EvaluationFilter->GetNumberOfAnalysedNavigationData(0) << " samples."; double rms = m_EvaluationFilter->GetPositionErrorRMS(0); MITK_INFO << "RMS: " << rms; MITK_INFO << "Position Mean: " << positionMean; m_MeanPoints->SetPoint(id, positionMean); - if (static_cast(m_RMSValues.size()) <= id) m_RMSValues.push_back(rms); - else m_RMSValues[id] = rms; + if (static_cast(m_RMSValues.size()) <= id) + m_RMSValues.push_back(rms); + else + m_RMSValues[id] = rms; m_EvaluationFilter->ResetStatistic(); EnableAllButtons(); } -void QmitkIGTTrackingSemiAutomaticMeasurementView::StartLoggingAdditionalCSVFile(std::string filePostfix) +void QmitkIGTTrackingSemiAutomaticMeasurementView::StartLoggingAdditionalCSVFile(std::string filePostfix) { - //write logfile header + // write logfile header QString header = "Nr;MITK_Time;Valid_Reference;"; QString tool = QString("MeasureTool_") + QString(m_MeasurementTrackingDeviceSource->GetOutput(0)->GetName()); - header = header + tool + "[x];" + tool + "[y];" + tool + "[z];" + tool + "[qx];" + tool + "[qy];" + tool + "[qz];" + tool + "[qr]\n"; - - //open logfile and write header - m_logFileCSV.open(std::string(m_Controls->m_OutputPath->text().toUtf8()).append("/LogFileCombined").append(filePostfix.c_str()).append(".csv").c_str(), std::ios::out); + header = header + tool + "[x];" + tool + "[y];" + tool + "[z];" + tool + "[qx];" + tool + "[qy];" + tool + "[qz];" + + tool + "[qr]\n"; + + // open logfile and write header + m_logFileCSV.open(std::string(m_Controls->m_OutputPath->text().toUtf8()) + .append("/LogFileCombined") + .append(filePostfix.c_str()) + .append(".csv") + .c_str(), + std::ios::out); m_logFileCSV << header.toStdString().c_str(); } -void QmitkIGTTrackingSemiAutomaticMeasurementView::LogAdditionalCSVFile() +void QmitkIGTTrackingSemiAutomaticMeasurementView::LogAdditionalCSVFile() { mitk::Point3D pos = m_MeasurementTrackingDeviceSource->GetOutput(0)->GetPosition(); mitk::Quaternion rot = m_MeasurementTrackingDeviceSource->GetOutput(0)->GetOrientation(); std::string valid = ""; - if (m_referenceValid) valid = "true"; - else valid = "false"; + if (m_referenceValid) + valid = "true"; + else + valid = "false"; std::stringstream timestamp; timestamp << m_MeasurementTrackingDeviceSource->GetOutput(0)->GetTimeStamp(); - QString dataSet = QString::number(m_loggedFrames) + ";" + QString(timestamp.str().c_str()) + ";" + QString(valid.c_str()) + ";" + QString::number(pos[0]) + ";" + QString::number(pos[1]) + ";" + QString::number(pos[2]) + ";" + QString::number(rot.x()) + ";" + QString::number(rot.y()) + ";" + QString::number(rot.z()) + ";" + QString::number(rot.r()) + "\n"; + QString dataSet = QString::number(m_loggedFrames) + ";" + QString(timestamp.str().c_str()) + ";" + + QString(valid.c_str()) + ";" + QString::number(pos[0]) + ";" + QString::number(pos[1]) + ";" + + QString::number(pos[2]) + ";" + QString::number(rot.x()) + ";" + QString::number(rot.y()) + ";" + + QString::number(rot.z()) + ";" + QString::number(rot.r()) + "\n"; m_logFileCSV << dataSet.toStdString(); } -void QmitkIGTTrackingSemiAutomaticMeasurementView::StopLoggingAdditionalCSVFile() +void QmitkIGTTrackingSemiAutomaticMeasurementView::StopLoggingAdditionalCSVFile() { m_logFileCSV.close(); } bool QmitkIGTTrackingSemiAutomaticMeasurementView::eventFilter(QObject *, QEvent *ev) { if (ev->type() == QEvent::KeyPress) { QKeyEvent *k = (QKeyEvent *)ev; bool down = k->key() == 16777239; if (down && m_tracking && !m_logging) StartNextMeasurement(); } return false; } diff --git a/Plugins/org.mitk.gui.qt.igt.app.hummelprotocolmeasurements/src/internal/QmitkIGTTrackingSemiAutomaticMeasurementViewControls.ui b/Plugins/org.mitk.gui.qt.igt.app.hummelprotocolmeasurements/src/internal/QmitkIGTTrackingSemiAutomaticMeasurementViewControls.ui index b85fd511cb..7dc4c68c3a 100644 --- a/Plugins/org.mitk.gui.qt.igt.app.hummelprotocolmeasurements/src/internal/QmitkIGTTrackingSemiAutomaticMeasurementViewControls.ui +++ b/Plugins/org.mitk.gui.qt.igt.app.hummelprotocolmeasurements/src/internal/QmitkIGTTrackingSemiAutomaticMeasurementViewControls.ui @@ -1,588 +1,598 @@ QmitkIGTTrackingSemiAutomaticMeasurementViewControls 0 0 419 931 0 0 QmitkTemplate 0 Tracking Initialization Measurement Tracking System Tool Storage: <none> Qt::Horizontal 40 20 Load Tool Storage Reference Trackingsystem Tool Storage: <none> Qt::Horizontal 40 20 Load Tool Storage Start Tracking Stop Tracking Qt::Vertical 20 40 Measurement Measurement List: Qt::Horizontal 40 20 Load List Output Path: C:/temp/ Last Measurement: <none> Next Measurement: <none> Collected Samples: <none> Qt::Horizontal 40 20 200 0 Start Next Measurement Qt::Horizontal 40 20 200 0 Repeat Last Measurement Qt::Horizontal 40 20 200 0 Create Results Qt::Vertical 20 281 Qt::Horizontal <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600; text-decoration: underline;">Measurement Sensors:</span></p></body></html> Qt::Horizontal <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600; text-decoration: underline;">Reference Sensors:</span></p></body></html> Set Reference Reference Postion: <none> Current Positions: <none> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:28pt; font-weight:600;">OK</span></p></body></html> Qt::AlignCenter Settings Sampling Rate (Times Per Second): Qt::Horizontal 40 20 999.000000000000000 15.000000000000000 Samples Per Measurement: Qt::Horizontal 40 20 1000 150 Threshold For Reference Tools: Qt::Horizontal 40 20 1.000000000000000 Use Reference Tracking System true Tracking Volume Small Volume (3 X 4 Positions) + + true + Medium Volume (5 X 5 Positions) - true + false Standard Volume (10 X 9 Positions) + + + + Widespread Volume (3 X 4 Positions, 15cm) + + + Qt::Vertical 20 40 QmitkToolTrackingStatusWidget QWidget
QmitkToolTrackingStatusWidget.h
1
QmitkTrackingDeviceConfigurationWidget QWidget
QmitkTrackingDeviceConfigurationWidget.h
1
diff --git a/Plugins/org.mitk.gui.qt.igt.app.hummelprotocolmeasurements/src/internal/mitkHummelProtocolEvaluation.cpp b/Plugins/org.mitk.gui.qt.igt.app.hummelprotocolmeasurements/src/internal/mitkHummelProtocolEvaluation.cpp index ebe843f9dc..5f536c59be 100644 --- a/Plugins/org.mitk.gui.qt.igt.app.hummelprotocolmeasurements/src/internal/mitkHummelProtocolEvaluation.cpp +++ b/Plugins/org.mitk.gui.qt.igt.app.hummelprotocolmeasurements/src/internal/mitkHummelProtocolEvaluation.cpp @@ -1,449 +1,522 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #define _USE_MATH_DEFINES #include "mitkHummelProtocolEvaluation.h" -#include +#include #include #include -#include #include #include +#include #include #include -#include +#include -bool mitk::HummelProtocolEvaluation::Evaluate15cmDistances(mitk::PointSet::Pointer p, HummelProtocolMeasurementVolume m, std::vector &Results) +bool mitk::HummelProtocolEvaluation::Evaluate15cmDistances(mitk::PointSet::Pointer p, + HummelProtocolMeasurementVolume m, + std::vector &Results) { - if (m != mitk::HummelProtocolEvaluation::standard) { MITK_WARN << "15 cm distances are only evaluated for standard volumes, aborting!"; return false; } + if ((m != mitk::HummelProtocolEvaluation::standard) && (m != mitk::HummelProtocolEvaluation::widespread)) + { + MITK_WARN << "15 cm distances are only evaluated for standard volumes, aborting!"; + return false; + } MITK_INFO << "########### 15 cm distance errors #############"; - //convert measurements to matrix - std::array ,9> matrix = ParseMatrixStandardVolume(p); - - //these are the variables for the results: + // these are the variables for the results: std::vector distances; std::vector descriptions; - //evaluation of rows - int distanceCounter = 0; - for (int row = 0; row < 9; row++) //rows - for (int distance = 0; distance < 7; distance++) + switch (m) + { + case widespread: { - distanceCounter++; - mitk::Point3D point1 = p->GetPoint(row * 10 + distance); - mitk::Point3D point2 = p->GetPoint(row * 10 + distance + 3); - distances.push_back(point1.EuclideanDistanceTo(point2)); - std::stringstream description; - description << "Distance(" << distanceCounter << ") " << (row + 1) << "/" << (distance + 1) << " to " << (row + 1) << "/" << (distance + 4); - descriptions.push_back(description.str()); + int distanceCounter = 0; + for (int row = 0; row < 3; row++) + { + for (int column = 0; column < 3; column++) + { + distanceCounter++; + int currentIndex = row * 3 + column; + std::cout << "Row: " << row << std::endl; + std::cout << "Column: " << column << std::endl; + std::cout << "Current index: " << currentIndex << std::endl; + std::cout << "Distance 1: " << currentIndex + 1 << std::endl; + std::cout << "Distance 2: " << currentIndex + 3 << std::endl; + // evaluation of columns + if (distanceCounter % 3 != 0) + { + distances.push_back(p->GetPoint(currentIndex).EuclideanDistanceTo(p->GetPoint(currentIndex + 1))); + std::stringstream description; + description << "Distance(" << distanceCounter << ") " << (row + 1) << "/" << (column + 1) << " to " + << (row + 1) << "/" << (column + 2); + descriptions.push_back(description.str()); + } + // evaluation of rows + distances.push_back(p->GetPoint(currentIndex).EuclideanDistanceTo(p->GetPoint(currentIndex + 3))); + std::stringstream description; + description << "Distance(" << distanceCounter << ") " << (row + 2) << "/" << (column + 1) << " to " + << (row + 2) << "/" << (column + 1); + descriptions.push_back(description.str()); + } + } + break; } - - //evaluation of columns - for (int column = 0; column < 10; column++) - for (int row = 0; row < 6; row++) + default: { - distanceCounter++; - mitk::Point3D point1 = matrix[row][column]; - mitk::Point3D point2 = matrix[row + 3][column]; - distances.push_back(point1.EuclideanDistanceTo(point2)); - std::stringstream description; - description << "Distance(" << distanceCounter << ") " << (row + 1) << "/" << (column + 1) << " to " << (row + 4) << "/" << (column + 1); - descriptions.push_back(description.str()); + // convert measurements to matrix + std::array, 9> matrix = ParseMatrixStandardVolume(p); + int counter = 0; + // evaluation of rows + for (int row = 0; row < 9; row++) // rows + { + for (int distance = 0; distance < 7; distance++) + { + counter++; + mitk::Point3D point1 = p->GetPoint(row * 10 + distance); + mitk::Point3D point2 = p->GetPoint(row * 10 + distance + 3); + distances.push_back(point1.EuclideanDistanceTo(point2)); + std::stringstream description; + description << "Distance(" << counter << ") " << (row + 1) << "/" << (distance + 1) << " to " << (row + 1) + << "/" << (distance + 4); + descriptions.push_back(description.str()); + } + } + + // evaluation of columns + for (int column = 0; column < 10; column++) + { + for (int row = 0; row < 6; row++) + { + counter++; + mitk::Point3D point1 = matrix[row][column]; + mitk::Point3D point2 = matrix[row + 3][column]; + distances.push_back(point1.EuclideanDistanceTo(point2)); + std::stringstream description; + description << "Distance(" << counter << ") " << (row + 1) << "/" << (column + 1) << " to " << (row + 4) + << "/" << (column + 1); + descriptions.push_back(description.str()); + } + } + break; } + } - //compute all errors + // compute all errors for (std::size_t i = 0; i < distances.size(); ++i) { HummelProtocolDistanceError currentError; currentError.distanceError = fabs(distances.at(i) - (double)150.0); currentError.description = descriptions.at(i); Results.push_back(currentError); MITK_INFO << "Error " << currentError.description << " : " << currentError.distanceError; } - //compute statistics + // compute statistics std::vector statistics = mitk::HummelProtocolEvaluation::ComputeStatistics(Results); for (auto currentError : statistics) { Results.push_back(currentError); MITK_INFO << currentError.description << " : " << currentError.distanceError; } return true; } -bool mitk::HummelProtocolEvaluation::Evaluate30cmDistances(mitk::PointSet::Pointer p, HummelProtocolMeasurementVolume m, std::vector &Results) +bool mitk::HummelProtocolEvaluation::Evaluate30cmDistances(mitk::PointSet::Pointer p, + HummelProtocolMeasurementVolume m, + std::vector &Results) { - if (m != mitk::HummelProtocolEvaluation::standard) { MITK_WARN << "30 cm distances are only evaluated for standard volumes, aborting!"; return false; } + if (m != mitk::HummelProtocolEvaluation::standard) + { + MITK_WARN << "30 cm distances are only evaluated for standard volumes, aborting!"; + return false; + } MITK_INFO << "########### 30 cm distance errors #############"; - //convert measurements to matrix - std::array ,9> matrix = ParseMatrixStandardVolume(p); + // convert measurements to matrix + std::array, 9> matrix = ParseMatrixStandardVolume(p); - //these are the variables for the results: + // these are the variables for the results: std::vector distances; std::vector descriptions; - //evaluation of rows + // evaluation of rows int distanceCounter = 0; - for (int row = 0; row < 9; row++) //rows + for (int row = 0; row < 9; row++) // rows for (int distance = 0; distance < 4; distance++) { distanceCounter++; mitk::Point3D point1 = p->GetPoint(row * 10 + distance); mitk::Point3D point2 = p->GetPoint(row * 10 + distance + 6); distances.push_back(point1.EuclideanDistanceTo(point2)); std::stringstream description; - description << "Distance(" << distanceCounter << ") " << (row + 1) << "/" << (distance + 1) << " to " << (row + 1) << "/" << (distance + 7); + description << "Distance(" << distanceCounter << ") " << (row + 1) << "/" << (distance + 1) << " to " << (row + 1) + << "/" << (distance + 7); descriptions.push_back(description.str()); } - //evaluation of columns + // evaluation of columns for (int column = 0; column < 10; column++) for (int row = 0; row < 3; row++) { distanceCounter++; mitk::Point3D point1 = matrix[row][column]; mitk::Point3D point2 = matrix[row + 6][column]; distances.push_back(point1.EuclideanDistanceTo(point2)); std::stringstream description; - description << "Distance(" << distanceCounter << ") " << (row + 1) << "/" << (column + 1) << " to " << (row + 7) << "/" << (column + 1); + description << "Distance(" << distanceCounter << ") " << (row + 1) << "/" << (column + 1) << " to " << (row + 7) + << "/" << (column + 1); descriptions.push_back(description.str()); } - //compute all errors + // compute all errors for (std::size_t i = 0; i < distances.size(); ++i) { HummelProtocolDistanceError currentError; currentError.distanceError = fabs(distances.at(i) - (double)300.0); currentError.description = descriptions.at(i); Results.push_back(currentError); MITK_INFO << "Error " << currentError.description << " : " << currentError.distanceError; } - //compute statistics + // compute statistics std::vector statistics = mitk::HummelProtocolEvaluation::ComputeStatistics(Results); for (auto currentError : statistics) { Results.push_back(currentError); MITK_INFO << currentError.description << " : " << currentError.distanceError; } return true; } -bool mitk::HummelProtocolEvaluation::EvaluateAccumulatedDistances(mitk::PointSet::Pointer p, HummelProtocolMeasurementVolume m, std::vector &Results) +bool mitk::HummelProtocolEvaluation::EvaluateAccumulatedDistances(mitk::PointSet::Pointer p, + HummelProtocolMeasurementVolume m, + std::vector &Results) { - if (m != mitk::HummelProtocolEvaluation::standard) { MITK_WARN << "Accumulated distances are only evaluated for standard volumes, aborting!"; return false; } + if (m != mitk::HummelProtocolEvaluation::standard) + { + MITK_WARN << "Accumulated distances are only evaluated for standard volumes, aborting!"; + return false; + } MITK_INFO << "########### accumulated distance errors #############"; int distanceCounter = 0; - //evaluation of rows - for (int row = 0; row < 9; row++) //rows + // evaluation of rows + for (int row = 0; row < 9; row++) // rows for (int distance = 0; distance < 9; distance++) { distanceCounter++; mitk::Point3D point1 = p->GetPoint(row * 10); mitk::Point3D point2 = p->GetPoint(row * 10 + distance + 1); std::stringstream description; - description << "Distance(" << distanceCounter << ") " << (row + 1) << "/1 to " << (row + 1) << "/" << (distance + 2); - //compute error + description << "Distance(" << distanceCounter << ") " << (row + 1) << "/1 to " << (row + 1) << "/" + << (distance + 2); + // compute error HummelProtocolDistanceError currentError; - currentError.distanceError = fabs(point1.EuclideanDistanceTo(point2) - (double)(50.0*(distance+1))); + currentError.distanceError = fabs(point1.EuclideanDistanceTo(point2) - (double)(50.0 * (distance + 1))); currentError.description = description.str(); Results.push_back(currentError); MITK_INFO << "Error " << currentError.description << " : " << currentError.distanceError; } - - - //compute statistics + // compute statistics std::vector statistics = mitk::HummelProtocolEvaluation::ComputeStatistics(Results); for (auto currentError : statistics) { Results.push_back(currentError); MITK_INFO << currentError.description << " : " << currentError.distanceError; } -return true; + return true; } - - -bool mitk::HummelProtocolEvaluation::Evaluate5cmDistances(mitk::PointSet::Pointer p, HummelProtocolMeasurementVolume m, std::vector &Results) -{ -MITK_INFO << "########### 5 cm distance errors #############"; -std::vector distances; -std::vector descriptions; -switch (m) -{ -case small: - if (p->GetSize() != 12) { - MITK_WARN << "Wrong number of points: " << p->GetSize() << " (expected 12), aborting"; - return false; - } - MITK_INFO << "Computing Hummel protocol distance errors for small measurement volumes (12 points)..."; - - //row 1 - distances.push_back(p->GetPoint(0).EuclideanDistanceTo(p->GetPoint(1))); //0 - descriptions.push_back("Distance 4/4 to 4/5"); - distances.push_back(p->GetPoint(1).EuclideanDistanceTo(p->GetPoint(2))); //1 - descriptions.push_back("Distance 4/5 to 4/6"); - distances.push_back(p->GetPoint(2).EuclideanDistanceTo(p->GetPoint(3))); //2 - descriptions.push_back("Distance 4/6 to 4/7"); - //row 2 - distances.push_back(p->GetPoint(4).EuclideanDistanceTo(p->GetPoint(5))); //3 - descriptions.push_back("Distance 5/4 to 5/5"); - distances.push_back(p->GetPoint(5).EuclideanDistanceTo(p->GetPoint(6))); //4 - descriptions.push_back("Distance 5/5 to 5/6"); - distances.push_back(p->GetPoint(6).EuclideanDistanceTo(p->GetPoint(7))); //5 - descriptions.push_back("Distance 5/6 to 5/7"); - //row 3 - distances.push_back(p->GetPoint(8).EuclideanDistanceTo(p->GetPoint(9))); //6 - descriptions.push_back("Distance 6/4 to 6/5"); - distances.push_back(p->GetPoint(9).EuclideanDistanceTo(p->GetPoint(10))); //7 - descriptions.push_back("Distance 6/5 to 6/6"); - distances.push_back(p->GetPoint(10).EuclideanDistanceTo(p->GetPoint(11))); //8 - descriptions.push_back("Distance 6/6 to 6/7"); - //column 1 - distances.push_back(p->GetPoint(0).EuclideanDistanceTo(p->GetPoint(4))); //9 - descriptions.push_back("Distance 4/4 to 5/4"); - distances.push_back(p->GetPoint(4).EuclideanDistanceTo(p->GetPoint(8))); //10 - descriptions.push_back("Distance 5/4 to 6/4"); - //column 2 - distances.push_back(p->GetPoint(1).EuclideanDistanceTo(p->GetPoint(5))); //11 - descriptions.push_back("Distance 4/5 to 5/5"); - distances.push_back(p->GetPoint(5).EuclideanDistanceTo(p->GetPoint(9))); //12 - descriptions.push_back("Distance 5/5 to 6/5"); - //column 3 - distances.push_back(p->GetPoint(2).EuclideanDistanceTo(p->GetPoint(6))); //13 - descriptions.push_back("Distance 4/6 to 5/6"); - distances.push_back(p->GetPoint(6).EuclideanDistanceTo(p->GetPoint(10))); //14 - descriptions.push_back("Distance 5/6 to 6/6"); - //column 4 - distances.push_back(p->GetPoint(3).EuclideanDistanceTo(p->GetPoint(7))); //15 - descriptions.push_back("Distance 4/7 to 5/7"); - distances.push_back(p->GetPoint(7).EuclideanDistanceTo(p->GetPoint(11))); //16 - descriptions.push_back("Distance 5/7 to 6/7"); - -break; - -case medium: +bool mitk::HummelProtocolEvaluation::Evaluate5cmDistances(mitk::PointSet::Pointer p, + HummelProtocolMeasurementVolume m, + std::vector &Results) { - if (p->GetSize() != 25) { - MITK_WARN << "Wrong number of points (expected 25), aborting"; - return false; - } - MITK_INFO << "Computing Hummel protocol distance errors for medium measurement volumes (25 points)..."; - - int distanceCounter = 0; - - //convert measurements to matrix - std::array, 5> matrix = ParseMatrixMediumVolume(p); - - //evaluation of rows - for (int row = 0; row < 5; row++) //rows - for (int distance = 0; distance < 4; distance++) + MITK_INFO << "########### 5 cm distance errors #############"; + std::vector distances; + std::vector descriptions; + switch (m) + { + case small: + if (p->GetSize() != 12) + { + MITK_WARN << "Wrong number of points: " << p->GetSize() << " (expected 12), aborting"; + return false; + } + MITK_INFO << "Computing Hummel protocol distance errors for small measurement volumes (12 points)..."; + + // row 1 + distances.push_back(p->GetPoint(0).EuclideanDistanceTo(p->GetPoint(1))); // 0 + descriptions.push_back("Distance 4/4 to 4/5"); + distances.push_back(p->GetPoint(1).EuclideanDistanceTo(p->GetPoint(2))); // 1 + descriptions.push_back("Distance 4/5 to 4/6"); + distances.push_back(p->GetPoint(2).EuclideanDistanceTo(p->GetPoint(3))); // 2 + descriptions.push_back("Distance 4/6 to 4/7"); + // row 2 + distances.push_back(p->GetPoint(4).EuclideanDistanceTo(p->GetPoint(5))); // 3 + descriptions.push_back("Distance 5/4 to 5/5"); + distances.push_back(p->GetPoint(5).EuclideanDistanceTo(p->GetPoint(6))); // 4 + descriptions.push_back("Distance 5/5 to 5/6"); + distances.push_back(p->GetPoint(6).EuclideanDistanceTo(p->GetPoint(7))); // 5 + descriptions.push_back("Distance 5/6 to 5/7"); + // row 3 + distances.push_back(p->GetPoint(8).EuclideanDistanceTo(p->GetPoint(9))); // 6 + descriptions.push_back("Distance 6/4 to 6/5"); + distances.push_back(p->GetPoint(9).EuclideanDistanceTo(p->GetPoint(10))); // 7 + descriptions.push_back("Distance 6/5 to 6/6"); + distances.push_back(p->GetPoint(10).EuclideanDistanceTo(p->GetPoint(11))); // 8 + descriptions.push_back("Distance 6/6 to 6/7"); + // column 1 + distances.push_back(p->GetPoint(0).EuclideanDistanceTo(p->GetPoint(4))); // 9 + descriptions.push_back("Distance 4/4 to 5/4"); + distances.push_back(p->GetPoint(4).EuclideanDistanceTo(p->GetPoint(8))); // 10 + descriptions.push_back("Distance 5/4 to 6/4"); + // column 2 + distances.push_back(p->GetPoint(1).EuclideanDistanceTo(p->GetPoint(5))); // 11 + descriptions.push_back("Distance 4/5 to 5/5"); + distances.push_back(p->GetPoint(5).EuclideanDistanceTo(p->GetPoint(9))); // 12 + descriptions.push_back("Distance 5/5 to 6/5"); + // column 3 + distances.push_back(p->GetPoint(2).EuclideanDistanceTo(p->GetPoint(6))); // 13 + descriptions.push_back("Distance 4/6 to 5/6"); + distances.push_back(p->GetPoint(6).EuclideanDistanceTo(p->GetPoint(10))); // 14 + descriptions.push_back("Distance 5/6 to 6/6"); + // column 4 + distances.push_back(p->GetPoint(3).EuclideanDistanceTo(p->GetPoint(7))); // 15 + descriptions.push_back("Distance 4/7 to 5/7"); + distances.push_back(p->GetPoint(7).EuclideanDistanceTo(p->GetPoint(11))); // 16 + descriptions.push_back("Distance 5/7 to 6/7"); + + break; + + case medium: { - distanceCounter++; - mitk::Point3D point1 = p->GetPoint(row * 5 + distance); - mitk::Point3D point2 = p->GetPoint(row * 5 + distance + 1); - distances.push_back(point1.EuclideanDistanceTo(point2)); - std::stringstream description; - description << "Distance(" << distanceCounter << ") " << (row + 1) << "/" << (distance + 1) << " to " << (row + 1) << "/" << (distance + 2); - descriptions.push_back(description.str()); + if (p->GetSize() != 25) + { + MITK_WARN << "Wrong number of points (expected 25), aborting"; + return false; + } + MITK_INFO << "Computing Hummel protocol distance errors for medium measurement volumes (25 points)..."; + + int distanceCounter = 0; + + // convert measurements to matrix + std::array, 5> matrix = ParseMatrixMediumVolume(p); + + // evaluation of rows + for (int row = 0; row < 5; row++) // rows + for (int distance = 0; distance < 4; distance++) + { + distanceCounter++; + mitk::Point3D point1 = p->GetPoint(row * 5 + distance); + mitk::Point3D point2 = p->GetPoint(row * 5 + distance + 1); + distances.push_back(point1.EuclideanDistanceTo(point2)); + std::stringstream description; + description << "Distance(" << distanceCounter << ") " << (row + 1) << "/" << (distance + 1) << " to " + << (row + 1) << "/" << (distance + 2); + descriptions.push_back(description.str()); + } + + // evaluation of columns + for (int column = 0; column < 5; column++) + for (int row = 0; row < 4; row++) + { + distanceCounter++; + mitk::Point3D point1 = matrix[row][column]; + mitk::Point3D point2 = matrix[row + 1][column]; + MITK_INFO << "Point 1:" << point1 << "/Point 2:" << point2 + << "/Distance:" << point1.EuclideanDistanceTo(point2); + distances.push_back(point1.EuclideanDistanceTo(point2)); + std::stringstream description; + description << "Distance(" << distanceCounter << ") " << (row + 1) << "/" << (column + 1) << " to " + << (row + 2) << "/" << (column + 1); + descriptions.push_back(description.str()); + } } + break; - //evaluation of columns - for (int column = 0; column < 5; column++) - for (int row = 0; row < 4; row++) + case standard: { - distanceCounter++; - mitk::Point3D point1 = matrix[row][column]; - mitk::Point3D point2 = matrix[row + 1][column]; - MITK_INFO << "Point 1:" << point1 << "/Point 2:" << point2 << "/Distance:" << point1.EuclideanDistanceTo(point2); - distances.push_back(point1.EuclideanDistanceTo(point2)); - std::stringstream description; - description << "Distance(" << distanceCounter << ") " << (row + 1) << "/" << (column + 1) << " to " << (row + 2) << "/" << (column + 1); - descriptions.push_back(description.str()); + if (p->GetSize() != 90) + { + MITK_WARN << "Wrong number of points (expected 90), aborting"; + return false; + } + MITK_INFO << "Computing Hummel protocol distance errors for standard measurement volumes (90 points)..."; + + int distanceCounter = 0; + + // convert measurements to matrix + std::array, 9> matrix = ParseMatrixStandardVolume(p); + + // evaluation of rows + for (int row = 0; row < 9; row++) // rows + for (int distance = 0; distance < 9; distance++) + { + distanceCounter++; + mitk::Point3D point1 = p->GetPoint(row * 10 + distance); + mitk::Point3D point2 = p->GetPoint(row * 10 + distance + 1); + distances.push_back(point1.EuclideanDistanceTo(point2)); + std::stringstream description; + description << "Distance(" << distanceCounter << ") " << (row + 1) << "/" << (distance + 1) << " to " + << (row + 1) << "/" << (distance + 2); + descriptions.push_back(description.str()); + } + + // evaluation of columns + for (int column = 0; column < 10; column++) + for (int row = 0; row < 8; row++) + { + distanceCounter++; + mitk::Point3D point1 = matrix[row][column]; + mitk::Point3D point2 = matrix[row + 1][column]; + distances.push_back(point1.EuclideanDistanceTo(point2)); + std::stringstream description; + description << "Distance(" << distanceCounter << ") " << (row + 1) << "/" << (column + 1) << " to " + << (row + 2) << "/" << (column + 1); + descriptions.push_back(description.str()); + } } + break; } - break; -case standard: -{ - if (p->GetSize() != 90) { - MITK_WARN << "Wrong number of points (expected 90), aborting"; - return false; + // compute all errors + for (std::size_t i = 0; i < distances.size(); ++i) + { + HummelProtocolDistanceError currentError; + currentError.distanceError = fabs(distances.at(i) - (double)50.0); + currentError.description = descriptions.at(i); + Results.push_back(currentError); + MITK_INFO << "Error " << currentError.description << " : " << currentError.distanceError; } - MITK_INFO << "Computing Hummel protocol distance errors for standard measurement volumes (90 points)..."; - - int distanceCounter = 0; - - //convert measurements to matrix - std::array, 9> matrix = ParseMatrixStandardVolume(p); - //evaluation of rows - for (int row = 0; row < 9; row++) //rows - for (int distance = 0; distance < 9; distance++) - { - distanceCounter++; - mitk::Point3D point1 = p->GetPoint(row * 10 + distance); - mitk::Point3D point2 = p->GetPoint(row * 10 + distance + 1); - distances.push_back(point1.EuclideanDistanceTo(point2)); - std::stringstream description; - description << "Distance(" << distanceCounter << ") " << (row + 1) << "/" << (distance + 1) << " to " << (row + 1) << "/" << (distance + 2); - descriptions.push_back(description.str()); - } - - //evaluation of columns - for (int column = 0; column < 10; column++) - for (int row = 0; row < 8; row++) - { - distanceCounter++; - mitk::Point3D point1 = matrix[row][column]; - mitk::Point3D point2 = matrix[row + 1][column]; - distances.push_back(point1.EuclideanDistanceTo(point2)); - std::stringstream description; - description << "Distance(" << distanceCounter << ") " << (row + 1) << "/" << (column + 1) << " to " << (row + 2) << "/" << (column + 1); - descriptions.push_back(description.str()); - } + // compute statistics + std::vector statistics = mitk::HummelProtocolEvaluation::ComputeStatistics(Results); + for (auto currentError : statistics) + { + Results.push_back(currentError); + MITK_INFO << currentError.description << " : " << currentError.distanceError; } - break; - -} - -//compute all errors -for (std::size_t i = 0; i < distances.size(); ++i) -{ -HummelProtocolDistanceError currentError; -currentError.distanceError = fabs(distances.at(i) - (double)50.0); -currentError.description = descriptions.at(i); -Results.push_back(currentError); -MITK_INFO << "Error " << currentError.description << " : " << currentError.distanceError; -} - -//compute statistics -std::vector statistics = mitk::HummelProtocolEvaluation::ComputeStatistics(Results); -for (auto currentError : statistics) -{ - Results.push_back(currentError); - MITK_INFO << currentError.description << " : " << currentError.distanceError; -} - -return true; + return true; } -std::array, 9> mitk::HummelProtocolEvaluation::ParseMatrixStandardVolume(mitk::PointSet::Pointer p) +std::array, 9> mitk::HummelProtocolEvaluation::ParseMatrixStandardVolume( + mitk::PointSet::Pointer p) { - - std::array ,9> returnValue; + std::array, 9> returnValue; if (p->GetSize() != 90) { MITK_WARN << "PointSet does not have the right size. Expected 90 got " << p->GetSize() << " ... aborting!"; return returnValue; } for (int row = 0; row < 9; row++) for (int column = 0; column < 10; column++) returnValue[row][column] = p->GetPoint(row * 10 + column); return returnValue; } -std::array, 5> mitk::HummelProtocolEvaluation::ParseMatrixMediumVolume(mitk::PointSet::Pointer p) +std::array, 5> mitk::HummelProtocolEvaluation::ParseMatrixMediumVolume( + mitk::PointSet::Pointer p) { - std::array, 5> returnValue; if (p->GetSize() != 25) { MITK_WARN << "PointSet does not have the right size. Expected 25 got " << p->GetSize() << " ... aborting!"; return returnValue; } for (int row = 0; row < 5; row++) for (int column = 0; column < 5; column++) { returnValue[row][column] = p->GetPoint(row * 5 + column); - //MITK_INFO << "m " << row << "/" << column << ":" << p->GetPoint(row * 5 + column); + // MITK_INFO << "m " << row << "/" << column << ":" << p->GetPoint(row * 5 + column); } return returnValue; - } - -std::vector mitk::HummelProtocolEvaluation::ComputeStatistics(std::vector values) +std::vector + mitk::HummelProtocolEvaluation::ComputeStatistics( + std::vector values) { std::vector returnValue; - //convert input values to boost / using boost accumulators for statistics - boost::accumulators::accumulator_set - > > acc; + // convert input values to boost / using boost accumulators for statistics + boost::accumulators::accumulator_set< + double, + boost::accumulators::features + >> + acc; for (mitk::HummelProtocolEvaluation::HummelProtocolDistanceError each : values) { acc(each.distanceError); } - returnValue.push_back({ static_cast(values.size()), "N" }); - returnValue.push_back({ boost::accumulators::mean(acc), "Mean" }); - //double quantile25th = boost::accumulators::quantile(acc, boost::accumulators::quantile_probability = 0.25); - //returnValue.push_back({ boost::accumulators::median(acc), "Median" }); - //returnValue.push_back({ boost::accumulators::variance(acc), "Variance" }); - returnValue.push_back({ boost::accumulators::min(acc), "Min" }); - returnValue.push_back({ boost::accumulators::max(acc), "Max" }); + returnValue.push_back({static_cast(values.size()), "N"}); + returnValue.push_back({boost::accumulators::mean(acc), "Mean"}); + // double quantile25th = boost::accumulators::quantile(acc, boost::accumulators::quantile_probability = 0.25); + // returnValue.push_back({ boost::accumulators::median(acc), "Median" }); + // returnValue.push_back({ boost::accumulators::variance(acc), "Variance" }); + returnValue.push_back({boost::accumulators::min(acc), "Min"}); + returnValue.push_back({boost::accumulators::max(acc), "Max"}); - //don't get the boost stuff working correctly, so computing the quantiles, median and standard deviation by myself: + // don't get the boost stuff working correctly, so computing the quantiles, median and standard deviation by myself: std::vector quantile; for (mitk::HummelProtocolEvaluation::HummelProtocolDistanceError each : values) - {quantile.push_back(each.distanceError);} + { + quantile.push_back(each.distanceError); + } auto const Q1 = quantile.size() / 4; auto const Q2 = quantile.size() / 2; auto const Q3 = Q1 + Q2; - std::sort(quantile.begin(),quantile.end()); + std::sort(quantile.begin(), quantile.end()); - returnValue.push_back({ quantile[Q1], "Quartile 1" }); - returnValue.push_back({ quantile[Q2], "Median" }); - returnValue.push_back({ quantile[Q3], "Quartile 3" }); + returnValue.push_back({quantile[Q1], "Quartile 1"}); + returnValue.push_back({quantile[Q2], "Median"}); + returnValue.push_back({quantile[Q3], "Quartile 3"}); double mean = boost::accumulators::mean(acc); double errorSum = 0; for (mitk::HummelProtocolEvaluation::HummelProtocolDistanceError each : values) { - double error = pow((each.distanceError - mean),2); + double error = pow((each.distanceError - mean), 2); errorSum += error; } double variance = errorSum / values.size(); - double sampleVariance = errorSum / (values.size()-1); + double sampleVariance = errorSum / (values.size() - 1); double standardDev = sqrt(variance); double sampleStandardDev = sqrt(sampleVariance); - returnValue.push_back({ variance, "Variance" }); - returnValue.push_back({ sampleVariance, "Sample Variance" }); - returnValue.push_back({ standardDev, "Standard Deviation" }); - returnValue.push_back({ sampleStandardDev, "Sample Standard Deviation" }); - + returnValue.push_back({variance, "Variance"}); + returnValue.push_back({sampleVariance, "Sample Variance"}); + returnValue.push_back({standardDev, "Standard Deviation"}); + returnValue.push_back({sampleStandardDev, "Sample Standard Deviation"}); return returnValue; - } diff --git a/Plugins/org.mitk.gui.qt.igt.app.hummelprotocolmeasurements/src/internal/mitkHummelProtocolEvaluation.h b/Plugins/org.mitk.gui.qt.igt.app.hummelprotocolmeasurements/src/internal/mitkHummelProtocolEvaluation.h index 4742f94273..a1d3d11d62 100644 --- a/Plugins/org.mitk.gui.qt.igt.app.hummelprotocolmeasurements/src/internal/mitkHummelProtocolEvaluation.h +++ b/Plugins/org.mitk.gui.qt.igt.app.hummelprotocolmeasurements/src/internal/mitkHummelProtocolEvaluation.h @@ -1,92 +1,93 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef MITKHummelProtocolEvaluation_H_HEADER_INCLUDED_ #define MITKHummelProtocolEvaluation_H_HEADER_INCLUDED_ #include #include namespace mitk { /**Documentation * \brief Static methods for evaluations according to the assessment protocol * for EM trackers published by Hummel et al. 2005 [1]. * * [1] Hummel, J. et al. - Design and application of an assessment protocol for electromagnetic tracking systems. Med Phys 32(7), July 2005 * * \ingroup IGT */ class HummelProtocolEvaluation { public: /** Distance error with description. */ struct HummelProtocolDistanceError {double distanceError; std::string description;}; /** Tracking volumes for evaluation. * standard: The standard volume of 9 x 10 measurment points as described in [1] * small: A small volume in the center 3 x 4 measurement points, for smaller field generators [2] + * widespread: A volume covering 3 x 4 widespread measurement points, for quick assessments * [2] Maier-Hein, L. et al. - Standardized assessment of new electromagnetic field generators in an interventional radiology setting. Med Phys 39(6), June 2012 */ - enum HummelProtocolMeasurementVolume { small, medium, standard }; + enum HummelProtocolMeasurementVolume { small, medium, standard, widespread }; /** Evaluates the 5 cm distances as defined by the Hummel protocol [1,2]. * @return Returns true if evaluation was successfull, false if not. * @param[out] Results Please give an empty vector. The results will be added to this vector. */ static bool Evaluate5cmDistances(mitk::PointSet::Pointer p, HummelProtocolMeasurementVolume m, std::vector &Results); - /** Evaluates the 15 cm distances as defined by the Hummel protocol [1,2]. + /** Evaluates the 15 cm distances as defined by the Hummel protocol [1,2]. Additionally it's possible to evaluate a 3 x 4 measurement grid with points being 15 cm apart. * @return Returns true if evaluation was successfull, false if not. * @param[out] Results Please give an empty vector. The results will be added to this vector. */ static bool Evaluate15cmDistances(mitk::PointSet::Pointer p, HummelProtocolMeasurementVolume m, std::vector &Results); /** Evaluates the 30 cm distances as defined by the Hummel protocol [1,2]. * @return Returns true if evaluation was successfull, false if not. * @param[out] Results Please give an empty vector. The results will be added to this vector. */ static bool Evaluate30cmDistances(mitk::PointSet::Pointer p, HummelProtocolMeasurementVolume m, std::vector &Results); /** Evaluates the accumulated distances as defined by the Hummel protocol [1,2]. * @return Returns true if evaluation was successfull, false if not. * @param[out] Results Please give an empty vector. The results will be added to this vector. */ static bool EvaluateAccumulatedDistances(mitk::PointSet::Pointer p, HummelProtocolMeasurementVolume m, std::vector &Results); /** Computes statistics (as mean, standard deviation, quantiles, min, max, etc.) on the given values. * The results are stored inside the return value. */ static std::vector ComputeStatistics(std::vector values); protected: /** Converts a pointset holding all measurement points of the hummel protocol in line-by-line order * to an array representing the hummel board. */ static std::array ,9> ParseMatrixStandardVolume(mitk::PointSet::Pointer p); //It would be really wonderfull if we could replace std::array ,9> by mitk::Matrix< mitk::Point3D, 9, 10 > but //unfortunatly this version does not compile under Linux. To be precise under Linux only matrices like this: mitk::Matriy compile //even the usage of a double pointer (eg mitk::Matrix) does not compile. We always got an error message saying: //vnl_c_vector.h:42:49: error: invalid use of incomplete type ‘class vnl_numeric_traits >’ //Under Windows this error does not appear there everything compiles fine. static std::array, 5> ParseMatrixMediumVolume(mitk::PointSet::Pointer p); }; } // namespace mitk #endif /* MITKHummelProtocolEvaluation_H_HEADER_INCLUDED_ */