MITK-IGT
IGT Extension of MITK
Loading...
Searching...
No Matches
QmitkUltrasoundCalibration.cpp
Go to the documentation of this file.
1/*============================================================================
2
3The Medical Imaging Interaction Toolkit (MITK)
4
5Copyright (c) German Cancer Research Center (DKFZ)
6All rights reserved.
7
8Use of this source code is governed by a 3-clause BSD license that can be
9found in the LICENSE file.
10
11============================================================================*/
12
13// Blueberry
14#include <berryISelectionService.h>
15#include <berryIWorkbenchWindow.h>
16
17// Qmitk
19#include <QTimer>
20
21// Qt
22#include <QFileDialog>
23#include <QMessageBox>
24#include <QTextStream>
25#include <QmitkServiceListWidget.h>
26
27// MITK
28#include <mitkVector.h>
29#include "mitkIOUtil.h"
30#include <mitkBaseData.h>
31#include <mitkImageGenerator.h>
32#include <mitkNodePredicateDataType.h>
33#include <mitkNodePredicateNot.h>
34#include <mitkNodePredicateProperty.h>
35#include <mitkPointSet.h>
36#include <mitkPointSetDataInteractor.h>
37#include <mitkPointSetShapeProperty.h>
38#include <mitkSceneIO.h>
39
40// us
41#include <usServiceReference.h>
42
43// VTK
44#include <vtkLandmarkTransform.h>
45#include <vtkMatrix4x4.h>
46#include <vtkPlane.h>
47#include <vtkPoints.h>
48#include <vtkSphereSource.h>
49#include <vtkTransform.h>
50
51#include <vtkVertexGlyphFilter.h>
52
54
55// sleep headers
56#include <chrono>
57#include <thread>
58
59const std::string QmitkUltrasoundCalibration::VIEW_ID = "org.mitk.views.ultrasoundcalibration";
60
62 : m_PhantomConfigurationPointSet(nullptr),
63 m_USDeviceChanged(this, &QmitkUltrasoundCalibration::OnUSDepthChanged)
64{
65 ctkPluginContext *pluginContext = mitk::PluginActivator::GetContext();
66
67 if (pluginContext)
68 {
69 // to be notified about service event of an USDevice
70 pluginContext->connectServiceListener(this,
71 "OnDeviceServiceEvent",
72 QString::fromStdString("(" + us::ServiceConstants::OBJECTCLASS() + "=" +
73 us_service_interface_iid<mitk::USDevice>() + ")"));
74 }
75}
76
78{
79 m_Controls.m_CombinedModalityManagerWidget->blockSignals(true);
80 mitk::AbstractUltrasoundTrackerDevice::Pointer combinedModality;
81 combinedModality = m_Controls.m_CombinedModalityManagerWidget->GetSelectedCombinedModality();
82 if (combinedModality.IsNotNull())
83 {
84 combinedModality->GetUltrasoundDevice()->RemovePropertyChangedListener(m_USDeviceChanged);
85 }
86 m_Timer->stop();
87
89
91
92 mitk::DataNode::Pointer node = this->GetDataStorage()->GetNamedNode("Needle Path");
93 if (node.IsNotNull())
94 this->GetDataStorage()->Remove(node);
95
96 this->GetDataStorage()->Remove(m_VerificationReferencePointsDataNode);
97 this->GetDataStorage()->Remove(m_SpacingNode);
98
99 delete m_Timer;
100
101 // remove observer for phantom-based point adding
102 m_CalibPointsImage->RemoveAllObservers();
103}
104
106{
107 m_Controls.m_ToolBox->setFocus();
108}
109
111{
112 // create GUI widgets from the Qt Designer's .ui file
113 m_Controls.setupUi(parent);
114
115 m_Controls.m_CombinedModalityManagerWidget->SetCalibrationLoadedNecessary(false);
116
117 m_Timer = new QTimer(this);
118 m_StreamingTimer = new QTimer(this);
119
120 m_Controls.m_SpacingBtnFreeze->setEnabled(true);
121 m_Controls.m_SpacingAddPoint->setEnabled(false);
122 m_Controls.m_CalculateSpacing->setEnabled(false);
123
125 m_SpacingPoints = mitk::PointSet::New();
126 m_SpacingNode = mitk::DataNode::New();
127 m_SpacingNode->SetName("Spacing Points");
128 m_SpacingNode->SetData(this->m_SpacingPoints);
129 this->GetDataStorage()->Add(m_SpacingNode);
130
131 // Pointset for Calibration Points
132 m_CalibPointsTool = mitk::PointSet::New();
133
134 // Pointset for Worldpoints
135 m_CalibPointsImage = mitk::PointSet::New();
136
138
139 // Evaluation Pointsets (Non-Visualized)
140 m_EvalPointsImage = mitk::PointSet::New();
141 m_EvalPointsTool = mitk::PointSet::New();
142 m_EvalPointsProjected = mitk::PointSet::New();
143
144 // Neelde Projection Filter
145 m_NeedleProjectionFilter = mitk::NeedleProjectionFilter::New();
146
147 // Tracking Status Widgets
148 m_Controls.m_CalibTrackingStatus->ShowStatusLabels();
149 m_Controls.m_EvalTrackingStatus->ShowStatusLabels();
150
151 // General & Device Selection
152 connect(m_Timer, SIGNAL(timeout()), this, SLOT(Update()));
153
154 // Calibration
155 connect(m_Controls.m_CalibBtnFreeze, SIGNAL(clicked()), this, SLOT(SwitchFreeze())); // Freeze
156 connect(m_Controls.m_CalibBtnAddPoint,
157 SIGNAL(clicked()),
158 this,
159 SLOT(OnAddCalibPoint())); // Tracking & Image Points (Calibration)
160 connect(m_Controls.m_CalibBtnCalibrate, SIGNAL(clicked()), this, SLOT(OnCalibration())); // Perform Calibration
161 // Phantom-based calibration
162 connect(m_Controls.m_CalibBtnLoadPhantomConfiguration,
163 SIGNAL(clicked()),
164 this,
165 SLOT(OnLoadPhantomConfiguration())); // Phantom configuration
166 connect(m_Controls.m_CalibBtnMatchAnnotationToPhantomConfiguration,
167 SIGNAL(clicked()),
168 this,
170 connect(m_Controls.m_CalibBtnMoveUp, SIGNAL(clicked()), this, SLOT(OnMovePhantomAnnotationsUp()));
171 connect(m_Controls.m_CalibBtnMoveDown, SIGNAL(clicked()), this, SLOT(OnMovePhantomAnnotationsDown()));
172 connect(m_Controls.m_CalibBtnMoveLeft, SIGNAL(clicked()), this, SLOT(OnMovePhantomAnnotationsLeft()));
173 connect(m_Controls.m_CalibBtnMoveRight, SIGNAL(clicked()), this, SLOT(OnMovePhantomAnnotationsRight()));
174 connect(m_Controls.m_CalibBtnRotateRight, SIGNAL(clicked()), this, SLOT(OnRotatePhantomAnnotationsRight()));
175 connect(m_Controls.m_CalibBtnRotateLeft, SIGNAL(clicked()), this, SLOT(OnRotatePhantomAnnotationsLeft()));
176
177 connect(m_Controls.m_CalibBtnPerformPhantomCalibration,
178 SIGNAL(clicked()),
179 this,
180 SLOT(OnPhantomBasedCalibration())); // Perform phantom-based calibration
181 connect(m_Controls.m_CalibBtnSavePhantomCalibration,
182 SIGNAL(clicked()),
183 this,
184 SLOT(OnSaveCalibration())); // Save phantom-based calibration
185
186 // Evaluation
187 connect(m_Controls.m_EvalBtnStep1, SIGNAL(clicked()), this, SLOT(OnAddEvalProjectedPoint())); // Needle Projection
188 connect(m_Controls.m_EvalBtnStep2, SIGNAL(clicked()), this, SLOT(SwitchFreeze())); // Freeze
189 connect(m_Controls.m_EvalBtnStep3,
190 SIGNAL(clicked()),
191 this,
192 SLOT(OnAddEvalTargetPoint())); // Tracking & Image Points (Evaluation)
193 connect(m_Controls.m_EvalBtnSave, SIGNAL(clicked()), this, SLOT(OnSaveEvaluation())); // Save Evaluation Results
194 connect(m_Controls.m_CalibBtnSaveCalibration,
195 SIGNAL(clicked()),
196 this,
197 SLOT(OnSaveCalibration())); // Save Evaluation Results
198 connect(m_Controls.m_BtnReset, SIGNAL(clicked()), this, SLOT(OnReset())); // Reset Pointsets
199
200 // PLUS Calibration
201 connect(m_Controls.m_GetCalibrationFromPLUS, SIGNAL(clicked()), this, SLOT(OnGetPlusCalibration()));
202 connect(m_Controls.m_StartStreaming, SIGNAL(clicked()), this, SLOT(OnStartStreaming()));
203 connect(m_StreamingTimer, SIGNAL(timeout()), this, SLOT(OnStreamingTimerTimeout()));
204 connect(m_Controls.m_StopPlusCalibration, SIGNAL(clicked()), this, SLOT(OnStopPlusCalibration()));
205 connect(m_Controls.m_SavePlusCalibration, SIGNAL(clicked()), this, SLOT(OnSaveCalibration()));
206 connect(this, SIGNAL(NewConnectionSignal()), this, SLOT(OnNewConnection()));
207
208 // Determine Spacing for Calibration of USVideoDevice
209 connect(m_Controls.m_SpacingBtnFreeze, SIGNAL(clicked()), this, SLOT(OnFreezeClicked()));
210 connect(m_Controls.m_SpacingAddPoint, SIGNAL(clicked()), this, SLOT(OnAddSpacingPoint()));
211 connect(m_Controls.m_CalculateSpacing, SIGNAL(clicked()), this, SLOT(OnCalculateSpacing()));
212
213 connect(m_Controls.m_CombinedModalityManagerWidget, SIGNAL(SignalReadyForNextStep()), this, SLOT(OnDeviceSelected()));
214 connect(m_Controls.m_CombinedModalityManagerWidget,
215 SIGNAL(SignalNoLongerReadyForNextStep()),
216 this,
217 SLOT(OnDeviceDeselected()));
218 connect(m_Controls.m_StartCalibrationButton, SIGNAL(clicked()), this, SLOT(OnStartCalibrationProcess()));
219 connect(m_Controls.m_StartPlusCalibrationButton, SIGNAL(clicked()), this, SLOT(OnStartPlusCalibration()));
220 connect(m_Controls.m_CalibBtnRestartCalibration, SIGNAL(clicked()), this, SLOT(OnReset()));
221 connect(m_Controls.m_CalibBtnStopCalibration, SIGNAL(clicked()), this, SLOT(OnStopCalibrationProcess()));
222
223 connect(m_Controls.m_AddReferencePoints, SIGNAL(clicked()), this, SLOT(OnAddCurrentTipPositionToReferencePoints()));
224 connect(m_Controls.m_AddCurrentPointerTipForVerification,
225 SIGNAL(clicked()),
226 this,
228 connect(m_Controls.m_StartVerification, SIGNAL(clicked()), this, SLOT(OnStartVerification()));
229
230 // initialize data storage combo box
231 m_Controls.m_ReferencePointsComboBox->SetDataStorage(this->GetDataStorage());
232 m_Controls.m_ReferencePointsComboBox->SetAutoSelectNewItems(true);
233 m_Controls.m_ReferencePointsComboBox->SetPredicate(mitk::NodePredicateDataType::New("PointSet"));
234
235 // initialize point list widget
237 {
238 m_VerificationReferencePoints = mitk::PointSet::New();
239 }
241 {
242 m_VerificationReferencePointsDataNode = mitk::DataNode::New();
243 m_VerificationReferencePointsDataNode->SetName("US Verification Reference Points");
245 this->GetDataStorage()->Add(m_VerificationReferencePointsDataNode);
246 }
247 m_Controls.m_ReferencePointsPointListWidget->SetPointSetNode(m_VerificationReferencePointsDataNode);
248
249 m_Controls.m_ToolBox->setCurrentIndex(0);
250}
251
252void QmitkUltrasoundCalibration::OnSelectionChanged(berry::IWorkbenchPart::Pointer /*source*/,
253 const QList<mitk::DataNode::Pointer> & /*nodes*/)
254{
255}
256
258{
259 switch (index)
260 {
261 case 0:
262 if (m_Controls.m_ToolBox->isItemEnabled(1) || m_Controls.m_ToolBox->isItemEnabled(2))
263 {
265 }
266 break;
267 default:;
268 }
269}
270
272{
273 mitk::AbstractUltrasoundTrackerDevice::Pointer combinedModality;
274 combinedModality = m_Controls.m_CombinedModalityManagerWidget->GetSelectedCombinedModality();
275 if (combinedModality.IsNotNull())
276 {
277 combinedModality->GetUltrasoundDevice()->AddPropertyChangedListener(m_USDeviceChanged);
278
279 m_Controls.m_StartCalibrationButton->setEnabled(true);
280 m_Controls.m_StartPlusCalibrationButton->setEnabled(true);
281 m_Controls.m_ToolBox->setItemEnabled(1, true);
282 m_Controls.m_ToolBox->setItemEnabled(2, true);
283 }
284}
285
287{
288 mitk::AbstractUltrasoundTrackerDevice::Pointer combinedModality;
289 combinedModality = m_Controls.m_CombinedModalityManagerWidget->GetSelectedCombinedModality();
290 if (combinedModality.IsNotNull())
291 {
292 combinedModality->GetUltrasoundDevice()->RemovePropertyChangedListener(m_USDeviceChanged);
293 }
294 m_Controls.m_StartCalibrationButton->setEnabled(false);
295 m_Controls.m_StartPlusCalibrationButton->setEnabled(false);
296 m_Controls.m_ToolBox->setCurrentIndex(0);
297 m_Controls.m_ToolBox->setItemEnabled(1, false);
298 m_Controls.m_ToolBox->setItemEnabled(2, false);
299}
300
302{
303 if (m_Controls.m_VerificationPointerChoser->GetSelectedNavigationDataSource().IsNull() ||
304 (m_Controls.m_VerificationPointerChoser->GetSelectedToolID() == -1))
305 {
306 MITK_WARN << "No tool selected, aborting";
307 return;
308 }
309 mitk::NavigationData::Pointer currentPointerData =
310 m_Controls.m_VerificationPointerChoser->GetSelectedNavigationDataSource()->GetOutput(
311 m_Controls.m_VerificationPointerChoser->GetSelectedToolID());
312 mitk::Point3D currentTipPosition = currentPointerData->GetPosition();
313 m_VerificationReferencePoints->InsertPoint(m_VerificationReferencePoints->GetSize(), currentTipPosition);
314}
315
317{
318 m_currentPoint = 0;
319 mitk::PointSet::Pointer selectedPointSet =
320 dynamic_cast<mitk::PointSet *>(m_Controls.m_ReferencePointsComboBox->GetSelectedNode()->GetData());
321 m_Controls.m_CurrentPointLabel->setText("Point " + QString::number(m_currentPoint) + " of " +
322 QString::number(selectedPointSet->GetSize()));
323 m_allErrors = std::vector<double>();
324 m_allReferencePoints = std::vector<mitk::Point3D>();
325 for (int i = 0; i < selectedPointSet->GetSize(); i++)
326 {
327 m_allReferencePoints.push_back(selectedPointSet->GetPoint(i));
328 }
329}
330
332{
333 if (m_currentPoint == -1)
334 {
335 MITK_WARN << "Cannot add point";
336 return;
337 }
338 if (m_Controls.m_VerificationPointerChoser->GetSelectedNavigationDataSource().IsNull() ||
339 (m_Controls.m_VerificationPointerChoser->GetSelectedToolID() == -1))
340 {
341 MITK_WARN << "No tool selected, aborting";
342 return;
343 }
344 mitk::NavigationData::Pointer currentPointerData =
345 m_Controls.m_VerificationPointerChoser->GetSelectedNavigationDataSource()->GetOutput(
346 m_Controls.m_VerificationPointerChoser->GetSelectedToolID());
347 mitk::Point3D currentTipPosition = currentPointerData->GetPosition();
348
349 double currentError = m_allReferencePoints.at(m_currentPoint).EuclideanDistanceTo(currentTipPosition);
350 MITK_INFO << "Current Error: " << currentError << " mm";
351 m_allErrors.push_back(currentError);
352
353 if (++m_currentPoint < static_cast<int>(m_allReferencePoints.size()))
354 {
355 m_Controls.m_CurrentPointLabel->setText("Point " + QString::number(m_currentPoint) + " of " +
356 QString::number(m_allReferencePoints.size()));
357 }
358 else
359 {
360 m_currentPoint = -1;
361 double meanError = 0;
362 for (std::size_t i = 0; i < m_allErrors.size(); ++i)
363 {
364 meanError += m_allErrors[i];
365 }
366 meanError /= m_allErrors.size();
367
368 QString result = "Finished verification! \n Verification of " + QString::number(m_allErrors.size()) +
369 " points, mean error: " + QString::number(meanError) + " mm";
370 m_Controls.m_ResultsTextEdit->setText(result);
371 MITK_INFO << result.toStdString();
372 }
373}
374
376{
377 // US Image Stream
378 m_Node = dynamic_cast<mitk::DataNode*>(this->GetDataStorage()->GetNamedNode("US Viewing Stream - Image 0")->CreateAnother().GetPointer());
379 m_Node->SetName("US Calibration Viewing Stream");
380 this->GetDataStorage()->Add(m_Node);
381
382 // data node for calibration point set
383 m_CalibNode = mitk::DataNode::New();
384 m_CalibNode->SetName("Tool Calibration Points");
385 m_CalibNode->SetData(this->m_CalibPointsTool);
386 this->GetDataStorage()->Add(m_CalibNode);
387
388 // data node for world point set
389 m_WorldNode = mitk::DataNode::New();
390 m_WorldNode->SetName("Image Calibration Points");
391 m_WorldNode->SetData(this->m_CalibPointsImage);
392 this->GetDataStorage()->Add(m_WorldNode);
393
394 m_CombinedModality = m_Controls.m_CombinedModalityManagerWidget->GetSelectedCombinedModality();
395 m_CombinedModality->SetCalibration(mitk::AffineTransform3D::New()); // dummy calibration because without a calibration
396 // the comined modality was laggy (maybe a bug?)
397 if (m_CombinedModality.IsNull())
398 {
399 return;
400 }
401
402 m_Tracker = m_CombinedModality->GetNavigationDataSource();
403
404 // Construct Pipeline
405 this->m_NeedleProjectionFilter->SetInput(0, m_Tracker->GetOutput(0));
406
407 QApplication::setOverrideCursor(Qt::WaitCursor);
408 // make sure that the combined modality is in connected state before using it
409 if (m_CombinedModality->GetUltrasoundDevice()->GetDeviceState() < mitk::USDevice::State_Connected)
410 {
411 m_CombinedModality->GetUltrasoundDevice()->Connect();
412 }
413 if (m_CombinedModality->GetUltrasoundDevice()->GetDeviceState() < mitk::USDevice::State_Activated)
414 {
415 m_CombinedModality->GetUltrasoundDevice()->Activate();
416 }
417 QApplication::restoreOverrideCursor();
418
419 this->SwitchFreeze();
420
421 // Trigger the ProbeChanged method for initializing/updating the spacing of the ultrasound image correctly
422 std::string probeName = m_CombinedModality->GetUltrasoundDevice()->GetCurrentProbe()->GetName();
423 m_CombinedModality->GetUltrasoundDevice()->ProbeChanged(probeName);
424
425 mitk::DataNode::Pointer usNode = this->GetDataStorage()->GetNamedNode("US Viewing Stream - Image 0");
426 if (usNode.IsNotNull())
427 {
428 this->GetDataStorage()->Remove(usNode);
429 }
430
431 // Todo: Maybe display this elsewhere
432 this->ShowNeedlePath();
433
434 // Switch active tab to Calibration page
435 m_Controls.m_ToolBox->setItemEnabled(1, true);
436 m_Controls.m_ToolBox->setCurrentIndex(1);
437}
438
440{
441 if (m_CombinedModality.IsNull())
442 {
443 m_CombinedModality = m_Controls.m_CombinedModalityManagerWidget->GetSelectedCombinedModality();
444 if (m_CombinedModality.IsNull())
445 {
446 return;
447 } // something went wrong, there is no combined modality
448 }
449
450 // setup server to send UltrasoundImages to PLUS
451 mitk::IGTLServer::Pointer m_USServer = mitk::IGTLServer::New(true);
452 m_USServer->SetName("EchoTrack Image Source");
453 m_USServer->SetHostname("127.0.0.1");
454 m_USServer->SetPortNumber(18944);
455
456 m_USMessageProvider = mitk::IGTLMessageProvider::New();
457 m_USMessageProvider->SetIGTLDevice(m_USServer);
458 m_USMessageProvider->SetFPS(5);
459
460 m_USImageToIGTLMessageFilter = mitk::ImageToIGTLMessageFilter::New();
461 m_USImageToIGTLMessageFilter->ConnectTo(m_CombinedModality->GetUltrasoundDevice());
462 m_USImageToIGTLMessageFilter->SetName("USImage Filter");
463
464 // setup server to send TrackingData to PLUS
465 m_TrackingServer = mitk::IGTLServer::New(true);
466 m_TrackingServer->SetName("EchoTrack Tracking Source");
467 m_TrackingServer->SetHostname("127.0.0.1");
468 m_TrackingServer->SetPortNumber(18945);
469
470 m_TrackingMessageProvider = mitk::IGTLMessageProvider::New();
472 m_TrackingMessageProvider->SetFPS(5);
473
474 m_TrackingToIGTLMessageFilter = mitk::NavigationDataToIGTLMessageFilter::New();
475 m_TrackingToIGTLMessageFilter->ConnectTo(m_CombinedModality->GetTrackingDeviceDataSource());
476 m_TrackingToIGTLMessageFilter->SetName("Tracker Filter");
477
478 typedef itk::SimpleMemberCommand<QmitkUltrasoundCalibration> CurCommandType;
479
480 CurCommandType::Pointer newConnectionCommand = CurCommandType::New();
481 newConnectionCommand->SetCallbackFunction(this, &QmitkUltrasoundCalibration::OnPlusConnected);
483 this->m_TrackingServer->AddObserver(mitk::NewClientConnectionEvent(), newConnectionCommand);
484
485 // Open connections of both servers
486 if (m_USServer->OpenConnection())
487 {
488 MITK_INFO << "US Server opened its connection successfully";
489 m_USServer->StartCommunication();
490 }
491 else
492 {
493 MITK_INFO << "US Server could not open its connection";
494 }
495 if (m_TrackingServer->OpenConnection())
496 {
497 MITK_INFO << "Tracking Server opened its connection successfully";
498 m_TrackingServer->StartCommunication();
499 }
500 else
501 {
502 MITK_INFO << "Tracking Server could not open its connection";
503 }
504 if (m_USMessageProvider->IsCommunicating() && m_TrackingMessageProvider->IsCommunicating())
505 {
506 m_Controls.m_StartPlusCalibrationButton->setEnabled(false);
507 m_Controls.m_GetCalibrationFromPLUS->setEnabled(true);
508 m_Controls.m_StartStreaming->setEnabled(false);
509 m_Controls.m_SavePlusCalibration->setEnabled(false);
510 m_Controls.m_SetupStatus->setStyleSheet("QLabel { color : green; }");
511 m_Controls.m_SetupStatus->setText("Setup successful you can now connect PLUS");
512 }
513 else
514 {
515 m_Controls.m_SetupStatus->setStyleSheet("QLabel { color : red; }");
516 m_Controls.m_SetupStatus->setText("Something went wrong. Please try again");
517 }
518}
519
521{
522 // closing all server and clients when PlusCalibration is finished
523 if (m_USMessageProvider.IsNotNull())
524 {
525 if (m_USMessageProvider->IsStreaming())
526 {
528 }
529 }
530 if (m_TrackingMessageProvider.IsNotNull())
531 {
532 if (m_TrackingMessageProvider->IsStreaming())
533 {
535 }
536 }
537 if (m_USServer.IsNotNull())
538 {
539 m_USServer->CloseConnection();
540 }
541 if (m_TrackingServer.IsNotNull())
542 {
543 m_TrackingServer->CloseConnection();
544 }
545 if (m_TransformClient.IsNotNull())
546 {
547 m_TransformClient->CloseConnection();
548 }
549 m_Controls.m_GotCalibrationLabel->setText("");
550 m_Controls.m_ConnectionStatus->setText("");
551 m_Controls.m_SetupStatus->setText("");
552 m_Controls.m_StartPlusCalibrationButton->setEnabled(true);
553 m_StreamingTimer->stop();
554 delete m_StreamingTimer;
555}
556
561
563{
564 m_Controls.m_StartStreaming->setEnabled(true);
565 m_Controls.m_ConnectionStatus->setStyleSheet("QLabel { color : green; }");
566 m_Controls.m_ConnectionStatus->setText("Connection successful you can now start streaming");
567}
568
574
576{
577 m_USMessageProvider->StartStreamingOfSource(m_USImageToIGTLMessageFilter, 5);
579 m_Controls.m_StartStreaming->setEnabled(false);
580 m_Controls.m_ConnectionStatus->setText("");
581 m_StreamingTimer->start((1.0 / 5.0 * 1000.0));
582}
583
585{
586 m_TransformClient = mitk::IGTLClient::New(true);
587 m_TransformClient->SetHostname("127.0.0.1");
588 m_TransformClient->SetPortNumber(18946);
589 m_TransformDeviceSource = mitk::IGTLDeviceSource::New();
591 m_TransformDeviceSource->Connect();
592 if (m_TransformDeviceSource->IsConnected())
593 {
594 MITK_INFO << "successfully connected";
595 m_TransformDeviceSource->StartCommunication();
596 if (m_TransformDeviceSource->IsCommunicating())
597 {
598 MITK_INFO << "communication started";
599 mitk::IGTLMessage::Pointer receivedMessage;
600 bool condition = false;
601 igtl::Matrix4x4 transformPLUS;
602 while (!(receivedMessage.IsNotNull() && receivedMessage->IsDataValid()))
603 {
604 std::this_thread::sleep_for(std::chrono::milliseconds(50));
605 m_TransformDeviceSource->Update();
606 receivedMessage = m_TransformDeviceSource->GetOutput();
607 igtl::TransformMessage::Pointer msg =
608 dynamic_cast<igtl::TransformMessage *>(m_TransformDeviceSource->GetOutput()->GetMessage().GetPointer());
609 if (msg == nullptr || msg.IsNull())
610 {
611 MITK_INFO << "Received message could not be casted to TransformMessage. Skipping...";
612 continue;
613 }
614 else
615 {
616 if (std::strcmp(msg->GetDeviceName(), "ImageToTracker") != 0)
617 {
618 MITK_INFO << "Was not Image to Tracker Transform. Skipping...";
619 continue;
620 }
621 else
622 {
623 msg->GetMatrix(transformPLUS);
624 condition = true;
625 break;
626 }
627 }
628 }
629 if (condition)
630 {
631 this->ProcessPlusCalibration(transformPLUS);
632 }
633 else
634 {
635 m_Controls.m_GotCalibrationLabel->setStyleSheet("QLabel { color : red; }");
636 m_Controls.m_GotCalibrationLabel->setText("Something went wrong. Please try again");
637 }
638 }
639 else
640 {
641 MITK_INFO << " no connection";
642 m_Controls.m_GotCalibrationLabel->setStyleSheet("QLabel { color : red; }");
643 m_Controls.m_GotCalibrationLabel->setText("Something went wrong. Please try again");
644 }
645 }
646 else
647 {
648 m_Controls.m_GotCalibrationLabel->setStyleSheet("QLabel { color : red; }");
649 m_Controls.m_GotCalibrationLabel->setText("Something went wrong. Please try again");
650 }
651}
652
653void QmitkUltrasoundCalibration::ProcessPlusCalibration(igtl::Matrix4x4 &imageToTracker)
654{
655 mitk::AffineTransform3D::Pointer imageToTrackerTransform = mitk::AffineTransform3D::New();
656 itk::Matrix<mitk::ScalarType, 3, 3> rotationFloat = itk::Matrix<mitk::ScalarType, 3, 3>();
657 itk::Vector<mitk::ScalarType, 3> translationFloat = itk::Vector<mitk::ScalarType, 3>();
658
659 rotationFloat[0][0] = imageToTracker[0][0];
660 rotationFloat[0][1] = imageToTracker[0][1];
661 rotationFloat[0][2] = imageToTracker[0][2];
662 rotationFloat[1][0] = imageToTracker[1][0];
663 rotationFloat[1][1] = imageToTracker[1][1];
664 rotationFloat[1][2] = imageToTracker[1][2];
665 rotationFloat[2][0] = imageToTracker[2][0];
666 rotationFloat[2][1] = imageToTracker[2][1];
667 rotationFloat[2][2] = imageToTracker[2][2];
668 translationFloat[0] = imageToTracker[0][3];
669 translationFloat[1] = imageToTracker[1][3];
670 translationFloat[2] = imageToTracker[2][3];
671
672 imageToTrackerTransform->SetTranslation(translationFloat);
673 imageToTrackerTransform->SetMatrix(rotationFloat);
674
675 m_CombinedModality->SetCalibration(imageToTrackerTransform);
676 m_Controls.m_ToolBox->setItemEnabled(2, true);
677 m_Controls.m_SavePlusCalibration->setEnabled(true);
678 m_Controls.m_GotCalibrationLabel->setStyleSheet("QLabel { color : green; }");
679 m_Controls.m_GotCalibrationLabel->setText("Received Calibration from PLUS you can now save it");
680}
681
683{
684 this->ClearTemporaryMembers();
685
686 m_Timer->stop();
687
688 this->GetDataStorage()->Remove(m_Node);
689 m_Node = nullptr;
690
691 this->GetDataStorage()->Remove(m_CalibNode);
692 m_CalibNode = nullptr;
693
694 this->GetDataStorage()->Remove(m_WorldNode);
695 m_WorldNode = nullptr;
696
697 m_Controls.m_ToolBox->setCurrentIndex(0);
698}
699
701{
702 if (m_CombinedModality.IsNull() || event.getType() != ctkServiceEvent::MODIFIED)
703 {
704 return;
705 }
706
707 ctkServiceReference service = event.getServiceReference();
708
709 QString curDepth =
710 service.getProperty(QString::fromStdString(mitk::USDevice::GetPropertyKeys().US_PROPKEY_BMODE_DEPTH)).toString();
711 if (m_CurrentDepth != curDepth)
712 {
713 m_CurrentDepth = curDepth;
714 this->OnReset();
715 }
716}
717
719{
720 auto world = this->GetRenderWindowPart(mitk::WorkbenchUtil::OPEN)->GetSelectedPosition();
721
722 this->m_CalibPointsImage->InsertPoint(m_CalibPointsCount, world);
723 this->m_CalibPointsTool->InsertPoint(m_CalibPointsCount, this->m_FreezePoint);
724
725 QString text = text.number(m_CalibPointsCount + 1);
726 text = "Point " + text;
727 this->m_Controls.m_CalibPointList->addItem(text);
728
730 SwitchFreeze();
731}
732
734{
735 if (m_CombinedModality.IsNull())
736 {
737 return;
738 }
739
740 // Compute transformation
742
743 transform->SetSourceLandmarks(this->ConvertPointSetToVtkPolyData(m_CalibPointsImage)->GetPoints());
744 transform->SetTargetLandmarks(this->ConvertPointSetToVtkPolyData(m_CalibPointsTool)->GetPoints());
745
746 if (!m_CombinedModality->GetIsTrackedUltrasoundActive())
747 {
748 if (m_Controls.m_ScaleTransform->isChecked())
749 {
750 transform->SetModeToSimilarity();
751 } // use affine transform
752 else
753 {
754 transform->SetModeToRigidBody();
755 } // use similarity transform: scaling is not touched
756 MITK_INFO << "TEST";
757 }
758 else
759 {
760 transform->SetModeToRigidBody(); // use similarity transform: scaling is not touched
761 }
762
763 transform->Modified();
764 transform->Update();
765
766 // Convert from vtk to itk data types
767 itk::Matrix<mitk::ScalarType, 3, 3> rotationFloat = itk::Matrix<mitk::ScalarType, 3, 3>();
768 itk::Vector<mitk::ScalarType, 3> translationFloat = itk::Vector<mitk::ScalarType, 3>();
769 vtkSmartPointer<vtkMatrix4x4> m = transform->GetMatrix();
770 rotationFloat[0][0] = m->GetElement(0, 0);
771 rotationFloat[0][1] = m->GetElement(0, 1);
772 rotationFloat[0][2] = m->GetElement(0, 2);
773 rotationFloat[1][0] = m->GetElement(1, 0);
774 rotationFloat[1][1] = m->GetElement(1, 1);
775 rotationFloat[1][2] = m->GetElement(1, 2);
776 rotationFloat[2][0] = m->GetElement(2, 0);
777 rotationFloat[2][1] = m->GetElement(2, 1);
778 rotationFloat[2][2] = m->GetElement(2, 2);
779 translationFloat[0] = m->GetElement(0, 3);
780 translationFloat[1] = m->GetElement(1, 3);
781 translationFloat[2] = m->GetElement(2, 3);
782
783 mitk::PointSet::Pointer ImagePointsTransformed = m_CalibPointsImage->Clone();
784 this->ApplyTransformToPointSet(ImagePointsTransformed, transform);
785 mitk::DataNode::Pointer CalibPointsImageTransformed =
786 this->GetDataStorage()->GetNamedNode("Calibration Points Image (Transformed)");
787 if (CalibPointsImageTransformed.IsNull())
788 {
789 CalibPointsImageTransformed = mitk::DataNode::New();
790 CalibPointsImageTransformed->SetName("Calibration Points Image (Transformed)");
791 this->GetDataStorage()->Add(CalibPointsImageTransformed);
792 }
793 CalibPointsImageTransformed->SetData(ImagePointsTransformed);
794
795 // Set new calibration transform
796 m_Transformation = mitk::AffineTransform3D::New();
797 m_Transformation->SetTranslation(translationFloat);
798 m_Transformation->SetMatrix(rotationFloat);
799 MITK_INFO << "New Calibration transform: " << m_Transformation;
800
801 mitk::SlicedGeometry3D::Pointer sliced3d = dynamic_cast<mitk::SlicedGeometry3D *>(m_Node->GetData()->GetGeometry());
802
803 mitk::PlaneGeometry::Pointer plane = const_cast<mitk::PlaneGeometry *>(sliced3d->GetPlaneGeometry(0));
804
805 plane->SetIndexToWorldTransform(m_Transformation);
806
807 // Save to US-Device
808 m_CombinedModality->SetCalibration(m_Transformation);
809 m_Controls.m_ToolBox->setItemEnabled(2, true);
810
811 // Save to NeedleProjectionFilter
813
814 // Update Calibration FRE
815 m_CalibrationStatistics = mitk::PointSetDifferenceStatisticsCalculator::New();
816 mitk::PointSet::Pointer p1 =
817 this->m_CalibPointsTool->Clone(); // We use clones to calculate statistics to avoid concurrency Problems
818
819 // Create point set with transformed image calibration points for
820 // calculating the difference of image calibration and tool
821 // calibration points in one geometry space
822 mitk::PointSet::Pointer p2 = mitk::PointSet::New();
823 int n = 0;
824 for (mitk::PointSet::PointsConstIterator it = m_CalibPointsImage->Begin(); it != m_CalibPointsImage->End(); ++it, ++n)
825 {
826 p2->InsertPoint(n, m_Transformation->TransformPoint(it->Value()));
827 }
828
829 m_CalibrationStatistics->SetPointSets(p1, p2);
830 // QString text = text.number(m_CalibrationStatistics->GetRMS());
831 QString text = QString::number(ComputeFRE(m_CalibPointsImage, m_CalibPointsTool, transform));
832 MITK_INFO << "Calibration FRE: " << text.toStdString().c_str();
833 m_Controls.m_EvalLblCalibrationFRE->setText(text);
834
835 m_Node->SetStringProperty("Calibration FRE", text.toStdString().c_str());
836 // Enable Button to save Calibration
837 m_Controls.m_CalibBtnSaveCalibration->setEnabled(true);
838}
839
841{
842 // clear all data
844 // reset UI
845 m_Controls.m_CalibBtnMatchAnnotationToPhantomConfiguration->setEnabled(false);
846 m_Controls.m_RefinePhantomAnnotationsGroupBox->setEnabled(false);
847 m_Controls.m_CalibBtnPerformPhantomCalibration->setEnabled(false);
848 m_Controls.m_CalibBtnSavePhantomCalibration->setEnabled(false);
849
850 // open phantom configuration
851 QString fileName = QFileDialog::getOpenFileName(nullptr, "Load phantom configuration", "", "*.mps");
852
853 // dialog closed or selection canceled
854 if (fileName.isNull())
855 {
856 return;
857 }
858
859 m_PhantomConfigurationPointSet = dynamic_cast<mitk::PointSet *>(mitk::IOUtil::Load(fileName.toStdString()).at(0).GetPointer());
860
861 // transform phantom fiducials to tracking space
862 mitk::NavigationData::Pointer currentSensorData = this->m_Tracker->GetOutput(0)->Clone();
863
864 for (int i = 0; i < m_PhantomConfigurationPointSet->GetSize(); i++)
865 {
866 mitk::Point3D phantomPoint = m_PhantomConfigurationPointSet->GetPoint(i);
867 mitk::Point3D transformedPoint = currentSensorData->TransformPoint(phantomPoint);
868 this->m_CalibPointsTool->InsertPoint(i, transformedPoint);
869 }
870
871 // add point set interactor for image calibration points
872 mitk::PointSetDataInteractor::Pointer imageCalibrationPointSetInteractor = mitk::PointSetDataInteractor::New();
873 imageCalibrationPointSetInteractor->LoadStateMachine("PointSet.xml");
874 imageCalibrationPointSetInteractor->SetEventConfig("PointSetConfig.xml");
875 imageCalibrationPointSetInteractor->SetDataNode(m_WorldNode);
876 imageCalibrationPointSetInteractor->SetMaxPoints(m_PhantomConfigurationPointSet->GetSize());
877 // Call On AddCalibPointPhantomBased() when point was added
878 itk::SimpleMemberCommand<QmitkUltrasoundCalibration>::Pointer pointAddedCommand =
879 itk::SimpleMemberCommand<QmitkUltrasoundCalibration>::New();
880 pointAddedCommand->SetCallbackFunction(this, &QmitkUltrasoundCalibration::OnPhantomCalibPointsChanged);
881 m_CalibPointsImage->AddObserver(mitk::PointSetAddEvent(), pointAddedCommand);
882 m_CalibPointsImage->AddObserver(mitk::PointSetMoveEvent(), pointAddedCommand);
883 // Set size of image points points
884 m_WorldNode->ReplaceProperty("point 2D size", mitk::FloatProperty::New(10.0));
885}
886
888{
889 int currentIndex = m_CalibPointsImage->SearchSelectedPoint();
890 mitk::Point3D currentImagePoint = m_CalibPointsImage->GetPoint(currentIndex);
892 // create sphere to show radius in which next point has to be placed
893 this->GetDataStorage()->Remove(this->GetDataStorage()->GetNamedNode("NextPointIndicator"));
894 if (currentIndex < m_CalibPointsTool->GetSize() - 1)
895 {
896 float distanceToNextPoint =
897 m_CalibPointsTool->GetPoint(currentIndex).EuclideanDistanceTo(m_CalibPointsTool->GetPoint(currentIndex + 1));
899 vtkHelperSphere->SetCenter(currentImagePoint[0], currentImagePoint[1], currentImagePoint[2]);
900 vtkHelperSphere->SetRadius(distanceToNextPoint);
901 vtkHelperSphere->SetPhiResolution(40);
902 vtkHelperSphere->SetThetaResolution(40);
903 vtkHelperSphere->Update();
904 mitk::Surface::Pointer helperSphere = mitk::Surface::New();
905 helperSphere->SetVtkPolyData(vtkHelperSphere->GetOutput());
906 mitk::DataNode::Pointer helperSphereNode = mitk::DataNode::New();
907 helperSphereNode->SetName("NextPointIndicator");
908 helperSphereNode->SetData(helperSphere);
909 helperSphereNode->SetColor(0.0, 1.0, 0.0);
910 this->GetDataStorage()->Add(helperSphereNode);
911 }
912 if (m_CalibPointsTool->GetSize() == m_CalibPointsImage->GetSize())
913 {
914 m_Controls.m_CalibBtnMatchAnnotationToPhantomConfiguration->setEnabled(true);
915 }
916}
917
919{
920 mitk::Point3D currentImagePoint = m_CalibPointsImage->GetPoint(index);
921 // create sphere to show current fiducial
922 std::stringstream pointName;
923 pointName << "Point";
924 pointName << index;
925 this->GetDataStorage()->Remove(this->GetDataStorage()->GetNamedNode(pointName.str()));
927 vtkPointSphere->SetCenter(currentImagePoint[0], currentImagePoint[1], currentImagePoint[2]);
928 vtkPointSphere->SetRadius(5.0);
929 vtkPointSphere->SetPhiResolution(40);
930 vtkPointSphere->SetThetaResolution(40);
931 vtkPointSphere->Update();
932 mitk::Surface::Pointer pointSphere = mitk::Surface::New();
933 pointSphere->SetVtkPolyData(vtkPointSphere->GetOutput());
934 mitk::DataNode::Pointer sphereNode = mitk::DataNode::New();
935 sphereNode->SetName(pointName.str());
936 sphereNode->SetData(pointSphere);
937 sphereNode->SetColor(1.0, 1.0, 0.0);
938 this->GetDataStorage()->Add(sphereNode);
939}
940
942{
943 // Transform pointset of phantom configuration to currently annotated image points
945 transform->SetModeToRigidBody();
947 transform->SetSourceLandmarks(toolLandmarks);
948 transform->SetTargetLandmarks(this->ConvertPointSetToVtkPolyData(m_CalibPointsImage)->GetPoints());
949 transform->Update();
950 // update image annotation with matched phantom configuration
952 transform->TransformPoints(toolLandmarks, transformedToolLandmarks);
953 for (int i = 0; i < transformedToolLandmarks->GetNumberOfPoints(); i++)
954 {
955 m_CalibPointsImage->InsertPoint(i, transformedToolLandmarks->GetPoint(i));
957 }
958 m_Controls.m_RefinePhantomAnnotationsGroupBox->setEnabled(true);
959 m_Controls.m_CalibBtnPerformPhantomCalibration->setEnabled(true);
960}
961
963{
965 transform->Translate(tx, ty, tz);
968 transform->TransformPoints(currentPoints, transformedPoints);
969 for (int i = 0; i < transformedPoints->GetNumberOfPoints(); i++)
970 {
971 m_CalibPointsImage->InsertPoint(i, transformedPoints->GetPoint(i));
973 }
974}
975
977{
979 transform->RotateZ(angle);
982 transform->TransformPoints(currentPoints, transformedPoints);
983 for (int i = 0; i < transformedPoints->GetNumberOfPoints(); i++)
984 {
985 m_CalibPointsImage->InsertPoint(i, transformedPoints->GetPoint(i));
987 }
988}
989
991{
992 this->TranslatePhantomAnnotations(0, -m_Image->GetGeometry()->GetSpacing()[1], 0);
993}
994
996{
997 this->TranslatePhantomAnnotations(0, m_Image->GetGeometry()->GetSpacing()[1], 0);
998}
999
1001{
1002 this->TranslatePhantomAnnotations(-m_Image->GetGeometry()->GetSpacing()[0], 0, 0);
1003}
1004
1006{
1007 this->TranslatePhantomAnnotations(m_Image->GetGeometry()->GetSpacing()[0], 0, 0);
1008}
1009
1011{
1012 mitk::BoundingBox::PointType centerOfPointSet = m_CalibPointsImage->GetGeometry()->GetBoundingBox()->GetCenter();
1013 this->TranslatePhantomAnnotations(-centerOfPointSet[0], -centerOfPointSet[1], -centerOfPointSet[2]);
1014 this->RotatePhantomAnnotations(0.5);
1015 this->TranslatePhantomAnnotations(centerOfPointSet[0], centerOfPointSet[1], centerOfPointSet[2]);
1016}
1017
1019{
1020 mitk::BoundingBox::PointType centerOfPointSet = m_CalibPointsImage->GetGeometry()->GetBoundingBox()->GetCenter();
1021 this->TranslatePhantomAnnotations(-centerOfPointSet[0], -centerOfPointSet[1], -centerOfPointSet[2]);
1022 this->RotatePhantomAnnotations(-0.5);
1023 this->TranslatePhantomAnnotations(centerOfPointSet[0], centerOfPointSet[1], centerOfPointSet[2]);
1024}
1025
1027{
1028 // perform calibration
1029 OnCalibration();
1030 m_Controls.m_CalibBtnSavePhantomCalibration->setEnabled(true);
1031}
1032
1034{
1035 auto world = this->GetRenderWindowPart(mitk::WorkbenchUtil::OPEN)->GetSelectedPosition();
1036
1037 this->m_EvalPointsImage->InsertPoint(m_EvalPointsImage->GetSize(), world);
1038 this->m_EvalPointsTool->InsertPoint(m_EvalPointsTool->GetSize(), this->m_FreezePoint);
1039
1040 QString text = text.number(this->m_EvalPointsTool->GetSize());
1041 this->m_Controls.m_EvalLblNumTargetPoints->setText(text);
1042
1043 // Update FREs
1044 // Update Evaluation FRE, but only if it contains more than one point (will crash otherwise)
1045 if ((m_EvalPointsProjected->GetSize() > 1) && (m_EvalPointsTool->GetSize() > 1))
1046 {
1047 m_EvaluationStatistics = mitk::PointSetDifferenceStatisticsCalculator::New();
1048 m_ProjectionStatistics = mitk::PointSetDifferenceStatisticsCalculator::New();
1049 mitk::PointSet::Pointer p1 =
1050 this->m_EvalPointsTool->Clone(); // We use clones to calculate statistics to avoid concurrency Problems
1051 mitk::PointSet::Pointer p2 = this->m_EvalPointsImage->Clone();
1052 mitk::PointSet::Pointer p3 = this->m_EvalPointsProjected->Clone();
1053 m_EvaluationStatistics->SetPointSets(p1, p2);
1054 m_ProjectionStatistics->SetPointSets(p1, p3);
1055 QString evalText = evalText.number(m_EvaluationStatistics->GetRMS());
1056 QString projText = projText.number(m_ProjectionStatistics->GetRMS());
1057 m_Controls.m_EvalLblEvaluationFRE->setText(evalText);
1058 m_Controls.m_EvalLblProjectionFRE->setText(projText);
1059 }
1060 SwitchFreeze();
1061}
1062
1064{
1065 MITK_WARN << "Projection Evaluation may currently be inaccurate.";
1066 // TODO: Verify correct Evaluation. Is the Point that is added really current?
1067 mitk::Point3D projection = this->m_NeedleProjectionFilter->GetProjection()->GetPoint(1);
1068 m_EvalPointsProjected->InsertPoint(m_EvalPointsProjected->GetSize(), projection);
1069 QString text = text.number(this->m_EvalPointsProjected->GetSize());
1070 this->m_Controls.m_EvalLblNumProjectionPoints->setText(text);
1071}
1072
1074{
1075 // Filename without suffix
1076 QString filename = m_Controls.m_EvalFilePath->text() + "//" + m_Controls.m_EvalFilePrefix->text();
1077
1078 MITK_WARN << "CANNOT SAVE, ABORTING!";
1079 /* not working any more TODO!
1080 mitk::PointSetWriter::Pointer psWriter = mitk::PointSetWriter::New();
1081 psWriter->SetInput(0, m_CalibPointsImage);
1082 psWriter->SetInput(1, m_CalibPointsTool);
1083 psWriter->SetInput(2, m_EvalPointsImage);
1084 psWriter->SetInput(3, m_EvalPointsTool);
1085 psWriter->SetInput(4, m_EvalPointsProjected);
1086 psWriter->SetFileName(filename.toStdString() + ".xml");
1087 psWriter->Write();
1088 */
1089
1090 // TODO: New writer for transformations must be implemented.
1091 /*
1092 mitk::TransformationFileWriter::Pointer tWriter = mitk::TransformationFileWriter::New();
1093 tWriter->SetInput(0, m_CalibPointsImage);
1094 tWriter->SetInput(1, m_CalibPointsTool);
1095 tWriter->SetInput(2, m_EvalPointsImage);
1096 tWriter->SetInput(3, m_EvalPointsTool);
1097 tWriter->SetInput(4, m_EvalPointsProjected);
1098 tWriter->SetOutputFilename(filename.toStdString() + ".txt");
1099 tWriter->DoWrite(this->m_Transformation);
1100 */
1101}
1102
1104{
1105 m_Controls.m_GotCalibrationLabel->setText("");
1106 QString filename =
1107 QFileDialog::getSaveFileName(QApplication::activeWindow(), "Save Calibration", "", "Calibration files *.cal");
1108
1109 QFile file(filename);
1110 if (!file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate))
1111 {
1112 MITK_WARN << "Cannot open file '" << filename.toStdString() << "' for writing.";
1113 return;
1114 }
1115
1116 std::string calibrationSerialization = m_CombinedModality->SerializeCalibration();
1117
1118 QTextStream outStream(&file);
1119 outStream << QString::fromStdString(calibrationSerialization);
1120
1121 // save additional information
1122 if (m_Controls.m_saveAdditionalCalibrationLog->isChecked())
1123 {
1124 mitk::SceneIO::Pointer mySceneIO = mitk::SceneIO::New();
1125 QString filenameScene = filename + "_mitkScene.mitk";
1126 mitk::NodePredicateNot::Pointer isNotHelperObject =
1127 mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object", mitk::BoolProperty::New(true)));
1128 mitk::DataStorage::SetOfObjects::ConstPointer nodesToBeSaved = this->GetDataStorage()->GetSubset(isNotHelperObject);
1129 mySceneIO->SaveScene(nodesToBeSaved, this->GetDataStorage(), filenameScene.toStdString().c_str());
1130 }
1131}
1132
1134{
1135 this->ClearTemporaryMembers();
1136
1137 if (m_Transformation.IsNull())
1138 {
1139 m_Transformation = mitk::AffineTransform3D::New();
1140 }
1141 m_Transformation->SetIdentity();
1142
1143 if (m_Node.IsNotNull() && (m_Node->GetData() != nullptr) && (m_Node->GetData()->GetGeometry() != nullptr))
1144 {
1145 mitk::SlicedGeometry3D::Pointer sliced3d = dynamic_cast<mitk::SlicedGeometry3D *>(m_Node->GetData()->GetGeometry());
1146 mitk::PlaneGeometry::Pointer plane = const_cast<mitk::PlaneGeometry *>(sliced3d->GetPlaneGeometry(0));
1147 plane->SetIndexToWorldTransform(m_Transformation);
1148 }
1149
1150 QString text1 = text1.number(this->m_EvalPointsTool->GetSize());
1151 this->m_Controls.m_EvalLblNumTargetPoints->setText(text1);
1152 QString text2 = text2.number(this->m_EvalPointsProjected->GetSize());
1153 this->m_Controls.m_EvalLblNumProjectionPoints->setText(text2);
1154}
1155
1157{
1158 // Update Tracking Data
1159 std::vector<mitk::NavigationData::Pointer> *datas = new std::vector<mitk::NavigationData::Pointer>();
1160 datas->push_back(m_Tracker->GetOutput());
1161 m_Controls.m_CalibTrackingStatus->SetNavigationDatas(datas);
1162 m_Controls.m_CalibTrackingStatus->Refresh();
1163 m_Controls.m_EvalTrackingStatus->SetNavigationDatas(datas);
1164 m_Controls.m_EvalTrackingStatus->Refresh();
1165
1166 m_CombinedModality->Modified();
1167 m_CombinedModality->Update();
1168
1169 // Update US Image
1170 mitk::Image::Pointer image = m_CombinedModality->GetOutput();
1171 // make sure that always the current image is set to the data node
1172 if (image.IsNotNull() && m_Node->GetData() != image.GetPointer() && image->IsInitialized())
1173 {
1174 m_Node->SetData(image);
1175 }
1176
1177 // Update Needle Projection
1178 m_NeedleProjectionFilter->Update();
1179
1180 // only update 2d window because it is faster
1181 this->RequestRenderWindowUpdate(mitk::RenderingManager::REQUEST_UPDATE_2DWINDOWS);
1182}
1183
1185{
1186 m_Controls.m_CalibBtnAddPoint->setEnabled(false); // generally deactivate
1187 // We use the activity state of the timer to determine whether we are currently viewing images
1188 if (!m_Timer->isActive()) // Activate Imaging
1189 {
1190 // if (m_Node) m_Node->ReleaseData();
1191 if (m_CombinedModality.IsNull())
1192 {
1193 m_Timer->stop();
1194 return;
1195 }
1196
1197 m_CombinedModality->Update();
1198 m_Image = m_CombinedModality->GetOutput();
1199 if (m_Image.IsNotNull() && m_Image->IsInitialized())
1200 {
1201 m_Node->SetData(m_Image);
1202 }
1203
1204 std::vector<mitk::NavigationData::Pointer> datas;
1205 datas.push_back(m_Tracker->GetOutput());
1206 m_Controls.m_CalibTrackingStatus->SetNavigationDatas(&datas);
1207 m_Controls.m_CalibTrackingStatus->ShowStatusLabels();
1208 m_Controls.m_CalibTrackingStatus->Refresh();
1209
1210 m_Controls.m_EvalTrackingStatus->SetNavigationDatas(&datas);
1211 m_Controls.m_EvalTrackingStatus->ShowStatusLabels();
1212 m_Controls.m_EvalTrackingStatus->Refresh();
1213
1214 int interval = 40;
1215 m_Timer->setInterval(interval);
1216 m_Timer->start();
1217
1218 m_CombinedModality->SetIsFreezed(false);
1219 }
1220 else if (this->m_Tracker->GetOutput(0)->IsDataValid())
1221 {
1222 // deactivate Imaging
1223 m_Timer->stop();
1224 // Remember last tracking coordinates
1225 m_FreezePoint = this->m_Tracker->GetOutput(0)->GetPosition();
1226 m_Controls.m_CalibBtnAddPoint->setEnabled(true); // activate only, if valid point is set
1227
1228 m_CombinedModality->SetIsFreezed(true);
1229 }
1230}
1231
1233{
1234 // Init Filter
1235 this->m_NeedleProjectionFilter->SelectInput(0);
1236
1237 // Create Node for Pointset
1238 mitk::DataNode::Pointer node = this->GetDataStorage()->GetNamedNode("Needle Path");
1239 if (node.IsNull())
1240 {
1241 node = mitk::DataNode::New();
1242 node->SetName("Needle Path");
1243 node->SetData(m_NeedleProjectionFilter->GetProjection());
1244 node->SetBoolProperty("show contour", true);
1245 this->GetDataStorage()->Add(node);
1246 }
1247}
1248
1250{
1251 m_CalibPointsTool->Clear();
1252 m_CalibPointsImage->Clear();
1254
1255 m_EvalPointsImage->Clear();
1256 m_EvalPointsTool->Clear();
1257 m_EvalPointsProjected->Clear();
1258
1259 this->m_Controls.m_CalibPointList->clear();
1260
1261 m_SpacingPoints->Clear();
1262 m_Controls.m_SpacingPointsList->clear();
1264}
1265
1267{
1270 for (int i = 0; i < PointSet->GetSize(); i++)
1271 {
1272 double point[3] = {PointSet->GetPoint(i)[0], PointSet->GetPoint(i)[1], PointSet->GetPoint(i)[2]};
1273 points->InsertNextPoint(point);
1274 }
1276 temp->SetPoints(points);
1277
1279 vertexFilter->SetInputData(temp);
1280 vertexFilter->Update();
1281
1282 returnValue->ShallowCopy(vertexFilter->GetOutput());
1283
1284 return returnValue;
1285}
1286
1287double QmitkUltrasoundCalibration::ComputeFRE(mitk::PointSet::Pointer imageFiducials,
1288 mitk::PointSet::Pointer realWorldFiducials,
1290{
1291 if (imageFiducials->GetSize() != realWorldFiducials->GetSize())
1292 return -1;
1293 double FRE = 0;
1294 for (int i = 0; i < imageFiducials->GetSize(); ++i)
1295 {
1296 itk::Point<double> current_image_fiducial_point = imageFiducials->GetPoint(i);
1297 if (transform != nullptr)
1298 {
1299 current_image_fiducial_point = transform->TransformPoint(
1300 imageFiducials->GetPoint(i)[0], imageFiducials->GetPoint(i)[1], imageFiducials->GetPoint(i)[2]);
1301 }
1302 double cur_error_squared = current_image_fiducial_point.SquaredEuclideanDistanceTo(realWorldFiducials->GetPoint(i));
1303 FRE += cur_error_squared;
1304 }
1305
1306 FRE = sqrt(FRE / (double)imageFiducials->GetSize());
1307
1308 return FRE;
1309}
1310
1311void QmitkUltrasoundCalibration::ApplyTransformToPointSet(mitk::PointSet::Pointer pointSet,
1313{
1314 for (int i = 0; i < pointSet->GetSize(); ++i)
1315 {
1316 itk::Point<double> current_point_transformed = itk::Point<double>();
1317 current_point_transformed =
1318 transform->TransformPoint(pointSet->GetPoint(i)[0], pointSet->GetPoint(i)[1], pointSet->GetPoint(i)[2]);
1319 pointSet->SetPoint(i, current_point_transformed);
1320 }
1321}
1322
1324{
1325 if (m_CombinedModality.IsNull())
1326 {
1327 return;
1328 }
1329
1330 if (m_CombinedModality->GetIsFreezed())
1331 {
1332 // device was already frozen so we need to delete all spacing points because they need to be collected all at once
1333 // no need to check if all four points are already collected, because if that's the case you can no longer click the
1334 // Freeze button
1335 m_SpacingPoints->Clear();
1336 m_Controls.m_SpacingPointsList->clear();
1338 m_Controls.m_SpacingAddPoint->setEnabled(false);
1339 }
1340 else
1341 {
1342 m_Controls.m_SpacingAddPoint->setEnabled(true);
1343 }
1344 SwitchFreeze();
1345}
1346
1348{
1349 auto point = this->GetRenderWindowPart(mitk::WorkbenchUtil::OPEN)->GetSelectedPosition();
1350
1351 this->m_SpacingPoints->InsertPoint(m_SpacingPointsCount, point);
1352
1353 QString text = text.number(m_SpacingPointsCount + 1);
1354 text = "Point " + text;
1355
1356 this->m_Controls.m_SpacingPointsList->addItem(text);
1357
1359
1360 if (m_SpacingPointsCount == 4) // now we have all 4 points needed
1361 {
1362 m_Controls.m_SpacingAddPoint->setEnabled(false);
1363 m_Controls.m_CalculateSpacing->setEnabled(true);
1364 m_Controls.m_SpacingBtnFreeze->setEnabled(false);
1365 }
1366}
1367
1369{
1370 mitk::Point3D horizontalOne = m_SpacingPoints->GetPoint(0);
1371 mitk::Point3D horizontalTwo = m_SpacingPoints->GetPoint(1);
1372 mitk::Point3D verticalOne = m_SpacingPoints->GetPoint(2);
1373 mitk::Point3D verticalTwo = m_SpacingPoints->GetPoint(3);
1374
1375 // Get the distances between the points in the image
1376 double xDistance = horizontalOne.EuclideanDistanceTo(horizontalTwo);
1377 double yDistance = verticalOne.EuclideanDistanceTo(verticalTwo);
1378
1379 // Calculate the spacing of the image and fill a vector with it
1380 double xSpacing = 30 / xDistance;
1381 double ySpacing = 20 / yDistance;
1382
1383 m_CombinedModality->GetUltrasoundDevice()->SetSpacing(xSpacing, ySpacing);
1384
1385 // Now that the spacing is set clear all stuff and return to Calibration
1386 m_SpacingPoints->Clear();
1387 m_Controls.m_SpacingPointsList->clear();
1389 m_CombinedModality->SetIsFreezed(false);
1390}
1391
1392void QmitkUltrasoundCalibration::OnUSDepthChanged(const std::string &key, const std::string &)
1393{
1394 // whenever depth of USImage is changed the spacing should no longer be overwritten
1395 if (key == mitk::USDevice::GetPropertyKeys().US_PROPKEY_BMODE_DEPTH)
1396 {
1397 }
1398}
QmitkUltrasoundCalibration.
void CreateQtPartControl(QWidget *parent) override
mitk::PointSet::Pointer m_CalibPointsImage
Pointset containing all tool points.
mitk::PointSetDifferenceStatisticsCalculator::Pointer m_CalibrationStatistics
StatisticsRegarding Calibration Accuracy. (Compares m_CalibPointsTool to a transformed copy of m_Cali...
mitk::NavigationDataSource::Pointer m_Tracker
NavigationDataSource used for tracking data. This will be gotten by the combined modality.
mitk::PointSetDifferenceStatisticsCalculator::Pointer m_EvaluationStatistics
StatisticsRegarding Evaluation Accuracy. (Compares m_EvalPointsTool to m_EvalPointsImage)
mitk::PointSet::Pointer m_EvalPointsTool
Pointset containing tracked evaluation points.
void OnStartPlusCalibration()
Method to use the PLUS-Toolkoit for Calibration of EchoTrack.
mitk::Image::Pointer m_Image
The current Ultrasound Image.
mitk::PointSet::Pointer m_SpacingPoints
void OnReset()
Triggered when the user clicks "Run Next Round". Also used as a reset mechanism.
void OnSaveEvaluation()
Triggered when the user clicks "Save Results" in the Evaluation tab.
void SwitchFreeze()
Freezes or unfreezes the image.
mitk::AffineTransform3D::Pointer m_Transformation
Result of the Calibration.
Ui::QmitkUltrasoundCalibrationControls m_Controls
void OnUSDepthChanged(const std::string &, const std::string &)
void NewConnectionSignal()
used for thread separation, the worker thread must not call OnNewConnection directly....
mitk::DataNode::Pointer m_VerificationReferencePointsDataNode
void OnDeviceServiceEvent(const ctkServiceEvent event)
void TranslatePhantomAnnotations(double tx, double ty, double tz)
translate the annotated image feature m_CalibPoints image by the specified translation vector
void ApplyTransformToPointSet(mitk::PointSet::Pointer pointSet, vtkSmartPointer< vtkAbstractTransform > transform)
mitk::PointSet::Pointer m_EvalPointsProjected
Pointset containing Projected Points (aka "where we thought the needle was gonna land")
std::vector< mitk::Point3D > m_allReferencePoints
double ComputeFRE(mitk::PointSet::Pointer imageFiducials, mitk::PointSet::Pointer realWorldFiducials, vtkSmartPointer< vtkLandmarkTransform > transform=nullptr)
void RotatePhantomAnnotations(double angle)
rotate the annotated image feature m_CalibPoints image by the specified angle
mitk::IGTLServer::Pointer m_USServer
void OnAddCalibPoint()
Triggered, when the user clicks "Add Point".
mitk::PointSet::Pointer m_PhantomConfigurationPointSet
pointset holding the feature position of the phantom in tool coordinates
mitk::PointSet::Pointer m_CalibPointsTool
Pointset containing corresponding points on the image.
void OnAddEvalProjectedPoint()
Triggered, when the user clicks "Add Point".
mitk::IGTLMessageProvider::Pointer m_TrackingMessageProvider
mitk::IGTLMessageProvider::Pointer m_USMessageProvider
mitk::AbstractUltrasoundTrackerDevice::Pointer m_CombinedModality
The combined modality used for imaging and tracking.
void OnTabSwitch(int index)
Triggered, whenever the user switches Tabs.
void OnCalibration()
Triggered, when the user clicks "Calibrate".
void ClearTemporaryMembers()
Clears all member attributes which are holding intermediate results for the calibration.
vtkSmartPointer< vtkPolyData > ConvertPointSetToVtkPolyData(mitk::PointSet::Pointer PointSet)
mitk::IGTLClient::Pointer m_TransformClient
mitk::ImageToIGTLMessageFilter::Pointer m_USImageToIGTLMessageFilter
int m_CalibPointsCount
Total number of calibration points set.
void OnSelectionChanged(berry::IWorkbenchPart::Pointer source, const QList< mitk::DataNode::Pointer > &nodes) override
mitk::IGTLDeviceSource::Pointer m_TransformDeviceSource
mitk::NeedleProjectionFilter::Pointer m_NeedleProjectionFilter
Creates a Pointset that projects the needle's path.
mitk::PointSet::Pointer m_VerificationReferencePoints
Pointset containing tracked evaluation points.
void ProcessPlusCalibration(igtl::Matrix4x4 &imageToTracker)
void OnDeviceSelected()
Triggered, when the user has clicked "select Devices".
void OnAddEvalTargetPoint()
Triggered, when the user clicks "Add Target Points".
mitk::PointSetDifferenceStatisticsCalculator::Pointer m_ProjectionStatistics
StatisticsRegarding Projection Accuracy. (Compares m_EvalPointsProjected to m_EvalPointsImage)
void Update()
Triggered in regular intervals by a timer, when live view is enabled.
void OnSaveCalibration()
Triggered when the user clicks "Save Calibration" in the Calibration tab.
void ShowNeedlePath()
Internal function that activates display of the needle path.
mitk::Point3D m_FreezePoint
Current point when the image was last frozen.
mitk::PointSet::Pointer m_EvalPointsImage
Pointset containing the evaluated points on the image.
mitk::NavigationDataToIGTLMessageFilter::Pointer m_TrackingToIGTLMessageFilter
void UpdatePhantomAnnotationPointVisualization(int index=-1)
mitk::IGTLServer::Pointer m_TrackingServer
static mitk::USDevice::PropertyKeys GetPropertyKeys()