diff --git a/Modules/IGT/DataManagement/mitkNavigationData.cpp b/Modules/IGT/DataManagement/mitkNavigationData.cpp index aa30fd03d9..c1199b8d9f 100644 --- a/Modules/IGT/DataManagement/mitkNavigationData.cpp +++ b/Modules/IGT/DataManagement/mitkNavigationData.cpp @@ -1,321 +1,321 @@ /*=================================================================== 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: This constructor is not tested! 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 { 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 = NULL; 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; + 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 = NavigationData::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; } diff --git a/Modules/IGT/DataManagement/mitkNavigationData.h b/Modules/IGT/DataManagement/mitkNavigationData.h index 150ea18fce..6e5c0532e9 100644 --- a/Modules/IGT/DataManagement/mitkNavigationData.h +++ b/Modules/IGT/DataManagement/mitkNavigationData.h @@ -1,275 +1,277 @@ /*=================================================================== 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 MITKNAVIGATIONDATA_H_HEADER_INCLUDED_ #define MITKNAVIGATIONDATA_H_HEADER_INCLUDED_ #include #include #include #include namespace mitk { /**Documentation * \brief Navigation Data * * This class represents the data object that is passed through the MITK-IGT navigation filter * pipeline. It encapsulates position and orientation of a tracked tool/sensor. Additionally, * it contains a data structure that contains error/plausibility information * * It provides methods to work with the affine transformation represented by its orientation and position. * Additionally, it provides a constructor to construct a NavigationData object from an AffineTransform3D and * a getter to create an AffineTransform3D from a NavigationData object. * * \ingroup IGT */ class MitkIGT_EXPORT NavigationData : public itk::DataObject { public: mitkClassMacro(NavigationData, itk::DataObject); itkFactorylessNewMacro(Self) itkCloneMacro(Self) mitkNewMacro2Param(Self, mitk::AffineTransform3D::Pointer, const bool); mitkNewMacro1Param(Self, mitk::AffineTransform3D::Pointer); mitkCloneMacro(NavigationData); /** * \brief Type that holds the position part of the tracking data */ typedef mitk::Point3D PositionType; /** * \brief Type that holds the orientation part of the tracking data */ typedef mitk::Quaternion OrientationType; /** * \brief type that holds the error characterization of the position and orientation measurements */ typedef itk::Matrix CovarianceMatrixType; /** * \brief type that holds the time at which the data was recorded */ typedef double TimeStampType; /** * \brief sets the position of the NavigationData object */ itkSetMacro(Position, PositionType); /** * \brief returns position of the NavigationData object */ itkGetConstMacro(Position, PositionType); /** * \brief sets the orientation of the NavigationData object */ itkSetMacro(Orientation, OrientationType); /** * \brief returns the orientation of the NavigationData object */ itkGetConstMacro(Orientation, OrientationType); /** * \brief returns true if the object contains valid data */ virtual bool IsDataValid() const; /** * \brief sets the dataValid flag of the NavigationData object indicating if the object contains valid data */ itkSetMacro(DataValid, bool); /** * \brief sets the IGT timestamp of the NavigationData object */ itkSetMacro(IGTTimeStamp, TimeStampType); /** * \brief gets the IGT timestamp of the NavigationData object */ itkGetConstMacro(IGTTimeStamp, TimeStampType); /** * \brief sets the HasPosition flag of the NavigationData object */ itkSetMacro(HasPosition, bool); /** * \brief gets the HasPosition flag of the NavigationData object */ itkGetConstMacro(HasPosition, bool); /** * \brief sets the HasOrientation flag of the NavigationData object */ itkSetMacro(HasOrientation, bool); /** * \brief gets the HasOrientation flag of the NavigationData object */ itkGetConstMacro(HasOrientation, bool); /** * \brief sets the 6x6 Error Covariance Matrix of the NavigationData object */ itkSetMacro(CovErrorMatrix, CovarianceMatrixType); /** * \brief gets the 6x6 Error Covariance Matrix of the NavigationData object */ itkGetConstMacro(CovErrorMatrix, CovarianceMatrixType); /** * \brief set the name of the NavigationData object */ itkSetStringMacro(Name); /** * \brief returns the name of the NavigationData object */ itkGetStringMacro(Name); /** * \brief Graft the data and information from one NavigationData to another. * * Copies the content of data into this object. * This is a convenience method to setup a second NavigationData object with all the meta * information of another NavigationData object. * Note that this method is different than just using two * SmartPointers to the same NavigationData object since separate DataObjects are * still maintained. */ virtual void Graft(const DataObject *data); /** * \brief copy meta data of a NavigationData object * * copies all meta data from NavigationData data to this object */ virtual void CopyInformation(const DataObject* data); /** * \brief Prints the object information to the given stream os. * \param os The stream which is used to print the output. * \param indent Defines the indentation of the output. */ void PrintSelf(std::ostream& os, itk::Indent indent) const; /** * Set the position part of m_CovErrorMatrix to I*error^2 * This means that all position variables are assumed to be independent */ void SetPositionAccuracy(mitk::ScalarType error); /** * Set the orientation part of m_CovErrorMatrix to I*error^2 * This means that all orientation variables are assumed to be independent */ void SetOrientationAccuracy(mitk::ScalarType error); /** * \brief Calculate AffineTransform3D from the transformation held by this NavigationData. * TODO: should throw an error if transformation is invalid. */ mitk::AffineTransform3D::Pointer GetAffineTransform3D() const; /** * \brief Calculate the RotationMatrix of this transformation. */ mitk::Matrix3D GetRotationMatrix() const; /** * \brief Transform by an affine transformation * * This method applies the affine transform given by self to a * given point, returning the transformed point. */ mitk::Point3D TransformPoint(const mitk::Point3D point) const; /** * Get inverse of the Transformation represented by this NavigationData. * @throws mitk::Exception in case the transformation is invalid (only case: quaternion is zero) */ mitk::NavigationData::Pointer GetInverse() const; /** Compose with another NavigationData * * This method composes self with another NavigationData of the * same dimension, modifying self to be the composition of self * and other. If the argument pre is true, then other is * precomposed with self; that is, the resulting transformation * consists of first applying other to the source, followed by * self. If pre is false or omitted, then other is post-composed * with self; that is the resulting transformation consists of * first applying self to the source, followed by other. */ void Compose(const mitk::NavigationData::Pointer n, const bool pre = false); protected: NavigationData(); /* * Copy constructor internally used. */ NavigationData(const mitk::NavigationData& toCopy); /** * Creates a NavigationData object from an affineTransform3D. + * Caution: NavigationData doesn't support spacing, only translation and rotation. If the affine + * transform includes spacing it cannot be converted to a NavigationData and an exception is thrown. * @param checkForRotationMatrix if this is true, the rotation matrix coming from the affineTransform is checked * for being a rotation matrix. If it isn't, an exception is thrown. Disable this check by * setting checkForRotationMatrix to false. * * @throws mitkException if checkForRotationMatrix is true and a non rotation matrix was introduced by * AffineTransform. */ NavigationData(mitk::AffineTransform3D::Pointer affineTransform3D, const bool checkForRotationMatrix = true); virtual ~NavigationData(); /** * \brief holds the position part of the tracking data */ PositionType m_Position; /** * \brief holds the orientation part of the tracking data */ OrientationType m_Orientation; /** * \brief A 6x6 covariance matrix parameterizing the Gaussian error * distribution of the measured position and orientation. * * The hasPosition/hasOrientation fields define which entries * are valid. */ CovarianceMatrixType m_CovErrorMatrix; ///< holds the error characterization of the position and orientation /** * \brief defines if position part of m_CovErrorMatrix is valid */ bool m_HasPosition; /** * \brief defines if orientation part of m_CovErrorMatrix is valid */ bool m_HasOrientation; /** * \brief defines if the object contains valid values */ bool m_DataValid; /** * \brief contains the time at which the tracking data was recorded */ TimeStampType m_IGTTimeStamp; /** * \brief name of the navigation data */ std::string m_Name; private: void ResetCovarianceValidity(); // pre = false static mitk::NavigationData::Pointer getComposition(const mitk::NavigationData::Pointer nd1, const mitk::NavigationData::Pointer nd2); }; } // namespace mitk #endif /* MITKNAVIGATIONDATA_H_HEADER_INCLUDED_ */ diff --git a/Plugins/org.mitk.gui.qt.igtexamples/documentation/UserManual/QmitkIGTTtrackingLab.dox b/Plugins/org.mitk.gui.qt.igtexamples/documentation/UserManual/QmitkIGTTtrackingLab.dox index a4c58bcfdc..296697e165 100644 --- a/Plugins/org.mitk.gui.qt.igtexamples/documentation/UserManual/QmitkIGTTtrackingLab.dox +++ b/Plugins/org.mitk.gui.qt.igtexamples/documentation/UserManual/QmitkIGTTtrackingLab.dox @@ -1,56 +1,55 @@ /** \page org_igttrackinglab IGT Tutorial Step 4: The IGT-TrackingLab Available sections: - \ref QmitkIGTTrackingLabUsersManualOverview - \ref QmitkIGTTrackingLabUsersManualPrel - \ref QmitkIGTTrackingLabUsersManualConf - \ref QmitkIGTTrackingLabUsersManualIntialReg - \ref QmitkIGTTrackingLabUsersManualPermReg - \ref QmitkIGTTrackingLabUsersManualPtSetRec - \ref QmitkIGTTrackingLabUsersManualCamView \section QmitkIGTTrackingLabUsersManualOverview Introduction The IGT-TrackingLab is the last step of the IGT tutorial. It is a plugin which shows examples usage for many IGT classes and is also an example navigation implemented with IGT. In the following you can learn how to use the plugin by reading this manual together with the source code. \section QmitkIGTTrackingLabUsersManualPrel Preliminaries -First connect your tracking device to your PC. Then start the MITK Workbench and configure your tracking device using the org_mitk_views_igttrackingtoolbox . +First connect your tracking device to your PC. Then start the MITK Workbench and configure your tracking device using the \ref org_mitk_views_igttrackingtoolbox "Tracking Toolbox View". \section QmitkIGTTrackingLabUsersManualConf Configuration -Select the desired Navigation Data Source. Now its time to define which tool shall be used as object marker and which tool shall be used as pointer. Next load the Book surface provided with the example data (TODO) in the Data Manager. Fixate the object marker on a real book of your choice. Now we need to tell MITK that the objectmarker has been fixated on a physical object. To do this, select the Book as surface in the Object Selection submenu. +Select the desired Navigation Data Source. Now it’s time to define which tool shall be used as object marker and which tool shall be used as pointer. Next load the Book surface provided with the example data (e.g. book.stl from the MITK-Data repository which comes with every superbuild or may also be checked out separately) into MITK. Fixate the object marker on a real book of your choice. Now we need to tell MITK that the object marker has been fixated on a physical object. To do this, select the Book as surface in the Object Selection submenu. \section QmitkIGTTrackingLabUsersManualIntialReg Initial Registration -Now we need to register the objectmarker to the surface it's fixed upon, in our case the book. To do this, first press the initial registration button. For MITK to be able to do this registration, we need to +Now we need to register the object marker to the surface it's fixed upon, in our case the book. To do this, first press the initial registration button. For MITK to be able to do this registration, we need to 1. Select landmarks on the virtual object (e.g. the corners of the book) -Press the plus button in the Image fiducials column. Shift + click on the corners on the book in the MITK Display. +Press the plus button in the Image fiducials column. Shift + click on the corners on the book in the MITK Display. 2. Point to the corresponding landmarks in the real world using the pointer. -Now press the plus button in the Real world fiducials column and point to the corners on the real book. Press Add current instrument position whenever you targeted a corner to tell MITK this is the desired landmark. Make sure you select the "real" edges in the same order as the edges in the image. +Now press the plus button in the Real world fiducials column and point to the corners on the real book. Press Add current instrument position whenever you targeted a corner to tell MITK this is the desired landmark. Make sure you select the "real" edges in the same order as the edges in the image. Press Register to finalize the initial registration. Now the object marker is registered onto the book. You can see this in the MITK image. If needed the FRE is shown in the widget. \section QmitkIGTTrackingLabUsersManualPermReg Permanent Registration -Now everything is set up and registered. We can thus activate permanent registration to continuously track the object, the objectmarker and the pointer. +Now everything is set up and registered. We can thus activate permanent registration to continuously track the object, the object marker and the pointer. For this, simply press the Permanent Registration button and select Activate permanent registration. You can now move the book in the real world and see the same movement in the MITK Display. A nice test to see if everything was correctly registered is to target the corners of the book with the pointer and check if the correct corners are pointed to in the MITK Display. \section QmitkIGTTrackingLabUsersManualPtSetRec PointSet Recording A user might now want to track a tool's trajectory. For this, the PointSet Recording was created. First click on PointSet Recording. Now select your tracking source and the tool whose trajectory shall be recorded. Activate the Point Set Recording checkbox. In the MITK Display little green points will now be drawn for every measured position. Deactivate the checkbox to stop recording. The trajectory is saved in the PointSet Recorded Points visible in the Data Manager. \section QmitkIGTTrackingLabUsersManualCamView Camera View -Another possible tracking application is the Camera View. Here, a virtual camera is placed at the pointers tip and its images are shown in the MITK Display. +Another possible tracking application is the Camera View. Here, a virtual camera is placed at the pointers tip and its images are shown in the MITK Display. -Select Camera View and as usual the Tracking Source and the tool you want to place the virtual camera on. Activate the "Activate Needle View" checkbox. and move the pointer around the book. You can now see the book from the pointers perspective. +Select Camera View and as usual the Tracking Source and the tool you want to place the virtual camera on. Activate the "Activate Needle View" checkbox and move the pointer around the book. You can now see the book from the pointers perspective. You may need to adjust the Needle View Direction and the View Up Vector. This is always relative to your tools coordinate center origin. An example of the NDI pointer tool coordinate system is shown below: \image html QmitkIGTExamples_Tool.png "The coordinate system of the NDI pointing tool" In the above case, the camera should look in inverse z-direction, and the view up vector should probably be set to positive x. Note this is just an example and may be different depending on your pointer. - */ diff --git a/Plugins/org.mitk.gui.qt.igtexamples/src/internal/QmitkIGTTrackingLabView.cpp b/Plugins/org.mitk.gui.qt.igtexamples/src/internal/QmitkIGTTrackingLabView.cpp index 05b50dc61f..b645400f24 100644 --- a/Plugins/org.mitk.gui.qt.igtexamples/src/internal/QmitkIGTTrackingLabView.cpp +++ b/Plugins/org.mitk.gui.qt.igtexamples/src/internal/QmitkIGTTrackingLabView.cpp @@ -1,699 +1,756 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // Blueberry #include #include // Qmitk #include "QmitkIGTTrackingLabView.h" #include "QmitkStdMultiWidget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include -#include #include // Qt #include #include #include // vtk #include const std::string QmitkIGTTrackingLabView::VIEW_ID = "org.mitk.views.igttrackinglab"; QmitkIGTTrackingLabView::QmitkIGTTrackingLabView() : QmitkAbstractView() ,m_Source(NULL) ,m_PermanentRegistrationFilter(NULL) ,m_Visualizer(NULL) ,m_VirtualView(NULL) ,m_PSRecordingPointSet(NULL) -,m_RegistrationTrackingFiducialsName("Tracking Fiducials") -,m_RegistrationImageFiducialsName("Image Fiducials") -,m_PointSetRecordingDataNodeName("Recorded Points") ,m_PointSetRecording(false) ,m_PermanentRegistration(false) ,m_CameraView(false) ,m_ImageFiducialsDataNode(NULL) ,m_TrackerFiducialsDataNode(NULL) ,m_PermanentRegistrationSourcePoints(NULL) { } -QmitkIGTTrackingLabView::~QmitkIGTTrackingLabView() -{ - if (m_Timer->isActive()) m_Timer->stop(); -} - -void QmitkIGTTrackingLabView::CreateQtPartControl( QWidget *parent ) -{ - // create GUI widgets from the Qt Designer's .ui file - m_Controls.setupUi( parent ); - - m_ToolBox = m_Controls.m_ToolBox; - - this->CreateBundleWidgets( parent ); - this->CreateConnections(); -} - - -void QmitkIGTTrackingLabView::CreateBundleWidgets( QWidget* parent ) -{ - //initialize registration widget - m_RegistrationWidget = m_Controls.m_RegistrationWidget; - m_RegistrationWidget->HideStaticRegistrationRadioButton(true); - m_RegistrationWidget->HideContinousRegistrationRadioButton(true); - m_RegistrationWidget->HideUseICPRegistrationCheckbox(true); -} - - -void QmitkIGTTrackingLabView::CreateConnections() -{ - //initialize timer - m_Timer = new QTimer(this); - - //create connections - connect(m_Timer, SIGNAL(timeout()), this, SLOT(UpdateTimer())); - connect( m_ToolBox, SIGNAL(currentChanged(int)), this, SLOT(OnToolBoxCurrentChanged(int)) ); - connect( m_Controls.m_UsePermanentRegistrationToggle, SIGNAL(toggled(bool)), this, SLOT(OnPermanentRegistration(bool)) ); - connect( m_Controls.m_TrackingDeviceSelectionWidget, SIGNAL(NavigationDataSourceSelected(mitk::NavigationDataSource::Pointer)), this, SLOT(OnSetupNavigation()) ); - connect( m_Controls.m_UseAsPointerButton, SIGNAL(clicked()), this, SLOT(OnInstrumentSelected()) ); - connect( m_Controls.m_UseAsObjectmarkerButton, SIGNAL(clicked()), this, SLOT(OnObjectmarkerSelected()) ); - connect( m_RegistrationWidget, SIGNAL(AddedTrackingFiducial()), this, SLOT(OnAddRegistrationTrackingFiducial()) ); - connect( m_RegistrationWidget, SIGNAL(PerformFiducialRegistration()), this, SLOT(OnRegisterFiducials()) ); - connect( m_Controls.m_PointSetRecordCheckBox, SIGNAL(toggled(bool)), this, SLOT(OnPointSetRecording(bool)) ); - connect( m_Controls.m_ActivateNeedleView, SIGNAL(toggled(bool)), this, SLOT(OnVirtualCamera(bool)) ); - - //start timer - m_Timer->start(30); - - //initialize Combo Boxes - m_Controls.m_ObjectComboBox->SetDataStorage(this->GetDataStorage()); - m_Controls.m_ObjectComboBox->SetAutoSelectNewItems(false); - m_Controls.m_ObjectComboBox->SetPredicate(mitk::NodePredicateDataType::New("Surface")); - - m_Controls.m_ImageComboBox->SetDataStorage(this->GetDataStorage()); - m_Controls.m_ImageComboBox->SetAutoSelectNewItems(false); - m_Controls.m_ImageComboBox->SetPredicate(mitk::NodePredicateDataType::New("Image")); -} - -void QmitkIGTTrackingLabView::SetFocus() -{ - m_Controls.m_UseAsPointerButton->setFocus(); -} +//############################################################################################### +//############################################################################################### +//############################## Timer method for IGT pipeline updating ######################### +//############################################################################################### +//############################################################################################### void QmitkIGTTrackingLabView::UpdateTimer() { if (m_PermanentRegistration && m_PermanentRegistrationFilter.IsNotNull()) { if(IsTransformDifferenceHigh(m_ObjectmarkerNavigationData, m_ObjectmarkerNavigationDataLastUpdate)) { m_ObjectmarkerNavigationDataLastUpdate->Graft(m_ObjectmarkerNavigationData); m_PermanentRegistrationFilter->Update(); } } if (m_CameraView && m_VirtualView.IsNotNull()) {m_VirtualView->Update();} if(m_PointSetRecording && m_PSRecordingPointSet.IsNotNull()) { int size = m_PSRecordingPointSet->GetSize(); mitk::NavigationData::Pointer nd = m_PointSetRecordingNavigationData; if(size > 0) { mitk::Point3D p = m_PSRecordingPointSet->GetPoint(size-1); if(p.EuclideanDistanceTo(nd->GetPosition()) > (double) m_Controls.m_PSRecordingSpinBox->value()) m_PSRecordingPointSet->InsertPoint(size, nd->GetPosition()); } else m_PSRecordingPointSet->InsertPoint(size, nd->GetPosition()); } } +//############################################################################################### +//############################################################################################### +//############################## Slots of CONFIGURATION step #################################### +//############################################################################################### +//############################################################################################### -void QmitkIGTTrackingLabView::OnAddRegistrationTrackingFiducial() +void QmitkIGTTrackingLabView::OnSetupNavigation() { - mitk::NavigationData::Pointer nd = m_InstrumentNavigationData; + if(m_Source.IsNotNull()) + if(m_Source->IsTracking()) + return; - if( nd.IsNull() || !nd->IsDataValid()) + mitk::DataStorage* ds = this->GetDataStorage(); + + if(ds == NULL) { - QMessageBox::warning( 0, "Invalid tracking data", "Navigation data is not available or invalid!", QMessageBox::Ok ); + MITK_WARN << "IGTSurfaceTracker: Error", "can not access DataStorage. Navigation not possible"; return; } - if(m_TrackerFiducialsDataNode.IsNotNull() && m_TrackerFiducialsDataNode->GetData() != NULL) + //Building up the filter pipeline + try { - mitk::PointSet::Pointer ps = dynamic_cast(m_TrackerFiducialsDataNode->GetData()); - ps->InsertPoint(ps->GetSize(), nd->GetPosition()); + this->InitializeRegistration(); + } + catch(mitk::IGTException& e) + { + MITK_WARN << "Error while building the IGT-Pipeline: " << e.GetDescription(); + this->DestroyIGTPipeline(); // destroy the pipeline if building is incomplete + return; + } + catch(...) + { + MITK_WARN << "Unexpected error while building the IGT-Pipeline"; + this->DestroyIGTPipeline(); + return; } - else - QMessageBox::warning(NULL, "IGTSurfaceTracker: Error", "Can not access Tracker Fiducials. Adding fiducial not possible!"); } void QmitkIGTTrackingLabView::OnInstrumentSelected() { if (m_Controls.m_TrackingDeviceSelectionWidget->GetSelectedNavigationDataSource().IsNotNull()) { m_InstrumentNavigationData = m_Controls.m_TrackingDeviceSelectionWidget->GetSelectedNavigationDataSource()->GetOutput(m_Controls.m_TrackingDeviceSelectionWidget->GetSelectedToolID()); } else { m_Controls.m_PointerNameLabel->setText(""); return; } if (m_InstrumentNavigationData.IsNotNull()) { m_Controls.m_PointerNameLabel->setText(m_InstrumentNavigationData->GetName()); } else { m_Controls.m_PointerNameLabel->setText(""); } } void QmitkIGTTrackingLabView::OnObjectmarkerSelected() { if (m_Controls.m_TrackingDeviceSelectionWidget->GetSelectedNavigationDataSource().IsNotNull()) { m_ObjectmarkerNavigationData = m_Controls.m_TrackingDeviceSelectionWidget->GetSelectedNavigationDataSource()->GetOutput(m_Controls.m_TrackingDeviceSelectionWidget->GetSelectedToolID()); MITK_INFO << "Objectmarker rotation: " << m_ObjectmarkerNavigationData->GetOrientation(); } else { m_Controls.m_ObjectmarkerNameLabel->setText(""); return; } if (m_ObjectmarkerNavigationData.IsNotNull()) { m_Controls.m_ObjectmarkerNameLabel->setText(m_ObjectmarkerNavigationData->GetName()); } else { m_Controls.m_ObjectmarkerNameLabel->setText(""); } } -void QmitkIGTTrackingLabView::OnSetupNavigation() -{ - if(m_Source.IsNotNull()) - if(m_Source->IsTracking()) - return; - - mitk::DataStorage* ds = this->GetDataStorage(); - - if(ds == NULL) - { - MITK_WARN << "IGTSurfaceTracker: Error", "can not access DataStorage. Navigation not possible"; - return; - } - - //Building up the filter pipeline - try - { - this->SetupIGTPipeline(); - } - catch(mitk::IGTException& e) - { - MITK_WARN << "Error while building the IGT-Pipeline: " << e.GetDescription(); - this->DestroyIGTPipeline(); // destroy the pipeline if building is incomplete - return; - } - catch(...) - { - MITK_WARN << "Unexpected error while building the IGT-Pipeline"; - this->DestroyIGTPipeline(); - return; - } -} - -void QmitkIGTTrackingLabView::SetupIGTPipeline() -{ - this->InitializeRegistration(); //initializes the registration widget -} +//############################################################################################### +//############################################################################################### +//####################### Slots of INITIAL REGISTRATION step #################################### +//############################################################################################### +//############################################################################################### -void QmitkIGTTrackingLabView::OnRegisterFiducials() +void QmitkIGTTrackingLabView::OnInitialRegistration() { //Check for initialization if (!CheckRegistrationInitialization()) return; /* retrieve fiducials from data storage */ mitk::DataStorage* ds = this->GetDataStorage(); mitk::PointSet::Pointer imageFiducials = dynamic_cast(m_ImageFiducialsDataNode->GetData()); mitk::PointSet::Pointer trackerFiducials = dynamic_cast(m_TrackerFiducialsDataNode->GetData()); + //############### conversion to vtk data types (we will use the vtk landmark based transform) ########################## //convert point sets to vtk poly data vtkSmartPointer sourcePoints = vtkSmartPointer::New(); vtkSmartPointer targetPoints = vtkSmartPointer::New(); for (int i=0; iGetSize(); i++) { double point[3] = {imageFiducials->GetPoint(i)[0],imageFiducials->GetPoint(i)[1],imageFiducials->GetPoint(i)[2]}; sourcePoints->InsertNextPoint(point); double point_targets[3] = {trackerFiducials->GetPoint(i)[0],trackerFiducials->GetPoint(i)[1],trackerFiducials->GetPoint(i)[2]}; targetPoints->InsertNextPoint(point_targets); } + //########################### here, the actual transform is computed ########################## //compute transform vtkSmartPointer transform = vtkSmartPointer::New(); transform->SetSourceLandmarks(sourcePoints); transform->SetTargetLandmarks(targetPoints); transform->SetModeToRigidBody(); transform->Modified(); transform->Update(); - - //compute FRE - double FRE = 0; - for(unsigned int i = 0; i < imageFiducials->GetSize(); i++) - { - itk::Point transformed = transform->TransformPoint(imageFiducials->GetPoint(i)[0],imageFiducials->GetPoint(i)[1],imageFiducials->GetPoint(i)[2]); - double cur_error_squared = transformed.SquaredEuclideanDistanceTo(trackerFiducials->GetPoint(i)); - FRE += cur_error_squared; - } - - FRE = sqrt(FRE/ (double) imageFiducials->GetSize()); + //compute FRE of transform + double FRE = ComputeFRE(imageFiducials,trackerFiducials,transform); m_Controls.m_RegistrationWidget->SetQualityDisplayText("FRE: " + QString::number(FRE) + " mm"); + //############################################################################################# + //############### conversion back to itk/mitk data types ########################## //convert from vtk to itk data types itk::Matrix rotationFloat = itk::Matrix(); itk::Vector translationFloat = itk::Vector(); itk::Matrix rotationDouble = itk::Matrix(); itk::Vector translationDouble = itk::Vector(); vtkSmartPointer m = transform->GetMatrix(); for(int k=0; k<3; k++) for(int l=0; l<3; l++) { rotationFloat[k][l] = m->GetElement(k,l); rotationDouble[k][l] = m->GetElement(k,l); } for(int k=0; k<3; k++) { translationFloat[k] = m->GetElement(k,3); translationDouble[k] = m->GetElement(k,3); } - //create affine transform 3D surface - mitk::AffineTransform3D::Pointer newTransform = mitk::AffineTransform3D::New(); - newTransform->SetMatrix(rotationDouble); - newTransform->SetOffset(translationDouble); + mitk::AffineTransform3D::Pointer mitkTransform = mitk::AffineTransform3D::New(); + mitkTransform->SetMatrix(rotationDouble); + mitkTransform->SetOffset(translationDouble); + //############################################################################################# + //############### object is transformed ########################## //save transform - m_T_ObjectReg = mitk::NavigationData::New(newTransform); + m_T_ObjectReg = mitk::NavigationData::New(mitkTransform); // this is stored in a member because it is needed for permanent registration later on //transform surface if(m_Controls.m_SurfaceActive->isChecked() && m_Controls.m_ObjectComboBox->GetSelectedNode().IsNotNull()) { - m_Controls.m_ObjectComboBox->GetSelectedNode()->GetData()->GetGeometry()->SetIndexToWorldTransform(newTransform); + m_Controls.m_ObjectComboBox->GetSelectedNode()->GetData()->GetGeometry()->SetIndexToWorldTransform(mitkTransform); } + //################################################################ + //############### if activated: ct image is also transformed ########################## //transform ct image + //todo: Erklären, dass hier AffineTransform3D verwendet wird, weil NavigationData kein Spacing unterstützt! if(m_Controls.m_ImageActive->isChecked() && m_Controls.m_ImageComboBox->GetSelectedNode().IsNotNull()) { + //first we have to store the original ct image transform to compose it with the new transform later mitk::AffineTransform3D::Pointer imageTransform = m_Controls.m_ImageComboBox->GetSelectedNode()->GetData()->GetGeometry()->GetIndexToWorldTransform(); - m_T_ImageGeo = mitk::AffineTransform3D::New(); + m_T_ImageGeo = mitk::AffineTransform3D::New(); // this is also stored in a member because it is needed for permanent registration later on + //now the new transform of the ct image is computed m_T_ImageGeo->Compose(imageTransform); - imageTransform->Compose(newTransform); + imageTransform->Compose(mitkTransform); mitk::AffineTransform3D::Pointer newImageTransform = mitk::AffineTransform3D::New(); //create new image transform... setting the composed directly leads to an error itk::Matrix rotationFloatNew = imageTransform->GetMatrix(); itk::Vector translationFloatNew = imageTransform->GetOffset(); newImageTransform->SetMatrix(rotationFloatNew); newImageTransform->SetOffset(translationFloatNew); m_Controls.m_ImageComboBox->GetSelectedNode()->GetData()->GetGeometry()->SetIndexToWorldTransform(newImageTransform); m_T_ImageReg = m_Controls.m_ImageComboBox->GetSelectedNode()->GetData()->GetGeometry()->GetIndexToWorldTransform(); } + //################################################################ } -void QmitkIGTTrackingLabView::DestroyIGTPipeline() + +void QmitkIGTTrackingLabView::OnAddRegistrationTrackingFiducial() { - if(m_Source.IsNotNull()) + mitk::NavigationData::Pointer nd = m_InstrumentNavigationData; + + if( nd.IsNull() || !nd->IsDataValid()) { - m_Source->StopTracking(); - m_Source->Disconnect(); - m_Source = NULL; + QMessageBox::warning( 0, "Invalid tracking data", "Navigation data is not available or invalid!", QMessageBox::Ok ); + return; } - m_PermanentRegistrationFilter = NULL; - m_Visualizer = NULL; - m_VirtualView = NULL; + + if(m_TrackerFiducialsDataNode.IsNotNull() && m_TrackerFiducialsDataNode->GetData() != NULL) + { + mitk::PointSet::Pointer ps = dynamic_cast(m_TrackerFiducialsDataNode->GetData()); + ps->InsertPoint(ps->GetSize(), nd->GetPosition()); + } + else + QMessageBox::warning(NULL, "IGTSurfaceTracker: Error", "Can not access Tracker Fiducials. Adding fiducial not possible!"); } + void QmitkIGTTrackingLabView::InitializeRegistration() { mitk::DataStorage* ds = this->GetDataStorage(); if( ds == NULL ) return; // let the registration widget know about the slice navigation controllers // in the active render window part (crosshair updates) foreach(QmitkRenderWindow* renderWindow, this->GetRenderWindowPart()->GetQmitkRenderWindows().values()) { - m_RegistrationWidget->AddSliceNavigationController(renderWindow->GetSliceNavigationController()); + m_Controls.m_RegistrationWidget->AddSliceNavigationController(renderWindow->GetSliceNavigationController()); } if(m_ImageFiducialsDataNode.IsNull()) { m_ImageFiducialsDataNode = mitk::DataNode::New(); mitk::PointSet::Pointer ifPS = mitk::PointSet::New(); m_ImageFiducialsDataNode->SetData(ifPS); mitk::Color color; color.Set(1.0f, 0.0f, 0.0f); - m_ImageFiducialsDataNode->SetName(m_RegistrationImageFiducialsName); + m_ImageFiducialsDataNode->SetName("Image Fiducials"); m_ImageFiducialsDataNode->SetColor(color); m_ImageFiducialsDataNode->SetBoolProperty( "updateDataOnRender", false ); ds->Add(m_ImageFiducialsDataNode); } - m_RegistrationWidget->SetImageFiducialsNode(m_ImageFiducialsDataNode); + m_Controls.m_RegistrationWidget->SetImageFiducialsNode(m_ImageFiducialsDataNode); if(m_TrackerFiducialsDataNode.IsNull()) { m_TrackerFiducialsDataNode = mitk::DataNode::New(); mitk::PointSet::Pointer tfPS = mitk::PointSet::New(); m_TrackerFiducialsDataNode->SetData(tfPS); mitk::Color color; color.Set(0.0f, 1.0f, 0.0f); - m_TrackerFiducialsDataNode->SetName(m_RegistrationTrackingFiducialsName); + m_TrackerFiducialsDataNode->SetName("Tracking Fiducials"); m_TrackerFiducialsDataNode->SetColor(color); m_TrackerFiducialsDataNode->SetBoolProperty( "updateDataOnRender", false ); ds->Add(m_TrackerFiducialsDataNode); } - m_RegistrationWidget->SetTrackerFiducialsNode(m_TrackerFiducialsDataNode); + m_Controls.m_RegistrationWidget->SetTrackerFiducialsNode(m_TrackerFiducialsDataNode); } +//############################################################################################### +//############################################################################################### +//####################### Slots of PERMANENT REGISTRATION step ################################## +//############################################################################################### +//############################################################################################### -void QmitkIGTTrackingLabView::OnToolBoxCurrentChanged(const int index) +void QmitkIGTTrackingLabView::OnPermanentRegistration(bool on) { - switch (index) - { - case RegistrationWidget: - this->InitializeRegistration(); - break; + if(on) + { + //###################################################################### + //######################## inititalization ############################# + //###################################################################### - default: - break; - } + //some initial checks + if(!CheckRegistrationInitialization()) + { + m_Controls.m_UsePermanentRegistrationToggle->setChecked(false); + return; + } + + //remember initial object transform to calculate the object to marker transform later on and convert it to navigation data + mitk::AffineTransform3D::Pointer transform = this->m_Controls.m_ObjectComboBox->GetSelectedNode()->GetData()->GetGeometry()->GetIndexToWorldTransform(); + mitk::NavigationData::Pointer T_Object = mitk::NavigationData::New(transform,false); //TODO: catch exception during conversion? + + //then reset the transform because we will now start to calculate the permanent registration + this->m_Controls.m_ObjectComboBox->GetSelectedNode()->GetData()->GetGeometry()->SetIdentity(); + if(m_Controls.m_ImageActive->isChecked()) {this->m_Controls.m_ImageComboBox->GetSelectedNode()->GetData()->GetGeometry()->SetIndexToWorldTransform(m_T_ImageGeo);} + + //create the permanent registration filter + m_PermanentRegistrationFilter = mitk::NavigationDataObjectVisualizationFilter::New(); + + //###################################################################### + //first: initialize permanent registration of surface (always activated) + //###################################################################### + + //connect filter to source + m_PermanentRegistrationFilter->SetInput(0,this->m_ObjectmarkerNavigationData); + + //set representation object + m_PermanentRegistrationFilter->SetRepresentationObject(0,this->m_Controls.m_ObjectComboBox->GetSelectedNode()->GetData()); + + //get the marker transform out of the navigation data + mitk::NavigationData::Pointer T_Marker = m_ObjectmarkerNavigationData; + + //compute transform from object to marker (T_MarkerRel = T_Object * T_Marker^-1) + mitk::NavigationData::Pointer T_MarkerRel = mitk::NavigationData::New(); + T_MarkerRel->Compose(T_Object); + T_MarkerRel->Compose(T_Marker->GetInverse()); + m_T_MarkerRel = T_MarkerRel; + m_PermanentRegistrationFilter->SetOffset(0,m_T_MarkerRel->GetAffineTransform3D()); + + //###################################################################### + //second: initialize permanent registration of image (if activated) + //###################################################################### + if (m_Controls.m_ImageActive->isChecked() && (m_Controls.m_ImageComboBox->GetSelectedNode().IsNotNull())) + { + mitk::DataNode::Pointer imageNode = this->m_Controls.m_ImageComboBox->GetSelectedNode(); + imageNode->AddProperty( "reslice interpolation", mitk::VtkResliceInterpolationProperty::New(VTK_RESLICE_LINEAR) ); + m_PermanentRegistrationFilter->SetInput(1,this->m_ObjectmarkerNavigationData); + m_PermanentRegistrationFilter->SetRepresentationObject(1,imageNode->GetData()); + + //for the image we can't use NavigationData objects as transforms because an image needs additional geometry information, e.g., spacing + //thus we use mitk::AffineTransform3D objects + + //computer transform from image to marker (T_ImageRel = T_ImageGeo * T_MarkerRel) + mitk::AffineTransform3D::Pointer T_ImageRel = mitk::AffineTransform3D::New(); + T_ImageRel->SetIdentity(); + T_ImageRel->Compose(m_T_ImageGeo); + T_ImageRel->Compose(m_T_MarkerRel->GetAffineTransform3D()); + m_PermanentRegistrationFilter->SetOffset(1,T_ImageRel); + } + + //some general stuff + m_PermanentRegistration = true; + m_ObjectmarkerNavigationDataLastUpdate = mitk::NavigationData::New(); + } + else //if off = disable the permanent registration + { + //stop permanent registration + m_PermanentRegistration = false; + + //restore old registration + if(m_T_ObjectReg.IsNotNull()) {this->m_Controls.m_ObjectComboBox->GetSelectedNode()->GetData()->GetGeometry()->SetIndexToWorldTransform(m_T_ObjectReg->GetAffineTransform3D());} + if(m_T_ImageReg.IsNotNull()) {this->m_Controls.m_ImageComboBox->GetSelectedNode()->GetData()->GetGeometry()->SetIndexToWorldTransform(m_T_ImageReg);} + + //delete filter + m_PermanentRegistrationFilter = NULL; + } } +//############################################################################################### +//############################################################################################### +//####################### Slots of POINT SET RECORDING step ##################################### +//############################################################################################### +//############################################################################################### + void QmitkIGTTrackingLabView::OnPointSetRecording(bool record) { mitk::DataStorage* ds = this->GetDataStorage(); if(record) { if (m_Controls.m_PointSetRecordingToolSelectionWidget->GetSelectedToolID() == -1) { QMessageBox::warning(NULL, "Error", "No tool selected for point set recording!"); m_Controls.m_PointSetRecordCheckBox->setChecked(false); return; } m_PointSetRecordingNavigationData = m_Controls.m_PointSetRecordingToolSelectionWidget->GetSelectedNavigationDataSource()->GetOutput(m_Controls.m_PointSetRecordingToolSelectionWidget->GetSelectedToolID()); //initialize point set - mitk::DataNode::Pointer psRecND = ds->GetNamedNode(m_PointSetRecordingDataNodeName); + mitk::DataNode::Pointer psRecND = ds->GetNamedNode("Recorded Points"); if(m_PSRecordingPointSet.IsNull() || psRecND.IsNull()) { m_PSRecordingPointSet = NULL; m_PSRecordingPointSet = mitk::PointSet::New(); mitk::DataNode::Pointer dn = mitk::DataNode::New(); - dn->SetName(m_PointSetRecordingDataNodeName); + dn->SetName("Recorded Points"); dn->SetColor(0.,1.,0.); dn->SetData(m_PSRecordingPointSet); ds->Add(dn); } else { m_PSRecordingPointSet->Clear(); } m_PointSetRecording = true; } else { m_PointSetRecording = false; } } +//############################################################################################### +//############################################################################################### +//####################### Slots of VIRTUAL CAMERA VIEW step ##################################### +//############################################################################################### +//############################################################################################### + void QmitkIGTTrackingLabView::OnVirtualCamera(bool on) { if (m_Controls.m_CameraViewSelection->GetSelectedToolID() == -1) { m_Controls.m_ActivateNeedleView->setChecked(false); QMessageBox::warning(NULL, "Error", "No tool selected for camera view!"); return; } if(on) { m_VirtualView = mitk::CameraVisualization::New(); m_VirtualView->SetInput(m_Controls.m_CameraViewSelection->GetSelectedNavigationDataSource()->GetOutput(m_Controls.m_CameraViewSelection->GetSelectedToolID())); mitk::Vector3D viewDirection; viewDirection[0] = (int)(m_Controls.m_NeedleViewX->isChecked()); viewDirection[1] = (int)(m_Controls.m_NeedleViewY->isChecked()); viewDirection[2] = (int)(m_Controls.m_NeedleViewZ->isChecked()); if (m_Controls.m_NeedleViewInvert->isChecked()) viewDirection *= -1; m_VirtualView->SetDirectionOfProjectionInToolCoordinates(viewDirection); mitk::Vector3D viewUpVector; viewUpVector[0] = (int)(m_Controls.m_NeedleUpX->isChecked()); viewUpVector[1] = (int)(m_Controls.m_NeedleUpY->isChecked()); viewUpVector[2] = (int)(m_Controls.m_NeedleUpZ->isChecked()); if (m_Controls.m_NeedleUpInvert->isChecked()) viewUpVector *= -1; m_VirtualView->SetViewUpInToolCoordinates(viewUpVector); m_VirtualView->SetRenderer(this->GetRenderWindowPart()->GetQmitkRenderWindow("3d")->GetRenderer()); //next line: better code when this plugin is migrated to mitk::abstractview //m_VirtualView->SetRenderer(mitk::BaseRenderer::GetInstance(this->GetRenderWindowPart()->GetRenderWindow("3d")->GetRenderWindow())); m_CameraView = true; //make pointer itself invisible m_Controls.m_CameraViewSelection->GetSelectedNavigationTool()->GetDataNode()->SetBoolProperty("visible",false); //disable UI elements m_Controls.m_ViewDirectionBox->setEnabled(false); m_Controls.m_ViewUpBox->setEnabled(false); } else { m_VirtualView = NULL; m_CameraView = false; m_Controls.m_CameraViewSelection->GetSelectedNavigationTool()->GetDataNode()->SetBoolProperty("visible",true); m_Controls.m_ViewDirectionBox->setEnabled(true); m_Controls.m_ViewUpBox->setEnabled(true); } } -bool QmitkIGTTrackingLabView::CheckRegistrationInitialization() +//############################################################################################### +//############################################################################################### +//############################## some general UI methods, always needed ######################### +//############################################################################################### +//############################################################################################### + +QmitkIGTTrackingLabView::~QmitkIGTTrackingLabView() { - mitk::DataStorage* ds = this->GetDataStorage(); - mitk::PointSet::Pointer imageFiducials = dynamic_cast(m_ImageFiducialsDataNode->GetData()); - mitk::PointSet::Pointer trackerFiducials = dynamic_cast(m_TrackerFiducialsDataNode->GetData()); + if (m_Timer->isActive()) m_Timer->stop(); +} - if (m_Controls.m_SurfaceActive->isChecked() && m_Controls.m_ObjectComboBox->GetSelectedNode().IsNull()) +void QmitkIGTTrackingLabView::CreateQtPartControl( QWidget *parent ) +{ + // create GUI widgets from the Qt Designer's .ui file + m_Controls.setupUi( parent ); + this->CreateBundleWidgets( parent ); + this->CreateConnections(); +} + + +void QmitkIGTTrackingLabView::CreateBundleWidgets( QWidget* parent ) +{ + //initialize registration widget + m_Controls.m_RegistrationWidget->HideStaticRegistrationRadioButton(true); + m_Controls.m_RegistrationWidget->HideContinousRegistrationRadioButton(true); + m_Controls.m_RegistrationWidget->HideUseICPRegistrationCheckbox(true); +} + + +void QmitkIGTTrackingLabView::CreateConnections() +{ + //initialize timer + m_Timer = new QTimer(this); + + //create connections + connect(m_Timer, SIGNAL(timeout()), this, SLOT(UpdateTimer())); + connect( m_Controls.m_UsePermanentRegistrationToggle, SIGNAL(toggled(bool)), this, SLOT(OnPermanentRegistration(bool)) ); + connect( m_Controls.m_TrackingDeviceSelectionWidget, SIGNAL(NavigationDataSourceSelected(mitk::NavigationDataSource::Pointer)), this, SLOT(OnSetupNavigation()) ); + connect( m_Controls.m_UseAsPointerButton, SIGNAL(clicked()), this, SLOT(OnInstrumentSelected()) ); + connect( m_Controls.m_UseAsObjectmarkerButton, SIGNAL(clicked()), this, SLOT(OnObjectmarkerSelected()) ); + connect( m_Controls.m_RegistrationWidget, SIGNAL(AddedTrackingFiducial()), this, SLOT(OnAddRegistrationTrackingFiducial()) ); + connect( m_Controls.m_RegistrationWidget, SIGNAL(PerformFiducialRegistration()), this, SLOT(OnInitialRegistration()) ); + connect( m_Controls.m_PointSetRecordCheckBox, SIGNAL(toggled(bool)), this, SLOT(OnPointSetRecording(bool)) ); + connect( m_Controls.m_ActivateNeedleView, SIGNAL(toggled(bool)), this, SLOT(OnVirtualCamera(bool)) ); + + //start timer + m_Timer->start(30); + + //initialize Combo Boxes + m_Controls.m_ObjectComboBox->SetDataStorage(this->GetDataStorage()); + m_Controls.m_ObjectComboBox->SetAutoSelectNewItems(false); + m_Controls.m_ObjectComboBox->SetPredicate(mitk::NodePredicateDataType::New("Surface")); + + m_Controls.m_ImageComboBox->SetDataStorage(this->GetDataStorage()); + m_Controls.m_ImageComboBox->SetAutoSelectNewItems(false); + m_Controls.m_ImageComboBox->SetPredicate(mitk::NodePredicateDataType::New("Image")); +} + +void QmitkIGTTrackingLabView::SetFocus() +{ + m_Controls.m_UseAsPointerButton->setFocus(); +} + +//############################################################################################### +//############################################################################################### +//####################### some additional slots and help methods ################################ +//####################### for cleaner code - not that important ################################ +//####################### to understand the basic functions ################################ +//############################################################################################### +//############################################################################################### + +void QmitkIGTTrackingLabView::DestroyIGTPipeline() +{ + if(m_Source.IsNotNull()) { - std::string warningMessage = "No surface selected for registration.\nRegistration is not possible"; - MITK_WARN << warningMessage; - QMessageBox::warning(NULL, "Registration not possible", warningMessage.c_str()); - return false; + m_Source->StopTracking(); + m_Source->Disconnect(); + m_Source = NULL; } + m_PermanentRegistrationFilter = NULL; + m_Visualizer = NULL; + m_VirtualView = NULL; +} - if (m_Controls.m_ImageActive->isChecked() && m_Controls.m_ImageComboBox->GetSelectedNode().IsNull()) +bool QmitkIGTTrackingLabView::CheckRegistrationInitialization() +{ + // a couple of variables which we need in this method + std::string warningMessage = ""; + bool initializationErrorDetected = false; + mitk::PointSet::Pointer imageFiducials,trackerFiducials; + + // check some initialization stuff + if (m_ImageFiducialsDataNode.IsNull() || m_TrackerFiducialsDataNode.IsNull()) { - std::string warningMessage = "No image selected for registration.\nRegistration is not possible"; + warningMessage = "Initialization not finished!"; MITK_WARN << warningMessage; QMessageBox::warning(NULL, "Registration not possible", warningMessage.c_str()); return false; } + else + { + imageFiducials = dynamic_cast(m_ImageFiducialsDataNode->GetData()); + trackerFiducials = dynamic_cast(m_TrackerFiducialsDataNode->GetData()); + } - if (imageFiducials.IsNull() || trackerFiducials.IsNull()) + // now, do a lot of other checks... + if (m_Controls.m_SurfaceActive->isChecked() && m_Controls.m_ObjectComboBox->GetSelectedNode().IsNull()) { - std::string warningMessage = "Fiducial data objects not found. \n" + warningMessage = "No surface selected for registration.\nRegistration is not possible"; + initializationErrorDetected = true; + } + else if (m_Controls.m_ImageActive->isChecked() && m_Controls.m_ImageComboBox->GetSelectedNode().IsNull()) + { + warningMessage = "No image selected for registration.\nRegistration is not possible"; + initializationErrorDetected = true; + } + else if (imageFiducials.IsNull() || trackerFiducials.IsNull()) + { + warningMessage = "Fiducial data objects not found. \n" "Please set 3 or more fiducials in the image and with the tracking system.\n\n" "Registration is not possible"; - MITK_WARN << warningMessage; - QMessageBox::warning(NULL, "Registration not possible", warningMessage.c_str()); - return false; + initializationErrorDetected = true; + } + else if ((imageFiducials->GetSize() < 3) || (trackerFiducials->GetSize() < 3) || (imageFiducials->GetSize() != trackerFiducials->GetSize())) + { + warningMessage = "Not enough fiducial pairs found. At least 3 fiducial must exist for the image and the tracking system respectively."; + initializationErrorDetected = true; } - unsigned int minFiducialCount = 3; // \Todo: move to view option - if ((imageFiducials->GetSize() < minFiducialCount) || (trackerFiducials->GetSize() < minFiducialCount) || (imageFiducials->GetSize() != trackerFiducials->GetSize())) + // finaly: if an err was detected, give a warning and an error popup, then return false + if(initializationErrorDetected) { - QMessageBox::warning(NULL, "Registration not possible", QString("Not enough fiducial pairs found. At least %1 fiducial must " - "exist for the image and the tracking system respectively.\n" - "Currently, %2 fiducials exist for the image, %3 fiducials exist for the tracking system").arg(minFiducialCount).arg(imageFiducials->GetSize()).arg(trackerFiducials->GetSize())); + MITK_WARN << warningMessage; + QMessageBox::warning(NULL, "Registration not possible", warningMessage.c_str()); return false; } + //if no error was detected simply return true + else {return true;} - return true; } -bool QmitkIGTTrackingLabView::IsTransformDifferenceHigh(mitk::NavigationData::Pointer transformA, mitk::NavigationData::Pointer transformB) +bool QmitkIGTTrackingLabView::IsTransformDifferenceHigh(mitk::NavigationData::Pointer transformA, mitk::NavigationData::Pointer transformB, double euclideanDistanceThreshold, double angularDifferenceThreshold) { - double euclideanDistanceThreshold = .8; - double angularDifferenceThreshold = .8; - if(transformA.IsNull() || transformA.IsNull()) {return false;} mitk::Point3D posA,posB; posA = transformA->GetPosition(); posB = transformB->GetPosition(); if(posA.EuclideanDistanceTo(posB) > euclideanDistanceThreshold) {return true;} double returnValue; mitk::Quaternion rotA,rotB; rotA = transformA->GetOrientation(); rotB = transformB->GetOrientation(); itk::Vector point; //caution 5D-Tools: Vector must lie in the YZ-plane for a correct result. point[0] = 0.0; point[1] = 0.0; point[2] = 100000.0; rotA.normalize(); rotB.normalize(); itk::Matrix rotMatrixA; for(int i=0; i<3; i++) for(int j=0; j<3; j++) rotMatrixA[i][j] = rotA.rotation_matrix_transpose().transpose()[i][j]; itk::Matrix rotMatrixB; for(int i=0; i<3; i++) for(int j=0; j<3; j++) rotMatrixB[i][j] = rotB.rotation_matrix_transpose().transpose()[i][j]; itk::Vector pt1 = rotMatrixA * point; itk::Vector pt2 = rotMatrixB * point; returnValue = (pt1[0]*pt2[0]+pt1[1]*pt2[1]+pt1[2]*pt2[2]) / ( sqrt(pow(pt1[0],2.0)+pow(pt1[1],2.0)+pow(pt1[2],2.0)) * sqrt(pow(pt2[0],2.0)+pow(pt2[1],2.0)+pow(pt2[2],2.0))); returnValue = acos(returnValue); if(returnValue*57.3 > angularDifferenceThreshold){return true;} return false; } - -void QmitkIGTTrackingLabView::OnPermanentRegistration(bool on) +double QmitkIGTTrackingLabView::ComputeFRE(mitk::PointSet::Pointer imageFiducials, mitk::PointSet::Pointer realWorldFiducials, vtkSmartPointer transform) { - if(on) - { - - if(!CheckRegistrationInitialization()) + if(imageFiducials->GetSize() != realWorldFiducials->GetSize()) return -1; + double FRE = 0; + for(unsigned int i = 0; i < imageFiducials->GetSize(); i++) { - m_Controls.m_UsePermanentRegistrationToggle->setChecked(false); - return; - } - - //remember initial object transform to calculate the object to marker transform later on - mitk::AffineTransform3D::Pointer transform = this->m_Controls.m_ObjectComboBox->GetSelectedNode()->GetData()->GetGeometry()->GetIndexToWorldTransform(); - - //TODO Exception abfangen? - mitk::NavigationData::Pointer T_Object = mitk::NavigationData::New(transform,false); - - - //then reset the transform because we will now start to calculate the permenent registration - this->m_Controls.m_ObjectComboBox->GetSelectedNode()->GetData()->GetGeometry()->SetIdentity(); - - if(m_Controls.m_ImageActive->isChecked()) - this->m_Controls.m_ImageComboBox->GetSelectedNode()->GetData()->GetGeometry()->SetIndexToWorldTransform(m_T_ImageGeo); - - //create the permanent registration filter - m_PermanentRegistrationFilter = mitk::NavigationDataObjectVisualizationFilter::New(); - //set to rotation mode transposed because we are working with VNL style quaternions - //m_PermanentRegistrationFilter->SetRotationMode(mitk::NavigationDataObjectVisualizationFilter::RotationTransposed); - - //first: surface (always activated) - - //connect filter to source - m_PermanentRegistrationFilter->SetInput(0,this->m_ObjectmarkerNavigationData); - - //set representation object - m_PermanentRegistrationFilter->SetRepresentationObject(0,this->m_Controls.m_ObjectComboBox->GetSelectedNode()->GetData()); - - //get the marker transform out of the navigation data - mitk::NavigationData::Pointer T_Marker = m_ObjectmarkerNavigationData; - - //compute transform from object to marker - mitk::NavigationData::Pointer T_MarkerRel = mitk::NavigationData::New(); - T_MarkerRel->Compose(T_Object); - T_MarkerRel->Compose(T_Marker->GetInverse()); - m_T_MarkerRel = T_MarkerRel; - m_PermanentRegistrationFilter->SetOffset(0,m_T_MarkerRel->GetAffineTransform3D()); - - //first: image (if activated) - //set interpolation mode - if (m_Controls.m_ImageActive->isChecked() && (m_Controls.m_ImageComboBox->GetSelectedNode().IsNotNull())) + itk::Point current_image_fiducial_point = imageFiducials->GetPoint(i); + if (transform != NULL) { - mitk::DataNode::Pointer imageNode = this->m_Controls.m_ImageComboBox->GetSelectedNode(); - imageNode->AddProperty( "reslice interpolation", mitk::VtkResliceInterpolationProperty::New(VTK_RESLICE_LINEAR) ); - m_PermanentRegistrationFilter->SetInput(1,this->m_ObjectmarkerNavigationData); - m_PermanentRegistrationFilter->SetRepresentationObject(1,imageNode->GetData()); - - mitk::AffineTransform3D::Pointer newTransform = mitk::AffineTransform3D::New(); - newTransform->SetIdentity(); - newTransform->Compose(m_T_ImageGeo); - newTransform->Compose(m_T_MarkerRel->GetAffineTransform3D()); - m_PermanentRegistrationFilter->SetOffset(1,newTransform); + current_image_fiducial_point = transform->TransformPoint(imageFiducials->GetPoint(i)[0],imageFiducials->GetPoint(i)[1],imageFiducials->GetPoint(i)[2]); } - - //some general stuff - m_PermanentRegistration = true; - m_ObjectmarkerNavigationDataLastUpdate = mitk::NavigationData::New(); + double cur_error_squared = current_image_fiducial_point.SquaredEuclideanDistanceTo(realWorldFiducials->GetPoint(i)); + FRE += cur_error_squared; } - else - { - //stop permanent registration - m_PermanentRegistration = false; - //restore old registration - if(m_T_ObjectReg.IsNotNull()) {this->m_Controls.m_ObjectComboBox->GetSelectedNode()->GetData()->GetGeometry()->SetIndexToWorldTransform(m_T_ObjectReg->GetAffineTransform3D());} - if(m_T_ImageReg.IsNotNull()) {this->m_Controls.m_ImageComboBox->GetSelectedNode()->GetData()->GetGeometry()->SetIndexToWorldTransform(m_T_ImageReg);} + FRE = sqrt(FRE/ (double) imageFiducials->GetSize()); - //delete filter - m_PermanentRegistrationFilter = NULL; - } + return FRE; } diff --git a/Plugins/org.mitk.gui.qt.igtexamples/src/internal/QmitkIGTTrackingLabView.h b/Plugins/org.mitk.gui.qt.igtexamples/src/internal/QmitkIGTTrackingLabView.h index bed48c7139..d473143914 100644 --- a/Plugins/org.mitk.gui.qt.igtexamples/src/internal/QmitkIGTTrackingLabView.h +++ b/Plugins/org.mitk.gui.qt.igtexamples/src/internal/QmitkIGTTrackingLabView.h @@ -1,207 +1,222 @@ /*=================================================================== 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 QmitkIGTTrackingLabView_h #define QmitkIGTTrackingLabView_h #include #include #include "ui_QmitkIGTTrackingLabViewControls.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include +#include + /*! - \brief QmitkIGTTrackingLabView +\brief QmitkIGTTrackingLabView - \warning This class is not yet documented. Use "git blame" and ask the author to provide basic documentation. +\warning This class is not yet documented. Use "git blame" and ask the author to provide basic documentation. - \sa QmitkFunctionality - \ingroup ${plugin_target}_internal +\sa QmitkFunctionality +\ingroup ${plugin_target}_internal */ class QmitkIGTTrackingLabView : 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: - - static const std::string VIEW_ID; - - /** - \brief default constructor - */ - QmitkIGTTrackingLabView(); - - /** - \brief default destructor - */ - virtual ~QmitkIGTTrackingLabView(); - - virtual void CreateQtPartControl(QWidget *parent); - - virtual void SetFocus(); - - protected slots: - - void UpdateTimer(); - - /** - \brief This method adds a new fiducial to the tracker fiducials PointSet. - */ - void OnAddRegistrationTrackingFiducial(); - /** - \brief This method calls the fiducial registration. - */ - void OnRegisterFiducials(); - /** - \brief This method sets up the navigation pipeline before tracking is started. - */ - void OnSetupNavigation(); - /** - \brief This method reacts on toolbox item changes. - */ - void OnToolBoxCurrentChanged(int index); - /** - \brief This method initializes the registration for the FiducialRegistrationWidget. - */ - void InitializeRegistration(); - /** - \brief This method starts the PointSet recording. - */ - void OnPointSetRecording(bool record); - /** - \brief This method activates the virtual camera. - */ - void OnVirtualCamera(bool on); - /** - \brief This method activates the permanent registration based on one tool's position. - */ - void OnPermanentRegistration(bool on); - - void OnInstrumentSelected(); +// 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 - void OnObjectmarkerSelected(); +public: +static const std::string VIEW_ID; +/** +\brief default constructor +*/ +QmitkIGTTrackingLabView(); - protected: - - - enum ToolBoxElement // enums for the different ToolBox item tabs. - { - NDIConfigurationWidget = 0, - RegistrationWidget = 1, - PermanentRecording = 2, - PointSetRecording = 3, - VirtualCamera = 4 - }; - - - Ui::QmitkIGTTrackingLabViewControls m_Controls; - /** - \brief This method creates all widgets this bundle needs. - */ - void CreateBundleWidgets( QWidget* parent ); - /** - \brief This method creates the SIGNAL SLOT connections. - */ - void CreateConnections(); - /** - \brief This method sets up the filter pipeline. - */ - void SetupIGTPipeline(); - /** - \brief This method destroys the filter pipeline. - */ - void DestroyIGTPipeline(); - +/** +\brief default destructor +*/ +virtual ~QmitkIGTTrackingLabView(); +virtual void CreateQtPartControl(QWidget *parent); - mitk::TrackingDeviceSource::Pointer m_Source; ///< source that connects to the tracking device +virtual void SetFocus(); - mitk::NavigationDataObjectVisualizationFilter::Pointer m_PermanentRegistrationFilter; ///< this filter transforms from tracking coordinates into mitk world coordinates if needed it is interconnected before the FiducialEegistrationFilter - mitk::NavigationDataObjectVisualizationFilter::Pointer m_Visualizer; ///< visualization filter - mitk::CameraVisualization::Pointer m_VirtualView; ///< filter to update the vtk camera according to the reference navigation data +protected slots: +/** This timer updates the IGT pipline, when nessecary: +* 1: if permanent registration is activated, then the permanent +* registration filter has to be updated +* 2: if the camera view is on it also must be updated +* 3: point set recording is based on another filter which needs to be +* updated when activated +*/ +void UpdateTimer(); - bool CheckRegistrationInitialization();///< Checks if everything is initialized for registration. Gives error messages and returns false if not. +//############## Configuration Step ##################### +/** +\brief This method sets up the navigation pipeline during initialization. +*/ +void OnSetupNavigation(); +/** This method is called when the instrument is selected. +* It stores the navigation data of the instrument. +*/ +void OnInstrumentSelected(); -private: +/** This method is called when the object marker is selected. +* It stores the navigation data of the object marker. +*/ +void OnObjectmarkerSelected(); - QTimer* m_Timer; +//############## Initial Registration Step ############## - QToolBox* m_ToolBox; +/** +\brief This method calls the initial fiducial registration. +*/ +void OnInitialRegistration(); - mitk::PointSet::Pointer m_PSRecordingPointSet; +/** +\brief This method adds a new fiducial to the tracker fiducials PointSet. +*/ +void OnAddRegistrationTrackingFiducial(); +/** +\brief This method initializes the registration for the FiducialRegistrationWidget. +*/ +void InitializeRegistration(); - QmitkFiducialRegistrationWidget* m_RegistrationWidget; // landmark registration widget +//############## Permanent Registration Step ############ - std::string m_RegistrationTrackingFiducialsName; - std::string m_RegistrationImageFiducialsName; +/** +\brief This method activates the permanent registration based on one tool's position. +*/ +void OnPermanentRegistration(bool on); - std::string m_PointSetRecordingDataNodeName; - bool m_PointSetRecording; - bool m_PermanentRegistration; - bool m_CameraView; +//############## Pointset Recording Step ################ - mitk::DataNode::Pointer m_ImageFiducialsDataNode; - mitk::DataNode::Pointer m_TrackerFiducialsDataNode; +/** +\brief This method starts the PointSet recording. +*/ +void OnPointSetRecording(bool record); - mitk::PointSet::Pointer m_PermanentRegistrationSourcePoints; +//############## Camera View Step ####################### +/** +\brief This method activates the virtual camera. +*/ +void OnVirtualCamera(bool on); - mitk::NavigationData::Pointer m_InstrumentNavigationData; - mitk::NavigationData::Pointer m_ObjectmarkerNavigationData; - mitk::NavigationData::Pointer m_PointSetRecordingNavigationData; +protected: - mitk::NavigationData::Pointer m_T_MarkerRel; - mitk::NavigationData::Pointer m_T_ObjectReg; - mitk::AffineTransform3D::Pointer m_T_ImageReg; - mitk::AffineTransform3D::Pointer m_T_ImageGeo; - mitk::NavigationData::Pointer m_ObjectmarkerNavigationDataLastUpdate; +Ui::QmitkIGTTrackingLabViewControls m_Controls; +/** +\brief This method creates all widgets this bundle needs. +*/ +void CreateBundleWidgets( QWidget* parent ); +/** +\brief This method creates the SIGNAL SLOT connections. +*/ +void CreateConnections(); +/** +* Checks if everything is initialized for registration. Gives error messages and returns false if not. +*/ +bool CheckRegistrationInitialization(); +/** +\brief This method destroys the filter pipeline. +*/ +void DestroyIGTPipeline(); + +//####################### Members for the IGT pipeline ###################################### +// The IGT pipeline is basically initialized in the method OnSetupNavigation(). Further initialization +// is done in the methods OnPermanentRegistration(), OnPointSetRecording() and OnVirtualCamera(). +// The pipline is updated in the method UpdateTimer(). When the complete pipeline is active, it is +// structured as follows: +// -> m_PermanentRegistrationFilter +// / +// m_Source -> m_Visualizer +// \ +// -> m_VirtualView +mitk::TrackingDeviceSource::Pointer m_Source; ///< source that connects to the tracking device +mitk::NavigationDataObjectVisualizationFilter::Pointer m_PermanentRegistrationFilter; ///< this filter transforms from tracking coordinates into mitk world coordinates if needed it is interconnected before the FiducialEegistrationFilter +mitk::NavigationDataObjectVisualizationFilter::Pointer m_Visualizer; ///< visualization filter +mitk::CameraVisualization::Pointer m_VirtualView; ///< filter to update the vtk camera according to the reference navigation data +//in addition to the pipeline objects, pointers to the navigation data objects are stored for fast access: +mitk::NavigationData::Pointer m_InstrumentNavigationData; ///< navigation data of instrument +mitk::NavigationData::Pointer m_ObjectmarkerNavigationData; ///< navigation data of object marker + +//####################### other members ###################################### +QTimer* m_Timer; ///< central timer which updates the IGT pipeline + +//members for the point set recording +mitk::NavigationData::Pointer m_PointSetRecordingNavigationData; +mitk::PointSet::Pointer m_PSRecordingPointSet; +bool m_PointSetRecording; +bool m_PermanentRegistration; +bool m_CameraView; + +//members for initial registration +mitk::DataNode::Pointer m_ImageFiducialsDataNode; +mitk::DataNode::Pointer m_TrackerFiducialsDataNode; + +//members for permanent registration +mitk::PointSet::Pointer m_PermanentRegistrationSourcePoints; +mitk::NavigationData::Pointer m_T_MarkerRel; +mitk::NavigationData::Pointer m_T_ObjectReg; +mitk::AffineTransform3D::Pointer m_T_ImageReg; +mitk::AffineTransform3D::Pointer m_T_ImageGeo; +mitk::NavigationData::Pointer m_ObjectmarkerNavigationDataLastUpdate; ///< this is the position of the object marker from the last call of update(); it is used to check the difference and decide if the visualization must be updated + +//######################## some internal help methods ############################ +/** @brief Computes the fiducial registration error out of two sets of fiducials. + * The two sets must have the same size and the points must correspond to each other. + * @param transform This transform is applied to the image fiducials before the FRE calculation if it is given. + * @return Returns the FRE. Returns -1 if there was an error. + */ +double ComputeFRE(mitk::PointSet::Pointer imageFiducials, mitk::PointSet::Pointer realWorldFiducials, vtkSmartPointer transform = NULL); +/** +* Checks if the difference between two given transformations is high which means the method returns +* true if the difference exeeds the given position and angular threshold. +*/ +bool IsTransformDifferenceHigh(mitk::NavigationData::Pointer transformA, mitk::NavigationData::Pointer transformB, double euclideanDistanceThreshold = .8, double angularDifferenceThreshold = .8); - bool IsTransformDifferenceHigh(mitk::NavigationData::Pointer transformA, mitk::NavigationData::Pointer transformB); - /** - \brief This method performs GlobalReinit() for the rendering widgets. - */ - //void GlobalReinit(); }; #endif // QmitkIGTTrackingLabView_h