NorMIT-nav  18.04
An IGT application
cxLandmarkRegistrationWidget.cpp
Go to the documentation of this file.
1 /*=========================================================================
2 This file is part of CustusX, an Image Guided Therapy Application.
3 
4 Copyright (c) SINTEF Department of Medical Technology.
5 All rights reserved.
6 
7 CustusX is released under a BSD 3-Clause license.
8 
9 See Lisence.txt (https://github.com/SINTEFMedtek/CustusX/blob/master/License.txt) for details.
10 =========================================================================*/
11 
13 
14 #include <sstream>
15 #include <QVBoxLayout>
16 #include <QPushButton>
17 #include <QTableWidget>
18 #include <QTableWidgetItem>
19 #include <QHeaderView>
20 #include <QLabel>
21 #include <QSlider>
22 #include <vtkDoubleArray.h>
23 #include <vtkImageData.h>
24 
25 #include "cxTypeConversions.h"
26 #include "cxManualTool.h"
27 #include "cxPatientModelService.h"
28 #include "cxRegistrationService.h"
29 #include "cxViewService.h"
30 #include"cxData.h"
31 #include "cxLogger.h"
32 #include "cxLandmark.h"
33 #include "cxTrackingService.h"
34 #include "cxLandmarkListener.h"
35 
36 namespace cx
37 {
38 LandmarkRegistrationWidget::LandmarkRegistrationWidget(RegServicesPtr services, QWidget* parent,
39  QString objectName, QString windowTitle, bool showAccuracy) :
40  RegistrationBaseWidget(services, parent, objectName, windowTitle), mVerticalLayout(new QVBoxLayout(this)),
41  mLandmarkTableWidget(new QTableWidget(this)), mAvarageAccuracyLabel(new QLabel(QString(" "), this)),
42  mLandmarkListener(new LandmarkListener(services)), mShowAccuracy(showAccuracy)
43 {
44  //table widget
45  connect(mLandmarkTableWidget, SIGNAL(cellClicked(int, int)), this, SLOT(cellClickedSlot(int, int)));
46  connect(mLandmarkTableWidget, SIGNAL(cellChanged(int,int)), this, SLOT(cellChangedSlot(int,int)));
47 
48  this->setLayout(mVerticalLayout);
49 }
50 
52 {
53 }
54 
56 {
57  if (row < 0 || column < 0)
58  return;
59 
61  reportDebug("mLandmarkTableWidget is null");
62 
63  mActiveLandmark = mLandmarkTableWidget->item(row, column)->data(Qt::UserRole).toString();
64 
65 
66  LandmarkMap targetData = this->getTargetLandmarks();
67  if (targetData.count(mActiveLandmark))
68  {
69  Vector3D p_d = targetData[mActiveLandmark].getCoord();
70  Vector3D p_r = this->getTargetTransform().coord(p_d);
71  Vector3D p_pr = mServices->patient()->get_rMpr().coord(p_r);
72  this->setManualToolPosition(p_r);
73  }
74 
75 }
76 
78 {
79  Transform3D rMpr = mServices->patient()->get_rMpr();
80  Vector3D p_pr = rMpr.inv().coord(p_r);
81 
82  // set the picked point as offset tip
83  ToolPtr tool = mServices->tracking()->getManualTool();
84  Vector3D offset = tool->get_prMt().vector(Vector3D(0, 0, tool->getTooltipOffset()));
85  p_pr -= offset;
86  p_r = rMpr.coord(p_pr);
87 
88  // TODO set center here will not do: must handle
89  mServices->patient()->setCenter(p_r);
90  Vector3D p0_pr = tool->get_prMt().coord(Vector3D(0, 0, 0));
91  tool->set_prMt(createTransformTranslate(p_pr - p0_pr) * tool->get_prMt());
92 }
93 
95 {
96  QWidget::showEvent(event);
97  mLandmarkListener->showRep();
99  connect(mServices->patient()->getPatientLandmarks().get(), &Landmarks::landmarkAdded, this, &LandmarkRegistrationWidget::landmarkUpdatedSlot);
100  connect(mServices->patient()->getPatientLandmarks().get(), &Landmarks::landmarkRemoved, this, &LandmarkRegistrationWidget::landmarkUpdatedSlot);
101 
103 
104 // mManager->restart();
105  mServices->registration()->setLastRegistrationTime(QDateTime::currentDateTime());
106  this->setModified();
107 
108 }
109 
111 {
112  QWidget::hideEvent(event);
114  disconnect(mServices->patient()->getPatientLandmarks().get(), &Landmarks::landmarkAdded, this, &LandmarkRegistrationWidget::landmarkUpdatedSlot);
115  disconnect(mServices->patient()->getPatientLandmarks().get(), &Landmarks::landmarkRemoved, this, &LandmarkRegistrationWidget::landmarkUpdatedSlot);
117  mLandmarkListener->hideRep();
118 }
119 
121 {
122  mLandmarkTableWidget->blockSignals(true);
123  mLandmarkTableWidget->clear();
124 
125  QString fixedName;
126  DataPtr fixedData = boost::dynamic_pointer_cast<Data>(mServices->registration()->getFixedData());
127  if (fixedData)
128  fixedName = fixedData->getName();
129 
130  std::vector<Landmark> landmarks = this->getAllLandmarks();
131  LandmarkMap targetData = this->getTargetLandmarks();
132  Transform3D rMtarget = this->getTargetTransform();
133 
134  //ready the table widget
135  mLandmarkTableWidget->setRowCount((int)landmarks.size());
136  QStringList headerItems(QStringList() << "Name" << "Status" << "Coordinates");
137  if (mShowAccuracy)
138  {
139  mLandmarkTableWidget->setColumnCount(4);
140  headerItems.append("Accuracy (mm)");
141  }
142  else
143  mLandmarkTableWidget->setColumnCount(3);
144 
145  mLandmarkTableWidget->setHorizontalHeaderLabels(headerItems);
146  mLandmarkTableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
147  mLandmarkTableWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
148 
149  for (unsigned i = 0; i < landmarks.size(); ++i)
150  {
151  std::vector<QTableWidgetItem*> items(4); // name, status, coordinates, accuracy
152 
153  LandmarkProperty prop = mServices->patient()->getLandmarkProperties()[landmarks[i].getUid()];
154  Vector3D coord = landmarks[i].getCoord();
155  coord = rMtarget.coord(coord); // display coordinates in space r (in principle, this means all coords should be equal)
156 
157  items[0] = new QTableWidgetItem(qstring_cast(prop.getName()));
158  items[0]->setToolTip(QString("Landmark name. Double-click to rename."));
159 
160  items[1] = new QTableWidgetItem;
161 
162  if (prop.getActive())
163  items[1]->setCheckState(Qt::Checked);
164  else
165  items[1]->setCheckState(Qt::Unchecked);
166  items[1]->setToolTip(QString("Check to use landmark in current registration."));
167 
168  QString coordText = "Not sampled";
169  if (targetData.count(prop.getUid()))
170  {
171  int width = 5;
172  int prec = 1;
173  coordText = tr("(%1, %2, %3)").arg(coord[0], width, 'f', prec).arg(coord[1], width, 'f', prec).arg(
174  coord[2], width, 'f', prec);
175  }
176 
177  items[2] = new QTableWidgetItem(coordText);
178  items[2]->setToolTip(QString("Landmark coordinates of target [%1] in reference space.").arg(this->getTargetName()));
179 
180  items[3] = new QTableWidgetItem(tr("%1").arg(this->getAccuracy(landmarks[i].getUid())));
181  items[3]->setToolTip(QString("Distance from target [%1] to fixed [%2].").arg(this->getTargetName()).arg(fixedName));
182 
183  for (unsigned j = 0; j < items.size(); ++j)
184  {
185  items[j]->setData(Qt::UserRole, qstring_cast(prop.getUid()));
186  mLandmarkTableWidget->setItem(i, j, items[j]);
187  }
188 
189  //highlight selected row
190  if (prop.getUid() == mActiveLandmark)
191  {
192  mLandmarkTableWidget->setCurrentItem(items[2]);
193  }
194  }
195 
197  mLandmarkTableWidget->blockSignals(false);
198 }
199 
201 {
202  mActiveLandmark = uid;
203  this->setModified();
204 }
205 
210 {
211  std::vector<Landmark> lm = this->getAllLandmarks();
212 
213  for (int i=0; i<lm.size()-1; ++i)
214  {
215  if (lm[i].getUid()==mActiveLandmark)
216  {
217  return lm[i+1].getUid();
218  }
219  }
220 
221  return "";
222 }
223 
224 std::vector<Landmark> LandmarkRegistrationWidget::getAllLandmarks() const
225 {
226  std::vector<Landmark> retval;
227  LandmarkMap targetData = this->getTargetLandmarks();
228  std::map<QString, LandmarkProperty> dataData = mServices->patient()->getLandmarkProperties();
229  std::map<QString, LandmarkProperty>::iterator iter;
230 
231  for (iter = dataData.begin(); iter != dataData.end(); ++iter)
232  {
233  if (targetData.count(iter->first))
234  retval.push_back(targetData[iter->first]);
235  else
236  retval.push_back(Landmark(iter->first));
237  }
238 
239  std::sort(retval.begin(), retval.end());
240 
241  return retval;
242 }
243 
245 {
246  QTableWidgetItem* item = mLandmarkTableWidget->item(row, column);
247  QString uid = item->data(Qt::UserRole).toString();
248 
249  if (column == 0)
250  {
251  QString name = item->text();
252  mServices->patient()->setLandmarkName(uid, name);
253  }
254  if (column == 1)
255  {
256  Qt::CheckState state = item->checkState();
257  mServices->patient()->setLandmarkActive(uid, state == Qt::Checked);
258  this->performRegistration(); // automatic when changing active state (Mantis #0000674)s
259  }
260  if (column == 2)
261  {
262  QString val = item->text();
263  // remove formatting stuff:
264  val = val.replace('(', " ");
265  val = val.replace(')', " ");
266  val = val.replace(',', " ");
267 
268  Transform3D rMtarget = this->getTargetTransform();
269 
270  Vector3D p_r = Vector3D::fromString(val);
271  Vector3D p_target = rMtarget.inv().coord(p_r);
272  this->setTargetLandmark(uid, p_target);
273  }
274 }
275 
277 {
278 // - This has too many side effects when we use the landmarks for several different registrations,
279 // i.e. image2image, patient, fast... Rather register explicitly, and add it to the buttons where you
280 // want the automation, such as in the patient reg sampler. (Mantis #0000674)
281 // this->performRegistration();
282  this->setModified();
283 }
284 
286 {
287  QString fixedName;
288  DataPtr fixedData = boost::dynamic_pointer_cast<Data>(mServices->registration()->getFixedData());
289  if (fixedData)
290  fixedName = fixedData->getName();
291 
292  if(this->isAverageAccuracyValid() && mShowAccuracy)
293  {
294  mAvarageAccuracyLabel->setText(tr("Mean accuracy %1 mm").arg(this->getAverageAccuracy(), 0, 'f', 2));
295  mAvarageAccuracyLabel->setToolTip(QString("Average landmark accuracy from target [%1] to fixed [%2].").arg(this->getTargetName()).arg(fixedName));
296  }
297  else
298  {
299  mAvarageAccuracyLabel->setText(" ");
300  mAvarageAccuracyLabel->setToolTip("");
301  }
302 }
303 
304 bool LandmarkRegistrationWidget::isAverageAccuracyValid()
305 {
306  int numActiveLandmarks = 0;
307  this->getAverageAccuracy(numActiveLandmarks);
308  if(numActiveLandmarks < 3)
309  return false;
310  return true;
311 }
312 
314 {
315  int numActiveLandmarks = 0;
316  return this->getAverageAccuracy(numActiveLandmarks);
317 }
318 
319 double LandmarkRegistrationWidget::getAverageAccuracy(int& numActiveLandmarks)
320 {
321  std::map<QString, LandmarkProperty> props = mServices->patient()->getLandmarkProperties();
322 
323  double sum = 0;
324  numActiveLandmarks = 0;
325  std::map<QString, LandmarkProperty>::iterator it = props.begin();
326  for (; it != props.end(); ++it)
327  {
328  if (!it->second.getActive()) //we don't want to take into account not active landmarks
329  continue;
330  QString uid = it->first;
331  double val = this->getAccuracy(uid);
332  if (!similar(val, 1000.0))
333  {
334  sum = sum + val;
335  numActiveLandmarks++;
336  }
337  }
338  if (numActiveLandmarks == 0)
339  return 1000;
340  return sum / numActiveLandmarks;
341 }
342 
344 {
345  DataPtr fixedData = mServices->registration()->getFixedData();
346  if (!fixedData)
347  return 1000.0;
348 
349  Landmark masterLandmark = fixedData->getLandmarks()->getLandmarks()[uid];
350  Landmark targetLandmark = this->getTargetLandmarks()[uid];
351  if (masterLandmark.getUid().isEmpty() || targetLandmark.getUid().isEmpty())
352  return 1000.0;
353 
354  Vector3D p_master_master = masterLandmark.getCoord();
355  Vector3D p_target_target = targetLandmark.getCoord();
356  Transform3D rMmaster = fixedData->get_rMd();
357  Transform3D rMtarget = this->getTargetTransform();
358 
359  Vector3D p_target_r = rMtarget.coord(p_target_target);
360  Vector3D p_master_r = rMmaster.coord(p_master_master);
361 
362  return (p_target_r - p_master_r).length();
363 }
364 
365 }//namespace cx
QString qstring_cast(const T &val)
QString getUid() const
Definition: cxLandmark.cpp:33
virtual void performRegistration()=0
virtual void showEvent(QShowEvent *event)
updates internal info before showing the widget
QLabel * mAvarageAccuracyLabel
label showing the average accuracy
void landmarkPropertiesChanged()
emitted when global info about a landmark changed
One landmark, or fiducial, coordinate.
Definition: cxLandmark.h:40
Transform3D Transform3D
Transform3D is a representation of an affine 3D transform.
void landmarkAdded(QString uid)
QString getUid() const
Definition: cxLandmark.cpp:174
QVBoxLayout * mVerticalLayout
vertical layout is used
virtual void hideEvent(QHideEvent *event)
boost::shared_ptr< class Data > DataPtr
virtual Transform3D getTargetTransform() const =0
Return transform from target space to reference space.
virtual QString getName() const
Definition: cxData.cpp:69
virtual void cellClickedSlot(int row, int column)
when a landmark is selected from the table
boost::shared_ptr< class RegServices > RegServicesPtr
Definition: cxRegServices.h:20
Transform3D createTransformTranslate(const Vector3D &translation)
virtual void prePaintEvent()
populates the table widget
Vector3D getCoord() const
Definition: cxLandmark.cpp:38
void activeLayoutChanged()
emitted when the active layout changes
Eigen::Vector3d Vector3D
Vector3D is a representation of a point or vector in 3D.
Definition: cxVector3D.h:42
virtual LandmarkMap getTargetLandmarks() const =0
Superclass for all data objects.
Definition: cxData.h:88
std::map< QString, class Landmark > LandmarkMap
bool similar(const CameraInfo &lhs, const CameraInfo &rhs, double tol)
QString mActiveLandmark
uid of surrently selected landmark.
QString getName() const
Definition: cxLandmark.cpp:184
std::vector< Landmark > getAllLandmarks() const
get all the landmarks from the image and the datamanager
virtual void setTargetLandmark(QString uid, Vector3D p_target)=0
virtual QString getTargetName() const =0
bool getActive() const
Definition: cxLandmark.cpp:179
void landmarkRemoved(QString uid)
void reportDebug(QString msg)
Definition: cxLogger.cpp:68
QTableWidget * mLandmarkTableWidget
the table widget presenting the landmarks
void cellChangedSlot(int row, int column)
reacts when the user types in a (landmark) name
Namespace for all CustusX production code.
boost::shared_ptr< class Tool > ToolPtr