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 db3ad0839c..12da728a93 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,1286 +1,1169 @@ /*========================================================================= The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. =========================================================================*/ #include // Blueberry #include #include // Qmitk #include "QmitkIGTTrackingDataEvaluationView.h" #include "QmitkStdMultiWidget.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(0) , 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 (int i = 0; i < m_FilenameVector.size(); i++) + 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)); - //check if the stream is valid and skip file if not - /* - if (!myPlayer->GetStreamValid()) - { - MITK_ERROR << "Error in file " << m_FilenameVector.at(i) << ": " << myPlayer->GetErrorMessage() << " ; Skipping file!"; - continue; - } - */ - //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 { - /* Drehen um eine Achse um das "Umschlagen" zu vermeiden - itk::Matrix rot180degreeAroundY; - rot180degreeAroundY.Fill(0); - rot180degreeAroundY[0][0] = -1; - rot180degreeAroundY[1][1] = 1; - rot180degreeAroundY[2][2] = -1; - point1 = rot180degreeAroundY * point1; - point2 = rot180degreeAroundY * point2; - point3 = rot180degreeAroundY * point3; - */ - 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::StdMultiWidgetAvailable(QmitkStdMultiWidget &stdMultiWidget) { m_MultiWidget = &stdMultiWidget; } void QmitkIGTTrackingDataEvaluationView::StdMultiWidgetNotAvailable() { 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++){ + for (int i = 0; i < files.size(); i++) + { std::string tmp = files.at(i).toStdString().c_str(); m_FilenameVector.push_back(tmp); } - /* - //save old locale - char * oldLocale; - oldLocale = setlocale( LC_ALL, 0 ); - //define own locale - std::locale C("C"); - setlocale( LC_ALL, "C" ); - */ //TODO: check if this is needed here, and load old locale if yes - - /* - //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 - while (!file.eof()) - { - std::string buffer; - std::getline(file, buffer); // read out file line by line - if (buffer.size() > 0) - { - std::string thisFilename = ""; - if (m_Controls->m_AddPath->isChecked()) thisFilename = m_Controls->m_ListPath->text().toStdString(); - thisFilename.append(buffer); - m_FilenameVector.push_back(thisFilename); - } - } - } - */ //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); } //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 (int i = 0; i < m_FilenameVector.size(); i++) + 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)); - //check if the stream is valid and skip file if not - /* - if (!myPlayer->GetStreamValid()) - { - MITK_ERROR << "Error in file " << m_FilenameVector.at(i) << ": " << myPlayer->GetErrorMessage() << " ; Skipping file!"; - - continue; - } - */ - //create evaluation filter mitk::NavigationDataEvaluationFilter::Pointer myEvaluationFilter = mitk::NavigationDataEvaluationFilter::New(); //connect pipeline - for (int j = 0; j < myPlayer->GetNumberOfOutputs(); j++) { myEvaluationFilter->SetInput(j, myPlayer->GetOutput(j)); } + for (unsigned int j = 0; j < myPlayer->GetNumberOfOutputs(); ++i) { 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(); - //Debug output: - //std::cout.precision(5); - //std::cout << "Euler " << j << ";" << myPlayer->GetOutput()->GetOrientation().rotation_euler_angles()[0] << ";" << myPlayer->GetOutput()->GetOrientation().rotation_euler_angles()[1] << ";" << myPlayer->GetOutput()->GetOrientation().rotation_euler_angles()[2] << "\n"; - } //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 (int i = 0; i < m_FilenameVector.size(); i++) + 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)); - //check if the stream is valid and skip file if not - /* - if (!myPlayer->GetStreamValid()) - { - MITK_ERROR << "Error in file " << m_FilenameVector.at(i) << ": " << myPlayer->GetErrorMessage() << " ; Skipping file!"; - - continue; - } - */ - //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 (int i = 0; i < m_FilenameVector.size(); i++) + 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)); - //check if the stream is valid and skip file if not - /* - if (!myPlayer->GetStreamValid()) - { - MITK_ERROR << "Error in file " << m_FilenameVector.at(i) << ": " << myPlayer->GetErrorMessage() << " ; Skipping file!"; - - continue; - } - */ - //create evaluation filter mitk::NavigationDataEvaluationFilter::Pointer myEvaluationFilter = mitk::NavigationDataEvaluationFilter::New(); //connect pipeline - for (int j = 0; j < myPlayer->GetNumberOfOutputs(); j++) { myEvaluationFilter->SetInput(j, myPlayer->GetOutput(j)); } + 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(); } + 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 (int i = 0; i < m_FilenameVector.size(); i++) + 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)); - //check if the stream is valid and skip file if not - /* - if (!myPlayer->GetStreamValid()) - { - MITK_ERROR << "Error in file " << m_FilenameVector.at(i) << ": " << myPlayer->GetErrorMessage() << " ; Skipping file!"; - } - else - */ - { - //create evaluation filter - mitk::NavigationDataEvaluationFilter::Pointer myEvaluationFilter = mitk::NavigationDataEvaluationFilter::New(); + //create evaluation filter + mitk::NavigationDataEvaluationFilter::Pointer myEvaluationFilter = mitk::NavigationDataEvaluationFilter::New(); - //connect pipeline - for (int j = 0; j < myPlayer->GetNumberOfOutputs(); j++) { myEvaluationFilter->SetInput(j, myPlayer->GetOutput(j)); } + //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(); - /* - if (!myPlayer->GetStreamValid()) - { - MITK_ERROR << "Error in file " << m_FilenameVector.at(i) << ": " << myPlayer->GetErrorMessage() << " ; Skipping file!"; - continue; - } - */ - } - //if (!myPlayer->IsAtEnd()) continue; + //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.Get_vnl_vector() + meanPos.Get_vnl_vector(); - 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.Get_vnl_vector() + meanPos.Get_vnl_vector(); - 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.Get_vnl_vector() + meanPos.Get_vnl_vector(); - 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); + //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 (int i = 0; i < m_CSVtoXMLInputFilenameVector.size(); i++) + for (std::size_t i = 0; i < m_CSVtoXMLInputFilenameVector.size(); ++i) { - int lines = ConvertOneFile(m_CSVtoXMLInputFilenameVector.at(i), m_CSVtoXMLOutputFilenameVector.at(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 (int i = 0; i < myNavigationDatas.size(); i++) + 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 (int i = 0; i < m_FilenameVector.size(); i++) + 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(); - //check if the stream is valid and skip file if not - /* - if (!myPlayer->GetStreamValid()) - { - MITK_ERROR << "Error in file " << m_FilenameVector.at(i) << ": " << myPlayer->GetErrorMessage() << " ; Skipping file!"; - } - else - */ - { - //connect pipeline - for (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(); } - } + //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 (int i = 0; i < m_FilenameVector.size(); i++) + for (std::size_t i = 0; i < m_FilenameVector.size(); ++i) { pos1 = QString::fromStdString(itksys::SystemTools::GetFilenameWithoutLastExtension(m_FilenameVector.at(i))); - for (int j = 0; j < m_FilenameVector.size(); j++) + 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) { - //double PI = 3.1415926535897932384626433832795; - //double angle_degree = angle * 180 / PI; - m_CurrentAngleDifferencesWriteFile << "Angle between " << pos1 << " and " << pos2 << ";" << idx1 << ";" << idx2 << ";" << angle << "\n";//<< ";" << angle_degree << "\n"; + 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 (int i = 1; i < fileContentLineByLine.size(); i++) //skip header so start at 1 + 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, 0); //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; - double time = myLineList.at(1).toDouble(); - 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->SetTimeStamp(time); 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/QmitkIGTTrackingSemiAutomaticMeasurementView.cpp b/Plugins/org.mitk.gui.qt.igt.app.hummelprotocolmeasurements/src/internal/QmitkIGTTrackingSemiAutomaticMeasurementView.cpp index 97178bd116..d63b994090 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,623 +1,623 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // Blueberry #include #include // Qmitk #include "QmitkIGTTrackingSemiAutomaticMeasurementView.h" #include "QmitkStdMultiWidget.h" // Qt #include #include #include #include // MITK #include #include #include #include "mitkHummelProtocolEvaluation.h" // POCO #include #include const std::string QmitkIGTTrackingSemiAutomaticMeasurementView::VIEW_ID = "org.mitk.views.igttrackingsemiautomaticmeasurement"; QmitkIGTTrackingSemiAutomaticMeasurementView::QmitkIGTTrackingSemiAutomaticMeasurementView() : QmitkFunctionality() , m_Controls(0) , 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() { } 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; if (m_Controls->m_mediumVolume->isChecked()) mitk::HummelProtocolEvaluation::Evaluate5cmDistances(m_MeanPoints, mitk::HummelProtocolEvaluation::medium, results5cmDistances); else if (m_Controls->m_smallVolume->isChecked()) mitk::HummelProtocolEvaluation::Evaluate5cmDistances(m_MeanPoints, mitk::HummelProtocolEvaluation::small, results5cmDistances); else if (m_Controls->m_standardVolume->isChecked()) mitk::HummelProtocolEvaluation::Evaluate5cmDistances(m_MeanPoints, mitk::HummelProtocolEvaluation::standard, results5cmDistances); } 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 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 qApp->installEventFilter(this); //timers connect(m_Timer, SIGNAL(timeout()), this, SLOT(UpdateTimer())); } //initialize some view m_Controls->m_StopTracking->setEnabled(false); } void QmitkIGTTrackingSemiAutomaticMeasurementView::StdMultiWidgetAvailable(QmitkStdMultiWidget &stdMultiWidget) { m_MultiWidget = &stdMultiWidget; } 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); } } void QmitkIGTTrackingSemiAutomaticMeasurementView::StdMultiWidgetNotAvailable() { m_MultiWidget = nullptr; } mitk::NavigationToolStorage::Pointer QmitkIGTTrackingSemiAutomaticMeasurementView::ReadStorage(std::string file) { mitk::NavigationToolStorage::Pointer returnValue; //initialize tool storage returnValue = mitk::NavigationToolStorage::New(); //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 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]) + "] "; m_ReferenceStartPositions.push_back(position); } m_Controls->m_ReferencePosAtStart->setText(Label); } void QmitkIGTTrackingSemiAutomaticMeasurementView::OnLoadMeasurementStorage() { //read in filename QString filename = QFileDialog::getOpenFileName(nullptr, tr("Open Toolfile"), "/", tr("All Files (*.*)")); 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(); m_Controls->m_MeasurementToolStorageLabel->setText(toolLabel); //update status widget m_Controls->m_ToolStatusWidget->RemoveStatusLabels(); m_Controls->m_ToolStatusWidget->PreShowTools(m_MeasurementStorage); } void QmitkIGTTrackingSemiAutomaticMeasurementView::OnLoadReferenceStorage() { //read in filename static QString oldFile; if (oldFile.isNull()) oldFile = "/"; QString filename = QFileDialog::getOpenFileName(nullptr, tr("Open Toolfile"), oldFile, tr("All Files (*.*)")); 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(); m_Controls->m_ReferenceToolStorageLabel->setText(toolLabel); } void QmitkIGTTrackingSemiAutomaticMeasurementView::OnStartTracking() { //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); if (m_MeasurementTrackingDeviceSource.IsNull()) { MessageBox(myTrackingDeviceSourceFactory1->GetErrorMessage()); return; } //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) if (m_Controls->m_UseReferenceTrackingSystem->isChecked()) { 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 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(); //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(); 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; //define own locale std::locale C("C"); setlocale(LC_ALL, "C"); //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 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); } } //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); } //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 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)); } void QmitkIGTTrackingSemiAutomaticMeasurementView::UpdateTimer() { if (m_EvaluationFilter.IsNotNull() && m_logging) m_EvaluationFilter->Update(); else m_MeasurementToolVisualizationFilter->Update(); m_Controls->m_ToolStatusWidget->Refresh(); //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; } m_Controls->m_ReferenceCurrentPos->setText(Label); if (distanceThresholdExceeded) { m_Controls->m_ReferenceOK->setText("NOT OK!"); m_referenceValid = false; } else { m_Controls->m_ReferenceOK->setText("OK"); m_referenceValid = true; } } //update logging if (m_logging) { //check for missing objects if (m_MeasurementLoggingFilterXML.IsNull() || m_MeasurementLoggingFilterCSV.IsNull() ) { return; } //log/measure m_MeasurementLoggingFilterXML->Update(); m_MeasurementLoggingFilterCSV->Update(); 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(); } //update logging label QString loggingLabel = "Collected Samples: " + QString::number(m_loggedFrames); m_Controls->m_CollectedSamples->setText(loggingLabel); } } void QmitkIGTTrackingSemiAutomaticMeasurementView::StartNextMeasurement() { - if (this->m_NextFile >= m_FilenameVector.size()) + 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 Poco::File myPath(std::string(m_Controls->m_OutputPath->text().toUtf8()).c_str()); 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 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"; 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"; 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 StartLoggingAdditionalCSVFile(m_FilenameVector.at(m_NextFile)); //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)); } //start filter m_MeasurementLoggingFilterXML->StartRecording(); m_MeasurementLoggingFilterCSV->StartRecording(); if (m_Controls->m_UseReferenceTrackingSystem->isChecked()) { m_ReferenceLoggingFilterXML->StartRecording(); m_ReferenceLoggingFilterCSV->StartRecording(); } //disable all buttons DisableAllButtons(); //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); m_Controls->m_NextMeasurement->setText(label.str().c_str()); //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; m_EvaluationFilter->ResetStatistic(); EnableAllButtons(); } void QmitkIGTTrackingSemiAutomaticMeasurementView::StartLoggingAdditionalCSVFile(std::string filePostfix) { //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); m_logFileCSV << header.toStdString().c_str(); } 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"; 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"; m_logFileCSV << dataSet.toStdString(); } 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/mitkHummelProtocolEvaluation.cpp b/Plugins/org.mitk.gui.qt.igt.app.hummelprotocolmeasurements/src/internal/mitkHummelProtocolEvaluation.cpp index e025986bc2..a1d02e33fd 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,454 +1,452 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #define _USE_MATH_DEFINES #include "mitkHummelProtocolEvaluation.h" #include #include #include #include #include #include #include #include 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; } MITK_INFO << "########### 15 cm distance errors #############"; //convert measurements to matrix std::array ,9> matrix = ParseMatrixStandardVolume(p); //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++) { 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()); } //evaluation of columns for (int column = 0; column < 10; column++) for (int row = 0; row < 6; row++) { 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()); } //compute all errors - for (int i = 0; i < distances.size(); i++) + 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 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) { 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); //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 < 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); descriptions.push_back(description.str()); } //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); descriptions.push_back(description.str()); } //compute all errors - for (int i = 0; i < distances.size(); i++) + 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 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) { if (m != mitk::HummelProtocolEvaluation::standard) { MITK_WARN << "Accumulated distances are only evaluated for standard volumes, aborting!"; return false; } - //convert measurements to matrix - std::array ,9> matrix = ParseMatrixStandardVolume(p); MITK_INFO << "########### accumulated distance errors #############"; int distanceCounter = 0; //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 HummelProtocolDistanceError currentError; 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 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::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: { 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; case standard: { 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; } //compute all errors -for (int i = 0; i < distances.size(); i++) +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; } std::array, 9> mitk::HummelProtocolEvaluation::ParseMatrixStandardVolume(mitk::PointSet::Pointer p) { 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> 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); } return returnValue; } 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; for (mitk::HummelProtocolEvaluation::HummelProtocolDistanceError each : values) { acc(each.distanceError); } - returnValue.push_back({ values.size(), "N" }); + 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: std::vector quantile; for (mitk::HummelProtocolEvaluation::HummelProtocolDistanceError each : values) {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()); 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); errorSum += error; } double variance = errorSum / values.size(); 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" }); return returnValue; } diff --git a/Plugins/org.mitk.gui.qt.igt.app.hummelprotocolmeasurements/src/internal/mitkNavigationDataCSVSequentialPlayer.cpp b/Plugins/org.mitk.gui.qt.igt.app.hummelprotocolmeasurements/src/internal/mitkNavigationDataCSVSequentialPlayer.cpp index aeb1b92954..b7a08eef9c 100644 --- a/Plugins/org.mitk.gui.qt.igt.app.hummelprotocolmeasurements/src/internal/mitkNavigationDataCSVSequentialPlayer.cpp +++ b/Plugins/org.mitk.gui.qt.igt.app.hummelprotocolmeasurements/src/internal/mitkNavigationDataCSVSequentialPlayer.cpp @@ -1,413 +1,318 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #define _USE_MATH_DEFINES #include "mitkNavigationDataCSVSequentialPlayer.h" #include #include #include #include #include mitk::NavigationDataCSVSequentialPlayer::NavigationDataCSVSequentialPlayer() : mitk::NavigationDataPlayerBase() { m_NavigationDatas = std::vector(); m_CurrentPos = 0; m_Filetype = mitk::NavigationDataCSVSequentialPlayer::ManualLoggingCSV; } mitk::NavigationDataCSVSequentialPlayer::~NavigationDataCSVSequentialPlayer() { } bool mitk::NavigationDataCSVSequentialPlayer::IsAtEnd() { - if (m_CurrentPos >= m_NavigationDatas.size()) return true; - else return false; + return m_CurrentPos >= static_cast(m_NavigationDatas.size()); } void mitk::NavigationDataCSVSequentialPlayer:: SetFileName(const std::string& fileName) { - this->SetNumberOfOutputs(1); + this->SetNumberOfIndexedOutputs(1); FillOutputEmpty(0); MITK_INFO << "Reading file: " << fileName; m_NavigationDatas = GetNavigationDatasFromFile(fileName); this->Modified(); } void mitk::NavigationDataCSVSequentialPlayer::FillOutputEmpty(int number) { this->SetNthOutput(number, GetEmptyNavigationData()); } mitk::NavigationData::Pointer mitk::NavigationDataCSVSequentialPlayer::GetEmptyNavigationData() { mitk::NavigationData::Pointer emptyNd = mitk::NavigationData::New(); mitk::NavigationData::PositionType position; mitk::NavigationData::OrientationType orientation(0.0, 0.0, 0.0, 0.0); position.Fill(0.0); emptyNd->SetPosition(position); emptyNd->SetOrientation(orientation); emptyNd->SetDataValid(false); return emptyNd; } int mitk::NavigationDataCSVSequentialPlayer::GetNumberOfSnapshots() { return m_NavigationDatas.size(); } void mitk::NavigationDataCSVSequentialPlayer::GenerateData() { for (unsigned int index = 0; index < this->GetNumberOfOutputs(); index++) { mitk::NavigationData* output = this->GetOutput(index); - if (m_CurrentPos > m_NavigationDatas.size()) + if (m_CurrentPos > static_cast(m_NavigationDatas.size())) { FillOutputEmpty(index); return; } output->Graft(this->m_NavigationDatas.at(m_CurrentPos)); m_CurrentPos++; } } void mitk::NavigationDataCSVSequentialPlayer::UpdateOutputInformation() { this->Modified(); // make sure that we need to be updated Superclass::UpdateOutputInformation(); } std::vector mitk::NavigationDataCSVSequentialPlayer::GetNavigationDatasFromFile(std::string filename) { std::vector returnValue = std::vector(); std::vector fileContentLineByLine = GetFileContentLineByLine(filename); - int i; - if (m_HeaderRow) //file has a header row, so it has to be skipped when reading the NavigationDatas - { - i = 1; - } - else - { - i = 0; //file has no header row, so no need to skip the first row - } - for (i; (i < fileContentLineByLine.size()); i++) + std::size_t i = m_HeaderRow + ? 1 //file has a header row, so it has to be skipped when reading the NavigationDatas + : 0; //file has no header row, so no need to skip the first row + + for ( ; i < fileContentLineByLine.size(); ++i) { returnValue.push_back(GetNavigationDataOutOfOneLine(fileContentLineByLine.at(i))); } return returnValue; } std::vector mitk::NavigationDataCSVSequentialPlayer::GetFileContentLineByLine(std::string filename) { std::vector readData = std::vector(); //save old locale char * oldLocale; oldLocale = setlocale(LC_ALL, 0); //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 int count = 0; - //int count2 = 0; while (!file.eof()) { std::string buffer; std::getline(file, buffer); // read out file line by line readData.push_back(buffer); - /* - //for Polhemus tracker: just take every 24th sample - if (count == 0) if (buffer.size() > 0) - { - //MITK_INFO << "read(" << count2 << "): " << buffer.substr(0,30); - //count2++; - readData.push_back(buffer); - } - */ - - count++; if (count == m_SampleCount) count = 0; + + ++count; if (count == m_SampleCount) count = 0; } } file.close(); //switch back to old locale setlocale(LC_ALL, oldLocale); return readData; } mitk::NavigationData::Pointer mitk::NavigationDataCSVSequentialPlayer::GetNavigationDataOutOfOneLine(std::string line) { mitk::NavigationData::Pointer returnValue = mitk::NavigationData::New(); QString myLine = QString(line.c_str()); QStringList myLineList = myLine.split(m_SeparatorSign); mitk::Point3D position; mitk::Quaternion orientation; bool valid = false; - double time; //this is for custom csv files. You have adapt the column numbers to correctly //interpret your csv file. - if (m_Filetype = mitk::NavigationDataCSVSequentialPlayer::ManualLoggingCSV) + if (m_Filetype == mitk::NavigationDataCSVSequentialPlayer::ManualLoggingCSV) { if (myLineList.size() < m_MinNumberOfColumns) { MITK_ERROR << "Error: cannot read line: only found " << myLineList.size() << " fields. Last field: " << myLineList.at(myLineList.size() - 1).toStdString(); returnValue = GetEmptyNavigationData(); return returnValue; } valid = true; //if no valid flag is given: simply set to true //############# Variant for the Aurora measurements ############### //############# (CUSTOM .csv files from MITK) ############### position[0] = myLineList.at(3).toDouble(); position[1] = myLineList.at(4).toDouble(); position[2] = myLineList.at(5).toDouble(); orientation[0] = myLineList.at(6).toDouble(); //qx orientation[1] = myLineList.at(7).toDouble(); //qy orientation[2] = myLineList.at(8).toDouble(); //qz orientation[3] = myLineList.at(9).toDouble(); //qr - - //Variant for the polhemus measurements in August 2016 - //(.csv files from the polhemus software) - - //Important: due to the documentation, Polhemus uses - //a left handed coordinate system while MITK uses a - //right handed. A conversion is not included in this - //read in method yet, because this is not required - //for this special rotation evaliation (no matter - //if it turns 11.25 degree to left or right). For - //other usage this might be important to adapt! - - /* - position[0] = myLineList.at(m_XPos).toDouble(); - position[1] = myLineList.at(m_YPos).toDouble(); - position[2] = myLineList.at(m_ZPos).toDouble(); - */ - if(!m_RightHanded) //MITK uses a right handed coordinate system, so the position needs to be converted { position[0] = position[0]*(-1); } if (m_UseQuats) //Use Quaternions to construct the orientation of the NavigationData { orientation[0] = myLineList.at(m_Qx).toDouble(); //qx orientation[1] = myLineList.at(m_Qy).toDouble(); //qy orientation[2] = myLineList.at(m_Qz).toDouble(); //qz orientation[3] = myLineList.at(m_Qr).toDouble(); //qr } else //Use the Euler Angles to construct the orientation of the NavigationData { double azimuthAngle; double elevationAngle; double rollAngle; if(m_Azimuth < 0) //azimuth is not defined so set him to zero { azimuthAngle = 0; } else { azimuthAngle = myLineList.at(m_Azimuth).toDouble(); } if(m_Elevation < 0)// elevation is not defined so set him to zero { elevationAngle = 0; } else { elevationAngle = myLineList.at(m_Elevation).toDouble(); } if(m_Roll < 0) //roll is not defined so set him to zero { rollAngle = 0; } else { rollAngle = myLineList.at(m_Roll).toDouble(); } if (!m_EulersInRadiants) //the Euler Angles are in Degrees but MITK uses radiants so they need to be converted { azimuthAngle = azimuthAngle / 180 * M_PI; elevationAngle = elevationAngle / 180 * M_PI; rollAngle = rollAngle / 180 * M_PI; } vnl_quaternion eulerQuat(rollAngle, elevationAngle, azimuthAngle); orientation = eulerQuat; } - - //Doesn't work... don't know how to interpret the - //Polhemus quaternions. They are seem to different - //different to other quaternions (NDI, Claron, etc.) - //http://www.mathepedia.de/Quaternionen.aspx - - /* - double qr = myLineList.at(7).toDouble(); - double qx = myLineList.at(8).toDouble(); - double qy = myLineList.at(9).toDouble(); - double qz = myLineList.at(10).toDouble(); - - vnl_quaternion newQuat(qx, qy, qz, qr); - - orientation = newQuat; - orientation.normalize();*/ - - /* - orientation[3] = qr; //qr - orientation[0] = qx; //qx - orientation[1] = qy; //qy - orientation[2] = qz; //qz - - orientation.normalize(); - */ - - - /* -// //Using Euler angles instead does work -// //azimuth: rotation about Z axis of reference frame -// double azimuthAngle = (myLineList.at(11).toDouble() / 180 * M_PI); -// //elevation: rotation about Y' axis (transformed Y axis of sonsor frame) -// double elevationAngle = (myLineList.at(12).toDouble() / 180 * M_PI); -// //roll: rotation about X axis of sensor frame -// double rollAngle = (myLineList.at(13).toDouble() / 180 * M_PI); -// vnl_quaternion eulerQuat(rollAngle, elevationAngle, azimuthAngle); -// orientation = eulerQuat; - */ - - /* - //code block for conversion from axis-angular representation - double rotationAngle = myLineList.at(7).toDouble(); - double rotationAxis[3]; - rotationAxis[0] = myLineList.at(8).toDouble(); - rotationAxis[1] = myLineList.at(9).toDouble(); - rotationAxis[2] = myLineList.at(10).toDouble(); - - double betragRotationAxis = sqrt(pow(rotationAxis[0], 2) + pow(rotationAxis[1], 2) + pow(rotationAxis[2], 2)); - rotationAngle /= betragRotationAxis; - rotationAxis[0] /= betragRotationAxis; - rotationAxis[1] /= betragRotationAxis; - rotationAxis[2] /= betragRotationAxis; - - - double qr = cos(rotationAngle/2); - double qx = rotationAxis[0] * sin(rotationAngle/2); - double qy = rotationAxis[1] * sin(rotationAngle/2); - double qz = rotationAxis[1] * sin(rotationAngle/2); - */ - if(!m_RightHanded) //MITK uses a right handed coordinate system, so the orientation needs to be converted { //code block for conversion from left-handed to right-handed mitk::Quaternion linksZuRechtsdrehend; double rotationAngle = -M_PI; double rotationAxis[3]; rotationAxis[0] = 0; rotationAxis[1] = 0; rotationAxis[2] = 1; linksZuRechtsdrehend[3] = cos(rotationAngle / 2); linksZuRechtsdrehend[0] = rotationAxis[0] * sin(rotationAngle / 2); linksZuRechtsdrehend[1] = rotationAxis[1] * sin(rotationAngle / 2); linksZuRechtsdrehend[2] = rotationAxis[2] * sin(rotationAngle / 2); orientation = orientation * linksZuRechtsdrehend; } } //this is for MITK csv files that have been recorded with the MITK //navigation data recorder. You can also use the navigation data player //class from the MITK-IGT module instead. - else if (m_Filetype = mitk::NavigationDataCSVSequentialPlayer::NavigationDataCSV) + else if (m_Filetype == mitk::NavigationDataCSVSequentialPlayer::NavigationDataCSV) { if (myLineList.size() < 8) { MITK_ERROR << "Error: cannot read line: only found " << myLineList.size() << " fields. Last field: " << myLineList.at(myLineList.size() - 1).toStdString(); returnValue = GetEmptyNavigationData(); return returnValue; } time = myLineList.at(2).toDouble(); if (myLineList.at(3).toStdString() == "1") valid = true; position[0] = myLineList.at(2).toDouble(); position[1] = myLineList.at(3).toDouble(); position[2] = myLineList.at(4).toDouble(); orientation[0] = myLineList.at(5).toDouble(); //qx orientation[1] = myLineList.at(6).toDouble(); //qy orientation[2] = myLineList.at(7).toDouble(); //qz orientation[3] = myLineList.at(8).toDouble(); //qr } //returnValue->SetTimeStamp(time); //DOES NOT WORK ANY MORE... CANNOT SET TIME TO itk::timestamp CLASS returnValue->SetDataValid(valid); returnValue->SetPosition(position); returnValue->SetOrientation(orientation); return returnValue; } void mitk::NavigationDataCSVSequentialPlayer::SetOptions(bool rightHanded, char separatorSign, int sampleCount, bool headerRow, int xPos, int yPos, int zPos, bool useQuats, int qx, int qy, int qz, int qr, int azimuth, int elevation, int roll, bool eulerInRadiants, int minNumberOfColumns) { m_RightHanded = rightHanded; m_SeparatorSign = separatorSign; m_SampleCount = sampleCount; m_HeaderRow = headerRow; m_XPos = xPos; m_YPos = yPos; m_ZPos = zPos; m_UseQuats = useQuats; m_Qx = qx; m_Qy = qy; m_Qz = qz; m_Qr = qr; m_Azimuth = azimuth; m_Elevation = elevation; m_Roll = roll; m_EulersInRadiants = eulerInRadiants; m_MinNumberOfColumns = minNumberOfColumns; }