diff --git a/Modules/IGTBase/src/mitkNavigationData.cpp b/Modules/IGTBase/src/mitkNavigationData.cpp index 629cbcc928..654cae7fcb 100644 --- a/Modules/IGTBase/src/mitkNavigationData.cpp +++ b/Modules/IGTBase/src/mitkNavigationData.cpp @@ -1,392 +1,385 @@ /*=================================================================== 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 "mitkNavigationData.h" #include "vnl/vnl_det.h" #include "mitkException.h" mitk::NavigationData::NavigationData() : itk::DataObject(), m_Position(), m_Orientation(0.0, 0.0, 0.0, 1.0), m_CovErrorMatrix(), m_HasPosition(true), m_HasOrientation(true), m_DataValid(false), m_IGTTimeStamp(0.0), m_Name() { m_Position.Fill(0.0); m_CovErrorMatrix.SetIdentity(); } mitk::NavigationData::NavigationData(const mitk::NavigationData& toCopy) : itk::DataObject(), m_Position(toCopy.GetPosition()), m_Orientation(toCopy.GetOrientation()), m_CovErrorMatrix(toCopy.GetCovErrorMatrix()), m_HasPosition(toCopy.GetHasPosition()), m_HasOrientation(toCopy.GetHasOrientation()), m_DataValid(toCopy.IsDataValid()), m_IGTTimeStamp(toCopy.GetIGTTimeStamp()), m_Name(toCopy.GetName()) {/* TODO SW: Graft does the same, remove code duplications, set Graft to deprecated, remove duplication in tescode */} mitk::NavigationData::~NavigationData() { } void mitk::NavigationData::Graft( const DataObject *data ) { // Attempt to cast data to an NavigationData const Self* nd; try { nd = dynamic_cast( data ); } catch( ... ) { itkExceptionMacro( << "mitk::NavigationData::Graft cannot cast " << typeid(data).name() << " to " << typeid(const Self *).name() ); return; } if (!nd) { // pointer could not be cast back down itkExceptionMacro( << "mitk::NavigationData::Graft cannot cast " << typeid(data).name() << " to " << typeid(const Self *).name() ); return; } // Now copy anything that is needed this->SetPosition(nd->GetPosition()); this->SetOrientation(nd->GetOrientation()); this->SetDataValid(nd->IsDataValid()); this->SetIGTTimeStamp(nd->GetIGTTimeStamp()); this->SetHasPosition(nd->GetHasPosition()); this->SetHasOrientation(nd->GetHasOrientation()); this->SetCovErrorMatrix(nd->GetCovErrorMatrix()); this->SetName(nd->GetName()); } bool mitk::NavigationData::IsDataValid() const { - if (m_Position[0] == 0.0 && m_Position[1] == 0.0 && m_Position[2] == 0.0) - { - return false; - } - else - { return m_DataValid; - } } void mitk::NavigationData::PrintSelf(std::ostream& os, itk::Indent indent) const { this->Superclass::PrintSelf(os, indent); os << indent << "data valid: " << this->IsDataValid() << std::endl; os << indent << "Position: " << this->GetPosition() << std::endl; os << indent << "Orientation: " << this->GetOrientation() << std::endl; os << indent << "TimeStamp: " << this->GetIGTTimeStamp() << std::endl; os << indent << "HasPosition: " << this->GetHasPosition() << std::endl; os << indent << "HasOrientation: " << this->GetHasOrientation() << std::endl; os << indent << "CovErrorMatrix: " << this->GetCovErrorMatrix() << std::endl; } void mitk::NavigationData::CopyInformation( const DataObject* data ) { this->Superclass::CopyInformation( data ); const Self * nd = nullptr; try { nd = dynamic_cast(data); } catch( ... ) { // data could not be cast back down itkExceptionMacro(<< "mitk::NavigationData::CopyInformation() cannot cast " << typeid(data).name() << " to " << typeid(Self*).name() ); } if ( !nd ) { // pointer could not be cast back down itkExceptionMacro(<< "mitk::NavigationData::CopyInformation() cannot cast " << typeid(data).name() << " to " << typeid(Self*).name() ); } /* copy all meta data */ } void mitk::NavigationData::SetPositionAccuracy(mitk::ScalarType error) { for ( int i = 0; i < 3; i++ ) for ( int j = 0; j < 3; j++ ) { m_CovErrorMatrix[ i ][ j ] = 0; // assume independence of position and orientation m_CovErrorMatrix[ i + 3 ][ j ] = 0; m_CovErrorMatrix[ i ][ j + 3 ] = 0; } m_CovErrorMatrix[0][0] = m_CovErrorMatrix[1][1] = m_CovErrorMatrix[2][2] = error * error; } void mitk::NavigationData::SetOrientationAccuracy(mitk::ScalarType error) { for ( int i = 0; i < 3; i++ ) for ( int j = 0; j < 3; j++ ) { m_CovErrorMatrix[ i + 3 ][ j + 3 ] = 0; // assume independence of position and orientation m_CovErrorMatrix[ i + 3 ][ j ] = 0; m_CovErrorMatrix[ i ][ j + 3 ] = 0; } m_CovErrorMatrix[3][3] = m_CovErrorMatrix[4][4] = m_CovErrorMatrix[5][5] = error * error; } void mitk::NavigationData::Compose(const mitk::NavigationData::Pointer n, const bool pre) { NavigationData::Pointer nd3; if (!pre) nd3 = getComposition(this, n); else nd3 = getComposition(n, this); this->Graft(nd3); } mitk::NavigationData::NavigationData( mitk::AffineTransform3D::Pointer affineTransform3D, const bool checkForRotationMatrix) : itk::DataObject(), m_Position(), m_CovErrorMatrix(), m_HasPosition(true), m_HasOrientation(true), m_DataValid(true), m_IGTTimeStamp(0.0), m_Name() { mitk::Vector3D offset = affineTransform3D->GetOffset(); m_Position[0] = offset[0]; m_Position[1] = offset[1]; m_Position[2] = offset[2]; vnl_matrix_fixed rotationMatrix = affineTransform3D->GetMatrix().GetVnlMatrix(); vnl_matrix_fixed rotationMatrixTransposed = rotationMatrix.transpose(); if (checkForRotationMatrix) { // a quadratic matrix is a rotation matrix exactly when determinant is 1 and transposed is inverse if (!Equal(1.0, vnl_det(rotationMatrix), 0.1) || !((rotationMatrix*rotationMatrixTransposed).is_identity(0.1))) { mitkThrow() << "tried to initialize NavigationData with non-rotation matrix :" << rotationMatrix << " (Does your AffineTransform3D object include spacing? This is not supported by NavigationData objects!)"; } } // the transpose is because vnl_quaterion expects a transposed rotation matrix m_Orientation = Quaternion(rotationMatrixTransposed); } mitk::AffineTransform3D::Pointer mitk::NavigationData::GetAffineTransform3D() const { AffineTransform3D::Pointer affineTransform3D = AffineTransform3D::New(); // first set rotation affineTransform3D->SetMatrix(this->GetRotationMatrix()); // now set offset Vector3D vector3D; for (int i = 0; i < 3; ++i) { vector3D[i] = m_Position[i]; } affineTransform3D->SetOffset(vector3D); return affineTransform3D; } mitk::Matrix3D mitk::NavigationData::GetRotationMatrix() const { vnl_matrix_fixed vnl_rotation = m_Orientation.rotation_matrix_transpose().transpose(); // :-) Matrix3D mitkRotation; for (int i = 0; i < 3; ++i) { for (int j = 0; j < 3; ++j) { mitkRotation[i][j] = vnl_rotation[i][j]; } } return mitkRotation; } mitk::Point3D mitk::NavigationData::TransformPoint(const mitk::Point3D point) const { vnl_vector_fixed vnlPoint; for (int i = 0; i < 3; ++i) { vnlPoint[i] = point[i]; } Quaternion normalizedQuaternion = this->GetOrientation().normalize(); // first get rotated point vnlPoint = normalizedQuaternion.rotate(vnlPoint); Point3D resultingPoint; for (int i = 0; i < 3; ++i) { // now copy it to our format + offset resultingPoint[i] = vnlPoint[i] + this->GetPosition()[i]; } return resultingPoint; } mitk::NavigationData::Pointer mitk::NavigationData::GetInverse() const { // non-zero quaternion does not have inverse: throw exception in this case. Quaternion zeroQuaternion; zeroQuaternion.fill(0); if (Equal(zeroQuaternion, this->GetOrientation())) mitkThrow() << "tried to invert zero quaternion in NavigationData"; mitk::NavigationData::Pointer navigationDataInverse = this->Clone(); navigationDataInverse->SetOrientation(this->GetOrientation().inverse()); // To vnl_vector vnl_vector_fixed vnlPoint; for (int i = 0; i < 3; ++i) { vnlPoint[i] = this->GetPosition()[i]; } // invert position vnlPoint = -(navigationDataInverse->GetOrientation().rotate(vnlPoint)); // back to Point3D Point3D invertedPosition = this->GetPosition(); for (int i = 0; i < 3; ++i) { invertedPosition[i] = vnlPoint[i]; } navigationDataInverse->SetPosition(invertedPosition); // Inversion does not care for covariances for now navigationDataInverse->ResetCovarianceValidity(); return navigationDataInverse; } void mitk::NavigationData::ResetCovarianceValidity() { this->SetHasPosition(false); this->SetHasOrientation(false); } mitk::NavigationData::Pointer mitk::NavigationData::getComposition(const mitk::NavigationData::Pointer nd1, const mitk::NavigationData::Pointer nd2) { NavigationData::Pointer nd3 = nd1->Clone(); // A2 * A1 nd3->SetOrientation(nd2->GetOrientation() * nd1->GetOrientation()); // first: b1, b2 vnl vector vnl_vector_fixed b1, b2, b3; for (int i = 0; i < 3; ++i) { b1[i] = nd1->GetPosition()[i]; b2[i] = nd2->GetPosition()[i]; } // b3 = A2b1 + b2 b3 = nd2->GetOrientation().rotate(b1) + b2; // back to mitk::Point3D Point3D point; for (int i = 0; i < 3; ++i) { point[i] = b3[i]; } nd3->SetPosition(point); nd3->ResetCovarianceValidity(); return nd3; } bool mitk::Equal(const mitk::NavigationData& leftHandSide, const mitk::NavigationData& rightHandSide, ScalarType eps, bool verbose) { bool returnValue = true; // Dimensionality if( !mitk::Equal(rightHandSide.GetPosition(), leftHandSide.GetPosition(), eps) ) { if(verbose) { MITK_INFO << "[( NavigationData )] Position differs."; MITK_INFO << "leftHandSide is " << leftHandSide.GetPosition() << "rightHandSide is " << rightHandSide.GetPosition(); } returnValue = false; } // Dimensionality if( !mitk::Equal(rightHandSide.GetOrientation(), leftHandSide.GetOrientation(), eps) ) { if(verbose) { MITK_INFO << "[( NavigationData )] Orientation differs."; MITK_INFO << "leftHandSide is " << leftHandSide.GetOrientation() << "rightHandSide is " << rightHandSide.GetOrientation(); } returnValue = false; } if( rightHandSide.GetCovErrorMatrix() != leftHandSide.GetCovErrorMatrix() ) { if(verbose) { MITK_INFO << "[( NavigationData )] CovErrorMatrix differs."; MITK_INFO << "leftHandSide is " << leftHandSide.GetCovErrorMatrix() << "rightHandSide is " << rightHandSide.GetCovErrorMatrix(); } returnValue = false; } if( std::string(rightHandSide.GetName()) != std::string(leftHandSide.GetName()) ) { if(verbose) { MITK_INFO << "[( NavigationData )] Name differs."; MITK_INFO << "leftHandSide is " << leftHandSide.GetName() << "rightHandSide is " << rightHandSide.GetName(); } returnValue = false; } if( rightHandSide.GetIGTTimeStamp() != leftHandSide.GetIGTTimeStamp() ) { if(verbose) { MITK_INFO << "[( NavigationData )] IGTTimeStamp differs."; MITK_INFO << "leftHandSide is " << leftHandSide.GetIGTTimeStamp() << "rightHandSide is " << rightHandSide.GetIGTTimeStamp(); } returnValue = false; } return returnValue; } diff --git a/Modules/US/USNavigation/Filter/mitkNeedleProjectionFilter.cpp b/Modules/US/USNavigation/Filter/mitkNeedleProjectionFilter.cpp index 4b4065d024..5053bafaec 100644 --- a/Modules/US/USNavigation/Filter/mitkNeedleProjectionFilter.cpp +++ b/Modules/US/USNavigation/Filter/mitkNeedleProjectionFilter.cpp @@ -1,169 +1,198 @@ /*=================================================================== 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. ===================================================================*/ // MITK #include "mitkNeedleProjectionFilter.h" #include #include "mitkUSCombinedModality.h" // VTK #include - - mitk::NeedleProjectionFilter::NeedleProjectionFilter() : m_Projection(mitk::PointSet::New()), m_OriginalPoints(mitk::PointSet::New()), - m_SelectedInput(-1) + m_SelectedInput(-1), + m_ShowToolAxis(false) { // Tool Coordinates:x axis is chosen as default axis when no axis is specified - for (int i = 0; i < 2; i++) + + MITK_DEBUG << "Constructor called"; + + mitk::Point3D toolAxis; + mitk::FillVector3D(toolAxis, 1, 0, 0); + m_ToolAxis = toolAxis; + InitializeOriginalPoints(toolAxis, m_ShowToolAxis); + + MITK_DEBUG << "orginal point 0 set constructor" << m_OriginalPoints->GetPoint(0); + MITK_DEBUG << "orginal point 1 set constructor" << m_OriginalPoints->GetPoint(1); +} + +void mitk::NeedleProjectionFilter::InitializeOriginalPoints(mitk::Point3D toolAxis, bool showToolAxis) +{ + m_OriginalPoints = mitk::PointSet::New(); + + mitk::Point3D projectionPoint; + projectionPoint.SetElement(0, toolAxis.GetElement(0) * 400); + projectionPoint.SetElement(1, toolAxis.GetElement(1) * 400); + projectionPoint.SetElement(2, toolAxis.GetElement(2) * 400); + m_OriginalPoints->InsertPoint(projectionPoint); + + mitk::Point3D toolOrigin; + toolOrigin.SetElement(0, 0); + toolOrigin.SetElement(1, 0); + toolOrigin.SetElement(2, 0); + m_OriginalPoints->InsertPoint(toolOrigin); + + if (showToolAxis) { - mitk::Point3D point; - point.SetElement(0, i * 400); - point.SetElement(1, 0); - point.SetElement(2, 0); - m_OriginalPoints->InsertPoint(i, point); + mitk::Point3D axisPoint; + axisPoint.SetElement(0, toolAxis.GetElement(0) * -400); + axisPoint.SetElement(1, toolAxis.GetElement(1) * -400); + axisPoint.SetElement(2, toolAxis.GetElement(2) * -400); + m_OriginalPoints->InsertPoint(axisPoint); } + +} + +void mitk::NeedleProjectionFilter::ShowToolAxis(bool enabled) +{ + m_ShowToolAxis = enabled; + InitializeOriginalPoints(m_ToolAxis,m_ShowToolAxis); } void mitk::NeedleProjectionFilter::SetToolAxisForFilter(mitk::Point3D point) { - // Tool Coordinates: First point - Tip of Needle, Second Point - 40 cm distance from needle - mitk::Point3D originPoint; - originPoint.SetElement(0, 0); - originPoint.SetElement(1, 0); - originPoint.SetElement(2, 0); - m_OriginalPoints->SetPoint(0, originPoint); - - mitk::Point3D endPoint; - endPoint.SetElement(0, point.GetElement(0) * 400); - endPoint.SetElement(1, point.GetElement(1) * 400); - endPoint.SetElement(2, point.GetElement(2) * 400); - MITK_INFO << "Tool axis in project filter:"; - MITK_INFO << endPoint; - m_OriginalPoints->SetPoint(1, endPoint); + m_ToolAxis = point; + InitializeOriginalPoints(m_ToolAxis, m_ShowToolAxis); + + MITK_DEBUG << "orginal point 1 set mutator" << m_OriginalPoints->GetPoint(1); + MITK_DEBUG << "orginal point 0 set mutator" << m_OriginalPoints->GetPoint(0); } mitk::NeedleProjectionFilter::~NeedleProjectionFilter() { } void mitk::NeedleProjectionFilter::SelectInput(int i) { if (i < 0) mitkThrow() << "Negative Input selected in NeedleProjectionFilter"; if (! (static_cast(i) < this->GetInputs().size())) mitkThrow() << "Selected input index is larger than actual number of inputs in NeedleProjectionFilter"; m_SelectedInput = i; } void mitk::NeedleProjectionFilter::GenerateData() { // copy the navigation data from the inputs to the outputs mitk::NavigationDataPassThroughFilter::GenerateData(); // If no reference has been set yet, warn and abort if (m_SelectedInput == -1) { MITK_INFO << "No input has been selected in NeedleProjection Filter. Only forwarding NavigationData..."; return; } // Cancel, if selected tool is currently not being tracked if (! GetInput(m_SelectedInput)->IsDataValid()) return; // Outputs have been updated, now to calculate the Projection // 1) Generate Pseudo-Geometry for Input mitk::AffineTransform3D::Pointer refTrans = this->NavigationDataToTransform(this->GetInput(m_SelectedInput)); mitk::Geometry3D::Pointer refGeom = this->TransformToGeometry(refTrans); + // 2) Transform Original Pointset m_OriginalPoints->SetGeometry(refGeom); // Update Projection (We do not clone, since we want to keep properties alive) m_Projection->SetPoint(0, m_OriginalPoints->GetPoint(0)); m_Projection->SetPoint(1, m_OriginalPoints->GetPoint(1)); + if (m_ShowToolAxis) { m_Projection->SetPoint(2, m_OriginalPoints->GetPoint(2)); } + // 3a) If no target Plane has been set, then leave it at that if (this->m_TargetPlane.IsNull()) return; // 3b) else, calculate intersection with plane mitk::PlaneGeometry::Pointer plane = mitk::PlaneGeometry::New(); plane->SetIndexToWorldTransform(m_TargetPlane); //plane->TransferItkToVtkTransform(); //included in SetIndexToWorldTransform double t; double x[3]; // Points that define the needle vector double p1[3] = {m_OriginalPoints->GetPoint(0)[0], m_OriginalPoints->GetPoint(0)[1], m_OriginalPoints->GetPoint(0)[2]}; double p2[3] = {m_OriginalPoints->GetPoint(1)[0], m_OriginalPoints->GetPoint(1)[1], m_OriginalPoints->GetPoint(1)[2]}; // Center of image plane and it's normal double center[3] = {plane->GetCenter()[0], plane->GetCenter()[1], plane->GetCenter()[2]}; double normal[3] = {plane->GetNormal()[0], plane->GetNormal()[1], plane->GetNormal()[2]}; vtkPlane::IntersectWithLine(p1, p2, normal, center, t, x); // change (cut) needle path only if the needle points to the image plane; // otherwise the needle path direction would be changed pointing to the image plane if ( t >= 0 ) { // Convert vtk to itk mitk::Point3D intersection; intersection[0] = x[0]; intersection[1] = x[1]; intersection[2] = x[2]; // Replace distant point with image intersection - m_Projection->SetPoint(1, intersection); + m_Projection->SetPoint(0, intersection); + } } mitk::AffineTransform3D::Pointer mitk::NeedleProjectionFilter::NavigationDataToTransform(const mitk::NavigationData * nd) { mitk::AffineTransform3D::Pointer affineTransform = mitk::AffineTransform3D::New(); affineTransform->SetIdentity(); //calculate the transform from the quaternions static itk::QuaternionRigidTransform::Pointer quatTransform = itk::QuaternionRigidTransform::New(); mitk::NavigationData::OrientationType orientation = nd->GetOrientation(); // convert mitk::ScalarType quaternion to double quaternion because of itk bug vnl_quaternion doubleQuaternion(orientation.x(), orientation.y(), orientation.z(), orientation.r()); quatTransform->SetIdentity(); quatTransform->SetRotation(doubleQuaternion); quatTransform->Modified(); /* because of an itk bug, the transform can not be calculated with float data type. To use it in the mitk geometry classes, it has to be transfered to mitk::ScalarType which is float */ static AffineTransform3D::MatrixType m; mitk::TransferMatrix(quatTransform->GetMatrix(), m); affineTransform->SetMatrix(m); /*set the offset by convert from itkPoint to itkVector and setting offset of transform*/ mitk::Vector3D pos; pos.SetVnlVector(nd->GetPosition().GetVnlVector()); affineTransform->SetOffset(pos); affineTransform->Modified(); return affineTransform; } mitk::Geometry3D::Pointer mitk::NeedleProjectionFilter::TransformToGeometry(mitk::AffineTransform3D::Pointer transform){ mitk::Geometry3D::Pointer g3d = mitk::Geometry3D::New(); mitk::ScalarType scale[] = {1.0, 1.0, 1.0}; g3d->SetSpacing(scale); g3d->SetIndexToWorldTransform(transform); //g3d->TransferItkToVtkTransform(); // update VTK Transform for rendering too //included in SetIndexToWorldTransform g3d->Modified(); return g3d; } diff --git a/Modules/US/USNavigation/Filter/mitkNeedleProjectionFilter.h b/Modules/US/USNavigation/Filter/mitkNeedleProjectionFilter.h index 1b03ffd6ea..4bbf1f4eb6 100644 --- a/Modules/US/USNavigation/Filter/mitkNeedleProjectionFilter.h +++ b/Modules/US/USNavigation/Filter/mitkNeedleProjectionFilter.h @@ -1,86 +1,97 @@ /*=================================================================== 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. ===================================================================*/ #ifndef NEEDLEPROJECTIONFILTER_H_INCLUDED #define NEEDLEPROJECTIONFILTER_H_INCLUDED #include // MITK #include #include #include #include namespace mitk { /** * \brief This filter projects a needle's path onto a plane. * * To use it, hook it up to a NavigationDataStream, * select an input and set an AffineTransform 3D that represents the target plane. * You can then call GetProjection to retrieve a pointset that represents the projected path. * You may change the PointSet's properties, these changes will not be overwritten. * If no Input is selected, the target Pointset will not update * If no Target Plane is selected, The projection line will always be 40 cm long * Any points you add to the pointSet will be overwritten during the next Update. * The point with index zero is the Tip of the Needle. * The Point with index one is the projection onto the plane. * * Projection will happen onto an extension of the plane as well - the filter does not regard boundaries * This Filter currently only supports projection of one needle. Extension to multiple needles / planes should be easy. * * \ingroup US */ class MITKUSNAVIGATION_EXPORT NeedleProjectionFilter : public NavigationDataPassThroughFilter { public: mitkClassMacro(NeedleProjectionFilter, NavigationDataPassThroughFilter); itkNewMacro(Self); virtual void SelectInput(int i); itkGetMacro(TargetPlane, mitk::AffineTransform3D::Pointer); itkSetMacro(TargetPlane, mitk::AffineTransform3D::Pointer); itkGetMacro(Projection, mitk::PointSet::Pointer); + /** Sets the tool axis for this filter. The default tool axis is along the x-axis in + * tool coordinates. */ void SetToolAxisForFilter(mitk::Point3D point); + /** Sets whether the tool axis should be visualized. This is required if no surface is available. + * If disabled only the projection and not the axis is shown. It's disabled by default. */ + void ShowToolAxis(bool enabled); protected: NeedleProjectionFilter(); virtual ~NeedleProjectionFilter(); virtual void GenerateData() override; mitk::AffineTransform3D::Pointer m_TargetPlane; mitk::PointSet::Pointer m_Projection; mitk::PointSet::Pointer m_OriginalPoints; + bool m_ShowToolAxis; + mitk::Point3D m_ToolAxis; int m_SelectedInput; + /** Internal method for initialization of the projection / tool axis representation + * by the point set m_OriginalPoints. */ + void InitializeOriginalPoints(mitk::Point3D toolAxis, bool showToolAxis); + /** * \brief Creates an Affine Transformation from a Navigation Data Object. */ mitk::AffineTransform3D::Pointer NavigationDataToTransform(const mitk::NavigationData * nd); /** * \brief Creates an Geometry 3D Object from an AffineTransformation. */ mitk::Geometry3D::Pointer TransformToGeometry(mitk::AffineTransform3D::Pointer transform); }; } // namespace mitk #endif diff --git a/Plugins/org.mitk.gui.qt.igt.app.echotrack/src/internal/NavigationStepWidgets/QmitkUSNavigationStepMarkerIntervention.cpp b/Plugins/org.mitk.gui.qt.igt.app.echotrack/src/internal/NavigationStepWidgets/QmitkUSNavigationStepMarkerIntervention.cpp index f00972ed30..ea4bf373d0 100644 --- a/Plugins/org.mitk.gui.qt.igt.app.echotrack/src/internal/NavigationStepWidgets/QmitkUSNavigationStepMarkerIntervention.cpp +++ b/Plugins/org.mitk.gui.qt.igt.app.echotrack/src/internal/NavigationStepWidgets/QmitkUSNavigationStepMarkerIntervention.cpp @@ -1,1020 +1,1076 @@ /*=================================================================== 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 "QmitkUSNavigationStepMarkerIntervention.h" #include "ui_QmitkUSNavigationStepMarkerIntervention.h" #include "mitkBaseRenderer.h" #include "mitkContourModel.h" #include "mitkNeedleProjectionFilter.h" #include "mitkNodeDisplacementFilter.h" #include "mitkSurface.h" #include "mitkTextAnnotation2D.h" #include #include #include "../Filter/mitkUSNavigationTargetIntersectionFilter.h" #include "../Filter/mitkUSNavigationTargetOcclusionFilter.h" #include "../Filter/mitkUSNavigationTargetUpdateFilter.h" #include "../USNavigationMarkerPlacement.h" #include "../Widgets/QmitkZoneProgressBar.h" #include "../mitkUSTargetPlacementQualityCalculator.h" #include "../Interactors/mitkUSPointMarkInteractor.h" #include "usModuleRegistry.h" #include "mitkLookupTable.h" #include "mitkLookupTableProperty.h" #include "mitkSurface.h" // VTK #include "vtkCellLocator.h" #include "vtkDataSet.h" #include "vtkDoubleArray.h" #include "vtkFloatArray.h" #include "vtkIdList.h" #include "vtkLinearTransform.h" #include "vtkLookupTable.h" #include "vtkMath.h" #include "vtkOBBTree.h" #include "vtkPointData.h" #include "vtkPointData.h" #include "vtkPolyData.h" #include "vtkSelectEnclosedPoints.h" #include "vtkSmartPointer.h" #include "vtkSphereSource.h" #include "vtkTransformPolyDataFilter.h" #include "vtkWarpScalar.h" QmitkUSNavigationStepMarkerIntervention::QmitkUSNavigationStepMarkerIntervention(QWidget *parent) : QmitkUSAbstractNavigationStep(parent), m_NumberOfTargets(0), m_TargetProgressBar(0), m_PlannedTargetProgressBar(0), m_CurrentTargetIndex(0), m_CurrentTargetReached(false), m_ShowPlanningColors(false), m_NeedleProjectionFilter(mitk::NeedleProjectionFilter::New()), m_NodeDisplacementFilter(mitk::NodeDisplacementFilter::New()), m_TargetUpdateFilter(mitk::USNavigationTargetUpdateFilter::New()), m_TargetOcclusionFilter(mitk::USNavigationTargetOcclusionFilter::New()), m_TargetIntersectionFilter(mitk::USNavigationTargetIntersectionFilter::New()), m_PlacementQualityCalculator(mitk::USTargetPlacementQualityCalculator::New()), m_ListenerTargetCoordinatesChanged(this, &QmitkUSNavigationStepMarkerIntervention::UpdateTargetCoordinates), m_TargetStructureWarnOverlay(mitk::TextAnnotation2D::New()), m_ReferenceSensorIndex(1), m_NeedleSensorIndex(0), ui(new Ui::QmitkUSNavigationStepMarkerIntervention) { m_ActiveTargetColor[0] = 1; m_ActiveTargetColor[1] = 1; m_ActiveTargetColor[2] = 0; m_InactiveTargetColor[0] = 1; m_InactiveTargetColor[1] = 1; m_InactiveTargetColor[2] = 0.5; m_ReachedTargetColor[0] = 0.6; m_ReachedTargetColor[1] = 1; m_ReachedTargetColor[2] = 0.6; ui->setupUi(this); connect(ui->freezeImageButton, SIGNAL(SignalFreezed(bool)), this, SLOT(OnFreeze(bool))); connect(ui->backToLastTargetButton, SIGNAL(clicked()), this, SLOT(OnBackToLastTargetClicked())); connect(ui->targetReachedButton, SIGNAL(clicked()), this, SLOT(OnTargetLeft())); connect(this, SIGNAL(TargetReached(int)), this, SLOT(OnTargetReached())); connect(this, SIGNAL(TargetLeft(int)), this, SLOT(OnTargetLeft())); connect(ui->riskStructuresRangeWidget, SIGNAL(SignalZoneViolated(const mitk::DataNode *, mitk::Point3D)), this, SLOT(OnRiskZoneViolated(const mitk::DataNode *, mitk::Point3D))); m_PointMarkInteractor = mitk::USPointMarkInteractor::New(); m_PointMarkInteractor->CoordinatesChangedEvent.AddListener(m_ListenerTargetCoordinatesChanged); this->GenerateTargetColorLookupTable(); m_TargetProgressBar = new QmitkZoneProgressBar(QString::fromStdString("Target: %1 mm"), 200, 0, this); m_TargetProgressBar->SetTextFormatInvalid("Target is not on Needle Path"); ui->targetStructuresRangeLayout->addWidget(m_TargetProgressBar); m_TargetUpdateFilter->SetScalarArrayIdentifier("USNavigation::ReachedTargetScores"); } +QmitkUSNavigationStepMarkerIntervention::QmitkUSNavigationStepMarkerIntervention(mitk::Point3D toolAxis, QWidget *parent) + : QmitkUSAbstractNavigationStep(parent), + m_NumberOfTargets(0), + m_TargetProgressBar(0), + m_PlannedTargetProgressBar(0), + m_CurrentTargetIndex(0), + m_CurrentTargetReached(false), + m_ShowPlanningColors(false), + m_NeedleProjectionFilter(mitk::NeedleProjectionFilter::New()), + m_NodeDisplacementFilter(mitk::NodeDisplacementFilter::New()), + m_TargetUpdateFilter(mitk::USNavigationTargetUpdateFilter::New()), + m_TargetOcclusionFilter(mitk::USNavigationTargetOcclusionFilter::New()), + m_TargetIntersectionFilter(mitk::USNavigationTargetIntersectionFilter::New()), + m_PlacementQualityCalculator(mitk::USTargetPlacementQualityCalculator::New()), + m_ListenerTargetCoordinatesChanged(this, &QmitkUSNavigationStepMarkerIntervention::UpdateTargetCoordinates), + m_TargetStructureWarnOverlay(mitk::TextAnnotation2D::New()), + m_ReferenceSensorIndex(1), + m_NeedleSensorIndex(0), + ui(new Ui::QmitkUSNavigationStepMarkerIntervention) +{ + m_ActiveTargetColor[0] = 1; + m_ActiveTargetColor[1] = 1; + m_ActiveTargetColor[2] = 0; + m_InactiveTargetColor[0] = 1; + m_InactiveTargetColor[1] = 1; + m_InactiveTargetColor[2] = 0.5; + m_ReachedTargetColor[0] = 0.6; + m_ReachedTargetColor[1] = 1; + m_ReachedTargetColor[2] = 0.6; + + ui->setupUi(this); + + m_NeedleProjectionFilter->SetToolAxisForFilter(toolAxis); + + connect(ui->freezeImageButton, SIGNAL(SignalFreezed(bool)), this, SLOT(OnFreeze(bool))); + connect(ui->backToLastTargetButton, SIGNAL(clicked()), this, SLOT(OnBackToLastTargetClicked())); + connect(ui->targetReachedButton, SIGNAL(clicked()), this, SLOT(OnTargetLeft())); + connect(this, SIGNAL(TargetReached(int)), this, SLOT(OnTargetReached())); + connect(this, SIGNAL(TargetLeft(int)), this, SLOT(OnTargetLeft())); + connect(ui->riskStructuresRangeWidget, + SIGNAL(SignalZoneViolated(const mitk::DataNode *, mitk::Point3D)), + this, + SLOT(OnRiskZoneViolated(const mitk::DataNode *, mitk::Point3D))); + + m_PointMarkInteractor = mitk::USPointMarkInteractor::New(); + m_PointMarkInteractor->CoordinatesChangedEvent.AddListener(m_ListenerTargetCoordinatesChanged); + + this->GenerateTargetColorLookupTable(); + + m_TargetProgressBar = new QmitkZoneProgressBar(QString::fromStdString("Target: %1 mm"), 200, 0, this); + m_TargetProgressBar->SetTextFormatInvalid("Target is not on Needle Path"); + ui->targetStructuresRangeLayout->addWidget(m_TargetProgressBar); + + m_TargetUpdateFilter->SetScalarArrayIdentifier("USNavigation::ReachedTargetScores"); +} + QmitkUSNavigationStepMarkerIntervention::~QmitkUSNavigationStepMarkerIntervention() { mitk::DataStorage::Pointer dataStorage = this->GetDataStorage(false); if (dataStorage.IsNotNull()) { // remove the node for the needle path mitk::DataNode::Pointer node = this->GetNamedDerivedNode("Needle Path", QmitkUSAbstractNavigationStep::DATANAME_BASENODE); if (node.IsNotNull()) { dataStorage->Remove(node); } } if (m_SegmentationNode.IsNotNull()) { this->GetDataStorage()->Remove(m_SegmentationNode); } if (m_ForegroundModelPointsNode.IsNotNull()) { this->GetDataStorage()->Remove(m_ForegroundModelPointsNode); } delete ui; m_PointMarkInteractor->CoordinatesChangedEvent.RemoveListener(m_ListenerTargetCoordinatesChanged); } bool QmitkUSNavigationStepMarkerIntervention::OnStartStep() { m_NeedleProjectionFilter->SelectInput(m_NeedleSensorIndex); // create node for Needle Projection mitk::DataNode::Pointer node = this->GetNamedDerivedNodeAndCreate("Needle Path", QmitkUSAbstractNavigationStep::DATANAME_BASENODE); node->SetData(m_NeedleProjectionFilter->GetProjection()); node->SetBoolProperty("show contour", true); // initialize warning overlay (and do not display it, yet) m_TargetStructureWarnOverlay->SetText("Warning: Needle is Inside the Target Structure."); m_TargetStructureWarnOverlay->SetVisibility(false); // set position and font size for the text overlay mitk::Point2D overlayPosition; overlayPosition.SetElement(0, 10.0f); overlayPosition.SetElement(1, 10.0f); m_TargetStructureWarnOverlay->SetPosition2D(overlayPosition); m_TargetStructureWarnOverlay->SetFontSize(18); // overlay should be red mitk::Color color; color[0] = 1; color[1] = 0; color[2] = 0; m_TargetStructureWarnOverlay->SetColor(color); mitk::ManualPlacementAnnotationRenderer::AddAnnotation(m_TargetStructureWarnOverlay.GetPointer(), "stdmulti.widget4"); return true; } bool QmitkUSNavigationStepMarkerIntervention::OnStopStep() { mitk::DataStorage::Pointer dataStorage = this->GetDataStorage(); // remove all reached nodes from the data storage for (QVector>::iterator it = m_ReachedTargetsNodes.begin(); it != m_ReachedTargetsNodes.end(); ++it) { dataStorage->Remove(*it); } m_ReachedTargetsNodes.clear(); m_CurrentTargetIndex = 0; // reset button states ui->freezeImageButton->setEnabled(false); ui->backToLastTargetButton->setEnabled(false); ui->targetReachedButton->setEnabled(true); // make sure that it is unfreezed after stopping the step ui->freezeImageButton->Unfreeze(); // remove base node for reached targets from the data storage mitk::DataNode::Pointer reachedTargetsNode = this->GetNamedDerivedNode( QmitkUSAbstractNavigationStep::DATANAME_BASENODE, USNavigationMarkerPlacement::DATANAME_REACHED_TARGETS); if (reachedTargetsNode.IsNotNull()) { dataStorage->Remove(reachedTargetsNode); } return true; } bool QmitkUSNavigationStepMarkerIntervention::OnFinishStep() { return true; } bool QmitkUSNavigationStepMarkerIntervention::OnActivateStep() { this->ClearZones(); // clear risk zones before adding new ones // get target node from data storage and make sure that it contains data m_TargetNode = this->GetNamedDerivedNode(USNavigationMarkerPlacement::DATANAME_TARGETSURFACE, USNavigationMarkerPlacement::DATANAME_TUMOUR); if (m_TargetNode.IsNull() || m_TargetNode->GetData() == 0) { mitkThrow() << "Target node (" << USNavigationMarkerPlacement::DATANAME_TARGETSURFACE << ") must not be null."; } // get target data and make sure that it is a surface m_TargetSurface = dynamic_cast(m_TargetNode->GetData()); if (m_TargetSurface.IsNull()) { mitkThrow() << "Target node (" << USNavigationMarkerPlacement::DATANAME_TARGETSURFACE << ") data must be of type mitk::Surface"; } // delete progress bars for reinitializing them again afterwards if (m_PlannedTargetProgressBar) { ui->targetStructuresRangeLayout->removeWidget(m_PlannedTargetProgressBar); delete m_PlannedTargetProgressBar; m_PlannedTargetProgressBar = 0; } m_NodeDisplacementFilter->SelectInput(m_ReferenceSensorIndex); this->UpdateTargetProgressDisplay(); mitk::DataNode::Pointer tumourNode = this->GetNamedDerivedNode(USNavigationMarkerPlacement::DATANAME_TUMOUR, QmitkUSAbstractNavigationStep::DATANAME_BASENODE); if (tumourNode.IsNotNull()) { // do not show tumour node during intervention (target surface is shown) tumourNode->SetBoolProperty("visible", false); // add tumour as a risk structure ui->riskStructuresRangeWidget->AddZone(tumourNode); } // set target structure for target update filter m_TargetUpdateFilter->SetTargetStructure(m_TargetNode); m_TargetOcclusionFilter->SetTargetStructure(m_TargetNode); // set lookup table of tumour node m_TargetNode->SetProperty("LookupTable", m_TargetColorLookupTableProperty); // mitk::DataNode::Pointer targetsBaseNode = this->GetNamedDerivedNode(USNavigationMarkerPlacement::DATANAME_TARGETS, QmitkUSAbstractNavigationStep::DATANAME_BASENODE); mitk::DataStorage::SetOfObjects::ConstPointer plannedTargetNodes; if (targetsBaseNode.IsNotNull()) { plannedTargetNodes = this->GetDataStorage()->GetDerivations(targetsBaseNode); } if (plannedTargetNodes.IsNotNull() && plannedTargetNodes->Size() > 0) { for (mitk::DataStorage::SetOfObjects::ConstIterator it = plannedTargetNodes->Begin(); it != plannedTargetNodes->End(); ++it) { m_PlannedTargetsNodes.push_back(it->Value()); } m_PlannedTargetProgressBar = new QmitkZoneProgressBar(QString::fromStdString("Planned Target"), 200, 0); ui->targetStructuresRangeLayout->addWidget(m_PlannedTargetProgressBar); } // add progress bars for risk zone nodes mitk::DataNode::Pointer zonesBaseNode = this->GetNamedDerivedNode(USNavigationMarkerPlacement::DATANAME_ZONES, QmitkUSAbstractNavigationStep::DATANAME_BASENODE); // only add progress bars if the base node for zones was created if (zonesBaseNode.IsNotNull()) { mitk::DataStorage::SetOfObjects::ConstPointer zoneNodes = this->GetDataStorage()->GetDerivations(zonesBaseNode); for (mitk::DataStorage::SetOfObjects::ConstIterator it = zoneNodes->Begin(); it != zoneNodes->End(); ++it) { ui->riskStructuresRangeWidget->AddZone(it->Value()); } m_TargetOcclusionFilter->SelectStartPositionInput(m_NeedleSensorIndex); m_TargetOcclusionFilter->SetObstacleStructures(zoneNodes); } return true; } bool QmitkUSNavigationStepMarkerIntervention::OnDeactivateStep() { ui->freezeImageButton->Unfreeze(); return true; } void QmitkUSNavigationStepMarkerIntervention::OnUpdate() { // get navigation data source and make sure that it is not null mitk::NavigationDataSource::Pointer navigationDataSource = this->GetCombinedModality()->GetNavigationDataSource(); if (navigationDataSource.IsNull()) { MITK_ERROR("QmitkUSAbstractNavigationStep") ("QmitkUSNavigationStepMarkerIntervention") << "Navigation Data Source of Combined Modality must not be null."; mitkThrow() << "Navigation Data Source of Combined Modality must not be null."; } ui->riskStructuresRangeWidget->UpdateDistancesToNeedlePosition(navigationDataSource->GetOutput(m_NeedleSensorIndex)); this->UpdateBodyMarkerStatus(navigationDataSource->GetOutput(m_ReferenceSensorIndex)); this->UpdateTargetColors(); this->UpdateTargetScore(); this->UpdateTargetViolationStatus(); } void QmitkUSNavigationStepMarkerIntervention::OnSettingsChanged(const itk::SmartPointer settingsNode) { if (settingsNode.IsNull()) { return; } int numberOfTargets; if (settingsNode->GetIntProperty("settings.number-of-targets", numberOfTargets)) { m_NumberOfTargets = numberOfTargets; m_TargetUpdateFilter->SetNumberOfTargets(numberOfTargets); m_PlacementQualityCalculator->SetOptimalAngle(m_TargetUpdateFilter->GetOptimalAngle()); } std::string referenceSensorName; if (settingsNode->GetStringProperty("settings.reference-name-selected", referenceSensorName)) { m_ReferenceSensorName = referenceSensorName; } std::string needleSensorName; if (settingsNode->GetStringProperty("settings.needle-name-selected", needleSensorName)) { m_NeedleSensorName = needleSensorName; } this->UpdateSensorsNames(); } QString QmitkUSNavigationStepMarkerIntervention::GetTitle() { return "Computer-assisted Intervention"; } bool QmitkUSNavigationStepMarkerIntervention::GetIsRestartable() { return true; } QmitkUSAbstractNavigationStep::FilterVector QmitkUSNavigationStepMarkerIntervention::GetFilter() { FilterVector filter; filter.push_back(m_NeedleProjectionFilter.GetPointer()); filter.push_back(m_NodeDisplacementFilter.GetPointer()); filter.push_back(m_TargetOcclusionFilter.GetPointer()); return filter; } void QmitkUSNavigationStepMarkerIntervention::OnSetCombinedModality() { mitk::USCombinedModality::Pointer combinedModality = this->GetCombinedModality(false); if (combinedModality.IsNotNull()) { mitk::AffineTransform3D::Pointer calibration = combinedModality->GetCalibration(); if (calibration.IsNotNull()) { m_NeedleProjectionFilter->SetTargetPlane(calibration); } } ui->freezeImageButton->SetCombinedModality(combinedModality, m_ReferenceSensorIndex); this->UpdateSensorsNames(); } void QmitkUSNavigationStepMarkerIntervention::OnTargetReached() { m_CurrentTargetReached = true; } void QmitkUSNavigationStepMarkerIntervention::OnTargetLeft() { m_CurrentTargetReached = false; m_CurrentTargetIndex++; if (m_CurrentTargetIndex >= 0 && static_cast(m_CurrentTargetIndex) >= m_NumberOfTargets) { ui->targetReachedButton->setDisabled(true); } ui->backToLastTargetButton->setEnabled(true); ui->freezeImageButton->setEnabled(true); this->UpdateTargetProgressDisplay(); if (m_ReachedTargetsNodes.size() < m_CurrentTargetIndex) { mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetName( (QString("Target ") + QString("%1").arg(m_CurrentTargetIndex, 2, 10, QLatin1Char('0'))).toStdString()); this->GetDataStorage()->Add( node, this->GetNamedDerivedNodeAndCreate(USNavigationMarkerPlacement::DATANAME_REACHED_TARGETS, QmitkUSAbstractNavigationStep::DATANAME_BASENODE)); m_ReachedTargetsNodes.push_back(node); } mitk::DataNode::Pointer node = m_ReachedTargetsNodes.at(m_CurrentTargetIndex - 1); mitk::Surface::Pointer zone = mitk::Surface::New(); // create a vtk sphere with given radius vtkSphereSource *vtkData = vtkSphereSource::New(); vtkData->SetRadius(5); vtkData->SetCenter(0, 0, 0); vtkData->Update(); zone->SetVtkPolyData(vtkData->GetOutput()); vtkData->Delete(); // set vtk sphere and origin to data node node->SetData(zone); node->GetData()->GetGeometry()->SetOrigin( this->GetCombinedModality()->GetNavigationDataSource()->GetOutput(m_NeedleSensorIndex)->GetPosition()); node->SetColor(0.2, 0.9, 0.2); this->UpdateTargetCoordinates(node); } void QmitkUSNavigationStepMarkerIntervention::OnBackToLastTargetClicked() { if (m_CurrentTargetIndex < 1) { MITK_WARN << "Cannot go back to last target as there is no last target."; return; } m_CurrentTargetIndex--; if (m_ReachedTargetsNodes.size() > m_CurrentTargetIndex) { this->GetDataStorage()->Remove(m_ReachedTargetsNodes.last()); MITK_INFO("QmitkUSAbstractNavigationStep") ("QmitkUSNavigationStepMarkerIntervention") << "Removed Target " << m_ReachedTargetsNodes.size(); m_ReachedTargetsNodes.pop_back(); } if (m_CurrentTargetIndex == 0) { ui->backToLastTargetButton->setDisabled(true); } if (m_CurrentTargetIndex >= 0 && static_cast(m_CurrentTargetIndex) < m_NumberOfTargets) { ui->targetReachedButton->setEnabled(true); } ui->freezeImageButton->setEnabled(false); ui->freezeImageButton->Unfreeze(); this->UpdateTargetProgressDisplay(); m_TargetUpdateFilter->RemovePositionOfTarget(m_CurrentTargetIndex); } void QmitkUSNavigationStepMarkerIntervention::OnFreeze(bool freezed) { if (freezed) { this->GetCombinedModality()->SetIsFreezed(true); // load state machine and event config for data interactor m_PointMarkInteractor->LoadStateMachine("USPointMarkInteractions.xml", us::ModuleRegistry::GetModule("MitkUS")); m_PointMarkInteractor->SetEventConfig("globalConfig.xml"); if (m_CurrentTargetIndex < 1) { mitkThrow() << "Current target index has to be greater zero when freeze button is clicked."; } if (m_ReachedTargetsNodes.size() < m_CurrentTargetIndex) { mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetName( (QString("Target ") + QString("%1").arg(m_CurrentTargetIndex, 2, 10, QLatin1Char('0'))).toStdString()); this->GetDataStorage()->Add( node, this->GetNamedDerivedNodeAndCreate(USNavigationMarkerPlacement::DATANAME_REACHED_TARGETS, QmitkUSAbstractNavigationStep::DATANAME_BASENODE)); m_ReachedTargetsNodes.push_back(node); } m_PointMarkInteractor->SetDataNode(m_ReachedTargetsNodes.last()); } else { m_PointMarkInteractor->SetDataNode(0); this->GetCombinedModality()->SetIsFreezed(false); } } void QmitkUSNavigationStepMarkerIntervention::OnShowPlanningView(bool show) { m_ShowPlanningColors = show; } void QmitkUSNavigationStepMarkerIntervention::OnRiskZoneViolated(const mitk::DataNode *node, mitk::Point3D position) { MITK_INFO << "Risk zone (" << node->GetName() << ") violated at position " << position << "."; } void QmitkUSNavigationStepMarkerIntervention::ClearZones() { ui->riskStructuresRangeWidget->ClearZones(); // remove all reached target nodes from the data storage and clear the list mitk::DataStorage::Pointer dataStorage = this->GetDataStorage(); for (QVector::iterator it = m_ReachedTargetsNodes.begin(); it != m_ReachedTargetsNodes.end(); ++it) { if (it->IsNotNull()) { dataStorage->Remove(*it); } } m_ReachedTargetsNodes.clear(); } void QmitkUSNavigationStepMarkerIntervention::UpdateTargetCoordinates(mitk::DataNode *dataNode) { m_NodeDisplacementFilter->ResetNodes(); for (QVector>::iterator it = m_ReachedTargetsNodes.begin(); it != m_ReachedTargetsNodes.end(); ++it) { if (it->IsNotNull() && (*it)->GetData() != 0) { m_NodeDisplacementFilter->AddNode(*it); } } mitk::BaseData *baseData = dataNode->GetData(); if (!baseData) { mitkThrow() << "Data of the data node must not be null."; } mitk::BaseGeometry::Pointer geometry = baseData->GetGeometry(); if (geometry.IsNull()) { mitkThrow() << "Geometry of the data node must not be null."; } m_TargetUpdateFilter->SetControlNode(m_CurrentTargetIndex - 1, dataNode); if (m_PlannedTargetsNodes.size() > m_CurrentTargetIndex - 1) { m_PlannedTargetsNodes.at(m_CurrentTargetIndex - 1)->SetVisibility(false); } MITK_INFO("QmitkUSAbstractNavigationStep") ("QmitkUSNavigationStepMarkerIntervention") << "Target " << m_CurrentTargetIndex << " reached at position " << geometry->GetOrigin(); this->CalculateTargetPlacementQuality(); } void QmitkUSNavigationStepMarkerIntervention::UpdateBodyMarkerStatus(mitk::NavigationData::Pointer bodyMarker) { if (bodyMarker.IsNull()) { MITK_ERROR("QmitkUSAbstractNavigationStep") ("QmitkUSNavigationStepMarkerIntervention") << "Current Navigation Data for body marker of Combined Modality must not be null."; mitkThrow() << "Current Navigation Data for body marker of Combined Modality must not be null."; } bool valid = bodyMarker->IsDataValid(); // update body marker status label if (valid) { ui->bodyMarkerTrackingStatusLabel->setStyleSheet( "background-color: #8bff8b; margin-right: 1em; margin-left: 1em; border: 1px solid grey"); ui->bodyMarkerTrackingStatusLabel->setText("Body marker is inside the tracking volume."); } else { ui->bodyMarkerTrackingStatusLabel->setStyleSheet( "background-color: #ff7878; margin-right: 1em; margin-left: 1em; border: 1px solid grey"); ui->bodyMarkerTrackingStatusLabel->setText("Body marker is not inside the tracking volume."); } ui->targetStructuresRangeGroupBox->setEnabled(valid); ui->riskStructuresRangeGroupBox->setEnabled(valid); } void QmitkUSNavigationStepMarkerIntervention::GenerateTargetColorLookupTable() { vtkSmartPointer lookupTable = vtkSmartPointer::New(); lookupTable->SetHueRange(0.0, 0.33); lookupTable->SetSaturationRange(1.0, 1.0); lookupTable->SetValueRange(1.0, 1.0); lookupTable->SetTableRange(0.0, 1.0); lookupTable->Build(); mitk::LookupTable::Pointer lut = mitk::LookupTable::New(); lut->SetVtkLookupTable(lookupTable); m_TargetColorLookupTableProperty = mitk::LookupTableProperty::New(lut); } void QmitkUSNavigationStepMarkerIntervention::UpdateTargetColors() { if (m_TargetNode.IsNull()) { return; } m_TargetNode->SetColor(1, 1, 1); mitk::BaseData *targetNodeData = m_TargetNode->GetData(); if (targetNodeData == 0) { return; } mitk::Surface::Pointer targetNodeSurface = dynamic_cast(targetNodeData); vtkSmartPointer targetNodeSurfaceVtk = targetNodeSurface->GetVtkPolyData(); vtkPointData *targetPointData = targetNodeSurface->GetVtkPolyData()->GetPointData(); vtkFloatArray *scalars = dynamic_cast(targetPointData->GetScalars("USNavigation::Occlusion")); vtkFloatArray *targetScoreScalars; if (m_ShowPlanningColors) { targetScoreScalars = dynamic_cast(targetPointData->GetScalars("USNavigation::PlanningScalars")); } else { targetScoreScalars = dynamic_cast(targetPointData->GetScalars("USNavigation::ReachedTargetScores")); } if (!scalars || !targetScoreScalars) { return; } unsigned int numberOfTupels = scalars->GetNumberOfTuples(); vtkSmartPointer colors = vtkSmartPointer::New(); colors->SetNumberOfComponents(1); colors->SetNumberOfTuples(numberOfTupels); colors->SetName("Colors"); double color, intersection, markerScore; for (unsigned int n = 0; n < numberOfTupels; n++) { scalars->GetTuple(n, &intersection); targetScoreScalars->GetTuple(n, &markerScore); if (intersection > 0) { color = 0; } else { color = markerScore; } colors->SetTuple(n, &color); } if (numberOfTupels > 0) { targetNodeSurfaceVtk->GetPointData()->SetScalars(colors); targetNodeSurfaceVtk->GetPointData()->Update(); } } void QmitkUSNavigationStepMarkerIntervention::UpdateTargetScore() { if (m_NeedleProjectionFilter->GetProjection()->GetSize() != 2) { return; } vtkSmartPointer targetSurfaceVtk = m_TargetSurface->GetVtkPolyData(); m_TargetIntersectionFilter->SetTargetSurface(m_TargetSurface); m_TargetIntersectionFilter->SetLine(m_NeedleProjectionFilter->GetProjection()); m_TargetIntersectionFilter->CalculateIntersection(); if (m_TargetIntersectionFilter->GetIsIntersecting()) { vtkFloatArray *scalars = dynamic_cast(targetSurfaceVtk->GetPointData()->GetScalars("USNavigation::ReachedTargetScores")); double score; scalars->GetTuple(m_TargetIntersectionFilter->GetIntersectionNearestSurfacePointId(), &score); double color[3]; m_TargetColorLookupTableProperty->GetLookupTable()->GetVtkLookupTable()->GetColor(score, color); float colorF[3]; colorF[0] = color[0]; colorF[1] = color[1]; colorF[2] = color[2]; m_TargetProgressBar->SetColor(colorF); m_TargetProgressBar->SetBorderColor(colorF); m_TargetProgressBar->setValue(m_TargetIntersectionFilter->GetDistanceToIntersection()); if (m_PlannedTargetProgressBar) { vtkFloatArray *scalars = dynamic_cast(targetSurfaceVtk->GetPointData()->GetScalars("USNavigation::PlanningScalars")); if (scalars) { double score; scalars->GetTuple(m_TargetIntersectionFilter->GetIntersectionNearestSurfacePointId(), &score); double color[3]; m_TargetColorLookupTableProperty->GetLookupTable()->GetVtkLookupTable()->GetColor(score, color); float colorF[3]; colorF[0] = color[0]; colorF[1] = color[1]; colorF[2] = color[2]; m_PlannedTargetProgressBar->SetColor(colorF); m_PlannedTargetProgressBar->SetBorderColor(colorF); m_PlannedTargetProgressBar->SetTextFormatValid("Planned Target: %1 mm"); mitk::Point3D intersectionPoint = m_TargetIntersectionFilter->GetIntersectionPoint(); mitk::ScalarType minDistance = -1; for (QVector>::iterator it = m_PlannedTargetsNodes.begin(); it != m_PlannedTargetsNodes.end(); ++it) { mitk::ScalarType distance = intersectionPoint.EuclideanDistanceTo((*it)->GetData()->GetGeometry()->GetOrigin()); if (minDistance < 0 || distance < minDistance) { minDistance = distance; } } m_PlannedTargetProgressBar->setValue(minDistance); } } } else { m_TargetProgressBar->setValueInvalid(); } } void QmitkUSNavigationStepMarkerIntervention::UpdateTargetProgressDisplay() { QString description; if (m_CurrentTargetIndex >= m_NumberOfTargets) { description = "All Targets Reached"; if (m_TargetProgressBar) { m_TargetProgressBar->hide(); } } else { description = QString("Distance to Target ") + QString::number(m_CurrentTargetIndex + 1) + QString(" of ") + QString::number(m_NumberOfTargets); if (m_TargetProgressBar) { m_TargetProgressBar->show(); } } ui->targetStructuresRangeGroupBox->setTitle(description); } void QmitkUSNavigationStepMarkerIntervention::UpdatePlannedTargetProgressDisplay() { // make sure that the needle projection consists of two points if (m_NeedleProjectionFilter->GetProjection()->GetSize() != 2) { return; } vtkSmartPointer targetSurfaceVtk = m_TargetSurface->GetVtkPolyData(); m_TargetIntersectionFilter->SetTargetSurface(m_TargetSurface); m_TargetIntersectionFilter->SetLine(m_NeedleProjectionFilter->GetProjection()); m_TargetIntersectionFilter->CalculateIntersection(); // update target progress bar according to the color of the intersection // point on the target surface and the distance to the intersection if (m_TargetIntersectionFilter->GetIsIntersecting()) { vtkFloatArray *scalars = dynamic_cast(targetSurfaceVtk->GetPointData()->GetScalars("Colors")); double score; scalars->GetTuple(m_TargetIntersectionFilter->GetIntersectionNearestSurfacePointId(), &score); double color[3]; m_TargetColorLookupTableProperty->GetLookupTable()->GetVtkLookupTable()->GetColor(score, color); float colorF[3]; colorF[0] = color[0]; colorF[1] = color[1]; colorF[2] = color[2]; m_TargetProgressBar->SetColor(colorF); m_TargetProgressBar->SetBorderColor(colorF); m_TargetProgressBar->setValue(m_TargetIntersectionFilter->GetDistanceToIntersection()); } else { float red[3] = {0.6, 0, 0}; m_TargetProgressBar->SetBorderColor(red); m_TargetProgressBar->setValueInvalid(); } } void QmitkUSNavigationStepMarkerIntervention::UpdateTargetViolationStatus() { // transform vtk polydata according to mitk geometry vtkSmartPointer transformFilter = vtkSmartPointer::New(); transformFilter->SetInputData(0, m_TargetSurface->GetVtkPolyData()); transformFilter->SetTransform(m_TargetSurface->GetGeometry()->GetVtkTransform()); transformFilter->Update(); vtkSmartPointer enclosedPoints = vtkSmartPointer::New(); enclosedPoints->Initialize(transformFilter->GetOutput()); mitk::Point3D needleTip = m_NeedleProjectionFilter->GetProjection()->GetPoint(0); // show warning if the needle tip is inside the target surface if (enclosedPoints->IsInsideSurface(needleTip[0], needleTip[1], needleTip[2])) { if (!m_TargetStructureWarnOverlay->IsVisible(NULL)) { m_TargetStructureWarnOverlay->SetVisibility(true); mitk::DataNode::Pointer targetViolationResult = mitk::DataNode::New(); targetViolationResult->SetName("TargetViolation"); targetViolationResult->SetProperty("USNavigation::TargetViolationPoint", mitk::Point3dProperty::New(needleTip)); emit SignalIntermediateResult(targetViolationResult); } MITK_INFO("QmitkUSAbstractNavigationStep") ("QmitkUSNavigationStepMarkerIntervention") << "Target surface violated at " << needleTip << "."; } else { m_TargetStructureWarnOverlay->SetVisibility(false); } } void QmitkUSNavigationStepMarkerIntervention::CalculateTargetPlacementQuality() { // clear quality display if there aren't all targets reached if (m_ReachedTargetsNodes.size() != m_NumberOfTargets) { ui->placementQualityGroupBox->setEnabled(false); ui->angleDifferenceValue->setText(""); ui->centersOfMassValue->setText(""); return; } ui->placementQualityGroupBox->setEnabled(true); mitk::Surface::Pointer targetSurface = dynamic_cast(m_TargetNode->GetData()); if (targetSurface.IsNull()) { mitkThrow() << "Target surface must not be null."; } m_PlacementQualityCalculator->SetTargetSurface(targetSurface); mitk::PointSet::Pointer targetPointSet = mitk::PointSet::New(); // copy the origins of all reached target nodes into a point set // for the quality calculator mitk::PointSet::PointIdentifier n = 0; for (QVector>::iterator it = m_ReachedTargetsNodes.begin(); it != m_ReachedTargetsNodes.end(); ++it) { targetPointSet->InsertPoint(n++, (*it)->GetData()->GetGeometry()->GetOrigin()); } m_PlacementQualityCalculator->SetTargetPoints(targetPointSet); m_PlacementQualityCalculator->Update(); double centersOfMassDistance = m_PlacementQualityCalculator->GetCentersOfMassDistance(); ui->centersOfMassValue->setText(QString::number(centersOfMassDistance, 103, 2) + " mm"); double meanAnglesDifference = m_PlacementQualityCalculator->GetMeanAngleDifference(); ui->angleDifferenceValue->setText(QString::number(meanAnglesDifference, 103, 2) + QString::fromLatin1(" °")); // create an intermediate result of the placement quality mitk::DataNode::Pointer placementQualityResult = mitk::DataNode::New(); placementQualityResult->SetName("PlacementQuality"); placementQualityResult->SetFloatProperty("USNavigation::CentersOfMassDistance", centersOfMassDistance); placementQualityResult->SetFloatProperty("USNavigation::MeanAngleDifference", meanAnglesDifference); placementQualityResult->SetProperty( "USNavigation::AngleDifferences", mitk::GenericProperty::New(m_PlacementQualityCalculator->GetAngleDifferences())); if (m_PlannedTargetsNodes.size() == m_NumberOfTargets) { mitk::VnlVector reachedPlannedDifferences; double reachedPlannedDifferencesSum = 0; double reachedPlannedDifferencesMax = 0; reachedPlannedDifferences.set_size(m_NumberOfTargets); // get sum and maximum of the planning / reality differences for (unsigned int n = 0; n < m_NumberOfTargets; ++n) { mitk::ScalarType distance = m_PlannedTargetsNodes.at(n)->GetData()->GetGeometry()->GetOrigin().EuclideanDistanceTo( m_ReachedTargetsNodes.at(n)->GetData()->GetGeometry()->GetOrigin()); reachedPlannedDifferences.put(n, distance); reachedPlannedDifferencesSum += distance; if (distance > reachedPlannedDifferencesMax) { reachedPlannedDifferencesMax = distance; } } // add distances between planning and reality to the quality intermediate result placementQualityResult->SetProperty("USNavigation::PlanningRealityDistances", mitk::GenericProperty::New(reachedPlannedDifferences)); placementQualityResult->SetProperty( "USNavigation::MeanPlanningRealityDistance", mitk::DoubleProperty::New(reachedPlannedDifferencesSum / static_cast(m_NumberOfTargets))); placementQualityResult->SetProperty("USNavigation::MaximumPlanningRealityDistance", mitk::DoubleProperty::New(reachedPlannedDifferencesMax)); } emit SignalIntermediateResult(placementQualityResult); } void QmitkUSNavigationStepMarkerIntervention::UpdateSensorsNames() { mitk::USCombinedModality::Pointer combinedModality = this->GetCombinedModality(false); if (combinedModality.IsNull()) { return; } mitk::NavigationDataSource::Pointer navigationDataSource = combinedModality->GetNavigationDataSource(); if (navigationDataSource.IsNull()) { return; } if (!m_NeedleSensorName.empty()) { try { m_NeedleSensorIndex = navigationDataSource->GetOutputIndex(m_NeedleSensorName); } catch (const std::exception &e) { MITK_WARN("QmitkUSAbstractNavigationStep") ("QmitkUSNavigationStepPlacementPlanning") << "Cannot get index for needle sensor name: " << e.what(); } } if (this->GetNavigationStepState() >= QmitkUSAbstractNavigationStep::State_Active) { m_NeedleProjectionFilter->SelectInput(m_NeedleSensorIndex); } if (!m_ReferenceSensorName.empty()) { try { m_ReferenceSensorIndex = navigationDataSource->GetOutputIndex(m_ReferenceSensorName); } catch (const std::exception &e) { MITK_WARN("QmitkUSAbstractNavigationStep") ("QmitkUSNavigationStepPlacementPlanning") << "Cannot get index for reference sensor name: " << e.what(); } } if (this->GetNavigationStepState() >= QmitkUSAbstractNavigationStep::State_Active) { m_NodeDisplacementFilter->SelectInput(m_ReferenceSensorIndex); } ui->freezeImageButton->SetCombinedModality(combinedModality, m_ReferenceSensorIndex); } diff --git a/Plugins/org.mitk.gui.qt.igt.app.echotrack/src/internal/NavigationStepWidgets/QmitkUSNavigationStepMarkerIntervention.h b/Plugins/org.mitk.gui.qt.igt.app.echotrack/src/internal/NavigationStepWidgets/QmitkUSNavigationStepMarkerIntervention.h index 2f54e50fc5..82dba14545 100644 --- a/Plugins/org.mitk.gui.qt.igt.app.echotrack/src/internal/NavigationStepWidgets/QmitkUSNavigationStepMarkerIntervention.h +++ b/Plugins/org.mitk.gui.qt.igt.app.echotrack/src/internal/NavigationStepWidgets/QmitkUSNavigationStepMarkerIntervention.h @@ -1,171 +1,172 @@ /*=================================================================== 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. ===================================================================*/ #ifndef QMITKUSNAVIGATIONSTEPMARKERINTERVENTION_H #define QMITKUSNAVIGATIONSTEPMARKERINTERVENTION_H #include "QmitkUSAbstractNavigationStep.h" namespace mitk { class DataNode; class NeedleProjectionFilter; class NodeDisplacementFilter; class USNavigationGrabCutSegmentationUpdateFilter; class USNavigationTargetUpdateFilter; class USNavigationTargetOcclusionFilter; class USNavigationTargetIntersectionFilter; class USPointMarkInteractor; class LookupTableProperty; class Surface; class TextAnnotation2D; class USTargetPlacementQualityCalculator; } namespace Ui { class QmitkUSNavigationStepMarkerIntervention; } class QmitkZoneProgressBar; /** * \brief Navigation step for the actual marker placement. */ class QmitkUSNavigationStepMarkerIntervention : public QmitkUSAbstractNavigationStep { Q_OBJECT signals: void TargetReached(int); void TargetLeft(int); protected slots: void OnTargetReached(); void OnTargetLeft(); void OnBackToLastTargetClicked(); void OnFreeze(bool); void OnShowPlanningView(bool); void OnRiskZoneViolated(const mitk::DataNode *, mitk::Point3D); public: explicit QmitkUSNavigationStepMarkerIntervention(QWidget *parent = 0); + QmitkUSNavigationStepMarkerIntervention(mitk::Point3D toolAxis, QWidget *parent = 0); ~QmitkUSNavigationStepMarkerIntervention(); virtual bool OnStartStep(); virtual bool OnStopStep(); virtual bool OnFinishStep(); virtual bool OnActivateStep(); virtual bool OnDeactivateStep(); virtual void OnUpdate(); virtual void OnSettingsChanged(const itk::SmartPointer settingsNode); virtual QString GetTitle(); virtual bool GetIsRestartable(); virtual FilterVector GetFilter(); protected: virtual void OnSetCombinedModality(); void ClearZones(); void UpdateTargetCoordinates(mitk::DataNode *); void UpdateBodyMarkerStatus(mitk::NavigationData::Pointer bodyMarker); void GenerateTargetColorLookupTable(); void UpdateTargetColors(); /** * \brief */ void UpdateTargetScore(); /** * \brief Updates display showing the number of the currently active target. */ void UpdateTargetProgressDisplay(); /** * \brief Updates color and distance of the progress bar for the planned target. * The intersection between needle path and target surface is calculated and * the color is got from the intersection point. */ void UpdatePlannedTargetProgressDisplay(); /** * \brief Tests for target violation and updates the display accordingly. * This method tests if the needle tip is inside the target surface. */ void UpdateTargetViolationStatus(); /** * \brief Calculates and display quality metrics if all three markers are placed. */ void CalculateTargetPlacementQuality(); void UpdateSensorsNames(); unsigned int m_NumberOfTargets; QVector> m_ZoneNodes; QVector> m_PlannedTargetsNodes; QVector> m_ReachedTargetsNodes; itk::SmartPointer m_SegmentationNode; itk::SmartPointer m_ForegroundModelPointsNode; QmitkZoneProgressBar *m_TargetProgressBar; QmitkZoneProgressBar *m_PlannedTargetProgressBar; int m_CurrentTargetIndex; bool m_CurrentTargetReached; mitk::ScalarType m_ActiveTargetColor[3]; mitk::ScalarType m_InactiveTargetColor[3]; mitk::ScalarType m_ReachedTargetColor[3]; bool m_ShowPlanningColors; itk::SmartPointer m_PointMarkInteractor; itk::SmartPointer m_TargetNode; itk::SmartPointer m_TargetColorLookupTableProperty; itk::SmartPointer m_TargetSurface; itk::SmartPointer m_NeedleProjectionFilter; itk::SmartPointer m_NodeDisplacementFilter; itk::SmartPointer m_TargetUpdateFilter; itk::SmartPointer m_TargetOcclusionFilter; itk::SmartPointer m_TargetIntersectionFilter; itk::SmartPointer m_PlacementQualityCalculator; itk::SmartPointer m_TargetStructureWarnOverlay; std::string m_ReferenceSensorName; std::string m_NeedleSensorName; unsigned int m_ReferenceSensorIndex; unsigned int m_NeedleSensorIndex; private: mitk::MessageDelegate1 m_ListenerTargetCoordinatesChanged; Ui::QmitkUSNavigationStepMarkerIntervention *ui; }; #endif // QMITKUSNAVIGATIONSTEPMARKERINTERVENTION_H diff --git a/Plugins/org.mitk.gui.qt.igt.app.echotrack/src/internal/NavigationStepWidgets/QmitkUSNavigationStepPunctuationIntervention.cpp b/Plugins/org.mitk.gui.qt.igt.app.echotrack/src/internal/NavigationStepWidgets/QmitkUSNavigationStepPunctuationIntervention.cpp index c3d0107d76..d73d0305be 100644 --- a/Plugins/org.mitk.gui.qt.igt.app.echotrack/src/internal/NavigationStepWidgets/QmitkUSNavigationStepPunctuationIntervention.cpp +++ b/Plugins/org.mitk.gui.qt.igt.app.echotrack/src/internal/NavigationStepWidgets/QmitkUSNavigationStepPunctuationIntervention.cpp @@ -1,286 +1,280 @@ /*=================================================================== 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 "QmitkUSNavigationStepPunctuationIntervention.h" #include "ui_QmitkUSNavigationStepPunctuationIntervention.h" #include "mitkNeedleProjectionFilter.h" #include "../Widgets/QmitkZoneProgressBar.h" #include "../USNavigationMarkerPlacement.h" #include "usModuleRegistry.h" #include QmitkUSNavigationStepPunctuationIntervention::QmitkUSNavigationStepPunctuationIntervention(QWidget *parent) : QmitkUSAbstractNavigationStep(parent), m_NeedleProjectionFilter(mitk::NeedleProjectionFilter::New()), ui(new Ui::QmitkUSNavigationStepPunctuationIntervention), m_SphereSource(vtkSmartPointer::New()), m_OBBTree(vtkSmartPointer::New()), - m_IntersectPoints(vtkSmartPointer::New()) + m_IntersectPoints(vtkSmartPointer::New()), + m_NeedleNavigationTool(mitk::NavigationTool::New()) { ui->setupUi(this); connect(ui->m_AddNewAblationZone, SIGNAL(clicked()), this, SLOT(OnAddAblationZoneClicked())); + connect(ui->m_ShowToolAxisN, SIGNAL(stateChanged(int)), this, SLOT(OnShowToolAxisEnabled(int))); connect(ui->m_EnableAblationMarking, SIGNAL(clicked()), this, SLOT(OnEnableAblationZoneMarkingClicked())); connect(ui->m_AblationZoneSizeSlider, SIGNAL(valueChanged(int)), this, SLOT(OnAblationZoneSizeSliderChanged(int))); ui->m_AblationZonesBox->setVisible(false); } -QmitkUSNavigationStepPunctuationIntervention::QmitkUSNavigationStepPunctuationIntervention(mitk::Point3D toolAxis, QWidget *parent) : -QmitkUSAbstractNavigationStep(parent), -m_NeedleProjectionFilter(mitk::NeedleProjectionFilter::New()), -ui(new Ui::QmitkUSNavigationStepPunctuationIntervention), -m_SphereSource(vtkSmartPointer::New()), -m_OBBTree(vtkSmartPointer::New()), -m_IntersectPoints(vtkSmartPointer::New()) +void QmitkUSNavigationStepPunctuationIntervention::SetNeedleMetaData(mitk::NavigationTool::Pointer needleNavigationTool) { - m_ToolAxis.SetElement(0, (toolAxis.GetElement(0))); - m_ToolAxis.SetElement(1, (toolAxis.GetElement(1))); - m_ToolAxis.SetElement(2, (toolAxis.GetElement(2))); - m_NeedleProjectionFilter->SetToolAxisForFilter(m_ToolAxis); - ui->setupUi(this); - connect(ui->m_AddNewAblationZone, SIGNAL(clicked()), this, SLOT(OnAddAblationZoneClicked())); - connect(ui->m_EnableAblationMarking, SIGNAL(clicked()), this, SLOT(OnEnableAblationZoneMarkingClicked())); - connect(ui->m_AblationZoneSizeSlider, SIGNAL(valueChanged(int)), this, SLOT(OnAblationZoneSizeSliderChanged(int))); - ui->m_AblationZonesBox->setVisible(false); + this->m_NeedleNavigationTool = needleNavigationTool; } void QmitkUSNavigationStepPunctuationIntervention::OnEnableAblationZoneMarkingClicked() { if(ui->m_EnableAblationMarking->isChecked()) ui->m_AblationZonesBox->setVisible(true); else ui->m_AblationZonesBox->setVisible(false); } void QmitkUSNavigationStepPunctuationIntervention::OnAblationZoneSizeSliderChanged(int size) { int id = ui->m_AblationZonesList->currentRow(); if (id!=-1) {emit AblationZoneChanged(id,size);} }// void QmitkUSNavigationStepPunctuationIntervention::OnAddAblationZoneClicked() { QListWidgetItem* newItem = new QListWidgetItem("Ablation Zone (initial size: " + QString::number(ui->m_AblationZoneSizeSlider->value()) + " mm)", ui->m_AblationZonesList); newItem->setSelected(true); emit AddAblationZoneClicked(ui->m_AblationZoneSizeSlider->value()); } QmitkUSNavigationStepPunctuationIntervention::~QmitkUSNavigationStepPunctuationIntervention() { mitk::DataStorage::Pointer dataStorage = this->GetDataStorage(false); if ( dataStorage.IsNotNull() ) { // remove needle path from data storage if it is there mitk::DataNode::Pointer node = this->GetNamedDerivedNode ("Needle Path", QmitkUSAbstractNavigationStep::DATANAME_BASENODE); if ( node.IsNotNull() ) { dataStorage->Remove(node); } } delete ui; } bool QmitkUSNavigationStepPunctuationIntervention::OnStartStep() { // create node for Needle Projection mitk::DataNode::Pointer node = this->GetNamedDerivedNodeAndCreate ("Needle Path", QmitkUSAbstractNavigationStep::DATANAME_BASENODE); node->SetData(m_NeedleProjectionFilter->GetProjection()); node->SetBoolProperty("show contour", true); - + m_NeedleProjectionFilter->SetToolAxisForFilter(m_NeedleNavigationTool->GetToolAxis()); return true; } bool QmitkUSNavigationStepPunctuationIntervention::OnRestartStep() { return this->OnActivateStep(); } bool QmitkUSNavigationStepPunctuationIntervention::OnFinishStep() { mitk::DataNode::Pointer finishPunctionResult = mitk::DataNode::New(); finishPunctionResult->SetName("PunctionResult"); mitk::Point3D needlePos = m_NeedleProjectionFilter->GetOutput(0)->GetPosition(); mitk::Quaternion needleRot = m_NeedleProjectionFilter->GetOutput(0)->GetOrientation(); finishPunctionResult->SetProperty("USNavigation::TipPositionEnd", mitk::Point3dProperty::New(needlePos)); MITK_INFO("USNavigationLogging") << "Instrument tip at end: " <ClearZones(); mitk::DataStorage::Pointer dataStorage = this->GetDataStorage(); // add progress bars for risk zone nodes m_ZoneNodes = dataStorage->GetDerivations(dataStorage->GetNamedNode(USNavigationMarkerPlacement::DATANAME_ZONES)); // add zones to the widgets for risk structures for (mitk::DataStorage::SetOfObjects::ConstIterator it = m_ZoneNodes->Begin(); it != m_ZoneNodes->End(); ++it) { ui->riskStructuresRangeWidget->AddZone(it->Value()); float rgb[3]; it->Value()->GetColor(rgb); mitk::Color color; color.SetRed(rgb[0]); color.SetGreen(rgb[1]); color.SetBlue(rgb[2]); m_OldColors[it->Value()] = color; } m_NeedleProjectionFilter->SelectInput(0); return true; } +void QmitkUSNavigationStepPunctuationIntervention::OnShowToolAxisEnabled(int enabled) +{ + if (enabled == 0) { m_NeedleProjectionFilter->ShowToolAxis(false); } + else { m_NeedleProjectionFilter->ShowToolAxis(true); } +} + void QmitkUSNavigationStepPunctuationIntervention::OnUpdate() { // get navigation data source and make sure that it is not null mitk::NavigationDataSource::Pointer navigationDataSource = this->GetCombinedModality()->GetNavigationDataSource(); if ( navigationDataSource.IsNull() ) { MITK_ERROR("QmitkUSAbstractNavigationStep")("QmitkUSNavigationStepPunctuationIntervention") << "Navigation Data Source of Combined Modality must not be null."; mitkThrow() << "Navigation Data Source of Combined Modality must not be null."; } // update body marker this->UpdateBodyMarkerStatus(navigationDataSource->GetOutput(1)); // update critical structures this->UpdateCriticalStructures(navigationDataSource->GetOutput(0),m_NeedleProjectionFilter->GetProjection()); //Update Distance to US image mitk::Point3D point1 = m_NeedleProjectionFilter->GetProjection()->GetPoint(0); mitk::Point3D point2 = m_NeedleProjectionFilter->GetProjection()->GetPoint(1); double distance = point1.EuclideanDistanceTo(point2); ui->m_DistanceToUSPlane->setText(QString::number(distance) + " mm"); } void QmitkUSNavigationStepPunctuationIntervention::OnSettingsChanged(const itk::SmartPointer settingsNode) { if ( settingsNode.IsNull() ) { return; } } QString QmitkUSNavigationStepPunctuationIntervention::GetTitle() { return "Computer-assisted Intervention"; } bool QmitkUSNavigationStepPunctuationIntervention::GetIsRestartable() { return false; } QmitkUSNavigationStepPunctuationIntervention::FilterVector QmitkUSNavigationStepPunctuationIntervention::GetFilter() { return FilterVector(1, m_NeedleProjectionFilter.GetPointer()); } void QmitkUSNavigationStepPunctuationIntervention::OnSetCombinedModality() { mitk::USCombinedModality::Pointer combinedModality = this->GetCombinedModality(false); if ( combinedModality.IsNotNull() ) { // set calibration of the combined modality to the needle projection filter mitk::AffineTransform3D::Pointer calibration = combinedModality->GetCalibration(); if ( calibration.IsNotNull() ) { m_NeedleProjectionFilter->SetTargetPlane(calibration); } } } void QmitkUSNavigationStepPunctuationIntervention::ClearZones() { ui->riskStructuresRangeWidget->ClearZones(); } void QmitkUSNavigationStepPunctuationIntervention::UpdateBodyMarkerStatus(mitk::NavigationData::Pointer bodyMarker) { if ( bodyMarker.IsNull() ) { MITK_ERROR("QmitkUSAbstractNavigationStep")("QmitkUSNavigationStepPunctuationIntervention") << "Current Navigation Data for body marker of Combined Modality must not be null."; mitkThrow() << "Current Navigation Data for body marker of Combined Modality must not be null."; } bool valid = bodyMarker->IsDataValid(); // update body marker status label if (valid) { ui->bodyMarkerTrackingStatusLabel->setStyleSheet( "background-color: #8bff8b; margin-right: 1em; margin-left: 1em; border: 1px solid grey"); ui->bodyMarkerTrackingStatusLabel->setText("Body marker is inside the tracking volume."); } else { ui->bodyMarkerTrackingStatusLabel->setStyleSheet( "background-color: #ff7878; margin-right: 1em; margin-left: 1em; border: 1px solid grey"); ui->bodyMarkerTrackingStatusLabel->setText("Body marker is not inside the tracking volume."); } ui->riskStructuresRangeGroupBox->setEnabled(valid); } void QmitkUSNavigationStepPunctuationIntervention::UpdateCriticalStructures(mitk::NavigationData::Pointer needle, mitk::PointSet::Pointer path) { // update the distances for the risk structures widget ui->riskStructuresRangeWidget->UpdateDistancesToNeedlePosition(needle); //iterate through all zones for (mitk::DataStorage::SetOfObjects::ConstIterator it = m_ZoneNodes->Begin(); it != m_ZoneNodes->End(); ++it) { mitk::DataNode::Pointer currentNode = it->Value(); //get center point and radius float radius = -1; mitk::Point3D center; currentNode->GetFloatProperty("zone.size", radius); center = currentNode->GetData()->GetGeometry()->GetIndexToWorldTransform()->GetTranslation(); mitk::Point3D point0 = path->GetPoint(0); mitk::Point3D point1 = path->GetPoint(1); if (CheckSphereLineIntersection(center,radius,point0,point1)) {currentNode->SetColor(mitk::IGTColor_WARNING);} else {currentNode->SetColor(m_OldColors[currentNode]);} } } bool QmitkUSNavigationStepPunctuationIntervention::CheckSphereLineIntersection(mitk::Point3D& sphereOrigin, float& sphereRadius, mitk::Point3D& lineStart, mitk::Point3D& lineEnd) { double center[3] = {sphereOrigin[0],sphereOrigin[1],sphereOrigin[2]}; m_SphereSource->SetCenter(center); m_SphereSource->SetRadius(sphereRadius); m_SphereSource->Update(); m_OBBTree->SetDataSet(m_SphereSource->GetOutput()); m_OBBTree->BuildLocator(); double lineP0[3] = {lineStart[0], lineStart[1], lineStart[2]}; double lineP1[3] = {lineEnd[0], lineEnd[1], lineEnd[2]}; m_OBBTree->IntersectWithLine(lineP0, lineP1, m_IntersectPoints, NULL); if (m_IntersectPoints->GetNumberOfPoints() > 0) {return true;} else {return false;} } diff --git a/Plugins/org.mitk.gui.qt.igt.app.echotrack/src/internal/NavigationStepWidgets/QmitkUSNavigationStepPunctuationIntervention.h b/Plugins/org.mitk.gui.qt.igt.app.echotrack/src/internal/NavigationStepWidgets/QmitkUSNavigationStepPunctuationIntervention.h index 9c6e54b87f..29b8f63215 100644 --- a/Plugins/org.mitk.gui.qt.igt.app.echotrack/src/internal/NavigationStepWidgets/QmitkUSNavigationStepPunctuationIntervention.h +++ b/Plugins/org.mitk.gui.qt.igt.app.echotrack/src/internal/NavigationStepWidgets/QmitkUSNavigationStepPunctuationIntervention.h @@ -1,119 +1,123 @@ /*=================================================================== 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. ===================================================================*/ #ifndef QMITKUSNAVIGATIONSTEPPUNCTUATIONINTERVENTION_H #define QMITKUSNAVIGATIONSTEPPUNCTUATIONINTERVENTION_H #include "QmitkUSAbstractNavigationStep.h" #include +#include #include #include #include #include namespace mitk { class DataNode; class NeedleProjectionFilter; class NodeDisplacementFilter; class USNavigationGrabCutSegmentationUpdateFilter; class USNavigationTargetUpdateFilter; class USNavigationTargetOcclusionFilter; class USPointMarkInteractor; class LookupTableProperty; class Surface; } namespace Ui { class QmitkUSNavigationStepPunctuationIntervention; } class QmitkZoneProgressBar; /** * \brief Navigations step for the actual punctuation intervention. * The needle path is projected onto the image plane and the distances to all * risk structures are displayed in the widget. * * The risk structures are expected to be in the data storage under * DATANAME_BASENODE -> DATANAME_ZONES. They should contain a property named * "zone.size" and it is assumed that they are spherical. */ class QmitkUSNavigationStepPunctuationIntervention : public QmitkUSAbstractNavigationStep { Q_OBJECT public: + + /** Sets the navigation tool of the needle for the meta data (tool axis etc.)*/ + void SetNeedleMetaData(mitk::NavigationTool::Pointer needleNavigationTool); explicit QmitkUSNavigationStepPunctuationIntervention(QWidget *parent = 0); - explicit QmitkUSNavigationStepPunctuationIntervention(mitk::Point3D toolAxis, QWidget *parent = 0); ~QmitkUSNavigationStepPunctuationIntervention(); virtual bool OnStartStep(); virtual bool OnRestartStep(); virtual bool OnFinishStep(); virtual bool OnActivateStep(); virtual void OnUpdate(); virtual void OnSettingsChanged(const itk::SmartPointer); virtual QString GetTitle(); virtual bool GetIsRestartable(); virtual FilterVector GetFilter(); signals: void AddAblationZoneClicked(int); void AblationZoneChanged(int,int); protected slots: void OnAddAblationZoneClicked(); void OnEnableAblationZoneMarkingClicked(); void OnAblationZoneSizeSliderChanged(int size); + void OnShowToolAxisEnabled(int enabled); protected: virtual void OnSetCombinedModality(); void ClearZones(); void UpdateBodyMarkerStatus(mitk::NavigationData::Pointer bodyMarker); /** Updates the critical structures which means that the distance to the needle tip is updated and also the color changes to red if the path projection intersects the critical structure. */ void UpdateCriticalStructures(mitk::NavigationData::Pointer needle, mitk::PointSet::Pointer path); /** Checks if the given line intersects the given sphere. */ bool CheckSphereLineIntersection(mitk::Point3D& sphereOrigin, float& sphereRadius, mitk::Point3D& lineStart, mitk::Point3D& lineEnd); mitk::DataStorage::SetOfObjects::ConstPointer m_ZoneNodes; - /** - * \brief Creates a Pointset that projects the needle's path - */ + /** \brief Creates a Pointset that projects the needle's path */ itk::SmartPointer m_NeedleProjectionFilter; - mitk::Point3D m_ToolAxis; + + /** holds the navigation tool of the needle for the meta data (tool axis etc.)*/ + mitk::NavigationTool::Pointer m_NeedleNavigationTool; std::map m_OldColors; //stores the original color of the critical structrue nodes //some help variables for the CheckSphereLineIntersection()-Method vtkSmartPointer m_SphereSource; vtkSmartPointer m_OBBTree; vtkSmartPointer m_IntersectPoints; private: Ui::QmitkUSNavigationStepPunctuationIntervention *ui; }; #endif // QMITKUSNAVIGATIONSTEPPUNCTUATIONINTERVENTION_H diff --git a/Plugins/org.mitk.gui.qt.igt.app.echotrack/src/internal/NavigationStepWidgets/QmitkUSNavigationStepPunctuationIntervention.ui b/Plugins/org.mitk.gui.qt.igt.app.echotrack/src/internal/NavigationStepWidgets/QmitkUSNavigationStepPunctuationIntervention.ui index a3b454f70a..4a176396d5 100644 --- a/Plugins/org.mitk.gui.qt.igt.app.echotrack/src/internal/NavigationStepWidgets/QmitkUSNavigationStepPunctuationIntervention.ui +++ b/Plugins/org.mitk.gui.qt.igt.app.echotrack/src/internal/NavigationStepWidgets/QmitkUSNavigationStepPunctuationIntervention.ui @@ -1,222 +1,229 @@ QmitkUSNavigationStepPunctuationIntervention 0 0 317 - 393 + 420 Form background-color: #ff7878; margin-right: 1em; margin-left: 1em; border: 1px solid grey Body marker is not inside the tracking volume. Qt::AlignCenter true Distance Needle to US plane: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter <not available> Distances to Critical Structures + + + + Show Tool Axis + + + Enable Manual Ablation Zone Marking true Ablation Zones Size: Qt::Vertical 20 40 100 10 Qt::Horizontal QSlider::TicksBelow 10 0 cm Qt::Horizontal 40 20 10 cm Qt::Horizontal 40 20 Add New Ablation Zone Qt::Vertical 20 40 QmitkUSNavigationZoneDistancesWidget QWidget
src/internal/Widgets/QmitkUSNavigationZoneDistancesWidget.h
1
diff --git a/Plugins/org.mitk.gui.qt.igt.app.echotrack/src/internal/USNavigationMarkerPlacement.cpp b/Plugins/org.mitk.gui.qt.igt.app.echotrack/src/internal/USNavigationMarkerPlacement.cpp index a0dcb70433..f8edc7a2fa 100644 --- a/Plugins/org.mitk.gui.qt.igt.app.echotrack/src/internal/USNavigationMarkerPlacement.cpp +++ b/Plugins/org.mitk.gui.qt.igt.app.echotrack/src/internal/USNavigationMarkerPlacement.cpp @@ -1,808 +1,807 @@ /*=================================================================== 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 "USNavigationMarkerPlacement.h" #include "ui_USNavigationMarkerPlacement.h" #include "NavigationStepWidgets/QmitkUSNavigationStepCombinedModality.h" #include "NavigationStepWidgets/QmitkUSNavigationStepMarkerIntervention.h" #include "NavigationStepWidgets/QmitkUSNavigationStepPlacementPlanning.h" #include "NavigationStepWidgets/QmitkUSNavigationStepPunctuationIntervention.h" #include "NavigationStepWidgets/QmitkUSNavigationStepTumourSelection.h" #include "NavigationStepWidgets/QmitkUSNavigationStepZoneMarking.h" #include "SettingsWidgets/QmitkUSNavigationCombinedSettingsWidget.h" #include "mitkIRenderingManager.h" #include "mitkNodeDisplacementFilter.h" #include "mitkUSCombinedModality.h" #include #include "IO/mitkUSNavigationExperimentLogging.h" #include "IO/mitkUSNavigationStepTimer.h" #include #include #include #include #include #include #include "QmitkRenderWindow.h" #include "QmitkStdMultiWidget.h" #include "QmitkStdMultiWidgetEditor.h" #include "mitkLayoutAnnotationRenderer.h" // scene serialization #include #include #include #include #include const std::string USNavigationMarkerPlacement::VIEW_ID = "org.mitk.views.usmarkerplacement"; const char *USNavigationMarkerPlacement::DATANAME_TUMOUR = "Tumour"; const char *USNavigationMarkerPlacement::DATANAME_TARGETSURFACE = "Target Surface"; const char *USNavigationMarkerPlacement::DATANAME_ZONES = "Zones"; const char *USNavigationMarkerPlacement::DATANAME_TARGETS = "Targets"; const char *USNavigationMarkerPlacement::DATANAME_TARGETS_PATHS = "Target Paths"; const char *USNavigationMarkerPlacement::DATANAME_REACHED_TARGETS = "Reached Targets"; USNavigationMarkerPlacement::USNavigationMarkerPlacement() - : m_UpdateTimer(new QTimer(this)), - m_ImageAndNavigationDataLoggingTimer(new QTimer(this)), - m_StdMultiWidget(0), - m_ReinitAlreadyDone(false), - m_IsExperimentRunning(false), - m_NavigationStepTimer(mitk::USNavigationStepTimer::New()), - m_ExperimentLogging(mitk::USNavigationExperimentLogging::New()), - m_AblationZonesDisplacementFilter(mitk::NodeDisplacementFilter::New()), - m_IconRunning(QPixmap(":/USNavigation/record.png")), - m_IconNotRunning(QPixmap(":/USNavigation/record-gray.png")), - m_USImageLoggingFilter(mitk::USImageLoggingFilter::New()), - m_NavigationDataRecorder(mitk::NavigationDataRecorder::New()), - m_SceneNumber(1), - m_WarnOverlay(mitk::TextAnnotation2D::New()), - m_ListenerDeviceChanged(this, &USNavigationMarkerPlacement::OnCombinedModalityPropertyChanged), - m_NeedleIndex(0), - m_MarkerIndex(1), - ui(new Ui::USNavigationMarkerPlacement) + : m_UpdateTimer(new QTimer(this)), + m_ImageAndNavigationDataLoggingTimer(new QTimer(this)), + m_StdMultiWidget(0), + m_ReinitAlreadyDone(false), + m_IsExperimentRunning(false), + m_NavigationStepTimer(mitk::USNavigationStepTimer::New()), + m_ExperimentLogging(mitk::USNavigationExperimentLogging::New()), + m_AblationZonesDisplacementFilter(mitk::NodeDisplacementFilter::New()), + m_IconRunning(QPixmap(":/USNavigation/record.png")), + m_IconNotRunning(QPixmap(":/USNavigation/record-gray.png")), + m_USImageLoggingFilter(mitk::USImageLoggingFilter::New()), + m_NavigationDataRecorder(mitk::NavigationDataRecorder::New()), + m_SceneNumber(1), + m_WarnOverlay(mitk::TextAnnotation2D::New()), + m_ListenerDeviceChanged(this, &USNavigationMarkerPlacement::OnCombinedModalityPropertyChanged), + m_NeedleIndex(0), + m_MarkerIndex(1), + ui(new Ui::USNavigationMarkerPlacement) { - connect(m_UpdateTimer, SIGNAL(timeout()), this, SLOT(OnTimeout())); - connect( - m_ImageAndNavigationDataLoggingTimer, SIGNAL(timeout()), this, SLOT(OnImageAndNavigationDataLoggingTimeout())); + connect(m_UpdateTimer, SIGNAL(timeout()), this, SLOT(OnTimeout())); + connect( + m_ImageAndNavigationDataLoggingTimer, SIGNAL(timeout()), this, SLOT(OnImageAndNavigationDataLoggingTimeout())); - // scale running (and not running) icon the specific height - m_IconRunning = m_IconRunning.scaledToHeight(20, Qt::SmoothTransformation); - m_IconNotRunning = m_IconNotRunning.scaledToHeight(20, Qt::SmoothTransformation); + // scale running (and not running) icon the specific height + m_IconRunning = m_IconRunning.scaledToHeight(20, Qt::SmoothTransformation); + m_IconNotRunning = m_IconNotRunning.scaledToHeight(20, Qt::SmoothTransformation); - // set prefix for experiment logging (only keys with this prefix are taken - // into consideration - m_ExperimentLogging->SetKeyPrefix("USNavigation::"); + // set prefix for experiment logging (only keys with this prefix are taken + // into consideration + m_ExperimentLogging->SetKeyPrefix("USNavigation::"); - m_UpdateTimer->start(33); // every 33 Milliseconds = 30 Frames/Second + m_UpdateTimer->start(33); // every 33 Milliseconds = 30 Frames/Second } USNavigationMarkerPlacement::~USNavigationMarkerPlacement() { - // remove listener for ultrasound device changes - if (m_CombinedModality.IsNotNull() && m_CombinedModality->GetUltrasoundDevice().IsNotNull()) - { - m_CombinedModality->GetUltrasoundDevice()->RemovePropertyChangedListener(m_ListenerDeviceChanged); - } + // remove listener for ultrasound device changes + if (m_CombinedModality.IsNotNull() && m_CombinedModality->GetUltrasoundDevice().IsNotNull()) + { + m_CombinedModality->GetUltrasoundDevice()->RemovePropertyChangedListener(m_ListenerDeviceChanged); + } // remove listener for ultrasound device changes if (m_CombinedModality.IsNotNull() && m_CombinedModality->GetUltrasoundDevice().IsNotNull()) { m_CombinedModality->GetUltrasoundDevice()->RemovePropertyChangedListener(m_ListenerDeviceChanged); } delete ui; } void USNavigationMarkerPlacement::OnChangeAblationZone(int id, int newSize) { - if ((m_AblationZonesVector.size() < id) || (id < 0)) - { - return; - } - - MITK_INFO << "Ablation Zone " << id << " changed, new size: " << newSize; - - // create a vtk sphere with given radius - vtkSphereSource *vtkData = vtkSphereSource::New(); - vtkData->SetRadius(newSize / 2); - vtkData->SetCenter(0, 0, 0); - vtkData->SetPhiResolution(20); - vtkData->SetThetaResolution(20); - vtkData->Update(); - - mitk::Surface::Pointer zoneSurface = dynamic_cast(m_AblationZonesVector.at(id)->GetData()); - zoneSurface->SetVtkPolyData(vtkData->GetOutput()); - vtkData->Delete(); + if ((m_AblationZonesVector.size() < id) || (id < 0)) + { + return; + } + + MITK_INFO << "Ablation Zone " << id << " changed, new size: " << newSize; + + // create a vtk sphere with given radius + vtkSphereSource *vtkData = vtkSphereSource::New(); + vtkData->SetRadius(newSize / 2); + vtkData->SetCenter(0, 0, 0); + vtkData->SetPhiResolution(20); + vtkData->SetThetaResolution(20); + vtkData->Update(); + + mitk::Surface::Pointer zoneSurface = dynamic_cast(m_AblationZonesVector.at(id)->GetData()); + zoneSurface->SetVtkPolyData(vtkData->GetOutput()); + vtkData->Delete(); } void USNavigationMarkerPlacement::OnAddAblationZone(int size) { - m_AblationZonesDisplacementFilter->SetInitialReferencePose( - m_CombinedModality->GetNavigationDataSource()->GetOutput(m_MarkerIndex)); - mitk::DataNode::Pointer NewAblationZone = mitk::DataNode::New(); - - mitk::Point3D origin = m_CombinedModality->GetNavigationDataSource()->GetOutput(m_NeedleIndex)->GetPosition(); - - MITK_INFO("USNavigationLogging") << "Ablation Zone Added, initial size: " << size << ", origin: " << origin; - - mitk::Surface::Pointer zone = mitk::Surface::New(); - - // create a vtk sphere with given radius - vtkSphereSource *vtkData = vtkSphereSource::New(); - vtkData->SetRadius(size / 2); - vtkData->SetCenter(0, 0, 0); - vtkData->SetPhiResolution(20); - vtkData->SetThetaResolution(20); - vtkData->Update(); - zone->SetVtkPolyData(vtkData->GetOutput()); - vtkData->Delete(); - - // set vtk sphere and origin to data node (origin must be set - // again, because of the new sphere set as data) - NewAblationZone->SetData(zone); - NewAblationZone->GetData()->GetGeometry()->SetOrigin(origin); - mitk::Color SphereColor = mitk::Color(); - // default color - SphereColor[0] = 102; - SphereColor[1] = 0; - SphereColor[2] = 204; - NewAblationZone->SetColor(SphereColor); - NewAblationZone->SetOpacity(0.3); - - // set name of zone - std::stringstream name; - name << "Ablation Zone" << m_AblationZonesVector.size(); - NewAblationZone->SetName(name.str()); - - // add zone to filter - m_AblationZonesDisplacementFilter->AddNode(NewAblationZone); - m_AblationZonesVector.push_back(NewAblationZone); - this->GetDataStorage()->Add(NewAblationZone); + m_AblationZonesDisplacementFilter->SetInitialReferencePose( + m_CombinedModality->GetNavigationDataSource()->GetOutput(m_MarkerIndex)); + mitk::DataNode::Pointer NewAblationZone = mitk::DataNode::New(); + + mitk::Point3D origin = m_CombinedModality->GetNavigationDataSource()->GetOutput(m_NeedleIndex)->GetPosition(); + + MITK_INFO("USNavigationLogging") << "Ablation Zone Added, initial size: " << size << ", origin: " << origin; + + mitk::Surface::Pointer zone = mitk::Surface::New(); + + // create a vtk sphere with given radius + vtkSphereSource *vtkData = vtkSphereSource::New(); + vtkData->SetRadius(size / 2); + vtkData->SetCenter(0, 0, 0); + vtkData->SetPhiResolution(20); + vtkData->SetThetaResolution(20); + vtkData->Update(); + zone->SetVtkPolyData(vtkData->GetOutput()); + vtkData->Delete(); + + // set vtk sphere and origin to data node (origin must be set + // again, because of the new sphere set as data) + NewAblationZone->SetData(zone); + NewAblationZone->GetData()->GetGeometry()->SetOrigin(origin); + mitk::Color SphereColor = mitk::Color(); + // default color + SphereColor[0] = 102; + SphereColor[1] = 0; + SphereColor[2] = 204; + NewAblationZone->SetColor(SphereColor); + NewAblationZone->SetOpacity(0.3); + + // set name of zone + std::stringstream name; + name << "Ablation Zone" << m_AblationZonesVector.size(); + NewAblationZone->SetName(name.str()); + + // add zone to filter + m_AblationZonesDisplacementFilter->AddNode(NewAblationZone); + m_AblationZonesVector.push_back(NewAblationZone); + this->GetDataStorage()->Add(NewAblationZone); } void USNavigationMarkerPlacement::CreateQtPartControl(QWidget *parent) { - m_Parent = parent; - ui->setupUi(parent); - - connect(ui->navigationProcessWidget, - SIGNAL(SignalCombinedModalityChanged(itk::SmartPointer)), - this, - SLOT(OnCombinedModalityChanged(itk::SmartPointer))); - - connect(ui->navigationProcessWidget, - SIGNAL(SignalSettingsChanged(itk::SmartPointer)), - this, - SLOT(OnSettingsChanged(itk::SmartPointer))); - - connect(ui->navigationProcessWidget, - SIGNAL(SignalActiveNavigationStepChanged(int)), - this, - SLOT(OnActiveNavigationStepChanged(int))); - - connect(ui->startExperimentButton, SIGNAL(clicked()), this, SLOT(OnStartExperiment())); - connect(ui->finishExperimentButton, SIGNAL(clicked()), this, SLOT(OnFinishExperiment())); + m_Parent = parent; + ui->setupUi(parent); + + connect(ui->navigationProcessWidget, + SIGNAL(SignalCombinedModalityChanged(itk::SmartPointer)), + this, + SLOT(OnCombinedModalityChanged(itk::SmartPointer))); + + connect(ui->navigationProcessWidget, + SIGNAL(SignalSettingsChanged(itk::SmartPointer)), + this, + SLOT(OnSettingsChanged(itk::SmartPointer))); + + connect(ui->navigationProcessWidget, + SIGNAL(SignalActiveNavigationStepChanged(int)), + this, + SLOT(OnActiveNavigationStepChanged(int))); + + connect(ui->navigationProcessWidget, + SIGNAL(SignalActiveNavigationStepChangeRequested(int)), + this, + SLOT(OnNextNavigationStepInitialization(int))); + + connect(ui->startExperimentButton, SIGNAL(clicked()), this, SLOT(OnStartExperiment())); + connect(ui->finishExperimentButton, SIGNAL(clicked()), this, SLOT(OnFinishExperiment())); connect(ui->m_enableNavigationLayout, SIGNAL(clicked()), this, SLOT(OnChangeLayoutClicked())); - connect(ui->navigationProcessWidget, - SIGNAL(SignalIntermediateResult(const itk::SmartPointer)), - this, - SLOT(OnIntermediateResultProduced(const itk::SmartPointer))); + connect(ui->navigationProcessWidget, + SIGNAL(SignalIntermediateResult(const itk::SmartPointer)), + this, + SLOT(OnIntermediateResultProduced(const itk::SmartPointer))); - ui->navigationProcessWidget->SetDataStorage(this->GetDataStorage()); + ui->navigationProcessWidget->SetDataStorage(this->GetDataStorage()); - // indicate that no experiment is running at start - ui->runningLabel->setPixmap(m_IconNotRunning); + // indicate that no experiment is running at start + ui->runningLabel->setPixmap(m_IconNotRunning); - ui->navigationProcessWidget->SetSettingsWidget(new QmitkUSNavigationCombinedSettingsWidget(m_Parent)); + ui->navigationProcessWidget->SetSettingsWidget(new QmitkUSNavigationCombinedSettingsWidget(m_Parent)); } void USNavigationMarkerPlacement::OnCombinedModalityPropertyChanged(const std::string &key, const std::string &) { - if (key == mitk::USDevice::GetPropertyKeys().US_PROPKEY_BMODE_DEPTH) - { - m_ReinitAlreadyDone = false; - this->ReinitOnImage(); - - if (m_CombinedModality.IsNotNull() && !m_CombinedModality->GetIsCalibratedForCurrentStatus()) - { - mitk::LayoutAnnotationRenderer::AddAnnotation( - m_WarnOverlay.GetPointer(), "stdmulti.widget1", mitk::LayoutAnnotationRenderer::TopLeft); - MITK_WARN << "No calibration available for the selected ultrasound image depth."; - } - } -} - - -void USNavigationMarkerPlacement::SetToolAxisMarkerPlacement() -{ - m_NavigationDataSource = m_CombinedModality->GetNavigationDataSource(); - m_ToolAxis.SetElement(0, 0); - m_ToolAxis.SetElement(1, 0); - m_ToolAxis.SetElement(2, 1); - if (m_NavigationDataSource.IsNull()) - { - MITK_WARN << "Cannot retrieve tool axis as tracking source is null."; - } - else + if (key == mitk::USDevice::GetPropertyKeys().US_PROPKEY_BMODE_DEPTH) { - us::ModuleContext* context = us::GetModuleContext(); - std::string id = m_NavigationDataSource->US_PROPKEY_ID; - std::string filter = "(" + mitk::NavigationToolStorage::US_PROPKEY_SOURCE_ID + "=" + id + ")"; - // Get Storage - std::vector > refs = context->GetServiceReferences(); - m_CurrentStorage = context->GetService(refs.front()); - if (m_CurrentStorage.IsNull()) - { - MITK_WARN << "Found an invalid storage object!"; - } - if (m_CurrentStorage->GetToolCount() != m_NavigationDataSource->GetNumberOfOutputs()) //there is something wrong with the storage + m_ReinitAlreadyDone = false; + this->ReinitOnImage(); + + if (m_CombinedModality.IsNotNull() && !m_CombinedModality->GetIsCalibratedForCurrentStatus()) { - MITK_WARN << "Found a tool storage, but it has not the same number of tools like the NavigationDataSource. This storage won't be used because it isn't the right one."; - m_CurrentStorage = NULL; + mitk::LayoutAnnotationRenderer::AddAnnotation( + m_WarnOverlay.GetPointer(), "stdmulti.widget1", mitk::LayoutAnnotationRenderer::TopLeft); + MITK_WARN << "No calibration available for the selected ultrasound image depth."; } - - //getting the first tool in the tool storage, assuming this is the needle - mitk::NavigationTool::Pointer needle; - needle = m_CurrentStorage->GetTool(0); - - m_ToolAxis.SetElement(0, (needle->GetToolAxis().GetElement(0))); - m_ToolAxis.SetElement(1, (needle->GetToolAxis().GetElement(1))); - m_ToolAxis.SetElement(2, (needle->GetToolAxis().GetElement(2))); } } - void USNavigationMarkerPlacement::SetFocus() { - this->ReinitOnImage(); + this->ReinitOnImage(); } void USNavigationMarkerPlacement::OnTimeout() { - if (!m_StdMultiWidget) - { - // try to get the standard multi widget if it couldn't be got before - mitk::IRenderWindowPart *renderWindow = this->GetRenderWindowPart(); - - QmitkStdMultiWidgetEditor *multiWidgetEditor = dynamic_cast(renderWindow); - - // if there is a standard multi widget now, disable the level window and - // change the layout to 2D up and 3d down - if (multiWidgetEditor) - { - m_StdMultiWidget = multiWidgetEditor->GetStdMultiWidget(); - if (m_StdMultiWidget) - { - m_StdMultiWidget->DisableStandardLevelWindow(); - m_StdMultiWidget->changeLayoutTo2DUpAnd3DDown(); - } - } - - this->CreateOverlays(); - } - - if (m_CombinedModality.IsNotNull() && - !this->m_CombinedModality->GetIsFreezed()) // if the combined modality is freezed: do nothing - { - ui->navigationProcessWidget->UpdateNavigationProgress(); - m_AblationZonesDisplacementFilter->Update(); - - // update the 3D window only every fourth time to speed up the rendering (at least in 2D) - this->RequestRenderWindowUpdate(mitk::RenderingManager::REQUEST_UPDATE_2DWINDOWS); - - // make sure that a reinit was performed on the image - this->ReinitOnImage(); - } + if (!m_StdMultiWidget) + { + // try to get the standard multi widget if it couldn't be got before + mitk::IRenderWindowPart *renderWindow = this->GetRenderWindowPart(); + + QmitkStdMultiWidgetEditor *multiWidgetEditor = dynamic_cast(renderWindow); + + // if there is a standard multi widget now, disable the level window and + // change the layout to 2D up and 3d down + if (multiWidgetEditor) + { + m_StdMultiWidget = multiWidgetEditor->GetStdMultiWidget(); + if (m_StdMultiWidget) + { + m_StdMultiWidget->DisableStandardLevelWindow(); + m_StdMultiWidget->changeLayoutTo2DUpAnd3DDown(); + } + } + + this->CreateOverlays(); + } + + if (m_CombinedModality.IsNotNull() && + !this->m_CombinedModality->GetIsFreezed()) // if the combined modality is freezed: do nothing + { + ui->navigationProcessWidget->UpdateNavigationProgress(); + m_AblationZonesDisplacementFilter->Update(); + + // update the 3D window only every fourth time to speed up the rendering (at least in 2D) + this->RequestRenderWindowUpdate(mitk::RenderingManager::REQUEST_UPDATE_2DWINDOWS); + + // make sure that a reinit was performed on the image + this->ReinitOnImage(); + } } void USNavigationMarkerPlacement::OnEnableNavigationLayout() { MITK_INFO << "Navigation Layout"; // try to get the standard multi widget if it couldn't be got before mitk::IRenderWindowPart *renderWindow = this->GetRenderWindowPart(); QmitkStdMultiWidgetEditor *multiWidgetEditor = dynamic_cast(renderWindow); // if there is a standard multi widget now, disable the level window and // change the layout to 2D up and 3d down if (multiWidgetEditor) { m_StdMultiWidget = multiWidgetEditor->GetStdMultiWidget(); if (m_StdMultiWidget) { m_StdMultiWidget->DisableStandardLevelWindow(); m_StdMultiWidget->changeLayoutTo2DUpAnd3DDown(); this->GetDataStorage()->GetNamedNode("stdmulti.widget1.plane")->SetVisibility(false); this->GetDataStorage()->GetNamedNode("stdmulti.widget3.plane")->SetVisibility(false); } } } void USNavigationMarkerPlacement::OnResetStandardLayout() { //reset render windows mitk::DataNode::Pointer widget1 = this->GetDataStorage()->GetNamedNode("stdmulti.widget1.plane"); if (widget1.IsNotNull()) { widget1->SetVisibility(true); } mitk::DataNode::Pointer widget3 = this->GetDataStorage()->GetNamedNode("stdmulti.widget3.plane"); if (widget3.IsNotNull()) { widget3->SetVisibility(true); } m_StdMultiWidget->changeLayoutToDefault(); } void USNavigationMarkerPlacement::OnChangeLayoutClicked() { if (ui->m_enableNavigationLayout->isChecked()) OnEnableNavigationLayout(); else OnResetStandardLayout(); } void USNavigationMarkerPlacement::OnImageAndNavigationDataLoggingTimeout() { - // update filter for logging navigation data and ultrasound images - if (m_CombinedModality.IsNotNull()) - { - m_NavigationDataRecorder->Update(); - // get last messages for logging filer and store them - std::vector messages = m_LoggingBackend.GetNavigationMessages(); - std::string composedMessage = ""; - for (int i = 0; i < messages.size(); i++) - { - composedMessage += messages.at(i); - } - m_USImageLoggingFilter->AddMessageToCurrentImage(composedMessage); - m_LoggingBackend.ClearNavigationMessages(); - // update logging filter - m_USImageLoggingFilter->Update(); - } + // update filter for logging navigation data and ultrasound images + if (m_CombinedModality.IsNotNull()) + { + m_NavigationDataRecorder->Update(); + // get last messages for logging filer and store them + std::vector messages = m_LoggingBackend.GetNavigationMessages(); + std::string composedMessage = ""; + for (int i = 0; i < messages.size(); i++) + { + composedMessage += messages.at(i); + } + m_USImageLoggingFilter->AddMessageToCurrentImage(composedMessage); + m_LoggingBackend.ClearNavigationMessages(); + // update logging filter + m_USImageLoggingFilter->Update(); + } } void USNavigationMarkerPlacement::OnStartExperiment() { - // get name for the experiment by a QInputDialog - bool ok; - if (m_ExperimentName.isEmpty()) - { // default: current date - m_ExperimentName = QString::number(QDateTime::currentDateTime().date().year()) + "_" + - QString::number(QDateTime::currentDateTime().date().month()) + "_" + - QString::number(QDateTime::currentDateTime().date().day()) + "_experiment_" + - QString::number(QDateTime::currentDateTime().time().hour()) + "." + - QString::number(QDateTime::currentDateTime().time().minute()); - } - m_ExperimentName = QInputDialog::getText( - m_Parent, QString("Experiment Name"), QString("Name of the Experiment"), QLineEdit::Normal, m_ExperimentName, &ok); - MITK_INFO("USNavigationLogging") << "Experiment started: " << m_ExperimentName.toStdString(); - if (ok && !m_ExperimentName.isEmpty()) - { - // display error message and call the function recursivly if a directory - // with the given name already exists - QDir experimentResultsDir(m_ResultsDirectory + QDir::separator() + m_ExperimentName); - if (experimentResultsDir.exists()) - { - QMessageBox::critical( - m_Parent, "Results Directory Exists", "The result directory already exists.\nPlease choose an other name."); - this->OnStartExperiment(); - } - else - { - QDir(m_ResultsDirectory).mkdir(m_ExperimentName); - m_ExperimentResultsSubDirectory = m_ResultsDirectory + QDir::separator() + m_ExperimentName; - - // experiment is running now - ui->runningLabel->setPixmap(m_IconRunning); - ui->navigationProcessWidget->EnableInteraction(true); - - // (re)start timer for navigation step durations - m_NavigationStepTimer->Reset(); - m_NavigationStepTimer->SetOutputFileName( - QString(m_ExperimentResultsSubDirectory + QDir::separator() + QString("durations.cvs")).toStdString()); - m_NavigationStepTimer->SetActiveIndex(0, m_NavigationSteps.at(0)->GetTitle().toStdString()); - - ui->finishExperimentButton->setEnabled(true); - ui->startExperimentButton->setDisabled(true); - - // initialize and register logging backend - QString loggingFilename = m_ExperimentResultsSubDirectory + QDir::separator() + "logging.txt"; - m_LoggingBackend.SetOutputFileName(loggingFilename.toStdString()); - mbilog::RegisterBackend(&m_LoggingBackend); - - // initialize and start navigation data recorder form xml recording - m_NavigationDataRecorder->StartRecording(); - - m_IsExperimentRunning = true; - - m_ImageAndNavigationDataLoggingTimer->start(1000); - - // (re)start experiment logging and set output file name - m_ExperimentLogging->Reset(); - m_ExperimentLogging->SetFileName( - QString(m_ExperimentResultsSubDirectory + QDir::separator() + "experiment-logging.xml").toStdString()); - } - } + // get name for the experiment by a QInputDialog + bool ok; + if (m_ExperimentName.isEmpty()) + { // default: current date + m_ExperimentName = QString::number(QDateTime::currentDateTime().date().year()) + "_" + + QString::number(QDateTime::currentDateTime().date().month()) + "_" + + QString::number(QDateTime::currentDateTime().date().day()) + "_experiment_" + + QString::number(QDateTime::currentDateTime().time().hour()) + "." + + QString::number(QDateTime::currentDateTime().time().minute()); + } + m_ExperimentName = QInputDialog::getText( + m_Parent, QString("Experiment Name"), QString("Name of the Experiment"), QLineEdit::Normal, m_ExperimentName, &ok); + MITK_INFO("USNavigationLogging") << "Experiment started: " << m_ExperimentName.toStdString(); + if (ok && !m_ExperimentName.isEmpty()) + { + // display error message and call the function recursivly if a directory + // with the given name already exists + QDir experimentResultsDir(m_ResultsDirectory + QDir::separator() + m_ExperimentName); + if (experimentResultsDir.exists()) + { + QMessageBox::critical( + m_Parent, "Results Directory Exists", "The result directory already exists.\nPlease choose an other name."); + this->OnStartExperiment(); + } + else + { + QDir(m_ResultsDirectory).mkdir(m_ExperimentName); + m_ExperimentResultsSubDirectory = m_ResultsDirectory + QDir::separator() + m_ExperimentName; + + // experiment is running now + ui->runningLabel->setPixmap(m_IconRunning); + ui->navigationProcessWidget->EnableInteraction(true); + + // (re)start timer for navigation step durations + m_NavigationStepTimer->Reset(); + m_NavigationStepTimer->SetOutputFileName( + QString(m_ExperimentResultsSubDirectory + QDir::separator() + QString("durations.cvs")).toStdString()); + m_NavigationStepTimer->SetActiveIndex(0, m_NavigationSteps.at(0)->GetTitle().toStdString()); + + ui->finishExperimentButton->setEnabled(true); + ui->startExperimentButton->setDisabled(true); + + // initialize and register logging backend + QString loggingFilename = m_ExperimentResultsSubDirectory + QDir::separator() + "logging.txt"; + m_LoggingBackend.SetOutputFileName(loggingFilename.toStdString()); + mbilog::RegisterBackend(&m_LoggingBackend); + + // initialize and start navigation data recorder form xml recording + m_NavigationDataRecorder->StartRecording(); + + m_IsExperimentRunning = true; + + m_ImageAndNavigationDataLoggingTimer->start(1000); + + // (re)start experiment logging and set output file name + m_ExperimentLogging->Reset(); + m_ExperimentLogging->SetFileName( + QString(m_ExperimentResultsSubDirectory + QDir::separator() + "experiment-logging.xml").toStdString()); + } + } } void USNavigationMarkerPlacement::OnFinishExperiment() { - this->WaitCursorOn(); - - MITK_INFO("USNavigationLogging") << "Experiment finished!"; - MITK_INFO("USNavigationLogging") - << "Position/Orientation of needle tip: " - << (dynamic_cast(m_CombinedModality->GetTrackingDevice()->GetOutput(0)))->GetPosition(); - MITK_INFO("USNavigationLogging") - << "Position of target: " << m_TargetNodeDisplacementFilter->GetRawDisplacementNavigationData(0)->GetPosition(); - MITK_INFO("USNavigationLogging") << "Total duration: " << m_NavigationStepTimer->GetTotalDuration(); - - ui->navigationProcessWidget->FinishCurrentNavigationStep(); - m_ImageAndNavigationDataLoggingTimer->stop(); - - ui->runningLabel->setPixmap(m_IconNotRunning); - ui->navigationProcessWidget->EnableInteraction(false); - - m_NavigationStepTimer->Stop(); - - // make sure that the navigation process will be start from beginning at the - // next experiment - ui->navigationProcessWidget->ResetNavigationProcess(); - - ui->finishExperimentButton->setDisabled(true); - ui->startExperimentButton->setEnabled(true); - - MITK_INFO("USNavigationLogging") << "Writing logging data to " << m_ExperimentResultsSubDirectory.toStdString(); - // save ultrasound images to the file system - QDir(m_ExperimentResultsSubDirectory).mkdir("ImageStream"); - m_USImageLoggingFilter->Update(); - m_USImageLoggingFilter->SetImageFilesExtension(".jpg"); - m_USImageLoggingFilter->SaveImages( - QString(m_ExperimentResultsSubDirectory + QDir::separator() + "ImageStream" + QDir::separator()).toStdString()); - m_USImageLoggingFilter = mitk::USImageLoggingFilter::New(); - - m_NavigationDataRecorder->StopRecording(); - - // Write data to csv and xml file - mitk::IOUtil::SaveBaseData( - m_NavigationDataRecorder->GetNavigationDataSet(), - (QString(m_ExperimentResultsSubDirectory + QDir::separator() + "navigation-data.xml").toStdString().c_str())); - mitk::IOUtil::SaveBaseData( - m_NavigationDataRecorder->GetNavigationDataSet(), - (QString(m_ExperimentResultsSubDirectory + QDir::separator() + "navigation-data.csv").toStdString().c_str())); - - // write logged navigation data messages to separate file - std::stringstream csvNavigationMessagesFilename; - csvNavigationMessagesFilename << m_ExperimentResultsSubDirectory.toStdString() << QDir::separator().toLatin1() - << "CSVNavigationMessagesLogFile.csv"; - MITK_INFO("USNavigationLogging") << "Writing logged navigation messages to separate csv file: " - << csvNavigationMessagesFilename.str(); - m_LoggingBackend.WriteCSVFileWithNavigationMessages(csvNavigationMessagesFilename.str()); - - mbilog::UnregisterBackend(&m_LoggingBackend); - - m_IsExperimentRunning = false; - - m_ImageAndNavigationDataLoggingTimer->stop(); - m_CombinedModality = 0; - - // reset scene number for next experiment - m_SceneNumber = 1; - - this->WaitCursorOff(); - MITK_INFO("USNavigationLogging") << "Finished!"; + this->WaitCursorOn(); + + MITK_INFO("USNavigationLogging") << "Experiment finished!"; + MITK_INFO("USNavigationLogging") + << "Position/Orientation of needle tip: " + << (dynamic_cast(m_CombinedModality->GetTrackingDevice()->GetOutput(0)))->GetPosition(); + MITK_INFO("USNavigationLogging") + << "Position of target: " << m_TargetNodeDisplacementFilter->GetRawDisplacementNavigationData(0)->GetPosition(); + MITK_INFO("USNavigationLogging") << "Total duration: " << m_NavigationStepTimer->GetTotalDuration(); + + ui->navigationProcessWidget->FinishCurrentNavigationStep(); + m_ImageAndNavigationDataLoggingTimer->stop(); + + ui->runningLabel->setPixmap(m_IconNotRunning); + ui->navigationProcessWidget->EnableInteraction(false); + + m_NavigationStepTimer->Stop(); + + // make sure that the navigation process will be start from beginning at the + // next experiment + ui->navigationProcessWidget->ResetNavigationProcess(); + + ui->finishExperimentButton->setDisabled(true); + ui->startExperimentButton->setEnabled(true); + + MITK_INFO("USNavigationLogging") << "Writing logging data to " << m_ExperimentResultsSubDirectory.toStdString(); + // save ultrasound images to the file system + QDir(m_ExperimentResultsSubDirectory).mkdir("ImageStream"); + m_USImageLoggingFilter->Update(); + m_USImageLoggingFilter->SetImageFilesExtension(".jpg"); + m_USImageLoggingFilter->SaveImages( + QString(m_ExperimentResultsSubDirectory + QDir::separator() + "ImageStream" + QDir::separator()).toStdString()); + m_USImageLoggingFilter = mitk::USImageLoggingFilter::New(); + + m_NavigationDataRecorder->StopRecording(); + + // Write data to csv and xml file + mitk::IOUtil::SaveBaseData( + m_NavigationDataRecorder->GetNavigationDataSet(), + (QString(m_ExperimentResultsSubDirectory + QDir::separator() + "navigation-data.xml").toStdString().c_str())); + mitk::IOUtil::SaveBaseData( + m_NavigationDataRecorder->GetNavigationDataSet(), + (QString(m_ExperimentResultsSubDirectory + QDir::separator() + "navigation-data.csv").toStdString().c_str())); + + // write logged navigation data messages to separate file + std::stringstream csvNavigationMessagesFilename; + csvNavigationMessagesFilename << m_ExperimentResultsSubDirectory.toStdString() << QDir::separator().toLatin1() + << "CSVNavigationMessagesLogFile.csv"; + MITK_INFO("USNavigationLogging") << "Writing logged navigation messages to separate csv file: " + << csvNavigationMessagesFilename.str(); + m_LoggingBackend.WriteCSVFileWithNavigationMessages(csvNavigationMessagesFilename.str()); + + mbilog::UnregisterBackend(&m_LoggingBackend); + + m_IsExperimentRunning = false; + + m_ImageAndNavigationDataLoggingTimer->stop(); + m_CombinedModality = 0; + + // reset scene number for next experiment + m_SceneNumber = 1; + + this->WaitCursorOff(); + MITK_INFO("USNavigationLogging") << "Finished!"; } void USNavigationMarkerPlacement::OnCombinedModalityChanged( - itk::SmartPointer combinedModality) + itk::SmartPointer combinedModality) { - // remove old listener for ultrasound device changes - if (m_CombinedModality.IsNotNull() && m_CombinedModality->GetUltrasoundDevice().IsNotNull()) - { - m_CombinedModality->GetUltrasoundDevice()->RemovePropertyChangedListener(m_ListenerDeviceChanged); - } - - m_CombinedModality = combinedModality; - m_ReinitAlreadyDone = false; - - // add a listener for ultrasound device changes - if (m_CombinedModality.IsNotNull() && m_CombinedModality->GetUltrasoundDevice().IsNotNull()) - { - m_CombinedModality->GetUltrasoundDevice()->AddPropertyChangedListener(m_ListenerDeviceChanged); - } - - // update navigation data recorder for using the new combined modality - mitk::NavigationDataSource::Pointer navigationDataSource = combinedModality->GetNavigationDataSource(); - m_NavigationDataRecorder->ConnectTo(navigationDataSource); - m_NavigationDataRecorder->ResetRecording(); - - //upate stored tool axis for current tool - this->SetToolAxisMarkerPlacement(); - //store new tool axis - QmitkUSNavigationStepPunctuationIntervention* stepIntervention = - new QmitkUSNavigationStepPunctuationIntervention(m_ToolAxis, m_Parent); - m_NavigationSteps.pop_back(); - m_NavigationSteps.push_back(stepIntervention); - - // TODO check for correct connection - // for (unsigned int n = 0; n < navigationDataSource->GetNumberOfIndexedOutputs(); ++n) - // { - // m_NavigationDataRecorder->AddNavigationData(navigationDataSource->GetOutput(n)); - // } + // remove old listener for ultrasound device changes + if (m_CombinedModality.IsNotNull() && m_CombinedModality->GetUltrasoundDevice().IsNotNull()) + { + m_CombinedModality->GetUltrasoundDevice()->RemovePropertyChangedListener(m_ListenerDeviceChanged); + } + + m_CombinedModality = combinedModality; + m_ReinitAlreadyDone = false; + + // add a listener for ultrasound device changes + if (m_CombinedModality.IsNotNull() && m_CombinedModality->GetUltrasoundDevice().IsNotNull()) + { + m_CombinedModality->GetUltrasoundDevice()->AddPropertyChangedListener(m_ListenerDeviceChanged); + } + + // update navigation data recorder for using the new combined modality + mitk::NavigationDataSource::Pointer navigationDataSource = combinedModality->GetNavigationDataSource(); + m_NavigationDataRecorder->ConnectTo(navigationDataSource); + m_NavigationDataRecorder->ResetRecording(); // update ultrasound image logging filter for using the new combined modality mitk::USDevice::Pointer ultrasoundImageSource = combinedModality->GetUltrasoundDevice(); for (unsigned int n = 0; n < ultrasoundImageSource->GetNumberOfIndexedOutputs(); ++n) { m_USImageLoggingFilter->SetInput(n, ultrasoundImageSource->GetOutput(n)); } // update ablation zone filter for using the new combined modality for (unsigned int n = 0; n < navigationDataSource->GetNumberOfIndexedOutputs(); ++n) { m_AblationZonesDisplacementFilter->SetInput(n, navigationDataSource->GetOutput(n)); } m_AblationZonesDisplacementFilter->SelectInput(m_MarkerIndex); // make sure that a reinit is done for the new images this->ReinitOnImage(); } void USNavigationMarkerPlacement::OnSettingsChanged(itk::SmartPointer settings) { - std::string applicationName; - if (!settings->GetStringProperty("settings.application", applicationName)) - { - // set default application if the string property is not available - applicationName = "Marker Placement"; - } - - // create navigation step widgets according to the selected application - if (applicationName != m_CurrentApplicationName) - { - m_CurrentApplicationName = applicationName; + std::string applicationName; + if (!settings->GetStringProperty("settings.application", applicationName)) + { + // set default application if the string property is not available + applicationName = "Marker Placement"; + } + + // create navigation step widgets according to the selected application + if (applicationName != m_CurrentApplicationName) + { + m_CurrentApplicationName = applicationName; QmitkUSNavigationProcessWidget::NavigationStepVector navigationSteps; if (applicationName == "Puncture") { QmitkUSNavigationStepCombinedModality* stepCombinedModality = new QmitkUSNavigationStepCombinedModality(m_Parent); QmitkUSNavigationStepTumourSelection* stepTumourSelection = new QmitkUSNavigationStepTumourSelection(m_Parent); stepTumourSelection->SetTargetSelectionOptional(true); m_TargetNodeDisplacementFilter = stepTumourSelection->GetTumourNodeDisplacementFilter(); QmitkUSNavigationStepZoneMarking* stepZoneMarking = new QmitkUSNavigationStepZoneMarking(m_Parent); QmitkUSNavigationStepPunctuationIntervention* stepIntervention = new QmitkUSNavigationStepPunctuationIntervention(m_Parent); - connect(stepIntervention, SIGNAL(AddAblationZoneClicked(int)), this, SLOT(OnAddAblationZone(int))); - connect(stepIntervention, SIGNAL(AblationZoneChanged(int, int)), this, SLOT(OnChangeAblationZone(int, int))); - - m_NavigationStepNames = std::vector(); - navigationSteps.push_back(stepCombinedModality); - m_NavigationStepNames.push_back("Combined Modality Initialization"); - navigationSteps.push_back(stepTumourSelection); - m_NavigationStepNames.push_back("Target Selection"); - navigationSteps.push_back(stepZoneMarking); - m_NavigationStepNames.push_back("Critical Structure Marking"); - navigationSteps.push_back(stepIntervention); - m_NavigationStepNames.push_back("Intervention"); - } - else if (applicationName == "Marker Placement") - { - QmitkUSNavigationStepCombinedModality *stepCombinedModality = new QmitkUSNavigationStepCombinedModality(m_Parent); - QmitkUSNavigationStepTumourSelection *stepTumourSelection = new QmitkUSNavigationStepTumourSelection(m_Parent); - m_TargetNodeDisplacementFilter = stepTumourSelection->GetTumourNodeDisplacementFilter(); - QmitkUSNavigationStepZoneMarking *stepZoneMarking = new QmitkUSNavigationStepZoneMarking(m_Parent); - QmitkUSNavigationStepPlacementPlanning *stepPlacementPlanning = - new QmitkUSNavigationStepPlacementPlanning(m_Parent); - QmitkUSNavigationStepMarkerIntervention *stepMarkerIntervention = - new QmitkUSNavigationStepMarkerIntervention(m_Parent); - - m_NavigationStepNames = std::vector(); - navigationSteps.push_back(stepCombinedModality); - m_NavigationStepNames.push_back("Combined Modality Initialization"); - navigationSteps.push_back(stepTumourSelection); - m_NavigationStepNames.push_back("Target Selection"); - navigationSteps.push_back(stepZoneMarking); - m_NavigationStepNames.push_back("Critical Structure Marking"); - navigationSteps.push_back(stepPlacementPlanning); - m_NavigationStepNames.push_back("Placement Planning"); - navigationSteps.push_back(stepMarkerIntervention); - m_NavigationStepNames.push_back("Marker Intervention"); - } - - // set navigation step widgets to the process widget - ui->navigationProcessWidget->SetNavigationSteps(navigationSteps); - - for (QmitkUSNavigationProcessWidget::NavigationStepIterator it = m_NavigationSteps.begin(); - it != m_NavigationSteps.end(); - ++it) - { - delete *it; - } - m_NavigationSteps.clear(); - m_NavigationSteps = navigationSteps; - } - - // initialize gui according to the experiment mode setting - bool experimentMode = false; - settings->GetBoolProperty("settings.experiment-mode", experimentMode); - ui->startExperimentButton->setVisible(experimentMode); - ui->finishExperimentButton->setVisible(experimentMode); - ui->runningLabel->setVisible(experimentMode); - if (experimentMode && !m_IsExperimentRunning) - { - ui->navigationProcessWidget->ResetNavigationProcess(); - ui->navigationProcessWidget->EnableInteraction(false); - ui->runningLabel->setPixmap(m_IconNotRunning); - } - else if (!experimentMode) - { - if (m_IsExperimentRunning) - { - this->OnFinishExperiment(); - } - ui->navigationProcessWidget->EnableInteraction(true); - } - - // get the results directory from the settings and use home directory if - // there is no results directory configured - std::string resultsDirectory; - if (settings->GetStringProperty("settings.experiment-results-directory", resultsDirectory)) - { - m_ResultsDirectory = QString::fromStdString(resultsDirectory); - } - else - { - m_ResultsDirectory = QDir::homePath(); - } - - // make sure that the results directory exists - QDir resultsDirectoryQDir = QDir(m_ResultsDirectory); - if (!resultsDirectoryQDir.exists()) - { - resultsDirectoryQDir.mkpath(m_ResultsDirectory); - } - - MITK_INFO << "Results Directory: " << m_ResultsDirectory.toStdString(); + connect(stepIntervention, SIGNAL(AddAblationZoneClicked(int)), this, SLOT(OnAddAblationZone(int))); + connect(stepIntervention, SIGNAL(AblationZoneChanged(int, int)), this, SLOT(OnChangeAblationZone(int, int))); + + m_NavigationStepNames = std::vector(); + navigationSteps.push_back(stepCombinedModality); + m_NavigationStepNames.push_back("Combined Modality Initialization"); + navigationSteps.push_back(stepTumourSelection); + m_NavigationStepNames.push_back("Target Selection"); + navigationSteps.push_back(stepZoneMarking); + m_NavigationStepNames.push_back("Critical Structure Marking"); + navigationSteps.push_back(stepIntervention); + m_NavigationStepNames.push_back("Intervention"); + } + else if (applicationName == "Marker Placement") + { + QmitkUSNavigationStepCombinedModality *stepCombinedModality = new QmitkUSNavigationStepCombinedModality(m_Parent); + QmitkUSNavigationStepTumourSelection *stepTumourSelection = new QmitkUSNavigationStepTumourSelection(m_Parent); + m_TargetNodeDisplacementFilter = stepTumourSelection->GetTumourNodeDisplacementFilter(); + QmitkUSNavigationStepZoneMarking *stepZoneMarking = new QmitkUSNavigationStepZoneMarking(m_Parent); + QmitkUSNavigationStepPlacementPlanning *stepPlacementPlanning = + new QmitkUSNavigationStepPlacementPlanning(m_Parent); + QmitkUSNavigationStepMarkerIntervention *stepMarkerIntervention = + new QmitkUSNavigationStepMarkerIntervention(m_Parent); + + m_NavigationStepNames = std::vector(); + navigationSteps.push_back(stepCombinedModality); + m_NavigationStepNames.push_back("Combined Modality Initialization"); + navigationSteps.push_back(stepTumourSelection); + m_NavigationStepNames.push_back("Target Selection"); + navigationSteps.push_back(stepZoneMarking); + m_NavigationStepNames.push_back("Critical Structure Marking"); + navigationSteps.push_back(stepPlacementPlanning); + m_NavigationStepNames.push_back("Placement Planning"); + navigationSteps.push_back(stepMarkerIntervention); + m_NavigationStepNames.push_back("Marker Intervention"); + } + + // set navigation step widgets to the process widget + ui->navigationProcessWidget->SetNavigationSteps(navigationSteps); + + for (QmitkUSNavigationProcessWidget::NavigationStepIterator it = m_NavigationSteps.begin(); + it != m_NavigationSteps.end(); + ++it) + { + delete *it; + } + m_NavigationSteps.clear(); + m_NavigationSteps = navigationSteps; + } + + // initialize gui according to the experiment mode setting + bool experimentMode = false; + settings->GetBoolProperty("settings.experiment-mode", experimentMode); + ui->startExperimentButton->setVisible(experimentMode); + ui->finishExperimentButton->setVisible(experimentMode); + ui->runningLabel->setVisible(experimentMode); + if (experimentMode && !m_IsExperimentRunning) + { + ui->navigationProcessWidget->ResetNavigationProcess(); + ui->navigationProcessWidget->EnableInteraction(false); + ui->runningLabel->setPixmap(m_IconNotRunning); + } + else if (!experimentMode) + { + if (m_IsExperimentRunning) + { + this->OnFinishExperiment(); + } + ui->navigationProcessWidget->EnableInteraction(true); + } + + // get the results directory from the settings and use home directory if + // there is no results directory configured + std::string resultsDirectory; + if (settings->GetStringProperty("settings.experiment-results-directory", resultsDirectory)) + { + m_ResultsDirectory = QString::fromStdString(resultsDirectory); + } + else + { + m_ResultsDirectory = QDir::homePath(); + } + + // make sure that the results directory exists + QDir resultsDirectoryQDir = QDir(m_ResultsDirectory); + if (!resultsDirectoryQDir.exists()) + { + resultsDirectoryQDir.mkpath(m_ResultsDirectory); + } + + MITK_INFO("USNavigation") << "Results Directory: " << m_ResultsDirectory.toStdString(); } void USNavigationMarkerPlacement::OnActiveNavigationStepChanged(int index) { - // update navigation step timer each time the active navigation step changes - m_NavigationStepTimer->SetActiveIndex(index, m_NavigationSteps.at(index)->GetTitle().toStdString()); - if (m_NavigationStepNames.size() <= index) - { - MITK_INFO("USNavigationLogging") << "Someting went wrong: unknown navigation step!"; - } - else - { - MITK_INFO("USNavigationLogging") << "Navigation step finished/changed, next step: " - << this->m_NavigationStepNames.at(index).toStdString() - << "; duration until now: " << m_NavigationStepTimer->GetTotalDuration(); - } + // update navigation step timer each time the active navigation step changes + m_NavigationStepTimer->SetActiveIndex(index, m_NavigationSteps.at(index)->GetTitle().toStdString()); + if (m_NavigationStepNames.size() <= index) + { + MITK_INFO("USNavigationLogging") << "Someting went wrong: unknown navigation step!"; + } + else + { + MITK_INFO("USNavigationLogging") << "Navigation step finished/changed, next step: " + << this->m_NavigationStepNames.at(index).toStdString() + << "; duration until now: " << m_NavigationStepTimer->GetTotalDuration(); + } + + } + + void USNavigationMarkerPlacement::OnNextNavigationStepInitialization(int index) + { + + MITK_DEBUG << "Next Step: " << m_NavigationSteps.at(index)->GetTitle().toStdString(); + + if (m_NavigationSteps.at(index)->GetTitle().toStdString() == "Computer-assisted Intervention") + { + QmitkUSNavigationStepPunctuationIntervention* navigationStepPunctuationIntervention = static_cast(m_NavigationSteps.at(index)); + if (navigationStepPunctuationIntervention != nullptr) + { + if (m_CurrentStorage.IsNull()) { this->UpdateToolStorage(); } + if (m_CurrentStorage.IsNull() || (m_CurrentStorage->GetTool(m_NeedleIndex).IsNull())) + { + MITK_WARN << "Found null pointer when setting the tool axis, aborting"; + } + else + { + navigationStepPunctuationIntervention->SetNeedleMetaData(m_CurrentStorage->GetTool(m_NeedleIndex)); + MITK_DEBUG << "Needle axis vector: " << m_CurrentStorage->GetTool(m_NeedleIndex)->GetToolAxis(); + } + } + } + } void USNavigationMarkerPlacement::OnIntermediateResultProduced(const itk::SmartPointer resultsNode) { - // intermediate results only matter during an experiment - if (!m_IsExperimentRunning) - { - return; - } - - this->WaitCursorOn(); - - // set results node to the experiment logging (for saving contents to the - // file system) - m_ExperimentLogging->SetResult(resultsNode); - - std::string resultsName; - if (!resultsNode->GetName(resultsName)) - { - MITK_WARN << "Could not get name of current results node."; - return; - } - - // save the mitk scene - std::string scenefile = QString(m_ExperimentResultsSubDirectory + QDir::separator() + - QString("Scene %1 - ").arg(m_SceneNumber++, 2, 10, QChar('0')) + - QString::fromStdString(resultsName).replace(":", "_") + ".mitk") - .toStdString(); - MITK_INFO << "Saving Scene File: " << scenefile; - - mitk::SceneIO::Pointer sceneIO = mitk::SceneIO::New(); - mitk::NodePredicateNot::Pointer isNotHelperObject = - mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object", mitk::BoolProperty::New(true))); - mitk::DataStorage::SetOfObjects::ConstPointer nodesToBeSaved = this->GetDataStorage()->GetSubset(isNotHelperObject); - - this->Convert2DImagesTo3D(nodesToBeSaved); - - sceneIO->SaveScene(nodesToBeSaved, this->GetDataStorage(), scenefile); - - this->WaitCursorOff(); + // intermediate results only matter during an experiment + if (!m_IsExperimentRunning) + { + return; + } + + this->WaitCursorOn(); + + // set results node to the experiment logging (for saving contents to the + // file system) + m_ExperimentLogging->SetResult(resultsNode); + + std::string resultsName; + if (!resultsNode->GetName(resultsName)) + { + MITK_WARN << "Could not get name of current results node."; + return; + } + + // save the mitk scene + std::string scenefile = QString(m_ExperimentResultsSubDirectory + QDir::separator() + + QString("Scene %1 - ").arg(m_SceneNumber++, 2, 10, QChar('0')) + + QString::fromStdString(resultsName).replace(":", "_") + ".mitk") + .toStdString(); + MITK_INFO << "Saving Scene File: " << scenefile; + + mitk::SceneIO::Pointer sceneIO = mitk::SceneIO::New(); + mitk::NodePredicateNot::Pointer isNotHelperObject = + mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object", mitk::BoolProperty::New(true))); + mitk::DataStorage::SetOfObjects::ConstPointer nodesToBeSaved = this->GetDataStorage()->GetSubset(isNotHelperObject); + + this->Convert2DImagesTo3D(nodesToBeSaved); + + sceneIO->SaveScene(nodesToBeSaved, this->GetDataStorage(), scenefile); + + this->WaitCursorOff(); } void USNavigationMarkerPlacement::ReinitOnImage() { - if (!m_ReinitAlreadyDone && m_CombinedModality.IsNotNull()) - { - // make sure that the output is already calibrated correctly - // (if the zoom level was changed recently) - m_CombinedModality->Modified(); - m_CombinedModality->Update(); - - mitk::Image::Pointer image = m_CombinedModality->GetOutput(); - if (image.IsNotNull() && image->IsInitialized()) - { - // make a reinit on the ultrasound image - mitk::IRenderWindowPart *renderWindow = this->GetRenderWindowPart(); - if (renderWindow != NULL && image->GetTimeGeometry()->IsValid()) - { - renderWindow->GetRenderingManager()->InitializeViews( - image->GetTimeGeometry(), mitk::RenderingManager::REQUEST_UPDATE_ALL, true); - renderWindow->GetRenderingManager()->RequestUpdateAll(); - } - - this->RequestRenderWindowUpdate(); - - m_ReinitAlreadyDone = true; - } - } + if (!m_ReinitAlreadyDone && m_CombinedModality.IsNotNull()) + { + // make sure that the output is already calibrated correctly + // (if the zoom level was changed recently) + m_CombinedModality->Modified(); + m_CombinedModality->Update(); + + mitk::Image::Pointer image = m_CombinedModality->GetOutput(); + if (image.IsNotNull() && image->IsInitialized()) + { + // make a reinit on the ultrasound image + mitk::IRenderWindowPart *renderWindow = this->GetRenderWindowPart(); + if (renderWindow != NULL && image->GetTimeGeometry()->IsValid()) + { + renderWindow->GetRenderingManager()->InitializeViews( + image->GetTimeGeometry(), mitk::RenderingManager::REQUEST_UPDATE_ALL, true); + renderWindow->GetRenderingManager()->RequestUpdateAll(); + } + + this->RequestRenderWindowUpdate(); + + m_ReinitAlreadyDone = true; + } + } } void USNavigationMarkerPlacement::Convert2DImagesTo3D(mitk::DataStorage::SetOfObjects::ConstPointer nodes) { - for (mitk::DataStorage::SetOfObjects::ConstIterator it = nodes->Begin(); it != nodes->End(); ++it) - { - if (it->Value()->GetData() && strcmp(it->Value()->GetData()->GetNameOfClass(), "Image") == 0) - { - // convert image to 3d image if it is 2d at the moment - mitk::Image::Pointer image = dynamic_cast(it->Value()->GetData()); - if (image.IsNotNull() && image->GetDimension() == 2 && !image->GetGeometry()->Is2DConvertable()) - { - mitk::Convert2Dto3DImageFilter::Pointer convert2DTo3DImageFilter = mitk::Convert2Dto3DImageFilter::New(); - convert2DTo3DImageFilter->SetInput(image); - convert2DTo3DImageFilter->Update(); - it->Value()->SetData(convert2DTo3DImageFilter->GetOutput()); - } - } - } + for (mitk::DataStorage::SetOfObjects::ConstIterator it = nodes->Begin(); it != nodes->End(); ++it) + { + if (it->Value()->GetData() && strcmp(it->Value()->GetData()->GetNameOfClass(), "Image") == 0) + { + // convert image to 3d image if it is 2d at the moment + mitk::Image::Pointer image = dynamic_cast(it->Value()->GetData()); + if (image.IsNotNull() && image->GetDimension() == 2 && !image->GetGeometry()->Is2DConvertable()) + { + mitk::Convert2Dto3DImageFilter::Pointer convert2DTo3DImageFilter = mitk::Convert2Dto3DImageFilter::New(); + convert2DTo3DImageFilter->SetInput(image); + convert2DTo3DImageFilter->Update(); + it->Value()->SetData(convert2DTo3DImageFilter->GetOutput()); + } + } + } } void USNavigationMarkerPlacement::CreateOverlays() { - // initialize warning overlay (and do not display it, yet) - m_WarnOverlay->SetText("Warning: No calibration available for current depth."); - - // set position and font size for the text overlay - // (nonesense postition as a layouter is used, but it ignored - // the overlay without setting a position here) - mitk::Point2D overlayPosition; - overlayPosition.SetElement(0, -50.0f); - overlayPosition.SetElement(1, -50.0f); - m_WarnOverlay->SetPosition2D(overlayPosition); - m_WarnOverlay->SetFontSize(22); - m_WarnOverlay->SetColor(1, 0, 0); // overlay should be red + // initialize warning overlay (and do not display it, yet) + m_WarnOverlay->SetText("Warning: No calibration available for current depth."); + + // set position and font size for the text overlay + // (nonesense postition as a layouter is used, but it ignored + // the overlay without setting a position here) + mitk::Point2D overlayPosition; + overlayPosition.SetElement(0, -50.0f); + overlayPosition.SetElement(1, -50.0f); + m_WarnOverlay->SetPosition2D(overlayPosition); + m_WarnOverlay->SetFontSize(22); + m_WarnOverlay->SetColor(1, 0, 0); // overlay should be red +} + +void USNavigationMarkerPlacement::UpdateToolStorage() +{ + if (m_NavigationDataSource.IsNull()) { m_NavigationDataSource = m_CombinedModality->GetNavigationDataSource(); } + if (m_NavigationDataSource.IsNull()) { MITK_WARN << "Found an invalid navigation data source object!"; } + us::ModuleContext* context = us::GetModuleContext(); + std::string id = m_NavigationDataSource->US_PROPKEY_ID; + std::string filter = "(" + mitk::NavigationToolStorage::US_PROPKEY_SOURCE_ID + "=" + id + ")"; + // Get Storage + std::vector > refs = context->GetServiceReferences(); + m_CurrentStorage = context->GetService(refs.front()); + + if (m_CurrentStorage.IsNull()) + { + MITK_WARN << "Found an invalid storage object!"; + } + else if (m_CurrentStorage->GetToolCount() != m_NavigationDataSource->GetNumberOfOutputs()) //there is something wrong with the storage + { + MITK_WARN << "Found a tool storage, but it has not the same number of tools like the NavigationDataSource. This storage won't be used because it isn't the right one."; + m_CurrentStorage = NULL; + } } diff --git a/Plugins/org.mitk.gui.qt.igt.app.echotrack/src/internal/USNavigationMarkerPlacement.h b/Plugins/org.mitk.gui.qt.igt.app.echotrack/src/internal/USNavigationMarkerPlacement.h index 550c100edf..b95448c856 100644 --- a/Plugins/org.mitk.gui.qt.igt.app.echotrack/src/internal/USNavigationMarkerPlacement.h +++ b/Plugins/org.mitk.gui.qt.igt.app.echotrack/src/internal/USNavigationMarkerPlacement.h @@ -1,206 +1,207 @@ /*=================================================================== 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. ===================================================================*/ #ifndef USNAVIGATIONMARKERPLACEMENT_H #define USNAVIGATIONMARKERPLACEMENT_H #include #include "IO/mitkUSNavigationLoggingBackend.h" #include "Widgets/QmitkUSNavigationProcessWidget.h" #include "mitkNavigationDataRecorder.h" #include "mitkNodeDisplacementFilter.h" #include "mitkUSImageLoggingFilter.h" #include #include #include #include namespace itk { template class SmartPointer; } namespace mitk { class USNavigationStepTimer; class USNavigationExperimentLogging; } namespace Ui { class USNavigationMarkerPlacement; } class QmitkUSAbstractNavigationStep; +class QmitkUSNavigationStepPunctuationIntervention; class QmitkStdMultiWidget; class QTimer; class QSignalMapper; /** * \brief View for navigated marker placement using the combined modality. * This view utilizes the QmitkUSNavigationProcessWidget to do the navigation * process. It can be switched between widgets for marker placement and widgets * for punctuation. * * An experiment mode allows for logging results, durations and the ultrasound * images. */ class USNavigationMarkerPlacement : public QmitkAbstractView { Q_OBJECT protected slots: /** * \brief Called periodically to update the rendering. * The standard multi widget is changed to fit the navigation process once it * is available and a reinit on the ultrasound image is done for a new image * node. */ void OnTimeout(); /** * \brief Called periodically during an experiment for logging the ultrasound images. */ void OnImageAndNavigationDataLoggingTimeout(); /** * \brief Initializes anything neccessary for an experiment. * The user is asked for a directory for storing the results and the logging * is started. */ void OnStartExperiment(); /** * \brief Stops logging and saves everything to the file system. */ void OnFinishExperiment(); void OnCombinedModalityChanged(itk::SmartPointer); /** * \brief Switches the navigation step widgets if the navigation application was changed. */ void OnSettingsChanged(itk::SmartPointer); /** * \brief Updates the timer for the navigation steps durations every time the active navigation step changes. */ void OnActiveNavigationStepChanged(int); + /** Initializes the next navigation step */ + void OnNextNavigationStepInitialization(int); + /** * \brief The data node is given to the experiment logging and scene is saved to the file system. */ void OnIntermediateResultProduced(const itk::SmartPointer); void OnAddAblationZone(int size); void OnEnableNavigationLayout(); void OnResetStandardLayout(); void OnChangeLayoutClicked(); void OnChangeAblationZone(int id, int newSize); public: static const char *DATANAME_TUMOUR; static const char *DATANAME_TARGETSURFACE; static const char *DATANAME_ZONES; static const char *DATANAME_TARGETS; static const char *DATANAME_TARGETS_PATHS; static const char *DATANAME_REACHED_TARGETS; explicit USNavigationMarkerPlacement(); ~USNavigationMarkerPlacement(); virtual void CreateQtPartControl(QWidget *parent); static const std::string VIEW_ID; void OnCombinedModalityPropertyChanged(const std::string &, const std::string &); - /** - * \returns the point defining the needle axis in the tool storage - */ - void SetToolAxisMarkerPlacement(); - mitk::Point3D m_ToolAxis; protected: /** * \brief A reinit on the ultrasound image is performed every time the view gets the focus. */ virtual void SetFocus(); /** * \brief Helper function which performs a reinit on the ultrasound image. */ void ReinitOnImage(); /** * \brief Helper function for being able to serialize the 2d ultrasound image. */ void Convert2DImagesTo3D(mitk::DataStorage::SetOfObjects::ConstPointer nodes); + void UpdateToolStorage(); + void CreateOverlays(); QWidget *m_Parent; QmitkUSNavigationProcessWidget::NavigationStepVector m_NavigationSteps; QTimer *m_UpdateTimer; QTimer *m_ImageAndNavigationDataLoggingTimer; QmitkStdMultiWidget *m_StdMultiWidget; itk::SmartPointer m_CombinedModality; bool m_ReinitAlreadyDone; bool m_IsExperimentRunning; std::string m_CurrentApplicationName; itk::SmartPointer m_NavigationStepTimer; itk::SmartPointer m_ExperimentLogging; QPixmap m_IconRunning; QPixmap m_IconNotRunning; QString m_ResultsDirectory; QString m_ExperimentName; QString m_ExperimentResultsSubDirectory; std::vector m_NavigationStepNames; // stores the names of the navigation steps which are currently used (for logging purposes) mitk::USNavigationLoggingBackend m_LoggingBackend; mitk::USImageLoggingFilter::Pointer m_USImageLoggingFilter; mitk::NavigationDataRecorder::Pointer m_NavigationDataRecorder; // records navigation data files mitk::NodeDisplacementFilter::Pointer m_TargetNodeDisplacementFilter; mitk::NodeDisplacementFilter::Pointer m_AblationZonesDisplacementFilter; std::vector m_AblationZonesVector; int m_NeedleIndex; int m_MarkerIndex; int m_SceneNumber; itk::SmartPointer m_WarnOverlay; //To get tool storage mitk::NavigationDataSource::Pointer m_NavigationDataSource; mitk::NavigationToolStorage::Pointer m_CurrentStorage; private: mitk::MessageDelegate2 m_ListenerDeviceChanged; Ui::USNavigationMarkerPlacement *ui; }; #endif // USNAVIGATIONMARKERPLACEMENT_H diff --git a/Plugins/org.mitk.gui.qt.igt.app.echotrack/src/internal/Widgets/QmitkUSNavigationProcessWidget.cpp b/Plugins/org.mitk.gui.qt.igt.app.echotrack/src/internal/Widgets/QmitkUSNavigationProcessWidget.cpp index 5da2076cf7..2f131ffd21 100644 --- a/Plugins/org.mitk.gui.qt.igt.app.echotrack/src/internal/Widgets/QmitkUSNavigationProcessWidget.cpp +++ b/Plugins/org.mitk.gui.qt.igt.app.echotrack/src/internal/Widgets/QmitkUSNavigationProcessWidget.cpp @@ -1,556 +1,557 @@ /*=================================================================== 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 "QmitkUSNavigationProcessWidget.h" #include "ui_QmitkUSNavigationProcessWidget.h" #include "../NavigationStepWidgets/QmitkUSAbstractNavigationStep.h" #include "../SettingsWidgets/QmitkUSNavigationAbstractSettingsWidget.h" #include "mitkDataNode.h" #include "mitkNavigationDataToNavigationDataFilter.h" #include #include #include QmitkUSNavigationProcessWidget::QmitkUSNavigationProcessWidget(QWidget* parent) : QWidget(parent), m_SettingsWidget(0), m_BaseNode(mitk::DataNode::New()), m_CurrentTabIndex(0), m_CurrentMaxStep(0), m_ImageAlreadySetToNode(false), m_ReadySignalMapper(new QSignalMapper(this)), m_NoLongerReadySignalMapper(new QSignalMapper(this)), m_StdMultiWidget(0), m_UsePlanningStepWidget(false), ui(new Ui::QmitkUSNavigationProcessWidget) { m_Parent = parent; ui->setupUi(this); // remove the default page ui->stepsToolBox->setCurrentIndex(1);// ->removeItem(0); //set shortcuts QShortcut *nextShortcut = new QShortcut(QKeySequence("F10"), parent); QShortcut *prevShortcut = new QShortcut(QKeySequence("F11"), parent); connect(nextShortcut, SIGNAL(activated()), this, SLOT(OnNextButtonClicked())); connect(prevShortcut, SIGNAL(activated()), this, SLOT(OnPreviousButtonClicked())); //connect other slots connect( ui->restartStepButton, SIGNAL(clicked()), this, SLOT(OnRestartStepButtonClicked()) ); connect( ui->previousButton, SIGNAL(clicked()), this, SLOT(OnPreviousButtonClicked()) ); connect( ui->nextButton, SIGNAL(clicked()), this, SLOT(OnNextButtonClicked()) ); connect( ui->stepsToolBox, SIGNAL(currentChanged(int)), this, SLOT(OnTabChanged(int)) ); connect (ui->settingsButton, SIGNAL(clicked()), this, SLOT(OnSettingsButtonClicked()) ); connect( m_ReadySignalMapper, SIGNAL(mapped(int)), this, SLOT(OnStepReady(int)) ); connect( m_NoLongerReadySignalMapper, SIGNAL(mapped(int)), this, SLOT(OnStepNoLongerReady(int)) ); ui->settingsFrameWidget->setHidden(true); } QmitkUSNavigationProcessWidget::~QmitkUSNavigationProcessWidget() { ui->stepsToolBox->blockSignals(true); for ( NavigationStepVector::iterator it = m_NavigationSteps.begin(); it != m_NavigationSteps.end(); ++it ) { if ( (*it)->GetNavigationStepState() > QmitkUSAbstractNavigationStep::State_Stopped ) { (*it)->StopStep(); } delete *it; } m_NavigationSteps.clear(); if ( m_SettingsNode.IsNotNull() && m_DataStorage.IsNotNull() ) { m_DataStorage->Remove(m_SettingsNode); } delete ui; } void QmitkUSNavigationProcessWidget::EnableInteraction(bool enable) { if (enable) { ui->restartStepButton->setEnabled(true); ui->previousButton->setEnabled(true); ui->nextButton->setEnabled(true); ui->stepsToolBox->setEnabled(true); } else { ui->restartStepButton->setEnabled(false); ui->previousButton->setEnabled(false); ui->nextButton->setEnabled(false); ui->stepsToolBox->setEnabled(false); } } void QmitkUSNavigationProcessWidget::SetDataStorage(itk::SmartPointer dataStorage) { m_DataStorage = dataStorage; if ( dataStorage.IsNull() ) { mitkThrow() << "Data Storage must not be null for QmitkUSNavigationProcessWidget."; } // test if base node is already in the data storage and add it if not m_BaseNode = dataStorage->GetNamedNode(QmitkUSAbstractNavigationStep::DATANAME_BASENODE); if ( m_BaseNode.IsNull() ) { m_BaseNode = mitk::DataNode::New(); m_BaseNode->SetName(QmitkUSAbstractNavigationStep::DATANAME_BASENODE); dataStorage->Add(m_BaseNode); } // base node and image stream node may be the same node if ( strcmp(QmitkUSAbstractNavigationStep::DATANAME_BASENODE, QmitkUSAbstractNavigationStep::DATANAME_IMAGESTREAM) != 0) { m_ImageStreamNode = dataStorage->GetNamedNode(QmitkUSAbstractNavigationStep::DATANAME_IMAGESTREAM); if (m_ImageStreamNode.IsNull()) { // Create Node for US Stream m_ImageStreamNode = mitk::DataNode::New(); m_ImageStreamNode->SetName(QmitkUSAbstractNavigationStep::DATANAME_IMAGESTREAM); dataStorage->Add(m_ImageStreamNode); } } else { m_ImageStreamNode = m_BaseNode; } m_SettingsNode = dataStorage->GetNamedDerivedNode(QmitkUSAbstractNavigationStep::DATANAME_SETTINGS, m_BaseNode); if ( m_SettingsNode.IsNull() ) { m_SettingsNode = mitk::DataNode::New(); m_SettingsNode->SetName(QmitkUSAbstractNavigationStep::DATANAME_SETTINGS); dataStorage->Add(m_SettingsNode, m_BaseNode); } if (m_SettingsWidget) { m_SettingsWidget->SetSettingsNode(m_SettingsNode); } } void QmitkUSNavigationProcessWidget::SetSettingsWidget(QmitkUSNavigationAbstractSettingsWidget* settingsWidget) { // disconnect slots to settings widget if there was a widget before if ( m_SettingsWidget ) { disconnect( ui->settingsSaveButton, SIGNAL(clicked()), m_SettingsWidget, SLOT(OnSave()) ); disconnect( ui->settingsCancelButton, SIGNAL(clicked()), m_SettingsWidget, SLOT(OnCancel()) ); disconnect (m_SettingsWidget, SIGNAL(Saved()), this, SLOT(OnSettingsWidgetReturned()) ); disconnect (m_SettingsWidget, SIGNAL(Canceled()), this, SLOT(OnSettingsWidgetReturned()) ); disconnect (m_SettingsWidget, SIGNAL(SettingsChanged(itk::SmartPointer)), this, SLOT(OnSettingsChanged(itk::SmartPointer)) ); ui->settingsWidget->removeWidget(m_SettingsWidget); } m_SettingsWidget = settingsWidget; if ( m_SettingsWidget ) { m_SettingsWidget->LoadSettings(); connect( ui->settingsSaveButton, SIGNAL(clicked()), m_SettingsWidget, SLOT(OnSave()) ); connect( ui->settingsCancelButton, SIGNAL(clicked()), m_SettingsWidget, SLOT(OnCancel()) ); connect (m_SettingsWidget, SIGNAL(Saved()), this, SLOT(OnSettingsWidgetReturned()) ); connect (m_SettingsWidget, SIGNAL(Canceled()), this, SLOT(OnSettingsWidgetReturned()) ); connect (m_SettingsWidget, SIGNAL(SettingsChanged(itk::SmartPointer)), this, SLOT(OnSettingsChanged(itk::SmartPointer)) ); if ( m_SettingsNode.IsNotNull() ) { m_SettingsWidget->SetSettingsNode(m_SettingsNode, true); } ui->settingsWidget->addWidget(m_SettingsWidget); } ui->settingsButton->setEnabled(m_SettingsWidget != 0); } void QmitkUSNavigationProcessWidget::SetNavigationSteps(NavigationStepVector navigationSteps) { disconnect( this, SLOT(OnTabChanged(int)) ); for ( int n = ui->stepsToolBox->count()-1; n >= 0; --n ) { //ui->stepsToolBox->removeItem(n); } connect( ui->stepsToolBox, SIGNAL(currentChanged(int)), this, SLOT(OnTabChanged(int)) ); m_NavigationSteps.clear(); m_NavigationSteps = navigationSteps; this->InitializeNavigationStepWidgets(); // notify all navigation step widgets about the current settings for (NavigationStepIterator it = m_NavigationSteps.begin(); it != m_NavigationSteps.end(); ++it) { (*it)->OnSettingsChanged(m_SettingsNode); } } void QmitkUSNavigationProcessWidget::ResetNavigationProcess() { MITK_INFO("QmitkUSNavigationProcessWidget") << "Resetting navigation process."; ui->stepsToolBox->blockSignals(true); for ( int n = 0; n <= m_CurrentMaxStep; ++n ) { m_NavigationSteps.at(n)->StopStep(); //if ( n > 0 ) { ui->stepsToolBox->setItemEnabled(n, false); } } ui->stepsToolBox->blockSignals(false); m_CurrentMaxStep = 0; ui->stepsToolBox->setCurrentIndex(0); if ( m_NavigationSteps.size() > 0 ) { m_NavigationSteps.at(0)->ActivateStep(); } this->UpdatePrevNextButtons(); } void QmitkUSNavigationProcessWidget::UpdateNavigationProgress() { if ( m_CombinedModality.IsNotNull() && !m_CombinedModality->GetIsFreezed() ) { m_CombinedModality->Modified(); m_CombinedModality->Update(); if ( m_LastNavigationDataFilter.IsNotNull() ) { m_LastNavigationDataFilter->Update(); } mitk::Image::Pointer image = m_CombinedModality->GetOutput(); // make sure that always the current image is set to the data node if ( image.IsNotNull() && m_ImageStreamNode->GetData() != image.GetPointer() && image->IsInitialized() ) { m_ImageStreamNode->SetData(image); m_ImageAlreadySetToNode = true; } } if ( m_CurrentTabIndex > 0 && static_cast(m_CurrentTabIndex) < m_NavigationSteps.size() ) { m_NavigationSteps.at(m_CurrentTabIndex)->Update(); } } void QmitkUSNavigationProcessWidget::OnNextButtonClicked() { if (m_CombinedModality.IsNotNull() && m_CombinedModality->GetIsFreezed()) {return;} //no moving through steps when the modality is NULL or frozen int currentIndex = ui->stepsToolBox->currentIndex(); if (currentIndex >= m_CurrentMaxStep) { MITK_WARN << "Next button clicked though no next tab widget is available."; return; } ui->stepsToolBox->setCurrentIndex(++currentIndex); this->UpdatePrevNextButtons(); } void QmitkUSNavigationProcessWidget::OnPreviousButtonClicked() { if (m_CombinedModality.IsNotNull() && m_CombinedModality->GetIsFreezed()) {return;} //no moving through steps when the modality is NULL or frozen int currentIndex = ui->stepsToolBox->currentIndex(); if (currentIndex <= 0) { MITK_WARN << "Previous button clicked though no previous tab widget is available."; return; } ui->stepsToolBox->setCurrentIndex(--currentIndex); this->UpdatePrevNextButtons(); } void QmitkUSNavigationProcessWidget::OnRestartStepButtonClicked() { - MITK_INFO("QmitkUSNavigationProcessWidget") << "Restarting step " + MITK_DEBUG("QmitkUSNavigationProcessWidget") << "Restarting step " << m_CurrentTabIndex << " (" << m_NavigationSteps.at(m_CurrentTabIndex)->GetTitle().toStdString() << ")."; m_NavigationSteps.at(ui->stepsToolBox->currentIndex())->RestartStep(); m_NavigationSteps.at(ui->stepsToolBox->currentIndex())->ActivateStep(); } void QmitkUSNavigationProcessWidget::OnTabChanged(int index) { + emit SignalActiveNavigationStepChangeRequested(index); if ( index < 0 || index >= static_cast(m_NavigationSteps.size()) ) { return; } else if ( m_CurrentTabIndex == index ) { // just activate the step if it is the same step againg m_NavigationSteps.at(index)->ActivateStep(); return; } - MITK_INFO("QmitkUSNavigationProcessWidget") << "Activating navigation step " + MITK_DEBUG("QmitkUSNavigationProcessWidget") << "Activating navigation step " << index << " (" << m_NavigationSteps.at(index)->GetTitle().toStdString() <<")."; if (index > m_CurrentTabIndex) { this->UpdateFilterPipeline(); // finish all previous steps to make sure that all data is valid for (int n = m_CurrentTabIndex; n < index; ++n) { m_NavigationSteps.at(n)->FinishStep(); } } // deactivate the previously active step if ( m_CurrentTabIndex > 0 && m_NavigationSteps.size() > static_cast(m_CurrentTabIndex) ) { m_NavigationSteps.at(m_CurrentTabIndex)->DeactivateStep(); } // start step of the current tab if it wasn't started before if ( m_NavigationSteps.at(index)->GetNavigationStepState() == QmitkUSAbstractNavigationStep::State_Stopped ) { m_NavigationSteps.at(index)->StartStep(); } m_NavigationSteps.at(index)->ActivateStep(); if (static_cast(index) < m_NavigationSteps.size()) ui->restartStepButton->setEnabled(m_NavigationSteps.at(index)->GetIsRestartable()); this->UpdatePrevNextButtons(); m_CurrentTabIndex = index; emit SignalActiveNavigationStepChanged(index); } void QmitkUSNavigationProcessWidget::OnSettingsButtonClicked() { this->SetSettingsWidgetVisible(true); } void QmitkUSNavigationProcessWidget::OnSettingsWidgetReturned() { this->SetSettingsWidgetVisible(false); } void QmitkUSNavigationProcessWidget::OnSettingsNodeChanged(itk::SmartPointer dataNode) { if ( m_SettingsWidget ) m_SettingsWidget->SetSettingsNode(dataNode); } void QmitkUSNavigationProcessWidget::OnStepReady(int index) { if (m_CurrentMaxStep <= index) { m_CurrentMaxStep = index + 1; this->UpdatePrevNextButtons(); for (int n = 0; n <= m_CurrentMaxStep; ++n) { //ui->stepsToolBox->setItemEnabled(n, true); } } emit SignalNavigationStepFinished(index, true); } void QmitkUSNavigationProcessWidget::OnStepNoLongerReady(int index) { if (m_CurrentMaxStep > index) { m_CurrentMaxStep = index; this->UpdatePrevNextButtons(); this->UpdateFilterPipeline(); for (int n = m_CurrentMaxStep+1; n < ui->stepsToolBox->count(); ++n) { //ui->stepsToolBox->setItemEnabled(n, false); m_NavigationSteps.at(n)->StopStep(); } } emit SignalNavigationStepFinished(index, false); } void QmitkUSNavigationProcessWidget::OnCombinedModalityChanged(itk::SmartPointer combinedModality) { m_CombinedModality = combinedModality; m_ImageAlreadySetToNode = false; if ( combinedModality.IsNotNull() ) { if ( combinedModality->GetNavigationDataSource().IsNull() ) { MITK_WARN << "There is no navigation data source set for the given combined modality."; return; } this->UpdateFilterPipeline(); } for (NavigationStepIterator it = m_NavigationSteps.begin(); it != m_NavigationSteps.end(); ++it) { (*it)->SetCombinedModality(combinedModality); } emit SignalCombinedModalityChanged(combinedModality); } void QmitkUSNavigationProcessWidget::OnSettingsChanged(const mitk::DataNode::Pointer dataNode) { static bool methodEntered = false; if ( methodEntered ) { MITK_WARN("QmitkUSNavigationProcessWidget") << "Ignoring recursive call to 'OnSettingsChanged()'. " << "Make sure to no emit 'SignalSettingsNodeChanged' in an 'OnSettingsChanged()' method."; return; } methodEntered = true; std::string application; if ( dataNode->GetStringProperty("settings.application", application) ) { QString applicationQString = QString::fromStdString(application); if ( applicationQString != ui->titleLabel->text() ) { ui->titleLabel->setText(applicationQString); } } // notify all navigation step widgets about the changed settings for (NavigationStepIterator it = m_NavigationSteps.begin(); it != m_NavigationSteps.end(); ++it) { (*it)->OnSettingsChanged(dataNode); } emit SignalSettingsChanged(dataNode); methodEntered = false; } void QmitkUSNavigationProcessWidget::InitializeNavigationStepWidgets() { // do not listen for steps tool box signal during insertion of items into tool box disconnect( ui->stepsToolBox, SIGNAL(currentChanged(int)), this, SLOT(OnTabChanged(int)) ); m_CurrentMaxStep = 0; mitk::DataStorage::Pointer dataStorage = m_DataStorage; for (unsigned int n = 0; n < m_NavigationSteps.size(); ++n) { QmitkUSAbstractNavigationStep* curNavigationStep = m_NavigationSteps.at(n); curNavigationStep->SetDataStorage(dataStorage); connect( curNavigationStep, SIGNAL(SignalReadyForNextStep()), m_ReadySignalMapper, SLOT(map())); connect( curNavigationStep, SIGNAL(SignalNoLongerReadyForNextStep()), m_NoLongerReadySignalMapper, SLOT(map()) ); connect( curNavigationStep, SIGNAL(SignalCombinedModalityChanged(itk::SmartPointer)), this, SLOT(OnCombinedModalityChanged(itk::SmartPointer)) ); connect( curNavigationStep, SIGNAL(SignalIntermediateResult(const itk::SmartPointer)), this, SIGNAL(SignalIntermediateResult(const itk::SmartPointer)) ); connect( curNavigationStep, SIGNAL(SignalSettingsNodeChanged(itk::SmartPointer)), this, SLOT(OnSettingsNodeChanged(itk::SmartPointer)) ); m_ReadySignalMapper->setMapping(curNavigationStep, n); m_NoLongerReadySignalMapper->setMapping(curNavigationStep, n); ui->stepsToolBox->insertWidget(n, curNavigationStep); //if ( n > 0 ) { ui->stepsToolBox->get(n, false); } } ui->restartStepButton->setEnabled(m_NavigationSteps.at(0)->GetIsRestartable()); ui->stepsToolBox->setCurrentIndex(0); // activate the first navigation step widgets if ( ! m_NavigationSteps.empty() ) { m_NavigationSteps.at(0)->ActivateStep(); } // after filling the steps tool box the signal is interesting again connect( ui->stepsToolBox, SIGNAL(currentChanged(int)), this, SLOT(OnTabChanged(int)) ); this->UpdateFilterPipeline(); } void QmitkUSNavigationProcessWidget::UpdatePrevNextButtons() { int currentIndex = ui->stepsToolBox->currentIndex(); ui->previousButton->setEnabled(currentIndex > 0); ui->nextButton->setEnabled(currentIndex < m_CurrentMaxStep); } void QmitkUSNavigationProcessWidget::UpdateFilterPipeline() { if ( m_CombinedModality.IsNull() ) { return; } std::vector filterList; mitk::NavigationDataSource::Pointer navigationDataSource = m_CombinedModality->GetNavigationDataSource(); for (unsigned int n = 0; n <= m_CurrentMaxStep && n < m_NavigationSteps.size(); ++n) { QmitkUSAbstractNavigationStep::FilterVector filter = m_NavigationSteps.at(n)->GetFilter(); if ( ! filter.empty() ) { filterList.insert(filterList.end(), filter.begin(), filter.end()); } } if ( ! filterList.empty() ) { for (unsigned int n = 0; n < navigationDataSource->GetNumberOfOutputs(); ++n) { filterList.at(0)->SetInput(n, navigationDataSource->GetOutput(n)); } for (std::vector::iterator it = filterList.begin()+1; it != filterList.end(); ++it) { std::vector::iterator prevIt = it-1; for (unsigned int n = 0; n < (*prevIt)->GetNumberOfOutputs(); ++n) { (*it)->SetInput(n, (*prevIt)->GetOutput(n)); } } m_LastNavigationDataFilter = filterList.at(filterList.size()-1); } else { m_LastNavigationDataFilter = navigationDataSource.GetPointer(); } } void QmitkUSNavigationProcessWidget::SetSettingsWidgetVisible(bool visible) { ui->settingsFrameWidget->setVisible(visible); ui->stepsToolBox->setHidden(visible); ui->settingsButton->setHidden(visible); ui->restartStepButton->setHidden(visible); ui->previousButton->setHidden(visible); ui->nextButton->setHidden(visible); } void QmitkUSNavigationProcessWidget::FinishCurrentNavigationStep() { int currentIndex = ui->stepsToolBox->currentIndex(); QmitkUSAbstractNavigationStep* curNavigationStep = m_NavigationSteps.at(currentIndex); curNavigationStep->FinishStep(); } diff --git a/Plugins/org.mitk.gui.qt.igt.app.echotrack/src/internal/Widgets/QmitkUSNavigationProcessWidget.h b/Plugins/org.mitk.gui.qt.igt.app.echotrack/src/internal/Widgets/QmitkUSNavigationProcessWidget.h index 44e6c3f967..1a78fd3998 100644 --- a/Plugins/org.mitk.gui.qt.igt.app.echotrack/src/internal/Widgets/QmitkUSNavigationProcessWidget.h +++ b/Plugins/org.mitk.gui.qt.igt.app.echotrack/src/internal/Widgets/QmitkUSNavigationProcessWidget.h @@ -1,174 +1,179 @@ /*=================================================================== 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. ===================================================================*/ #ifndef USNAVIGATIONPROCESSWIDGET_H #define USNAVIGATIONPROCESSWIDGET_H #include #include #include #include namespace itk { template class SmartPointer; } namespace mitk { class NodeDisplacementFilter; } namespace Ui { class QmitkUSNavigationProcessWidget; } class QmitkUSAbstractNavigationStep; class QmitkUSNavigationAbstractSettingsWidget; class QmitkUSNavigationStepCombinedModality; class QmitkStdMultiWidget; class QTimer; class QSignalMapper; /** * \brief Widget for handling navigation steps. * The navigation steps (subclasses of QmitkUSAbstractNavigationStep) can be set * in a vector. The coordination of this steps is done by this widget then. */ class QmitkUSNavigationProcessWidget : public QWidget { Q_OBJECT signals: /** * \brief Signals a replacement of the combined modality by one of the navigation steps. */ void SignalCombinedModalityChanged(itk::SmartPointer); /** * \brief Signals a change of the navigation settings. */ void SignalSettingsChanged(itk::SmartPointer); /** - * \brief Signals a change of the currently active navigation step. + * \brief Signals if a change of the currently active navigation step is requested. Gives the index of the new step. + */ + void SignalActiveNavigationStepChangeRequested(int); + + /** + * \brief Signals a change of the currently active navigation step after the step was changed. */ void SignalActiveNavigationStepChanged(int); /** * \brief Signals that the navigation step with the given id was finished. */ void SignalNavigationStepFinished(int, bool); /** * \brief Signals the creation of an intermediate result. * The result data is given as properties of the data node. */ void SignalIntermediateResult(const itk::SmartPointer); protected slots: void OnNextButtonClicked(); void OnPreviousButtonClicked(); void OnRestartStepButtonClicked(); void OnTabChanged(int index); void OnSettingsButtonClicked(); void OnSettingsWidgetReturned(); void OnSettingsNodeChanged(itk::SmartPointer); void OnStepReady(int); void OnStepNoLongerReady(int); void OnCombinedModalityChanged(itk::SmartPointer); void OnSettingsChanged(const itk::SmartPointer); public: typedef std::vector NavigationStepVector; typedef NavigationStepVector::iterator NavigationStepIterator; explicit QmitkUSNavigationProcessWidget(QWidget* parent = 0); ~QmitkUSNavigationProcessWidget(); /** * \brief Setter for the data storage used for storing the navigation progress. */ void SetDataStorage(itk::SmartPointer dataStorage); void SetSettingsWidget(QmitkUSNavigationAbstractSettingsWidget*); /** * \brief Setter for a vector of navigation step widgets. * These widgets are used for the navigation process in the order of their * appearance in this vector. */ void SetNavigationSteps(NavigationStepVector navigationSteps); /** * \brief Forget the current progress of the navigation process. * The process will then start again at the beginning. */ void ResetNavigationProcess(); /** Enables/disables the (navigation step) interaction with this widget. * The settings button is not affected by this flag. */ void EnableInteraction(bool enable); /** Finishes the current navigation step. */ void FinishCurrentNavigationStep(); /** Updates the navigation process widget, which includes updating the * navigation pipeline. Has to be called from outside this class with * a given update rate. So no additional internal timer is needed. */ void UpdateNavigationProgress(); protected: void InitializeNavigationStepWidgets(); void UpdatePrevNextButtons(); void UpdateFilterPipeline(); void SetSettingsWidgetVisible(bool visible); itk::SmartPointer m_DataStorage; NavigationStepVector m_NavigationSteps; QmitkUSNavigationAbstractSettingsWidget* m_SettingsWidget; itk::SmartPointer m_BaseNode; itk::SmartPointer m_SettingsNode; int m_CurrentTabIndex; int m_CurrentMaxStep; itk::SmartPointer m_ImageStreamNode; bool m_ImageAlreadySetToNode; itk::SmartPointer m_CombinedModality; itk::SmartPointer m_LastNavigationDataFilter; QWidget* m_Parent; QSignalMapper* m_ReadySignalMapper; QSignalMapper* m_NoLongerReadySignalMapper; QmitkStdMultiWidget* m_StdMultiWidget; bool m_UsePlanningStepWidget; private: Ui::QmitkUSNavigationProcessWidget *ui; }; #endif // USNAVIGATIONPROCESSWIDGET_H diff --git a/Plugins/org.mitk.gui.qt.igttracking/src/internal/IGTNavigationToolCalibration.cpp b/Plugins/org.mitk.gui.qt.igttracking/src/internal/IGTNavigationToolCalibration.cpp index a4ebf05c1a..f4fbaa3fc4 100644 --- a/Plugins/org.mitk.gui.qt.igttracking/src/internal/IGTNavigationToolCalibration.cpp +++ b/Plugins/org.mitk.gui.qt.igttracking/src/internal/IGTNavigationToolCalibration.cpp @@ -1,636 +1,687 @@ /*=================================================================== 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 "IGTNavigationToolCalibration.h" // mitk #include #include #include #include #include #include #include // Qt #include #include //vtk #include const std::string IGTNavigationToolCalibration::VIEW_ID = "org.mitk.views.igtnavigationtoolcalibration"; IGTNavigationToolCalibration::IGTNavigationToolCalibration() {} IGTNavigationToolCalibration::~IGTNavigationToolCalibration() { //The following code is required due to a bug in the point list widget. //If this is removed, MITK crashes when closing the view: m_Controls.m_RegistrationLandmarkWidget->SetPointSetNode(NULL); m_Controls.m_CalibrationLandmarkWidget->SetPointSetNode(NULL); } void IGTNavigationToolCalibration::SetFocus() { } void IGTNavigationToolCalibration::OnToolCalibrationMethodChanged(int index) { //if pivot calibration (3) or manual(0) is chosen only calibration pointer is needed if (index == 0 || index == 3) { if (!CheckInitialization(false)) { return; } } else{ if (!CheckInitialization()) { return; } } UpdateManualToolTipCalibrationView(); m_Controls.m_CalibrationMethodsWidget->setCurrentIndex(index); m_IndexCurrentCalibrationMethod = index; } void IGTNavigationToolCalibration::CreateQtPartControl(QWidget *parent) { //initialize manual tool editing widget m_ManualToolTipEditWidget = new QmitkNavigationToolCreationAdvancedWidget(parent); m_ManualToolTipEditWidget->setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint); m_ManualToolTipEditWidget->setWindowTitle("Edit Tool Tip Manually"); m_ManualToolTipEditWidget->setModal(false); m_ManualToolTipEditWidget->SetDataStorage(this->GetDataStorage()); m_TrackingTimer = new QTimer(this); // create GUI widgets from the Qt Designer's .ui file m_Controls.setupUi(parent); connect(m_Controls.m_SetToolToCalibrate, SIGNAL(clicked()), this, SLOT(SetToolToCalibrate())); connect(m_Controls.m_SetPointer, SIGNAL(clicked()), this, SLOT(SetCalibrationPointer())); connect(m_TrackingTimer, SIGNAL(timeout()), this, SLOT(UpdateTrackingTimer())); connect(m_Controls.m_AddLandmark, SIGNAL(clicked()), this, SLOT(AddLandmark())); connect(m_Controls.m_SaveCalibratedTool, SIGNAL(clicked()), this, SLOT(SaveCalibratedTool())); connect(m_Controls.m_AddPivotPose, SIGNAL(clicked()), this, SLOT(OnAddPivotPose())); connect(m_Controls.m_ComputePivot, SIGNAL(clicked()), this, SLOT(OnComputePivot())); connect(m_Controls.m_UseComputedPivotPoint, SIGNAL(clicked()), this, SLOT(OnUseComputedPivotPoint())); connect(m_Controls.m_StartEditTooltipManually, SIGNAL(clicked()), this, SLOT(OnStartManualToolTipCalibration())); + connect(m_Controls.m_GetPositions, SIGNAL(clicked()), this, SLOT(OnGetPositions())); + connect(m_Controls.m_CalibrateToolAxis, SIGNAL(clicked()), this, SLOT(OnCalibrateToolAxis())); connect((QObject*)(m_ManualToolTipEditWidget), SIGNAL(RetrieveDataForManualToolTipManipulation()), this, SLOT(OnRetrieveDataForManualTooltipManipulation())); connect((QObject*)(m_ManualToolTipEditWidget), SIGNAL(DialogCloseRequested()), this, SLOT(OnProcessManualTooltipEditDialogCloseRequest())); connect(m_Controls.m_CalibrationMethodComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(OnToolCalibrationMethodChanged(int))); connect((QObject*)(m_Controls.m_RunCalibrationButton), SIGNAL(clicked()), (QObject*) this, SLOT(OnRunSingleRefToolCalibrationClicked())); connect((QObject*)(m_Controls.m_CollectNavigationDataButton), SIGNAL(clicked()), (QObject*) this, SLOT(OnLoginSingleRefToolNavigationDataClicked())); connect((QObject*)(m_Controls.m_SetNewToolTipPosButton), SIGNAL(clicked()), (QObject*) this, SLOT(OnSetNewToolTipPosButtonClicked())); m_IDToolToCalibrate = -1; m_IDCalibrationPointer = -1; m_IndexCurrentCalibrationMethod = -1; m_OnLoginSingleRefToolNavigationDataClicked = false; m_NumberOfNavigationDataCounter = 0; m_NumberOfNavigationData = -1; //for pivot calibration m_OnAddPivotPoseClicked = false; PivotCount = 0; m_PivotPoses = std::vector(); m_CalibrationLandmarks = mitk::PointSet::New(); m_CalibrationLandmarksNode = mitk::DataNode::New(); m_CalibrationLandmarksNode->SetData(m_CalibrationLandmarks); m_Controls.m_CalibrationLandmarkWidget->SetPointSetNode(m_CalibrationLandmarksNode); m_RegistrationLandmarks = mitk::PointSet::New(); m_RegistrationLandmarksNode = mitk::DataNode::New(); m_RegistrationLandmarksNode->SetData(m_RegistrationLandmarks); m_Controls.m_RegistrationLandmarkWidget->SetPointSetNode(m_RegistrationLandmarksNode); m_ToolSurfaceInToolCoordinatesDataNode = mitk::DataNode::New(); m_ToolSurfaceInToolCoordinatesDataNode->SetName("ToolSurface(ToolCoordinates)"); m_LoggedNavigationDataDifferences = std::vector< mitk::NavigationData::Pointer >(); } void IGTNavigationToolCalibration::OnRunSingleRefToolCalibrationClicked() { if (!CheckInitialization()) { return; } mitk::NavigationData::Pointer ToolTipTransform = mitk::NavigationData::New(); if (m_Controls.m_CalibratePosition->isChecked()) { //1: Compute mean translational offset vector m_ResultOffsetVector.Fill(0); for (std::vector::iterator vecIter = m_LoggedNavigationDataOffsets.begin(); vecIter != m_LoggedNavigationDataOffsets.end(); vecIter++) { m_ResultOffsetVector[0] = m_ResultOffsetVector[0] + (*vecIter)[0]; m_ResultOffsetVector[1] = m_ResultOffsetVector[1] + (*vecIter)[1]; m_ResultOffsetVector[2] = m_ResultOffsetVector[2] + (*vecIter)[2]; } m_ResultOffsetVector[0] = m_ResultOffsetVector[0] / m_LoggedNavigationDataOffsets.size(); m_ResultOffsetVector[1] = m_ResultOffsetVector[1] / m_LoggedNavigationDataOffsets.size(); m_ResultOffsetVector[2] = m_ResultOffsetVector[2] / m_LoggedNavigationDataOffsets.size(); this->m_Controls.m_ResultOfCalibration->setText( QString("x: ") + QString(QString::number(m_ResultOffsetVector[0], 103, 3)) + QString("; y: ") + (QString::number(m_ResultOffsetVector[1], 103, 3)) + QString("; z: ") + (QString::number(m_ResultOffsetVector[2], 103, 3))); ToolTipTransform->SetPosition(m_ResultOffsetVector); } if (m_Controls.m_CalibrateOrientation->isChecked()) { //2: Compute mean orientation mitk::Quaternion meanOrientation; std::vector allOrientations = std::vector (); for (int i = 0; i < m_LoggedNavigationDataDifferences.size(); i++) { allOrientations.push_back(m_LoggedNavigationDataDifferences.at(i)->GetOrientation()); } meanOrientation = mitk::QuaternionAveraging::CalcAverage(allOrientations); this->m_Controls.m_ResultOfCalibrationOrientation->setText( QString("qx: ") + QString(QString::number(meanOrientation.x(), 103, 3)) + QString("; qy: ") + (QString::number(meanOrientation.y(), 103, 3)) + QString("; qz: ") + (QString::number(meanOrientation.z(), 103, 3)) + QString("; qr: ") + (QString::number(meanOrientation.r(), 103, 3))); ToolTipTransform->SetOrientation(meanOrientation); } MITK_INFO << "Computed calibration: "; MITK_INFO << "Translation Vector: " << ToolTipTransform->GetPosition(); MITK_INFO << "Quaternion: (" << ToolTipTransform->GetOrientation() << ")"; MITK_INFO << "Euler Angles [rad]: (" << ToolTipTransform->GetOrientation().rotation_euler_angles() << ")"; MITK_INFO << "Matrix:"; vnl_matrix_fixed rotMatrix = ToolTipTransform->GetOrientation().rotation_matrix_transpose(); MITK_INFO << rotMatrix[0][0] << " " << rotMatrix[0][1] << " " << rotMatrix[0][2] << std::endl; MITK_INFO << rotMatrix[1][0] << " " << rotMatrix[1][1] << " " << rotMatrix[1][2] << std::endl; MITK_INFO << rotMatrix[2][0] << " " << rotMatrix[2][1] << " " << rotMatrix[2][2] << std::endl; //3: write everything into the final tool tip transform and save it as member (it will be written to the tool later on) mitk::NavigationData::Pointer ToolTipInTrackingCoordinates = mitk::NavigationData::New(); ToolTipInTrackingCoordinates->Compose(ToolTipTransform); ToolTipInTrackingCoordinates->Compose(m_NavigationDataSourceOfToolToCalibrate->GetOutput(m_IDToolToCalibrate)); ShowToolTipPreview(ToolTipInTrackingCoordinates); m_Controls.m_SetNewToolTipPosButton->setEnabled(true); m_ComputedToolTipTransformation = ToolTipTransform; } void IGTNavigationToolCalibration::OnLoginSingleRefToolNavigationDataClicked() { if (!CheckInitialization()) { return; } m_OnLoginSingleRefToolNavigationDataClicked = true; m_Controls.m_CollectNavigationDataButton->setEnabled(false); m_NumberOfNavigationData = m_Controls.m_NumberOfNavigationDataToCollect->value(); MITK_INFO << "Collecting " << m_NumberOfNavigationData << " NavigationData ... " << endl; } void IGTNavigationToolCalibration::LoginSingleRefToolNavigationData() { if (!CheckInitialization()) { return; } if (m_NumberOfNavigationDataCounter < m_NumberOfNavigationData) { //update label text QString labelText = "Collecting Data: " + QString::number(m_NumberOfNavigationDataCounter); m_Controls.m_CollectionStatus->setText(labelText); mitk::NavigationData::Pointer referenceTool = m_NavigationDataSourceOfCalibrationPointer->GetOutput(m_IDCalibrationPointer); mitk::NavigationData::Pointer toolToCalibrate = m_NavigationDataSourceOfToolToCalibrate->GetOutput(m_IDToolToCalibrate); //compute difference: // differenceND = toolToCalibrate^-1 * referenceTool mitk::NavigationData::Pointer differenceND = mitk::NavigationData::New(); differenceND->Compose(referenceTool); differenceND->Compose(toolToCalibrate->GetInverse()); //inverse mode... if (m_Controls.m_InvertQuaternions->isChecked()) { // negate identity matrix to directly show parameters that will set up in NDI 6D Software Architect differenceND = differenceND->GetInverse(); } //save difference in member m_LoggedNavigationDataOffsets.push_back(differenceND->GetPosition()); m_LoggedNavigationDataDifferences.push_back(differenceND); m_NumberOfNavigationDataCounter++; } if (m_NumberOfNavigationDataCounter == m_NumberOfNavigationData) { m_NumberOfNavigationDataCounter = 0; m_OnLoginSingleRefToolNavigationDataClicked = false; m_Controls.m_CollectNavigationDataButton->setEnabled(true); m_Controls.m_RunCalibrationButton->setEnabled(true); MITK_INFO << "Collecting " << m_NumberOfNavigationData << " NavigationData ... Finished" << endl; QString labelText = "Collected " + QString::number(m_NumberOfNavigationData) + " data samples!"; m_Controls.m_CollectionStatus->setText(labelText); } } void IGTNavigationToolCalibration::OnSetNewToolTipPosButtonClicked() { ApplyToolTipTransform(m_ComputedToolTipTransformation); RemoveToolTipPreview(); } void IGTNavigationToolCalibration::ClearOldPivot() { mitk::NavigationData::Pointer tempND = mitk::NavigationData::New(); this->ApplyToolTipTransform(tempND); UpdateManualToolTipCalibrationView(); m_ManualToolTipEditWidget->hide(); this->GetDataStorage()->Remove(m_ToolSurfaceInToolCoordinatesDataNode); } void IGTNavigationToolCalibration::OnAddPivotPose() { ClearOldPivot(); //When the collect Poses Button is Clicked m_OnAddPivotPoseClicked = true; m_NumberOfNavigationData = m_Controls.m_PosesToCollect->value(); } void IGTNavigationToolCalibration::AddPivotPose() { //Save the poses to be used in computation if (PivotCount < m_NumberOfNavigationData) { mitk::NavigationData::Pointer currentPose = mitk::NavigationData::New(); currentPose->Graft(m_Controls.m_SelectionWidget->GetSelectedNavigationDataSource()->GetOutput(m_IDToolToCalibrate)); m_PivotPoses.push_back(currentPose); m_Controls.m_PoseNumber->setText(QString::number(m_PivotPoses.size())); PivotCount++; } if (PivotCount == m_NumberOfNavigationData) { m_OnAddPivotPoseClicked = false; } } void IGTNavigationToolCalibration::OnComputePivot() { mitk::PivotCalibration::Pointer myPivotCalibration = mitk::PivotCalibration::New(); for (int i = 0; i < this->m_PivotPoses.size(); i++) { myPivotCalibration->AddNavigationData(m_PivotPoses.at(i)); } QString resultString; if (myPivotCalibration->ComputePivotResult()) { mitk::NavigationData::Pointer markerTransformationTrackingCoordinates = m_PivotPoses.at(0); //Get computed pivot transfromation in tool coordinates mitk::NavigationData::Pointer ToolTipToTool = mitk::NavigationData::New(); ToolTipToTool->SetPosition(myPivotCalibration->GetResultPivotPoint()); ToolTipToTool->SetOrientation(myPivotCalibration->GetResultPivotRotation()); mitk::NavigationData::Pointer TrackerToTool = mitk::NavigationData::New(); TrackerToTool->SetOrientation(markerTransformationTrackingCoordinates->GetOrientation()); TrackerToTool->SetPosition(markerTransformationTrackingCoordinates->GetPosition()); TrackerToTool->Compose(ToolTipToTool); // Compute pivot point in relation to marker transformation for preview mitk::NavigationData::Pointer ToolTipToTracker = mitk::NavigationData::New(); ToolTipToTracker->Compose(ToolTipToTool); ToolTipToTracker->Compose(markerTransformationTrackingCoordinates); //add the preview node to the data storage ShowToolTipPreview(ToolTipToTracker); //parse result string resultString = QString("Pivot computation succeeded!\n") + QString("RMS Error: ") + QString::number(myPivotCalibration->GetResultRMSError()) + QString("\n") + QString("Pivot Point: ") + QString::number(myPivotCalibration->GetResultPivotPoint()[0]) + ";" + QString::number(myPivotCalibration->GetResultPivotPoint()[1]) + ";" + QString::number(myPivotCalibration->GetResultPivotPoint()[2]) + QString("\n") + QString("Pivot Rotation: ") + QString::number(myPivotCalibration->GetResultPivotRotation()[0]) + ";" + QString::number(myPivotCalibration->GetResultPivotRotation()[1]) + ";" + QString::number(myPivotCalibration->GetResultPivotRotation()[2]) + ";" + QString::number(myPivotCalibration->GetResultPivotRotation()[3]) + QString("\n"); //finally: save results to member variable m_ComputedToolTipTransformation = ToolTipToTool; //enable button to use the computed point with the tool m_Controls.m_UseComputedPivotPoint->setEnabled(true); } else { resultString = "Pivot computation failed!"; } MITK_INFO << resultString.toStdString().c_str(); m_Controls.m_ResultText->setText(resultString); } void IGTNavigationToolCalibration::UpdatePivotCount() { PivotCount = 0; while (!m_PivotPoses.empty()) { m_PivotPoses.pop_back(); } m_Controls.m_PoseNumber->setText(QString::number(PivotCount)); } void IGTNavigationToolCalibration::OnUseComputedPivotPoint() { RemoveToolTipPreview(); QString resultString = QString("Pivoted tool tip transformation was written to the tool ") + m_ToolToCalibrate->GetToolName().c_str(); ApplyToolTipTransform(m_ComputedToolTipTransformation, resultString.toStdString()); m_Controls.m_ResultText->setText(resultString); UpdatePivotCount(); } void IGTNavigationToolCalibration::ApplyToolTipTransform(mitk::NavigationData::Pointer ToolTipTransformInToolCoordinates, std::string message) { if (!CheckInitialization(false)) { return; } //Update tool in tool storage m_ToolToCalibrate->SetToolTipPosition(ToolTipTransformInToolCoordinates->GetPosition()); m_ToolToCalibrate->SetToolTipOrientation(ToolTipTransformInToolCoordinates->GetOrientation()); //And also update tracking device, so the transform is directly used mitk::TrackingDeviceSource::Pointer trackingDeviceSource; try { trackingDeviceSource = dynamic_cast(m_NavigationDataSourceOfToolToCalibrate.GetPointer()); mitk::TrackingTool::Pointer TrackingToolToCalibrate = trackingDeviceSource->GetTrackingDevice()->GetTool(m_IDToolToCalibrate); TrackingToolToCalibrate->SetToolTip(ToolTipTransformInToolCoordinates->GetPosition(), ToolTipTransformInToolCoordinates->GetOrientation()); } catch (std::exception& e) { MITK_ERROR << "Error while trying to set the tool tip to the running tracking device. Aborting! (" << e.what() << ")"; } MITK_INFO << message; } void IGTNavigationToolCalibration::ShowToolTipPreview(mitk::NavigationData::Pointer ToolTipInTrackingCoordinates) { mitk::DataNode::Pointer m_ToolTipPointPreview = mitk::DataNode::New(); m_ToolTipPointPreview->SetName("Modified Tool Tip Preview"); mitk::Color blue; blue.SetBlue(1); m_ToolTipPointPreview->SetColor(blue); mitk::Surface::Pointer mySphere = mitk::Surface::New(); vtkSphereSource *vtkData = vtkSphereSource::New(); vtkData->SetRadius(3.0f); vtkData->SetCenter(0.0, 0.0, 0.0); vtkData->Update(); mySphere->SetVtkPolyData(vtkData->GetOutput()); vtkData->Delete(); m_ToolTipPointPreview->SetData(mySphere); m_ToolTipPointPreview->GetData()->GetGeometry()->SetIndexToWorldTransform(ToolTipInTrackingCoordinates->GetAffineTransform3D()); this->GetDataStorage()->Add(m_ToolTipPointPreview); } void IGTNavigationToolCalibration::RemoveToolTipPreview() { this->GetDataStorage()->Remove(m_ToolTipPointPreview.GetPointer()); } void IGTNavigationToolCalibration::UpdateManualToolTipCalibrationView() { if (m_ToolToCalibrate.IsNull()) { return; } //parse human readable transformation data and display it std::stringstream translation; std::stringstream orientation; translation << m_ToolToCalibrate->GetToolTipPosition(); orientation << "Quaternion: (" << m_ToolToCalibrate->GetToolTipOrientation() << ")" << std::endl; orientation << std::endl; orientation << "Euler Angles [rad]: (" << m_ToolToCalibrate->GetToolTipOrientation().rotation_euler_angles() << ")" << std::endl; orientation << std::endl; orientation << "Matrix:" << std::endl; vnl_matrix_fixed rotMatrix = m_ToolToCalibrate->GetToolTipOrientation().rotation_matrix_transpose(); orientation << rotMatrix[0][0] << " " << rotMatrix[0][1] << " " << rotMatrix[0][2] << std::endl; orientation << rotMatrix[1][0] << " " << rotMatrix[1][1] << " " << rotMatrix[1][2] << std::endl; orientation << rotMatrix[2][0] << " " << rotMatrix[2][1] << " " << rotMatrix[2][2] << std::endl; m_Controls.m_ManualCurrentTranslation->setText(translation.str().c_str()); m_Controls.m_ManualCurrentOrientation->setPlainText(orientation.str().c_str()); } void IGTNavigationToolCalibration::OnStartManualToolTipCalibration() { if (!CheckInitialization(false)) { return; } m_ManualToolTipEditWidget->SetToolTipSurface(false, m_ToolToCalibrate->GetDataNode()); m_ManualToolTipEditWidget->show(); m_ManualToolTipEditWidget->SetDefaultTooltip(m_ToolToCalibrate->GetToolTipTransform()); m_ManualToolTipEditWidget->ReInitialize(); } void IGTNavigationToolCalibration::OnRetrieveDataForManualTooltipManipulation() { this->GetDataStorage()->Add(m_ToolSurfaceInToolCoordinatesDataNode); m_ManualToolTipEditWidget->SetToolTipSurface(false, m_ToolSurfaceInToolCoordinatesDataNode); } void IGTNavigationToolCalibration::OnProcessManualTooltipEditDialogCloseRequest() { mitk::NavigationData::Pointer tempND = mitk::NavigationData::New(m_ManualToolTipEditWidget->GetManipulatedToolTip()); this->ApplyToolTipTransform(tempND); UpdateManualToolTipCalibrationView(); m_ManualToolTipEditWidget->hide(); this->GetDataStorage()->Remove(m_ToolSurfaceInToolCoordinatesDataNode); } +void IGTNavigationToolCalibration::OnGetPositions() +{ + //tool tip from tool to be calibrated + mitk::NavigationData::Pointer StartTool = mitk::NavigationData::New(); + StartTool->Graft(m_Controls.m_SelectionWidget->GetSelectedNavigationDataSource()->GetOutput(m_IDToolToCalibrate)); + double startX = StartTool->GetPosition().GetVnlVector()[0]; + double startY = StartTool->GetPosition().GetVnlVector()[1]; + double startZ = StartTool->GetPosition().GetVnlVector()[2]; + m_StartToolPosition.SetElement(0, startX); + m_StartToolPosition.SetElement(1, startY); + m_StartToolPosition.SetElement(2, startZ); + + //tool end from calibration tool + mitk::NavigationData::Pointer EndTool = mitk::NavigationData::New(); + EndTool->Graft(m_Controls.m_SelectionWidget->GetSelectedNavigationDataSource()->GetOutput(m_IDCalibrationPointer)); + double endX = EndTool->GetPosition().GetVnlVector()[0]; + double endY = EndTool->GetPosition().GetVnlVector()[1]; + double endZ = EndTool->GetPosition().GetVnlVector()[2]; + m_EndToolPosition.SetElement(0, endX); + m_EndToolPosition.SetElement(1, endY); + m_EndToolPosition.SetElement(2, endZ); + + mitk::NavigationData::Pointer trackingToToolTransform = m_Controls.m_SelectionWidget->GetSelectedNavigationDataSource()->GetOutput(m_IDToolToCalibrate)->GetInverse(); + mitk::Vector3D trackingToToolPos; + trackingToToolPos.SetElement(0, trackingToToolTransform->GetPosition()[0]); + trackingToToolPos.SetElement(1, trackingToToolTransform->GetPosition()[1]); + trackingToToolPos.SetElement(2, trackingToToolTransform->GetPosition()[2]); + + mitk::Vector3D startPosToolCoordinates = trackingToToolTransform->GetRotationMatrix() * (m_StartToolPosition)+trackingToToolPos; + mitk::Vector3D endPosToolCoordinates = trackingToToolTransform->GetRotationMatrix() * (m_EndToolPosition)+trackingToToolPos; + m_CalibratedToolAxis.SetElement(0, (-1 * endPosToolCoordinates[0] + startPosToolCoordinates[0])); + m_CalibratedToolAxis.SetElement(1, (-1 * endPosToolCoordinates[1] + startPosToolCoordinates[1])); + m_CalibratedToolAxis.SetElement(2, (-1 * endPosToolCoordinates[2] + startPosToolCoordinates[2])); + m_CalibratedToolAxis.Normalize(); +} + +void IGTNavigationToolCalibration::OnCalibrateToolAxis() +{ + QString xString = QString::number(m_CalibratedToolAxis.GetElement(0),'g',3); + QString yString = QString::number(m_CalibratedToolAxis.GetElement(1), 'g', 3); + QString zString = QString::number(m_CalibratedToolAxis.GetElement(2),'g',3); + + QString calibratedToolAxisString = xString + ", " + yString + ", " + zString; + m_Controls.m_ToolAxis->setText(calibratedToolAxisString); + MITK_INFO << m_CalibratedToolAxis; + + m_ToolToCalibrate->SetToolAxis(m_CalibratedToolAxis); +} + void IGTNavigationToolCalibration::SetToolToCalibrate() { m_IDToolToCalibrate = m_Controls.m_SelectionWidget->GetSelectedToolID(); m_ToolToCalibrate = m_Controls.m_SelectionWidget->GetSelectedNavigationTool(); if (m_IDToolToCalibrate == -1) //no valid tool to calibrate { m_Controls.m_CalToolLabel->setText(""); m_Controls.m_StatusWidgetToolToCalibrate->RemoveStatusLabels(); m_TrackingTimer->stop(); } else { m_NavigationDataSourceOfToolToCalibrate = m_Controls.m_SelectionWidget->GetSelectedNavigationDataSource(); m_Controls.m_CalToolLabel->setText(m_NavigationDataSourceOfToolToCalibrate->GetOutput(m_IDToolToCalibrate)->GetName()); //initialize widget m_Controls.m_StatusWidgetToolToCalibrate->RemoveStatusLabels(); m_Controls.m_StatusWidgetToolToCalibrate->SetShowPositions(true); m_Controls.m_StatusWidgetToolToCalibrate->SetTextAlignment(Qt::AlignLeft); m_Controls.m_StatusWidgetToolToCalibrate->AddNavigationData(m_NavigationDataSourceOfToolToCalibrate->GetOutput(m_IDToolToCalibrate)); m_Controls.m_StatusWidgetToolToCalibrate->ShowStatusLabels(); //initialize manual tool tip calibration view UpdateManualToolTipCalibrationView(); //save tool surface in tool coordinates for further editing mitk::Surface::Pointer ToolSurface = dynamic_cast(m_ToolToCalibrate->GetDataNode()->GetData())->Clone(); m_ToolSurfaceInToolCoordinatesDataNode->SetData(ToolSurface); m_ToolSurfaceInToolCoordinatesDataNode->GetData()->GetGeometry()->SetIdentity(); //start updating timer for status widgets, etc. if (!m_TrackingTimer->isActive()) m_TrackingTimer->start(100); } } void IGTNavigationToolCalibration::SetCalibrationPointer() { m_IDCalibrationPointer = m_Controls.m_SelectionWidget->GetSelectedToolID(); m_NavigationDataSourceOfCalibrationPointer = m_Controls.m_SelectionWidget->GetSelectedNavigationDataSource(); if (m_IDCalibrationPointer == -1) { m_Controls.m_PointerLabel->setText(""); m_Controls.m_StatusWidgetCalibrationPointer->RemoveStatusLabels(); m_TrackingTimer->stop(); } else { m_Controls.m_PointerLabel->setText(m_NavigationDataSourceOfCalibrationPointer->GetOutput(m_IDCalibrationPointer)->GetName()); //initialize widget m_Controls.m_StatusWidgetCalibrationPointer->RemoveStatusLabels(); m_Controls.m_StatusWidgetCalibrationPointer->SetShowPositions(true); m_Controls.m_StatusWidgetCalibrationPointer->SetTextAlignment(Qt::AlignLeft); m_Controls.m_StatusWidgetCalibrationPointer->AddNavigationData(m_NavigationDataSourceOfCalibrationPointer->GetOutput(m_IDCalibrationPointer)); m_Controls.m_StatusWidgetCalibrationPointer->ShowStatusLabels(); if (!m_TrackingTimer->isActive()) m_TrackingTimer->start(100); } } void IGTNavigationToolCalibration::UpdateOffsetCoordinates() { if (m_NavigationDataSourceOfCalibrationPointer.IsNull() || m_NavigationDataSourceOfToolToCalibrate.IsNull()) { return; } mitk::NavigationData::Pointer referenceToolND = m_NavigationDataSourceOfCalibrationPointer->GetOutput(m_IDCalibrationPointer); mitk::NavigationData::Pointer toolToCalibrateND = m_NavigationDataSourceOfToolToCalibrate->GetOutput(m_IDToolToCalibrate); if (referenceToolND->IsDataValid() && toolToCalibrateND->IsDataValid()) { //computation: difference between both tools (in tool coordinates) //differenceND = toolToCalibrateND^-1 * referenceToolND mitk::NavigationData::Pointer differenceND = mitk::NavigationData::New(); differenceND->Compose(referenceToolND); differenceND->Compose(toolToCalibrateND->GetInverse()); //display this orientation in the UI m_Controls.m_OffsetCoordinates->setText( QString("x: ") + QString(QString::number(differenceND->GetPosition()[0], 103, 3)) + QString("; y: ") + (QString::number(differenceND->GetPosition()[1], 103, 3)) + QString("; z: ") + (QString::number(differenceND->GetPosition()[2], 103, 3))); m_Controls.m_OrientationOffsetCoordinates->setText( QString("qx: ") + QString(QString::number(differenceND->GetOrientation().x(), 103, 3)) + QString("; qy: ") + (QString::number(differenceND->GetOrientation().y(), 103, 3)) + QString("; qz: ") + (QString::number(differenceND->GetOrientation().z(), 103, 3)) + QString("; qr: ") + (QString::number(differenceND->GetOrientation().r(), 103, 3))); //also update preview if active if (m_ToolTipPointPreview.IsNotNull()) //NOT WORKING! TODO: fix or remove! { mitk::NavigationData::Pointer ToolTipTransform = mitk::NavigationData::New(); ToolTipTransform->SetPosition(m_ResultOffsetVector); mitk::NavigationData::Pointer ToolTipInTrackingCoordinates = mitk::NavigationData::New(); //maybe store as for better peformance... ToolTipInTrackingCoordinates->Compose(m_NavigationDataSourceOfToolToCalibrate->GetOutput(m_IDToolToCalibrate)); ToolTipInTrackingCoordinates->Compose(ToolTipTransform); m_ToolTipPointPreview->GetData()->GetGeometry()->SetIndexToWorldTransform(ToolTipInTrackingCoordinates->GetAffineTransform3D()); } } } void IGTNavigationToolCalibration::UpdateTrackingTimer() { m_Controls.m_StatusWidgetToolToCalibrate->Refresh(); m_Controls.m_StatusWidgetCalibrationPointer->Refresh(); if (m_OnLoginSingleRefToolNavigationDataClicked) LoginSingleRefToolNavigationData(); if (m_OnAddPivotPoseClicked) AddPivotPose(); // 1 == Single Reference Calibration Method if (m_IndexCurrentCalibrationMethod == 1) UpdateOffsetCoordinates(); } void IGTNavigationToolCalibration::AddLandmark() { if (!CheckInitialization()) { return; } mitk::NavigationData::Pointer navDataTool = m_NavigationDataSourceOfToolToCalibrate->GetOutput(m_IDToolToCalibrate); mitk::Point3D landmark = m_NavigationDataSourceOfCalibrationPointer->GetOutput(m_IDCalibrationPointer)->GetPosition(); //convert to itk transform itk::Vector translation; for (int k = 0; k < 3; k++) translation[k] = navDataTool->GetPosition()[k]; itk::Matrix rotation; for (int k = 0; k < 3; k++) for (int l = 0; l < 3; l++) rotation[k][l] = navDataTool->GetOrientation().rotation_matrix_transpose()[k][l]; rotation = rotation.GetTranspose(); itk::Vector landmarkItk; landmarkItk[0] = landmark[0]; landmarkItk[1] = landmark[1]; landmarkItk[2] = landmark[2]; //compute landmark in tool coordinates itk::Matrix rotationInverse; for (int k = 0; k < 3; k++) for (int l = 0; l < 3; l++) rotationInverse[k][l] = rotation.GetInverse()[k][l]; landmarkItk = rotationInverse * (landmarkItk - translation); //convert back and add landmark to pointset landmark[0] = landmarkItk[0]; landmark[1] = landmarkItk[1]; landmark[2] = landmarkItk[2]; m_RegistrationLandmarks->InsertPoint(m_RegistrationLandmarks->GetSize(), landmark); } void IGTNavigationToolCalibration::SaveCalibratedTool() { if (m_ToolToCalibrate.IsNotNull()) { mitk::NavigationTool::Pointer calibratedTool = m_ToolToCalibrate; calibratedTool->SetToolCalibrationLandmarks(this->m_CalibrationLandmarks); calibratedTool->SetToolRegistrationLandmarks(this->m_RegistrationLandmarks); mitk::NavigationToolWriter::Pointer myWriter = mitk::NavigationToolWriter::New(); std::string filename = QFileDialog::getSaveFileName(NULL, tr("Save Navigation Tool"), "/", "*.IGTTool").toUtf8().data(); filename.append(".IGTTool"); if (filename == "") return; if (myWriter->DoWrite(filename, calibratedTool)) MITK_INFO << "Saved calibrated tool to file " << filename; else MITK_WARN << "Can't write tool to file " << filename; } else { MITK_ERROR << "Did not find navigation tool storage of calibrated tool, aborting!"; } } bool IGTNavigationToolCalibration::CheckInitialization(bool CalibrationPointerRequired) { if ((m_IDToolToCalibrate == -1) || ((CalibrationPointerRequired) && (m_IDCalibrationPointer == -1) ) ) { QMessageBox msgBox; msgBox.setText("Tool to calibrate and/or calibration pointer not initialized, cannot proceed!"); msgBox.exec(); return false; } else { return true; } } diff --git a/Plugins/org.mitk.gui.qt.igttracking/src/internal/IGTNavigationToolCalibration.h b/Plugins/org.mitk.gui.qt.igttracking/src/internal/IGTNavigationToolCalibration.h index ae582db713..90ddce2f52 100644 --- a/Plugins/org.mitk.gui.qt.igttracking/src/internal/IGTNavigationToolCalibration.h +++ b/Plugins/org.mitk.gui.qt.igttracking/src/internal/IGTNavigationToolCalibration.h @@ -1,135 +1,141 @@ /*=================================================================== 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. ===================================================================*/ #ifndef IGTNavigationToolCalibration_h #define IGTNavigationToolCalibration_h #include #include #include #include #include "ui_IGTNavigationToolCalibrationControls.h" #include //QT headers #include /*! \brief IGTNavigationToolCalibration \warning This class is not yet documented. Use "git blame" and ask the author to provide basic documentation. \ingroup ${plugin_target}_internal */ class IGTNavigationToolCalibration : public QmitkAbstractView { // this is needed for all Qt objects that should have a Qt meta-object // (everything that derives from QObject and wants to have signal/slots) Q_OBJECT public: IGTNavigationToolCalibration(); virtual ~IGTNavigationToolCalibration(); static const std::string VIEW_ID; virtual void CreateQtPartControl(QWidget *parent); protected slots: void OnAddPivotPose(); void OnComputePivot(); void OnUseComputedPivotPoint(); void SetToolToCalibrate(); void SetCalibrationPointer(); void UpdateTrackingTimer(); void AddLandmark(); void SaveCalibratedTool(); void OnToolCalibrationMethodChanged(int index); void OnStartManualToolTipCalibration(); void OnRetrieveDataForManualTooltipManipulation(); void OnProcessManualTooltipEditDialogCloseRequest(); void OnRunSingleRefToolCalibrationClicked(); void OnLoginSingleRefToolNavigationDataClicked(); void OnSetNewToolTipPosButtonClicked(); - + void OnGetPositions(); + void OnCalibrateToolAxis(); protected: virtual void SetFocus(); void UpdateOffsetCoordinates(); int m_IndexCurrentCalibrationMethod; Ui::IGTNavigationToolCalibrationControls m_Controls; //some general members mitk::NavigationTool::Pointer m_ToolToCalibrate; //<<< tool that will be calibrated int m_IDToolToCalibrate; //<<< id of tool that will be calibrated (of the navigation data source) mitk::NavigationDataSource::Pointer m_NavigationDataSourceOfToolToCalibrate; //<<< navigation data source of the tool that will be calibrated mitk::NavigationDataSource::Pointer m_NavigationDataSourceOfCalibrationPointer; //<<< navigation data source of the calibration pointer mitk::DataNode::Pointer m_ToolSurfaceInToolCoordinatesDataNode; //<<< holds the tool surface in tool coordinates (for preview purposes) int m_IDCalibrationPointer; //<<< id of the calibration pointer (of the corresponding navigation data source) QTimer* m_TrackingTimer; //<<< tracking timer that updates the status widgets void ApplyToolTipTransform(mitk::NavigationData::Pointer ToolTipTransformInToolCoordinates, std::string message = "Tool was updated with the calibrated tool tip!"); //<<< applys the given tool tip transform to the tool to calibrate bool CheckInitialization(bool CalibrationPointerRequired = true); //<<< checks if the tool to calibrate and (if required) the calibration pointer is initialized. Displays a warning and returns false if not. mitk::NavigationData::Pointer m_ComputedToolTipTransformation; //<<< holds the new tooltip transformation after it was computed to write it into the tool later // members and helper methods for pivot tool calibration std::vector m_PivotPoses; void AddPivotPose(); void ClearOldPivot(); void UpdatePivotCount(); bool m_OnAddPivotPoseClicked; int PivotCount; // members and helper methods for manual tool calibration void UpdateManualToolTipCalibrationView(); QmitkNavigationToolCreationAdvancedWidget* m_ManualToolTipEditWidget; // members and helper methods for single reference tool calibration void LoginSingleRefToolNavigationData(); std::vector< mitk::Point3D > m_LoggedNavigationDataOffsets; std::vector< mitk::NavigationData::Pointer > m_LoggedNavigationDataDifferences; bool m_OnLoginSingleRefToolNavigationDataClicked; int m_NumberOfNavigationData; int m_NumberOfNavigationDataCounter; mitk::Point3D m_ResultOffsetVector; // members and helper methods for tool tip preview mitk::DataNode::Pointer m_ToolTipPointPreview; //<<< Data node of the tool tip preview void ShowToolTipPreview(mitk::NavigationData::Pointer ToolTipInTrackingCoordinates); //<<< Adds a preview of the tool tip into the data storage void RemoveToolTipPreview(); //<<< Removes the preview // members for the tool landmark calibration mitk::PointSet::Pointer m_CalibrationLandmarks; mitk::DataNode::Pointer m_CalibrationLandmarksNode; mitk::PointSet::Pointer m_RegistrationLandmarks; mitk::DataNode::Pointer m_RegistrationLandmarksNode; + //members and helper methods for tool axis calibration + mitk::Vector3D m_CalibratedToolAxis; + mitk::Vector3D m_EndToolPosition; + mitk::Vector3D m_StartToolPosition; + }; #endif // IGTNavigationToolCalibration_h diff --git a/Plugins/org.mitk.gui.qt.igttracking/src/internal/IGTNavigationToolCalibrationControls.ui b/Plugins/org.mitk.gui.qt.igttracking/src/internal/IGTNavigationToolCalibrationControls.ui index d4688a157a..8b9d44b573 100644 --- a/Plugins/org.mitk.gui.qt.igttracking/src/internal/IGTNavigationToolCalibrationControls.ui +++ b/Plugins/org.mitk.gui.qt.igttracking/src/internal/IGTNavigationToolCalibrationControls.ui @@ -1,1004 +1,1077 @@ IGTNavigationToolCalibrationControls 0 0 - 430 + 551 958 0 0 QmitkTemplate <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt; font-weight:600;">Navigation Tool Calibration</span></p></body></html> Tool to calibrate 0 50 16777215 50 Calibration pointer 0 30 true 0 Initialization <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:7.8pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; text-decoration: underline;">Choose Tracking Device and Tools</span></p></body></html> Qt::Horizontal 120 0 Tool to Calibrate: Qt::Horizontal 40 20 <none> Qt::Horizontal 40 20 150 0 Use as Tool to Calibrate Qt::Horizontal 120 0 Calibration Pointer: Qt::Horizontal 40 20 <none> Qt::Horizontal 40 20 150 0 Use as Calibration Pointer Qt::Horizontal Qt::Vertical 20 586 Tool Tip Calibration 50 false Calibration Method: Qt::Horizontal 40 20 Manual Single Reference Tool Multiple Tools Reference Pivoting Qt::Horizontal 0 Current Tool Tip Translation: true true Current Tool Tip Orientation: true true Qt::Horizontal 40 20 Start Edit Tooltip 0 0 319 160 319 160 QFrame { border-image: url(:/IGTNavigationToolCalibration/Description.svg); } QFrame::Box QFrame::Plain 1 0 0 Current Offset between Tool to calibrate and Calibration pointer: Pos: 8 Qt::AlignCenter Rot: 8 Qt::AlignCenter Qt::Horizontal 0 0 Number of tracking data to collect: 0 0 10000 100 Qt::AlignCenter 0 0 1: Collect Navigation Data Qt::Horizontal Invert calibration transformation Calibrate position true Calibrate orientation true false 2: Run Calibration Qt::Horizontal 0 0 New Tool Tip Position and/or Orientation of Tool to calibrate: Pos: 8 Qt::AlignCenter Rot: 8 Qt::AlignCenter false 3: Set New Tool Tip Position and/or Tool Orientation Qt::Vertical 20 40 Number of pivot poses to collect: 10 1000 100 Number of saved poses: 0 Qt::Horizontal 40 20 Collect Pivot Poses Calibration Result: Qt::Horizontal 40 20 175 0 Compute Pivot Point Qt::Horizontal 40 20 false 0 0 175 0 Use Computed Pivot Point Qt::Vertical 20 99 Tool Landmark Calibration <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:7.8pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; text-decoration: underline;">Calibrate tool by marking the landmarks with the pointer</span></p></body></html> Registration Landmarks 0 170 Add Pointer Position Calibration Landmarks Qt::Vertical 20 259 + + + Tool Axis Calibration + + + + + 30 + 30 + 341 + 16 + + + + Calibrate tool axis with calibrated needle holder and already calibrated tip + + + + + + 210 + 60 + 75 + 23 + + + + Get Positions + + + + + + 30 + 60 + 171 + 21 + + + + Select when sensor is at tools end + + + + + + 30 + 100 + 101 + 23 + + + + Calibrate Tool Axis + + + + + true + + + + 160 + 100 + 241 + 21 + + + + true + + + Qt::Horizontal 40 20 Save Calibrated Navigation Tool tabWidget label groupBox groupBox_2 QmitkNavigationDataSourceSelectionWidget QWidget
QmitkNavigationDataSourceSelectionWidget.h
1
QmitkToolTrackingStatusWidget QWidget
QmitkToolTrackingStatusWidget.h
1
QmitkPointListWidget QWidget
QmitkPointListWidget.h
1