CustusX  2021.05.28-dev+develop.ccf84
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  int size = int(lm.size());
214  for (unsigned i=0; int(i) < size-1; ++i)
215  {
216  if (lm[i].getUid()==mActiveLandmark)
217  {
218  return lm[i+1].getUid();
219  }
220  }
221 
222  return "";
223 }
224 
225 std::vector<Landmark> LandmarkRegistrationWidget::getAllLandmarks() const
226 {
227  std::vector<Landmark> retval;
228  LandmarkMap targetData = this->getTargetLandmarks();
229  std::map<QString, LandmarkProperty> dataData = mServices->patient()->getLandmarkProperties();
230  std::map<QString, LandmarkProperty>::iterator iter;
231 
232  for (iter = dataData.begin(); iter != dataData.end(); ++iter)
233  {
234  if (targetData.count(iter->first))
235  retval.push_back(targetData[iter->first]);
236  else
237  retval.push_back(Landmark(iter->first));
238  }
239 
240  std::sort(retval.begin(), retval.end());
241 
242  return retval;
243 }
244 
246 {
247  QTableWidgetItem* item = mLandmarkTableWidget->item(row, column);
248  QString uid = item->data(Qt::UserRole).toString();
249 
250  if (column == 0)
251  {
252  QString name = item->text();
253  mServices->patient()->setLandmarkName(uid, name);
254  }
255  if (column == 1)
256  {
257  Qt::CheckState state = item->checkState();
258  mServices->patient()->setLandmarkActive(uid, state == Qt::Checked);
259  this->performRegistration(); // automatic when changing active state (Mantis #0000674)s
260  }
261  if (column == 2)
262  {
263  QString val = item->text();
264  // remove formatting stuff:
265  val = val.replace('(', " ");
266  val = val.replace(')', " ");
267  val = val.replace(',', " ");
268 
269  Transform3D rMtarget = this->getTargetTransform();
270 
271  Vector3D p_r = Vector3D::fromString(val);
272  Vector3D p_target = rMtarget.inv().coord(p_r);
273  this->setTargetLandmark(uid, p_target);
274  }
275 }
276 
278 {
279 // - This has too many side effects when we use the landmarks for several different registrations,
280 // i.e. image2image, patient, fast... Rather register explicitly, and add it to the buttons where you
281 // want the automation, such as in the patient reg sampler. (Mantis #0000674)
282 // this->performRegistration();
283  this->setModified();
284 }
285 
287 {
288  QString fixedName;
289  DataPtr fixedData = boost::dynamic_pointer_cast<Data>(mServices->registration()->getFixedData());
290  if (fixedData)
291  fixedName = fixedData->getName();
292 
293  if(this->isAverageAccuracyValid() && mShowAccuracy)
294  {
295  mAvarageAccuracyLabel->setText(tr("Mean accuracy %1 mm").arg(this->getAverageAccuracy(), 0, 'f', 2));
296  mAvarageAccuracyLabel->setToolTip(QString("Average landmark accuracy from target [%1] to fixed [%2].").arg(this->getTargetName()).arg(fixedName));
297  }
298  else
299  {
300  mAvarageAccuracyLabel->setText(" ");
301  mAvarageAccuracyLabel->setToolTip("");
302  }
303 }
304 
305 bool LandmarkRegistrationWidget::isAverageAccuracyValid()
306 {
307  int numActiveLandmarks = 0;
308  this->getAverageAccuracy(numActiveLandmarks);
309  if(numActiveLandmarks < 3)
310  return false;
311  return true;
312 }
313 
315 {
316  int numActiveLandmarks = 0;
317  return this->getAverageAccuracy(numActiveLandmarks);
318 }
319 
320 double LandmarkRegistrationWidget::getAverageAccuracy(int& numActiveLandmarks)
321 {
322  std::map<QString, LandmarkProperty> props = mServices->patient()->getLandmarkProperties();
323 
324  double sum = 0;
325  numActiveLandmarks = 0;
326  std::map<QString, LandmarkProperty>::iterator it = props.begin();
327  for (; it != props.end(); ++it)
328  {
329  if (!it->second.getActive()) //we don't want to take into account not active landmarks
330  continue;
331  QString uid = it->first;
332  double val = this->getAccuracy(uid);
333  if (!similar(val, 1000.0))
334  {
335  sum = sum + val;
336  numActiveLandmarks++;
337  }
338  }
339  if (numActiveLandmarks == 0)
340  return 1000;
341  return sum / numActiveLandmarks;
342 }
343 
345 {
346  DataPtr fixedData = mServices->registration()->getFixedData();
347  if (!fixedData)
348  return 1000.0;
349 
350  Landmark masterLandmark = fixedData->getLandmarks()->getLandmarks()[uid];
351  Landmark targetLandmark = this->getTargetLandmarks()[uid];
352  if (masterLandmark.getUid().isEmpty() || targetLandmark.getUid().isEmpty())
353  return 1000.0;
354 
355  Vector3D p_master_master = masterLandmark.getCoord();
356  Vector3D p_target_target = targetLandmark.getCoord();
357  Transform3D rMmaster = fixedData->get_rMd();
358  Transform3D rMtarget = this->getTargetTransform();
359 
360  Vector3D p_target_r = rMtarget.coord(p_target_target);
361  Vector3D p_master_r = rMmaster.coord(p_master_master);
362 
363  return (p_target_r - p_master_r).length();
364 }
365 
366 }//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:89
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