Fraxinus  2023.01.05-dev+develop.0da12
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 <QCheckBox>
23 #include <vtkDoubleArray.h>
24 #include <vtkImageData.h>
25 
26 #include "cxTypeConversions.h"
27 #include "cxManualTool.h"
28 #include "cxPatientModelService.h"
29 #include "cxRegistrationService.h"
30 #include "cxViewService.h"
31 #include"cxData.h"
32 #include "cxLogger.h"
33 #include "cxLandmark.h"
34 #include "cxTrackingService.h"
35 #include "cxLandmarkListener.h"
36 
37 namespace cx
38 {
39 LandmarkRegistrationWidget::LandmarkRegistrationWidget(RegServicesPtr services, QWidget* parent, 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  mActiveLandmark(""),
43  mLandmarkListener(new LandmarkListener(services)), mShowAccuracy(showAccuracy),
44  mMouseClickSample(nullptr)
45 {
46  //table widget
47  connect(mLandmarkTableWidget, SIGNAL(cellClicked(int, int)), this, SLOT(cellClickedSlot(int, int)));
48  connect(mLandmarkTableWidget, SIGNAL(cellChanged(int,int)), this, SLOT(cellChangedSlot(int,int)));
49 
50  this->setLayout(mVerticalLayout);
51 
52  mMouseClickSample = new QCheckBox("Resample with mouse clicks in anyplane view.", this);
53  mMouseClickSample->setToolTip("Allow mouse clicks in 2D anyplane view to sample patient landmarks.");
54  mMouseClickSample->hide();
55 }
56 
58 {
59 }
60 
62 {
63  if (row < 0 || column < 0)
64  return;
65 
67  reportDebug("mLandmarkTableWidget is null");
68 
69  mActiveLandmark = mLandmarkTableWidget->item(row, column)->data(Qt::UserRole).toString();
70 
71 
72  LandmarkMap targetData = this->getTargetLandmarks();
73  if (targetData.count(mActiveLandmark))
74  {
75  Vector3D p_d = targetData[mActiveLandmark].getCoord();
76  Vector3D p_r = this->getTargetTransform().coord(p_d);
77  Vector3D p_pr = mServices->patient()->get_rMpr().coord(p_r);
78  this->setManualToolPosition(p_r);
79  }
80 
81 }
82 
84 {
85  Transform3D rMpr = mServices->patient()->get_rMpr();
86  Vector3D p_pr = rMpr.inv().coord(p_r);
87 
88  // set the picked point as offset tip
89  ToolPtr tool = mServices->tracking()->getManualTool();
90  Vector3D offset = tool->get_prMt().vector(Vector3D(0, 0, tool->getTooltipOffset()));
91  p_pr -= offset;
92  p_r = rMpr.coord(p_pr);
93 
94  // TODO set center here will not do: must handle
95  mServices->patient()->setCenter(p_r);
96  Vector3D p0_pr = tool->get_prMt().coord(Vector3D(0, 0, 0));
97  tool->set_prMt(createTransformTranslate(p_pr - p0_pr) * tool->get_prMt());
98 }
99 
101 {
103  QWidget::showEvent(event);
104  mMouseClickSample->setChecked(false);
105  mLandmarkListener->showRep();
107  connect(mServices->patient()->getPatientLandmarks().get(), &Landmarks::landmarkAdded, this, &LandmarkRegistrationWidget::landmarkUpdatedSlot);
108  connect(mServices->patient()->getPatientLandmarks().get(), &Landmarks::landmarkRemoved, this, &LandmarkRegistrationWidget::landmarkUpdatedSlot);
109 
111 
112  //mManager->restart();
113  mServices->registration()->setLastRegistrationTime(QDateTime::currentDateTime());
114  this->setModified();
115 
116 }
117 
119 {
120  std::vector<Landmark> landmarks = this->getAllLandmarks();
121  if(mActiveLandmark.isEmpty() || mActiveLandmark.toInt() > landmarks.size())
122  {
123  if(landmarks.size() > 0)
124  {
125  QString firstLandmarkUid = landmarks[0].getUid();
126  this->activateLandmark(firstLandmarkUid);
127  }
128  }
129  if(!mActiveLandmark.isEmpty() && landmarks.empty())
130  this->activateLandmark("");
131 }
132 
134 {
135  QWidget::hideEvent(event);
136  mMouseClickSample->setChecked(false);
138  disconnect(mServices->patient()->getPatientLandmarks().get(), &Landmarks::landmarkAdded, this, &LandmarkRegistrationWidget::landmarkUpdatedSlot);
139  disconnect(mServices->patient()->getPatientLandmarks().get(), &Landmarks::landmarkRemoved, this, &LandmarkRegistrationWidget::landmarkUpdatedSlot);
141  mLandmarkListener->hideRep();
142 }
143 
145 {
146  mLandmarkTableWidget->blockSignals(true);
147  mLandmarkTableWidget->clear();
148 
149  QString fixedName;
150  DataPtr fixedData = boost::dynamic_pointer_cast<Data>(mServices->registration()->getFixedData());
151  if (fixedData)
152  fixedName = fixedData->getName();
153 
154  std::vector<Landmark> landmarks = this->getAllLandmarks();
155  LandmarkMap targetData = this->getTargetLandmarks();
156  Transform3D rMtarget = this->getTargetTransform();
157 
158  //ready the table widget
159  mLandmarkTableWidget->setRowCount((int)landmarks.size());
160  QStringList headerItems(QStringList() << "Name" << "Status" << "Coordinates");
161  if (mShowAccuracy)
162  {
163  mLandmarkTableWidget->setColumnCount(4);
164  headerItems.append("Accuracy (mm)");
165  }
166  else
167  mLandmarkTableWidget->setColumnCount(3);
168 
169  mLandmarkTableWidget->setHorizontalHeaderLabels(headerItems);
170  mLandmarkTableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
171  mLandmarkTableWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
172 
173  for (unsigned i = 0; i < landmarks.size(); ++i)
174  {
175  std::vector<QTableWidgetItem*> items(4); // name, status, coordinates, accuracy
176 
177  LandmarkProperty prop = mServices->patient()->getLandmarkProperties()[landmarks[i].getUid()];
178  Vector3D coord = landmarks[i].getCoord();
179  coord = rMtarget.coord(coord); // display coordinates in space r (in principle, this means all coords should be equal)
180 
181  items[0] = new QTableWidgetItem(qstring_cast(prop.getName()));
182  items[0]->setToolTip(QString("Landmark name. Double-click to rename."));
183 
184  items[1] = new QTableWidgetItem;
185 
186  if (prop.getActive())
187  items[1]->setCheckState(Qt::Checked);
188  else
189  items[1]->setCheckState(Qt::Unchecked);
190  items[1]->setToolTip(QString("Check to use landmark in current registration."));
191 
192  QString coordText = "Not sampled";
193  if (targetData.count(prop.getUid()))
194  {
195  int width = 5;
196  int prec = 1;
197  coordText = tr("(%1, %2, %3)").arg(coord[0], width, 'f', prec).arg(coord[1], width, 'f', prec).arg(
198  coord[2], width, 'f', prec);
199  }
200 
201  items[2] = new QTableWidgetItem(coordText);
202  items[2]->setToolTip(QString("Landmark coordinates of target [%1] in reference space.").arg(this->getTargetName()));
203 
204  items[3] = new QTableWidgetItem(tr("%1").arg(this->getAccuracy(landmarks[i].getUid())));
205  items[3]->setToolTip(QString("Distance from target [%1] to fixed [%2].").arg(this->getTargetName()).arg(fixedName));
206 
207  for (unsigned j = 0; j < items.size(); ++j)
208  {
209  items[j]->setData(Qt::UserRole, qstring_cast(prop.getUid()));
210  mLandmarkTableWidget->setItem(i, j, items[j]);
211  }
212 
213  //highlight selected row
214  if (prop.getUid() == mActiveLandmark)
215  {
216  mLandmarkTableWidget->setCurrentItem(items[2]);
217  }
218  }
219 
221  mLandmarkTableWidget->blockSignals(false);
222 }
223 
225 {
226  mActiveLandmark = uid;
227  this->setModified();
229 }
230 
235 {
236  std::vector<Landmark> lm = this->getAllLandmarks();
237 
238  int size = int(lm.size());
239  for (unsigned i=0; int(i) < size-1; ++i)
240  {
241  if (lm[i].getUid()==mActiveLandmark)
242  {
243  return lm[i+1].getUid();
244  }
245  }
246 
247  return "";
248 }
249 
250 std::vector<Landmark> LandmarkRegistrationWidget::getAllLandmarks() const
251 {
252  std::vector<Landmark> retval;
253  LandmarkMap targetData = this->getTargetLandmarks();
254  std::map<QString, LandmarkProperty> dataData = mServices->patient()->getLandmarkProperties();
255  std::map<QString, LandmarkProperty>::iterator iter;
256 
257  for (iter = dataData.begin(); iter != dataData.end(); ++iter)
258  {
259  if (targetData.count(iter->first))
260  retval.push_back(targetData[iter->first]);
261  else
262  retval.push_back(Landmark(iter->first));
263  }
264 
265  std::sort(retval.begin(), retval.end());
266 
267  return retval;
268 }
269 
271 {
272  QTableWidgetItem* item = mLandmarkTableWidget->item(row, column);
273  QString uid = item->data(Qt::UserRole).toString();
274 
275  if (column == 0)
276  {
277  QString name = item->text();
278  mServices->patient()->setLandmarkName(uid, name);
279  }
280  if (column == 1)
281  {
282  Qt::CheckState state = item->checkState();
283  mServices->patient()->setLandmarkActive(uid, state == Qt::Checked);
284  this->performRegistration(); // automatic when changing active state (Mantis #0000674)s
285  }
286  if (column == 2)
287  {
288  QString val = item->text();
289  // remove formatting stuff:
290  val = val.replace('(', " ");
291  val = val.replace(')', " ");
292  val = val.replace(',', " ");
293 
294  Transform3D rMtarget = this->getTargetTransform();
295 
296  Vector3D p_r = Vector3D::fromString(val);
297  Vector3D p_target = rMtarget.inv().coord(p_r);
298  this->setTargetLandmark(uid, p_target);
299  }
300 }
301 
303 {
304  //- This has too many side effects when we use the landmarks for several different registrations,
305  //i.e. image2image, patient, fast... Rather register explicitly, and add it to the buttons where you
306  //want the automation, such as in the patient reg sampler. (Mantis #0000674)
307  //this->performRegistration();
308  this->setModified();
310 }
311 
313 {
314  QString fixedName;
315  DataPtr fixedData = boost::dynamic_pointer_cast<Data>(mServices->registration()->getFixedData());
316  if (fixedData)
317  fixedName = fixedData->getName();
318 
319  if(this->isAverageAccuracyValid() && mShowAccuracy)
320  {
321  mAvarageAccuracyLabel->setText(tr("Mean accuracy %1 mm").arg(this->getAverageAccuracy(), 0, 'f', 2));
322  mAvarageAccuracyLabel->setToolTip(QString("Average landmark accuracy from target [%1] to fixed [%2].").arg(this->getTargetName()).arg(fixedName));
323  }
324  else
325  {
326  mAvarageAccuracyLabel->setText(" ");
327  mAvarageAccuracyLabel->setToolTip("");
328  }
329 }
330 
331 bool LandmarkRegistrationWidget::isAverageAccuracyValid()
332 {
333  int numActiveLandmarks = 0;
334  this->getAverageAccuracy(numActiveLandmarks);
335  if(numActiveLandmarks < 3)
336  return false;
337  return true;
338 }
339 
341 {
342  int numActiveLandmarks = 0;
343  return this->getAverageAccuracy(numActiveLandmarks);
344 }
345 
346 double LandmarkRegistrationWidget::getAverageAccuracy(int& numActiveLandmarks)
347 {
348  std::map<QString, LandmarkProperty> props = mServices->patient()->getLandmarkProperties();
349 
350  double sum = 0;
351  numActiveLandmarks = 0;
352  std::map<QString, LandmarkProperty>::iterator it = props.begin();
353  for (; it != props.end(); ++it)
354  {
355  if (!it->second.getActive()) //we don't want to take into account not active landmarks
356  continue;
357  QString uid = it->first;
358  double val = this->getAccuracy(uid);
359  if (!similar(val, 1000.0))
360  {
361  sum = sum + val;
362  numActiveLandmarks++;
363  }
364  }
365  if (numActiveLandmarks == 0)
366  return 1000;
367  return sum / numActiveLandmarks;
368 }
369 
371 {
372  DataPtr fixedData = mServices->registration()->getFixedData();
373  if (!fixedData)
374  return 1000.0;
375 
376  Landmark masterLandmark = fixedData->getLandmarks()->getLandmarks()[uid];
377  Landmark targetLandmark = this->getTargetLandmarks()[uid];
378  if (masterLandmark.getUid().isEmpty() || targetLandmark.getUid().isEmpty())
379  return 1000.0;
380 
381  Vector3D p_master_master = masterLandmark.getCoord();
382  Vector3D p_target_target = targetLandmark.getCoord();
383  Transform3D rMmaster = fixedData->get_rMd();
384  Transform3D rMtarget = this->getTargetTransform();
385 
386  Vector3D p_target_r = rMtarget.coord(p_target_target);
387  Vector3D p_master_r = rMmaster.coord(p_master_master);
388 
389  return (p_target_r - p_master_r).length();
390 }
391 
393 {
394  if(mMouseClickSample->isChecked())
396  else
398 }
399 
401 {
403  return NULL;
404 
405  int row = mLandmarkTableWidget->currentRow();
406  int column = mLandmarkTableWidget->currentColumn();
407 
408  if((row < 0) && (mLandmarkTableWidget->rowCount() >= 0))
409  row = 0;
410  if((column < 0) && (mLandmarkTableWidget->columnCount() >= 0))
411  column = 0;
412 
413  QTableWidgetItem* item = mLandmarkTableWidget->item(row, column);
414 
415  return item;
416 }
417 
418 }//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
void pointSampled(Vector3D p_r)
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 currently 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