MITK-IGT
IGT Extension of MITK
Loading...
Searching...
No Matches
QmitkUSNavigationStepMarkerIntervention.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
14#include "ui_QmitkUSNavigationStepMarkerIntervention.h"
15
16#include "mitkBaseRenderer.h"
17#include "mitkContourModel.h"
20#include "mitkSurface.h"
21#include "mitkTextAnnotation2D.h"
22#include <mitkBaseGeometry.h>
23#include <mitkManualPlacementAnnotationRenderer.h>
24
28
32
34#include "usModuleRegistry.h"
35
36#include "mitkLookupTable.h"
37#include "mitkLookupTableProperty.h"
38
39#include "mitkSurface.h"
40
41// VTK
42#include "vtkCellLocator.h"
43#include "vtkDataSet.h"
44#include "vtkDoubleArray.h"
45#include "vtkFloatArray.h"
46#include "vtkIdList.h"
47#include "vtkLinearTransform.h"
48#include "vtkLookupTable.h"
49#include "vtkMath.h"
50#include "vtkOBBTree.h"
51#include "vtkPointData.h"
52#include "vtkPointData.h"
53#include "vtkPolyData.h"
54#include "vtkSelectEnclosedPoints.h"
55#include "vtkSmartPointer.h"
56#include <vtkSphereSource.h>
57#include "vtkTransformPolyDataFilter.h"
58#include "vtkWarpScalar.h"
59
60
63 m_NumberOfTargets(0),
64 m_PlannedTargetsNodes(),
65 m_ReachedTargetsNodes(),
66 m_TargetProgressBar(new QmitkZoneProgressBar(QString::fromStdString("Target: %1 mm"), 200, 0, this)),
67 m_PlannedTargetProgressBar(nullptr),
68 m_CurrentTargetIndex(0),
69 m_CurrentTargetReached(false),
70 m_ShowPlanningColors(false),
71 m_PointMarkInteractor(mitk::USPointMarkInteractor::New()),
72 m_TargetNode(nullptr),
73 m_TargetColorLookupTableProperty(nullptr),
74 m_TargetSurface(nullptr),
75 m_NeedleProjectionFilter(mitk::NeedleProjectionFilter::New()),
76 m_NodeDisplacementFilter(mitk::NodeDisplacementFilter::New()),
77 m_TargetUpdateFilter(mitk::USNavigationTargetUpdateFilter::New()),
78 m_TargetOcclusionFilter(mitk::USNavigationTargetOcclusionFilter::New()),
79 m_TargetIntersectionFilter(mitk::USNavigationTargetIntersectionFilter::New()),
80 m_PlacementQualityCalculator(mitk::USTargetPlacementQualityCalculator::New()),
81 m_TargetStructureWarnOverlay(mitk::TextAnnotation2D::New()),
82 m_ReferenceSensorName(),
83 m_NeedleSensorName(),
84 m_ReferenceSensorIndex(1),
85 m_NeedleSensorIndex(0),
86 m_ListenerTargetCoordinatesChanged(this, &QmitkUSNavigationStepMarkerIntervention::UpdateTargetCoordinates),
88{
94 m_InactiveTargetColor[2] = 0.5;
95 m_ReachedTargetColor[0] = 0.6;
97 m_ReachedTargetColor[2] = 0.6;
98
99 ui->setupUi(this);
100
101 connect(ui->freezeImageButton, SIGNAL(SignalFreezed(bool)), this, SLOT(OnFreeze(bool)));
102 connect(ui->backToLastTargetButton, SIGNAL(clicked()), this, SLOT(OnBackToLastTargetClicked()));
103 connect(ui->targetReachedButton, SIGNAL(clicked()), this, SLOT(OnTargetLeft()));
104 connect(this, SIGNAL(TargetReached(int)), this, SLOT(OnTargetReached()));
105 connect(this, SIGNAL(TargetLeft(int)), this, SLOT(OnTargetLeft()));
106 connect(ui->riskStructuresRangeWidget,
107 SIGNAL(SignalZoneViolated(const mitk::DataNode *, mitk::Point3D)),
108 this,
109 SLOT(OnRiskZoneViolated(const mitk::DataNode *, mitk::Point3D)));
110
111 m_PointMarkInteractor->CoordinatesChangedEvent.AddListener(m_ListenerTargetCoordinatesChanged);
112
114
115 m_TargetProgressBar->SetTextFormatInvalid("Target is not on Needle Path");
116 ui->targetStructuresRangeLayout->addWidget(m_TargetProgressBar);
117
118 m_TargetUpdateFilter->SetScalarArrayIdentifier("USNavigation::ReachedTargetScores");
119}
120
122{
123 mitk::DataStorage::Pointer dataStorage = this->GetDataStorage(false);
124 if (dataStorage.IsNotNull())
125 {
126 // remove the node for the needle path
127 mitk::DataNode::Pointer node =
129 if (node.IsNotNull())
130 {
131 dataStorage->Remove(node);
132 }
133 }
134
135 delete ui;
136
137 m_PointMarkInteractor->CoordinatesChangedEvent.RemoveListener(m_ListenerTargetCoordinatesChanged);
138}
139
141{
143
144 // create node for Needle Projection
145 mitk::DataNode::Pointer node =
147 node->SetData(m_NeedleProjectionFilter->GetProjection());
148 node->SetBoolProperty("show contour", true);
149
150 // initialize warning overlay (and do not display it, yet)
151 m_TargetStructureWarnOverlay->SetText("Warning: Needle is Inside the Target Structure.");
152 m_TargetStructureWarnOverlay->SetVisibility(false);
153
154 // set position and font size for the text overlay
155 mitk::Point2D overlayPosition;
156 overlayPosition.SetElement(0, 10.0f);
157 overlayPosition.SetElement(1, 10.0f);
158 m_TargetStructureWarnOverlay->SetPosition2D(overlayPosition);
159 m_TargetStructureWarnOverlay->SetFontSize(18);
160
161 // overlay should be red
162 mitk::Color color;
163 color[0] = 1;
164 color[1] = 0;
165 color[2] = 0;
166 m_TargetStructureWarnOverlay->SetColor(color);
167
168 auto allRenderWindows = mitk::BaseRenderer::GetAll3DRenderWindows();
169 for (auto mapit = allRenderWindows.begin(); mapit != allRenderWindows.end(); ++mapit)
170 {
171 mitk::ManualPlacementAnnotationRenderer::AddAnnotation(m_TargetStructureWarnOverlay.GetPointer(), mapit->second);
172 }
173
174 return true;
175}
176
178{
179 mitk::DataStorage::Pointer dataStorage = this->GetDataStorage();
180
181 // remove all reached nodes from the data storage
182 for (QVector<itk::SmartPointer<mitk::DataNode>>::iterator it = m_ReachedTargetsNodes.begin();
183 it != m_ReachedTargetsNodes.end();
184 ++it)
185 {
186 dataStorage->Remove(*it);
187 }
188 m_ReachedTargetsNodes.clear();
190
191 // reset button states
192 ui->freezeImageButton->setEnabled(false);
193 ui->backToLastTargetButton->setEnabled(false);
194 ui->targetReachedButton->setEnabled(true);
195
196 // make sure that it is unfreezed after stopping the step
197 ui->freezeImageButton->Unfreeze();
198
199 // remove base node for reached targets from the data storage
200 mitk::DataNode::Pointer reachedTargetsNode = this->GetNamedDerivedNode(
202 if (reachedTargetsNode.IsNotNull())
203 {
204 dataStorage->Remove(reachedTargetsNode);
205 }
206
207 return true;
208}
209
214
216{
217 this->ClearZones(); // clear risk zones before adding new ones
218
219 // get target node from data storage and make sure that it contains data
222 if (m_TargetNode.IsNull() || m_TargetNode->GetData() == nullptr)
223 {
224 mitkThrow() << "Target node (" << QmitkUSNavigationMarkerPlacement::DATANAME_TARGETSURFACE << ") must not be null.";
225 }
226
227 // get target data and make sure that it is a surface
228 m_TargetSurface = dynamic_cast<mitk::Surface *>(m_TargetNode->GetData());
229 if (m_TargetSurface.IsNull())
230 {
231 mitkThrow() << "Target node (" << QmitkUSNavigationMarkerPlacement::DATANAME_TARGETSURFACE
232 << ") data must be of type mitk::Surface";
233 }
234
235 // delete progress bars for reinitializing them again afterwards
237 {
238 ui->targetStructuresRangeLayout->removeWidget(m_PlannedTargetProgressBar);
241 }
242
244
246
247 mitk::DataNode::Pointer tumourNode = this->GetNamedDerivedNode(QmitkUSNavigationMarkerPlacement::DATANAME_TUMOUR,
249 if (tumourNode.IsNotNull())
250 {
251 // do not show tumour node during intervention (target surface is shown)
252 tumourNode->SetBoolProperty("visible", false);
253
254 // add tumour as a risk structure
255 ui->riskStructuresRangeWidget->AddZone(tumourNode);
256 }
257
258 // set target structure for target update filter
259 m_TargetUpdateFilter->SetTargetStructure(m_TargetNode);
260 m_TargetOcclusionFilter->SetTargetStructure(m_TargetNode);
261
262 // set lookup table of tumour node
263 m_TargetNode->SetProperty("LookupTable", m_TargetColorLookupTableProperty);
264
265 //
266 mitk::DataNode::Pointer targetsBaseNode = this->GetNamedDerivedNode(QmitkUSNavigationMarkerPlacement::DATANAME_TARGETS,
268 mitk::DataStorage::SetOfObjects::ConstPointer plannedTargetNodes;
269 if (targetsBaseNode.IsNotNull())
270 {
271 plannedTargetNodes = this->GetDataStorage()->GetDerivations(targetsBaseNode);
272 }
273 if (plannedTargetNodes.IsNotNull() && plannedTargetNodes->Size() > 0)
274 {
275 for (mitk::DataStorage::SetOfObjects::ConstIterator it = plannedTargetNodes->Begin();
276 it != plannedTargetNodes->End();
277 ++it)
278 {
279 m_PlannedTargetsNodes.push_back(it->Value());
280 }
281
282 m_PlannedTargetProgressBar = new QmitkZoneProgressBar(QString::fromStdString("Planned Target"), 200, 0);
283 ui->targetStructuresRangeLayout->addWidget(m_PlannedTargetProgressBar);
284 }
285
286 // add progress bars for risk zone nodes
287 mitk::DataNode::Pointer zonesBaseNode = this->GetNamedDerivedNode(QmitkUSNavigationMarkerPlacement::DATANAME_ZONES,
289 // only add progress bars if the base node for zones was created
290 if (zonesBaseNode.IsNotNull())
291 {
292 mitk::DataStorage::SetOfObjects::ConstPointer zoneNodes = this->GetDataStorage()->GetDerivations(zonesBaseNode);
293
294 for (mitk::DataStorage::SetOfObjects::ConstIterator it = zoneNodes->Begin(); it != zoneNodes->End(); ++it)
295 {
296 ui->riskStructuresRangeWidget->AddZone(it->Value());
297 }
298
299 m_TargetOcclusionFilter->SelectStartPositionInput(m_NeedleSensorIndex);
300 m_TargetOcclusionFilter->SetObstacleStructures(zoneNodes);
301 }
302
303 return true;
304}
305
307{
308 ui->freezeImageButton->Unfreeze();
309
310 return true;
311}
312
314{
315 // get navigation data source and make sure that it is not null
316 mitk::NavigationDataSource::Pointer navigationDataSource = this->GetCombinedModality()->GetNavigationDataSource();
317 if (navigationDataSource.IsNull())
318 {
319 MITK_ERROR("QmitkUSAbstractNavigationStep")
320 ("QmitkUSNavigationStepMarkerIntervention") << "Navigation Data Source of Combined Modality must not be null.";
321 mitkThrow() << "Navigation Data Source of Combined Modality must not be null.";
322 }
323
324 ui->riskStructuresRangeWidget->UpdateDistancesToNeedlePosition(navigationDataSource->GetOutput(m_NeedleSensorIndex));
325 this->UpdateBodyMarkerStatus(navigationDataSource->GetOutput(m_ReferenceSensorIndex));
326
327 this->UpdateTargetColors();
328 this->UpdateTargetScore();
329
331}
332
334{
335 if (settingsNode.IsNull())
336 {
337 return;
338 }
339
340 int numberOfTargets;
341 if (settingsNode->GetIntProperty("settings.number-of-targets", numberOfTargets))
342 {
343 m_NumberOfTargets = numberOfTargets;
344 m_TargetUpdateFilter->SetNumberOfTargets(numberOfTargets);
345
346 m_PlacementQualityCalculator->SetOptimalAngle(m_TargetUpdateFilter->GetOptimalAngle());
347 }
348
349 std::string referenceSensorName;
350 if (settingsNode->GetStringProperty("settings.reference-name-selected", referenceSensorName))
351 {
352 m_ReferenceSensorName = referenceSensorName;
353 }
354
355 std::string needleSensorName;
356 if (settingsNode->GetStringProperty("settings.needle-name-selected", needleSensorName))
357 {
358 m_NeedleSensorName = needleSensorName;
359 }
360
361 this->UpdateSensorsNames();
362}
363
365{
366 return "Computer-assisted Intervention";
367}
368
373
375{
376 FilterVector filter;
377 filter.push_back(m_NeedleProjectionFilter.GetPointer());
378 filter.push_back(m_NodeDisplacementFilter.GetPointer());
379 filter.push_back(m_TargetOcclusionFilter.GetPointer());
380 return filter;
381}
382
384{
385 mitk::AbstractUltrasoundTrackerDevice::Pointer combinedModality = this->GetCombinedModality(false);
386 if (combinedModality.IsNotNull())
387 {
388 mitk::AffineTransform3D::Pointer calibration = combinedModality->GetCalibration();
389 if (calibration.IsNotNull())
390 {
391 m_NeedleProjectionFilter->SetTargetPlane(calibration);
392 }
393 }
394
395 ui->freezeImageButton->SetCombinedModality(combinedModality, m_ReferenceSensorIndex);
396
397 this->UpdateSensorsNames();
398}
399
404
406{
408
410
411 if (m_CurrentTargetIndex >= 0 && static_cast<unsigned int>(m_CurrentTargetIndex) >= m_NumberOfTargets)
412 {
413 ui->targetReachedButton->setDisabled(true);
414 }
415
416 ui->backToLastTargetButton->setEnabled(true);
417 ui->freezeImageButton->setEnabled(true);
418
420
422 {
423 mitk::DataNode::Pointer node = mitk::DataNode::New();
424 node->SetName(
425 (QString("Target ") + QString("%1").arg(m_CurrentTargetIndex, 2, 10, QLatin1Char('0'))).toStdString());
426 this->GetDataStorage()->Add(
427 node,
430 m_ReachedTargetsNodes.push_back(node);
431 }
432
433 mitk::DataNode::Pointer node = m_ReachedTargetsNodes.at(m_CurrentTargetIndex - 1);
434 mitk::Surface::Pointer zone = mitk::Surface::New();
435
436 // create a vtk sphere with given radius
438 vtkSphere->SetRadius(5);
439 vtkSphere->SetCenter(0, 0, 0);
440 vtkSphere->Update();
441 zone->SetVtkPolyData(vtkSphere->GetOutput());
442
443 // set vtk sphere and origin to data node
444 node->SetData(zone);
445 node->GetData()->GetGeometry()->SetOrigin(
446 this->GetCombinedModality()->GetNavigationDataSource()->GetOutput(m_NeedleSensorIndex)->GetPosition());
447 node->SetColor(0.2, 0.9, 0.2);
448 this->UpdateTargetCoordinates(node);
449}
450
452{
453 if (m_CurrentTargetIndex < 1)
454 {
455 MITK_WARN << "Cannot go back to last target as there is no last target.";
456 return;
457 }
458
460
462 {
463 this->GetDataStorage()->Remove(m_ReachedTargetsNodes.last());
464 MITK_INFO("QmitkUSAbstractNavigationStep")
465 ("QmitkUSNavigationStepMarkerIntervention") << "Removed Target " << m_ReachedTargetsNodes.size();
466 m_ReachedTargetsNodes.pop_back();
467 }
468
469 if (m_CurrentTargetIndex == 0)
470 {
471 ui->backToLastTargetButton->setDisabled(true);
472 }
473 if (m_CurrentTargetIndex >= 0 && static_cast<unsigned int>(m_CurrentTargetIndex) < m_NumberOfTargets)
474 {
475 ui->targetReachedButton->setEnabled(true);
476 }
477
478 ui->freezeImageButton->setEnabled(false);
479 ui->freezeImageButton->Unfreeze();
480
482
483 m_TargetUpdateFilter->RemovePositionOfTarget(m_CurrentTargetIndex);
484}
485
487{
488 if (freezed)
489 {
490 this->GetCombinedModality()->SetIsFreezed(true);
491 // load state machine and event config for data interactor
492 m_PointMarkInteractor->LoadStateMachine("USPointMarkInteractions.xml", us::ModuleRegistry::GetModule("MitkUS"));
493 m_PointMarkInteractor->SetEventConfig("globalConfig.xml");
494
495 if (m_CurrentTargetIndex < 1)
496 {
497 mitkThrow() << "Current target index has to be greater zero when freeze button is clicked.";
498 }
499
501 {
502 mitk::DataNode::Pointer node = mitk::DataNode::New();
503 node->SetName(
504 (QString("Target ") + QString("%1").arg(m_CurrentTargetIndex, 2, 10, QLatin1Char('0'))).toStdString());
505 this->GetDataStorage()->Add(
506 node,
509 m_ReachedTargetsNodes.push_back(node);
510 }
511
512 m_PointMarkInteractor->SetDataNode(m_ReachedTargetsNodes.last());
513 }
514 else
515 {
516 m_PointMarkInteractor->SetDataNode(nullptr);
517 this->GetCombinedModality()->SetIsFreezed(false);
518 }
519}
520
525
526void QmitkUSNavigationStepMarkerIntervention::OnRiskZoneViolated(const mitk::DataNode *node, mitk::Point3D position)
527{
528 MITK_INFO << "Risk zone (" << node->GetName() << ") violated at position " << position << ".";
529}
530
532{
533 ui->riskStructuresRangeWidget->ClearZones();
534
535 // remove all reached target nodes from the data storage and clear the list
536 mitk::DataStorage::Pointer dataStorage = this->GetDataStorage();
537 for (QVector<mitk::DataNode::Pointer>::iterator it = m_ReachedTargetsNodes.begin(); it != m_ReachedTargetsNodes.end();
538 ++it)
539 {
540 if (it->IsNotNull())
541 {
542 dataStorage->Remove(*it);
543 }
544 }
545 m_ReachedTargetsNodes.clear();
546}
547
549{
550 m_NodeDisplacementFilter->ResetNodes();
551 for (QVector<itk::SmartPointer<mitk::DataNode>>::iterator it = m_ReachedTargetsNodes.begin();
552 it != m_ReachedTargetsNodes.end();
553 ++it)
554 {
555 if (it->IsNotNull() && (*it)->GetData() != nullptr)
556 {
557 m_NodeDisplacementFilter->AddNode(*it);
558 }
559 }
560
561 mitk::BaseData *baseData = dataNode->GetData();
562 if (!baseData)
563 {
564 mitkThrow() << "Data of the data node must not be null.";
565 }
566
567 mitk::BaseGeometry::Pointer geometry = baseData->GetGeometry();
568 if (geometry.IsNull())
569 {
570 mitkThrow() << "Geometry of the data node must not be null.";
571 }
572
573 m_TargetUpdateFilter->SetControlNode(m_CurrentTargetIndex - 1, dataNode);
574
576 {
577 m_PlannedTargetsNodes.at(m_CurrentTargetIndex - 1)->SetVisibility(false);
578 }
579
580 MITK_INFO("QmitkUSAbstractNavigationStep")
581 ("QmitkUSNavigationStepMarkerIntervention") << "Target " << m_CurrentTargetIndex << " reached at position "
582 << geometry->GetOrigin();
583
585}
586
587void QmitkUSNavigationStepMarkerIntervention::UpdateBodyMarkerStatus(mitk::NavigationData::Pointer bodyMarker)
588{
589 if (bodyMarker.IsNull())
590 {
591 MITK_ERROR("QmitkUSAbstractNavigationStep")
592 ("QmitkUSNavigationStepMarkerIntervention")
593 << "Current Navigation Data for body marker of Combined Modality must not be null.";
594 mitkThrow() << "Current Navigation Data for body marker of Combined Modality must not be null.";
595 }
596
597 bool valid = bodyMarker->IsDataValid();
598
599 // update body marker status label
600 if (valid)
601 {
602 ui->bodyMarkerTrackingStatusLabel->setStyleSheet(
603 "background-color: #8bff8b; margin-right: 1em; margin-left: 1em; border: 1px solid grey");
604 ui->bodyMarkerTrackingStatusLabel->setText("Body marker is inside the tracking volume.");
605 }
606 else
607 {
608 ui->bodyMarkerTrackingStatusLabel->setStyleSheet(
609 "background-color: #ff7878; margin-right: 1em; margin-left: 1em; border: 1px solid grey");
610 ui->bodyMarkerTrackingStatusLabel->setText("Body marker is not inside the tracking volume.");
611 }
612
613 ui->targetStructuresRangeGroupBox->setEnabled(valid);
614 ui->riskStructuresRangeGroupBox->setEnabled(valid);
615}
616
618{
620 lookupTable->SetHueRange(0.0, 0.33);
621 lookupTable->SetSaturationRange(1.0, 1.0);
622 lookupTable->SetValueRange(1.0, 1.0);
623 lookupTable->SetTableRange(0.0, 1.0);
624 lookupTable->Build();
625
626 mitk::LookupTable::Pointer lut = mitk::LookupTable::New();
627 lut->SetVtkLookupTable(lookupTable);
628
629 m_TargetColorLookupTableProperty = mitk::LookupTableProperty::New(lut);
630}
631
633{
634 if (m_TargetNode.IsNull())
635 {
636 return;
637 }
638
639 m_TargetNode->SetColor(1, 1, 1);
640
641 mitk::BaseData *targetNodeData = m_TargetNode->GetData();
642 if (targetNodeData == nullptr)
643 {
644 return;
645 }
646
647 mitk::Surface::Pointer targetNodeSurface = dynamic_cast<mitk::Surface *>(targetNodeData);
648 vtkSmartPointer<vtkPolyData> targetNodeSurfaceVtk = targetNodeSurface->GetVtkPolyData();
649
650 vtkPointData *targetPointData = targetNodeSurface->GetVtkPolyData()->GetPointData();
651
652 vtkFloatArray *scalars = dynamic_cast<vtkFloatArray *>(targetPointData->GetScalars("USNavigation::Occlusion"));
653 vtkFloatArray *targetScoreScalars;
654
656 {
657 targetScoreScalars = dynamic_cast<vtkFloatArray *>(targetPointData->GetScalars("USNavigation::PlanningScalars"));
658 }
659 else
660 {
661 targetScoreScalars =
662 dynamic_cast<vtkFloatArray *>(targetPointData->GetScalars("USNavigation::ReachedTargetScores"));
663 }
664
665 if (!scalars || !targetScoreScalars)
666 {
667 return;
668 }
669
670 unsigned int numberOfTupels = scalars->GetNumberOfTuples();
671
673 colors->SetNumberOfComponents(1);
674 colors->SetNumberOfTuples(numberOfTupels);
675 colors->SetName("Colors");
676
677 double color, intersection, markerScore;
678
679 for (unsigned int n = 0; n < numberOfTupels; n++)
680 {
681 scalars->GetTuple(n, &intersection);
682 targetScoreScalars->GetTuple(n, &markerScore);
683
684 if (intersection > 0)
685 {
686 color = 0;
687 }
688 else
689 {
690 color = markerScore;
691 }
692
693 colors->SetTuple(n, &color);
694 }
695
696 if (numberOfTupels > 0)
697 {
698 targetNodeSurfaceVtk->GetPointData()->SetScalars(colors);
699 targetNodeSurfaceVtk->GetPointData()->Update();
700 }
701}
702
704{
705 if (m_NeedleProjectionFilter->GetProjection()->GetSize() != 2)
706 {
707 return;
708 }
709
710 vtkSmartPointer<vtkPolyData> targetSurfaceVtk = m_TargetSurface->GetVtkPolyData();
711
713 m_TargetIntersectionFilter->SetLine(m_NeedleProjectionFilter->GetProjection());
714
715 m_TargetIntersectionFilter->CalculateIntersection();
716
717 if (m_TargetIntersectionFilter->GetIsIntersecting())
718 {
719 vtkFloatArray *scalars =
720 dynamic_cast<vtkFloatArray *>(targetSurfaceVtk->GetPointData()->GetScalars("USNavigation::ReachedTargetScores"));
721 double score;
722 scalars->GetTuple(m_TargetIntersectionFilter->GetIntersectionNearestSurfacePointId(), &score);
723
724 double color[3];
725 m_TargetColorLookupTableProperty->GetLookupTable()->GetVtkLookupTable()->GetColor(score, color);
726
727 float colorF[3];
728 colorF[0] = color[0];
729 colorF[1] = color[1];
730 colorF[2] = color[2];
733 m_TargetProgressBar->setValue(m_TargetIntersectionFilter->GetDistanceToIntersection());
734
736 {
737 vtkFloatArray *scalars =
738 dynamic_cast<vtkFloatArray *>(targetSurfaceVtk->GetPointData()->GetScalars("USNavigation::PlanningScalars"));
739 if (scalars)
740 {
741 double score;
742 scalars->GetTuple(m_TargetIntersectionFilter->GetIntersectionNearestSurfacePointId(), &score);
743
744 double color[3];
745 m_TargetColorLookupTableProperty->GetLookupTable()->GetVtkLookupTable()->GetColor(score, color);
746
747 float colorF[3];
748 colorF[0] = color[0];
749 colorF[1] = color[1];
750 colorF[2] = color[2];
753 m_PlannedTargetProgressBar->SetTextFormatValid("Planned Target: %1 mm");
754
755 mitk::Point3D intersectionPoint = m_TargetIntersectionFilter->GetIntersectionPoint();
756
757 mitk::ScalarType minDistance = -1;
758 for (QVector<itk::SmartPointer<mitk::DataNode>>::iterator it = m_PlannedTargetsNodes.begin();
759 it != m_PlannedTargetsNodes.end();
760 ++it)
761 {
762 mitk::ScalarType distance =
763 intersectionPoint.EuclideanDistanceTo((*it)->GetData()->GetGeometry()->GetOrigin());
764 if (minDistance < 0 || distance < minDistance)
765 {
766 minDistance = distance;
767 }
768 }
769
771 }
772 }
773 }
774 else
775 {
777 }
778}
779
781{
782 QString description;
783 if (m_CurrentTargetIndex >= static_cast<int>(m_NumberOfTargets))
784 {
785 description = "All Targets Reached";
787 {
788 m_TargetProgressBar->hide();
789 }
790 }
791 else
792 {
793 description = QString("Distance to Target ") + QString::number(m_CurrentTargetIndex + 1) + QString(" of ") +
794 QString::number(m_NumberOfTargets);
796 {
797 m_TargetProgressBar->show();
798 }
799 }
800
801 ui->targetStructuresRangeGroupBox->setTitle(description);
802}
803
805{
806 // make sure that the needle projection consists of two points
807 if (m_NeedleProjectionFilter->GetProjection()->GetSize() != 2)
808 {
809 return;
810 }
811
812 vtkSmartPointer<vtkPolyData> targetSurfaceVtk = m_TargetSurface->GetVtkPolyData();
813
815 m_TargetIntersectionFilter->SetLine(m_NeedleProjectionFilter->GetProjection());
816
817 m_TargetIntersectionFilter->CalculateIntersection();
818
819 // update target progress bar according to the color of the intersection
820 // point on the target surface and the distance to the intersection
821 if (m_TargetIntersectionFilter->GetIsIntersecting())
822 {
823 vtkFloatArray *scalars = dynamic_cast<vtkFloatArray *>(targetSurfaceVtk->GetPointData()->GetScalars("Colors"));
824 double score;
825 scalars->GetTuple(m_TargetIntersectionFilter->GetIntersectionNearestSurfacePointId(), &score);
826
827 double color[3];
828 m_TargetColorLookupTableProperty->GetLookupTable()->GetVtkLookupTable()->GetColor(score, color);
829
830 float colorF[3];
831 colorF[0] = color[0];
832 colorF[1] = color[1];
833 colorF[2] = color[2];
836 m_TargetProgressBar->setValue(m_TargetIntersectionFilter->GetDistanceToIntersection());
837 }
838 else
839 {
840 float red[3] = {0.6f, 0.0f, 0.0f};
843 }
844}
845
847{
848 // transform vtk polydata according to mitk geometry
850 transformFilter->SetInputData(0, m_TargetSurface->GetVtkPolyData());
851 transformFilter->SetTransform(m_TargetSurface->GetGeometry()->GetVtkTransform());
852 transformFilter->Update();
853
855 enclosedPoints->Initialize(transformFilter->GetOutput());
856
857 mitk::Point3D needleTip = m_NeedleProjectionFilter->GetProjection()->GetPoint(0);
858
859 // show warning if the needle tip is inside the target surface
860 if (enclosedPoints->IsInsideSurface(needleTip[0], needleTip[1], needleTip[2]))
861 {
862 if (!m_TargetStructureWarnOverlay->IsVisible(nullptr))
863 {
864 m_TargetStructureWarnOverlay->SetVisibility(true);
865
866 mitk::DataNode::Pointer targetViolationResult = mitk::DataNode::New();
867 targetViolationResult->SetName("TargetViolation");
868 targetViolationResult->SetProperty("USNavigation::TargetViolationPoint", mitk::Point3dProperty::New(needleTip));
869
870 emit SignalIntermediateResult(targetViolationResult);
871 }
872
873 MITK_INFO("QmitkUSAbstractNavigationStep")
874 ("QmitkUSNavigationStepMarkerIntervention") << "Target surface violated at " << needleTip << ".";
875 }
876 else
877 {
878 m_TargetStructureWarnOverlay->SetVisibility(false);
879 }
880}
881
883{
884 // clear quality display if there aren't all targets reached
885 if (m_ReachedTargetsNodes.size() != static_cast<int>(m_NumberOfTargets))
886 {
887 ui->placementQualityGroupBox->setEnabled(false);
888 ui->angleDifferenceValue->setText("");
889 ui->centersOfMassValue->setText("");
890 return;
891 }
892
893 ui->placementQualityGroupBox->setEnabled(true);
894
895 mitk::Surface::Pointer targetSurface = dynamic_cast<mitk::Surface *>(m_TargetNode->GetData());
896 if (targetSurface.IsNull())
897 {
898 mitkThrow() << "Target surface must not be null.";
899 }
900
901 m_PlacementQualityCalculator->SetTargetSurface(targetSurface);
902 mitk::PointSet::Pointer targetPointSet = mitk::PointSet::New();
903
904 // copy the origins of all reached target nodes into a point set
905 // for the quality calculator
906 mitk::PointSet::PointIdentifier n = 0;
907 for (QVector<itk::SmartPointer<mitk::DataNode>>::iterator it = m_ReachedTargetsNodes.begin();
908 it != m_ReachedTargetsNodes.end();
909 ++it)
910 {
911 targetPointSet->InsertPoint(n++, (*it)->GetData()->GetGeometry()->GetOrigin());
912 }
913 m_PlacementQualityCalculator->SetTargetPoints(targetPointSet);
914
916
917 double centersOfMassDistance = m_PlacementQualityCalculator->GetCentersOfMassDistance();
918 ui->centersOfMassValue->setText(QString::number(centersOfMassDistance, 103, 2) + " mm");
919
920 double meanAnglesDifference = m_PlacementQualityCalculator->GetMeanAngleDifference();
921 ui->angleDifferenceValue->setText(QString::number(meanAnglesDifference, 103, 2) + QString::fromLatin1(" °"));
922
923 // create an intermediate result of the placement quality
924 mitk::DataNode::Pointer placementQualityResult = mitk::DataNode::New();
925 placementQualityResult->SetName("PlacementQuality");
926 placementQualityResult->SetFloatProperty("USNavigation::CentersOfMassDistance", centersOfMassDistance);
927 placementQualityResult->SetFloatProperty("USNavigation::MeanAngleDifference", meanAnglesDifference);
928 placementQualityResult->SetProperty(
929 "USNavigation::AngleDifferences",
930 mitk::GenericProperty<mitk::VnlVector>::New(m_PlacementQualityCalculator->GetAngleDifferences()));
931
932 if (m_PlannedTargetsNodes.size() == static_cast<int>(m_NumberOfTargets))
933 {
934 mitk::VnlVector reachedPlannedDifferences;
935 double reachedPlannedDifferencesSum = 0;
936 double reachedPlannedDifferencesMax = 0;
937 reachedPlannedDifferences.set_size(m_NumberOfTargets);
938
939 // get sum and maximum of the planning / reality differences
940 for (unsigned int n = 0; n < m_NumberOfTargets; ++n)
941 {
942 mitk::ScalarType distance =
943 m_PlannedTargetsNodes.at(n)->GetData()->GetGeometry()->GetOrigin().EuclideanDistanceTo(
944 m_ReachedTargetsNodes.at(n)->GetData()->GetGeometry()->GetOrigin());
945
946 reachedPlannedDifferences.put(n, distance);
947 reachedPlannedDifferencesSum += distance;
948
949 if (distance > reachedPlannedDifferencesMax)
950 {
951 reachedPlannedDifferencesMax = distance;
952 }
953 }
954
955 // add distances between planning and reality to the quality intermediate result
956 placementQualityResult->SetProperty("USNavigation::PlanningRealityDistances",
957 mitk::GenericProperty<mitk::VnlVector>::New(reachedPlannedDifferences));
958 placementQualityResult->SetProperty(
959 "USNavigation::MeanPlanningRealityDistance",
960 mitk::DoubleProperty::New(reachedPlannedDifferencesSum / static_cast<double>(m_NumberOfTargets)));
961 placementQualityResult->SetProperty("USNavigation::MaximumPlanningRealityDistance",
962 mitk::DoubleProperty::New(reachedPlannedDifferencesMax));
963 }
964
965 emit SignalIntermediateResult(placementQualityResult);
966}
967
969{
970 mitk::AbstractUltrasoundTrackerDevice::Pointer combinedModality = this->GetCombinedModality(false);
971 if (combinedModality.IsNull())
972 {
973 return;
974 }
975
976 mitk::NavigationDataSource::Pointer navigationDataSource = combinedModality->GetNavigationDataSource();
977 if (navigationDataSource.IsNull())
978 {
979 return;
980 }
981
982 if (!m_NeedleSensorName.empty())
983 {
984 try
985 {
986 m_NeedleSensorIndex = navigationDataSource->GetOutputIndex(m_NeedleSensorName);
987 }
988 catch (const std::exception &e)
989 {
990 MITK_WARN("QmitkUSAbstractNavigationStep")
991 ("QmitkUSNavigationStepPlacementPlanning") << "Cannot get index for needle sensor name: " << e.what();
992 }
993 }
995 {
997 }
998
999 if (!m_ReferenceSensorName.empty())
1000 {
1001 try
1002 {
1003 m_ReferenceSensorIndex = navigationDataSource->GetOutputIndex(m_ReferenceSensorName);
1004 }
1005 catch (const std::exception &e)
1006 {
1007 MITK_WARN("QmitkUSAbstractNavigationStep")
1008 ("QmitkUSNavigationStepPlacementPlanning") << "Cannot get index for reference sensor name: " << e.what();
1009 }
1010 }
1012 {
1014 }
1015
1016 ui->freezeImageButton->SetCombinedModality(combinedModality, m_ReferenceSensorIndex);
1017}
Abstract base class for navigation step widgets.
itk::SmartPointer< mitk::DataStorage > GetDataStorage(bool throwNull=true)
Returns the data storage set for the navigation step.
NavigationStepState GetNavigationStepState()
Get the current state of the navigation step.
itk::SmartPointer< mitk::DataNode > GetNamedDerivedNodeAndCreate(const char *name, const char *sourceName)
Returns node with the given name and the given source node (parent) from the data storage....
itk::SmartPointer< mitk::AbstractUltrasoundTrackerDevice > GetCombinedModality(bool throwNull=true)
Returns the combined modality set for the navigation step.
std::vector< itk::SmartPointer< mitk::NavigationDataToNavigationDataFilter > > FilterVector
void SignalIntermediateResult(const itk::SmartPointer< mitk::DataNode >)
Signals that an intermediate result was produced. The properties of the given data node must contain ...
itk::SmartPointer< mitk::DataNode > GetNamedDerivedNode(const char *name, const char *sourceName)
Returns node with the given name and the given source node (parent) from the data storage.
Navigation step for the actual marker placement.
itk::SmartPointer< mitk::USPointMarkInteractor > m_PointMarkInteractor
bool OnStopStep() override
Callen when the navigation step gets stopped. This method may be implemented by a concrete subclass t...
void UpdateBodyMarkerStatus(mitk::NavigationData::Pointer bodyMarker)
QVector< itk::SmartPointer< mitk::DataNode > > m_ReachedTargetsNodes
itk::SmartPointer< mitk::NeedleProjectionFilter > m_NeedleProjectionFilter
bool OnActivateStep() override
Called when the navigation step gets activated. This method has to be implemented by a concrete subcl...
itk::SmartPointer< mitk::USNavigationTargetUpdateFilter > m_TargetUpdateFilter
void OnRiskZoneViolated(const mitk::DataNode *, mitk::Point3D)
itk::SmartPointer< mitk::LookupTableProperty > m_TargetColorLookupTableProperty
void UpdateTargetProgressDisplay()
Updates display showing the number of the currently active target.
void OnUpdate() override
Called periodically while a navigation step is active. This method has to be implemented by a concret...
void OnSettingsChanged(const itk::SmartPointer< mitk::DataNode > settingsNode) override
Called every time the settings for the navigation process where changed. This method may be implement...
void OnSetCombinedModality() override
Called every time SetCombinedModality() was called. This method may be implemented by a concrete subc...
void UpdateTargetViolationStatus()
Tests for target violation and updates the display accordingly. This method tests if the needle tip i...
void CalculateTargetPlacementQuality()
Calculates and display quality metrics if all three markers are placed.
itk::SmartPointer< mitk::NodeDisplacementFilter > m_NodeDisplacementFilter
itk::SmartPointer< mitk::TextAnnotation2D > m_TargetStructureWarnOverlay
bool OnFinishStep() override
Called when all necessary actions for the step where done. This method has to be implemented by a con...
bool GetIsRestartable() override
Indicates if it makes sense to be able to restart the step. This method must be implemented by concre...
QString GetTitle() override
Getter for the title of the navigation step. This title should be human readable and can be used to d...
bool OnDeactivateStep() override
Called when the navigation step gets deactivated (-> state started). This method may be implemented b...
QVector< itk::SmartPointer< mitk::DataNode > > m_PlannedTargetsNodes
bool OnStartStep() override
Called when the navigation step gets started. This method has to be implemented by a concrete subclas...
itk::SmartPointer< mitk::USNavigationTargetIntersectionFilter > m_TargetIntersectionFilter
itk::SmartPointer< mitk::USTargetPlacementQualityCalculator > m_PlacementQualityCalculator
itk::SmartPointer< mitk::USNavigationTargetOcclusionFilter > m_TargetOcclusionFilter
void UpdatePlannedTargetProgressDisplay()
Updates color and distance of the progress bar for the planned target. The intersection between needl...
FilterVector GetFilter() override
Getter for navigation data filters of the navigation step. This method may be implemented by a concre...
QProgressBar for displaying distances to zones. Colors are changed according to the distance to the z...
void SetBorderColor(float color[3])
void setValueInvalid()
Can be called to indicate that there is currently no valid distance value available....
void SetTextFormatValid(QString format)
Setter for the text on the progress bar. Defaults to the string given as name to the constructor....
void SetColor(float color[3])
void SetTextFormatInvalid(QString format)
IGT Exceptions.