Fraxinus  17.12
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) 2008-2014, SINTEF Department of Medical Technology
5 All rights reserved.
6 
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions are met:
9 
10 1. Redistributions of source code must retain the above copyright notice,
11  this list of conditions and the following disclaimer.
12 
13 2. Redistributions in binary form must reproduce the above copyright notice,
14  this list of conditions and the following disclaimer in the documentation
15  and/or other materials provided with the distribution.
16 
17 3. Neither the name of the copyright holder nor the names of its contributors
18  may be used to endorse or promote products derived from this software
19  without specific prior written permission.
20 
21 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 =========================================================================*/
32 
34 
35 #include <sstream>
36 #include <QVBoxLayout>
37 #include <QPushButton>
38 #include <QTableWidget>
39 #include <QTableWidgetItem>
40 #include <QHeaderView>
41 #include <QLabel>
42 #include <QSlider>
43 #include <vtkDoubleArray.h>
44 #include <vtkImageData.h>
45 
46 #include "cxTypeConversions.h"
47 #include "cxManualTool.h"
48 #include "cxPatientModelService.h"
49 #include "cxRegistrationService.h"
50 #include "cxViewService.h"
51 #include"cxData.h"
52 #include "cxLogger.h"
53 #include "cxLandmark.h"
54 #include "cxTrackingService.h"
55 #include "cxLandmarkListener.h"
56 
57 namespace cx
58 {
59 LandmarkRegistrationWidget::LandmarkRegistrationWidget(RegServicesPtr services, QWidget* parent,
60  QString objectName, QString windowTitle, bool showAccuracy) :
61  RegistrationBaseWidget(services, parent, objectName, windowTitle), mVerticalLayout(new QVBoxLayout(this)),
62  mLandmarkTableWidget(new QTableWidget(this)), mAvarageAccuracyLabel(new QLabel(QString(" "), this)),
63  mLandmarkListener(new LandmarkListener(services)), mShowAccuracy(showAccuracy)
64 {
65  //table widget
66  connect(mLandmarkTableWidget, SIGNAL(cellClicked(int, int)), this, SLOT(cellClickedSlot(int, int)));
67  connect(mLandmarkTableWidget, SIGNAL(cellChanged(int,int)), this, SLOT(cellChangedSlot(int,int)));
68 
69  this->setLayout(mVerticalLayout);
70 }
71 
73 {
74 }
75 
77 {
78  if (row < 0 || column < 0)
79  return;
80 
82  reportDebug("mLandmarkTableWidget is null");
83 
84  mActiveLandmark = mLandmarkTableWidget->item(row, column)->data(Qt::UserRole).toString();
85 
86 
87  LandmarkMap targetData = this->getTargetLandmarks();
88  if (targetData.count(mActiveLandmark))
89  {
90  Vector3D p_d = targetData[mActiveLandmark].getCoord();
91  Vector3D p_r = this->getTargetTransform().coord(p_d);
92  Vector3D p_pr = mServices->patient()->get_rMpr().coord(p_r);
93  this->setManualToolPosition(p_r);
94  }
95 
96 }
97 
99 {
100  Transform3D rMpr = mServices->patient()->get_rMpr();
101  Vector3D p_pr = rMpr.inv().coord(p_r);
102 
103  // set the picked point as offset tip
104  ToolPtr tool = mServices->tracking()->getManualTool();
105  Vector3D offset = tool->get_prMt().vector(Vector3D(0, 0, tool->getTooltipOffset()));
106  p_pr -= offset;
107  p_r = rMpr.coord(p_pr);
108 
109  // TODO set center here will not do: must handle
110  mServices->patient()->setCenter(p_r);
111  Vector3D p0_pr = tool->get_prMt().coord(Vector3D(0, 0, 0));
112  tool->set_prMt(createTransformTranslate(p_pr - p0_pr) * tool->get_prMt());
113 }
114 
116 {
117  QWidget::showEvent(event);
118  mLandmarkListener->showRep();
120  connect(mServices->patient()->getPatientLandmarks().get(), &Landmarks::landmarkAdded, this, &LandmarkRegistrationWidget::landmarkUpdatedSlot);
121  connect(mServices->patient()->getPatientLandmarks().get(), &Landmarks::landmarkRemoved, this, &LandmarkRegistrationWidget::landmarkUpdatedSlot);
122 
124 
125 // mManager->restart();
126  mServices->registration()->setLastRegistrationTime(QDateTime::currentDateTime());
127  this->setModified();
128 
129 }
130 
132 {
133  QWidget::hideEvent(event);
135  disconnect(mServices->patient()->getPatientLandmarks().get(), &Landmarks::landmarkAdded, this, &LandmarkRegistrationWidget::landmarkUpdatedSlot);
136  disconnect(mServices->patient()->getPatientLandmarks().get(), &Landmarks::landmarkRemoved, this, &LandmarkRegistrationWidget::landmarkUpdatedSlot);
138  mLandmarkListener->hideRep();
139 }
140 
142 {
143  mLandmarkTableWidget->blockSignals(true);
144  mLandmarkTableWidget->clear();
145 
146  QString fixedName;
147  DataPtr fixedData = boost::dynamic_pointer_cast<Data>(mServices->registration()->getFixedData());
148  if (fixedData)
149  fixedName = fixedData->getName();
150 
151  std::vector<Landmark> landmarks = this->getAllLandmarks();
152  LandmarkMap targetData = this->getTargetLandmarks();
153  Transform3D rMtarget = this->getTargetTransform();
154 
155  //ready the table widget
156  mLandmarkTableWidget->setRowCount((int)landmarks.size());
157  QStringList headerItems(QStringList() << "Name" << "Status" << "Coordinates");
158  if (mShowAccuracy)
159  {
160  mLandmarkTableWidget->setColumnCount(4);
161  headerItems.append("Accuracy (mm)");
162  }
163  else
164  mLandmarkTableWidget->setColumnCount(3);
165 
166  mLandmarkTableWidget->setHorizontalHeaderLabels(headerItems);
167  mLandmarkTableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
168  mLandmarkTableWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
169 
170  for (unsigned i = 0; i < landmarks.size(); ++i)
171  {
172  std::vector<QTableWidgetItem*> items(4); // name, status, coordinates, accuracy
173 
174  LandmarkProperty prop = mServices->patient()->getLandmarkProperties()[landmarks[i].getUid()];
175  Vector3D coord = landmarks[i].getCoord();
176  coord = rMtarget.coord(coord); // display coordinates in space r (in principle, this means all coords should be equal)
177 
178  items[0] = new QTableWidgetItem(qstring_cast(prop.getName()));
179  items[0]->setToolTip(QString("Landmark name. Double-click to rename."));
180 
181  items[1] = new QTableWidgetItem;
182 
183  if (prop.getActive())
184  items[1]->setCheckState(Qt::Checked);
185  else
186  items[1]->setCheckState(Qt::Unchecked);
187  items[1]->setToolTip(QString("Check to use landmark in current registration."));
188 
189  QString coordText = "Not sampled";
190  if (targetData.count(prop.getUid()))
191  {
192  int width = 5;
193  int prec = 1;
194  coordText = tr("(%1, %2, %3)").arg(coord[0], width, 'f', prec).arg(coord[1], width, 'f', prec).arg(
195  coord[2], width, 'f', prec);
196  }
197 
198  items[2] = new QTableWidgetItem(coordText);
199  items[2]->setToolTip(QString("Landmark coordinates of target [%1] in reference space.").arg(this->getTargetName()));
200 
201  items[3] = new QTableWidgetItem(tr("%1").arg(this->getAccuracy(landmarks[i].getUid())));
202  items[3]->setToolTip(QString("Distance from target [%1] to fixed [%2].").arg(this->getTargetName()).arg(fixedName));
203 
204  for (unsigned j = 0; j < items.size(); ++j)
205  {
206  items[j]->setData(Qt::UserRole, qstring_cast(prop.getUid()));
207  mLandmarkTableWidget->setItem(i, j, items[j]);
208  }
209 
210  //highlight selected row
211  if (prop.getUid() == mActiveLandmark)
212  {
213  mLandmarkTableWidget->setCurrentItem(items[2]);
214  }
215  }
216 
218  mLandmarkTableWidget->blockSignals(false);
219 }
220 
222 {
223  mActiveLandmark = uid;
224  this->setModified();
225 }
226 
231 {
232  std::vector<Landmark> lm = this->getAllLandmarks();
233 
234  for (int i=0; i<lm.size()-1; ++i)
235  {
236  if (lm[i].getUid()==mActiveLandmark)
237  {
238  return lm[i+1].getUid();
239  }
240  }
241 
242  return "";
243 }
244 
245 std::vector<Landmark> LandmarkRegistrationWidget::getAllLandmarks() const
246 {
247  std::vector<Landmark> retval;
248  LandmarkMap targetData = this->getTargetLandmarks();
249  std::map<QString, LandmarkProperty> dataData = mServices->patient()->getLandmarkProperties();
250  std::map<QString, LandmarkProperty>::iterator iter;
251 
252  for (iter = dataData.begin(); iter != dataData.end(); ++iter)
253  {
254  if (targetData.count(iter->first))
255  retval.push_back(targetData[iter->first]);
256  else
257  retval.push_back(Landmark(iter->first));
258  }
259 
260  std::sort(retval.begin(), retval.end());
261 
262  return retval;
263 }
264 
266 {
267  QTableWidgetItem* item = mLandmarkTableWidget->item(row, column);
268  QString uid = item->data(Qt::UserRole).toString();
269 
270  if (column == 0)
271  {
272  QString name = item->text();
273  mServices->patient()->setLandmarkName(uid, name);
274  }
275  if (column == 1)
276  {
277  Qt::CheckState state = item->checkState();
278  mServices->patient()->setLandmarkActive(uid, state == Qt::Checked);
279  this->performRegistration(); // automatic when changing active state (Mantis #0000674)s
280  }
281  if (column == 2)
282  {
283  QString val = item->text();
284  // remove formatting stuff:
285  val = val.replace('(', " ");
286  val = val.replace(')', " ");
287  val = val.replace(',', " ");
288 
289  Transform3D rMtarget = this->getTargetTransform();
290 
291  Vector3D p_r = Vector3D::fromString(val);
292  Vector3D p_target = rMtarget.inv().coord(p_r);
293  this->setTargetLandmark(uid, p_target);
294  }
295 }
296 
298 {
299 // - This has too many side effects when we use the landmarks for several different registrations,
300 // i.e. image2image, patient, fast... Rather register explicitly, and add it to the buttons where you
301 // want the automation, such as in the patient reg sampler. (Mantis #0000674)
302 // this->performRegistration();
303  this->setModified();
304 }
305 
307 {
308  QString fixedName;
309  DataPtr fixedData = boost::dynamic_pointer_cast<Data>(mServices->registration()->getFixedData());
310  if (fixedData)
311  fixedName = fixedData->getName();
312 
313  if(this->isAverageAccuracyValid() && mShowAccuracy)
314  {
315  mAvarageAccuracyLabel->setText(tr("Mean accuracy %1 mm").arg(this->getAverageAccuracy(), 0, 'f', 2));
316  mAvarageAccuracyLabel->setToolTip(QString("Average landmark accuracy from target [%1] to fixed [%2].").arg(this->getTargetName()).arg(fixedName));
317  }
318  else
319  {
320  mAvarageAccuracyLabel->setText(" ");
321  mAvarageAccuracyLabel->setToolTip("");
322  }
323 }
324 
325 bool LandmarkRegistrationWidget::isAverageAccuracyValid()
326 {
327  int numActiveLandmarks = 0;
328  this->getAverageAccuracy(numActiveLandmarks);
329  if(numActiveLandmarks < 3)
330  return false;
331  return true;
332 }
333 
335 {
336  int numActiveLandmarks = 0;
337  return this->getAverageAccuracy(numActiveLandmarks);
338 }
339 
340 double LandmarkRegistrationWidget::getAverageAccuracy(int& numActiveLandmarks)
341 {
342  std::map<QString, LandmarkProperty> props = mServices->patient()->getLandmarkProperties();
343 
344  double sum = 0;
345  numActiveLandmarks = 0;
346  std::map<QString, LandmarkProperty>::iterator it = props.begin();
347  for (; it != props.end(); ++it)
348  {
349  if (!it->second.getActive()) //we don't want to take into account not active landmarks
350  continue;
351  QString uid = it->first;
352  double val = this->getAccuracy(uid);
353  if (!similar(val, 1000.0))
354  {
355  sum = sum + val;
356  numActiveLandmarks++;
357  }
358  }
359  if (numActiveLandmarks == 0)
360  return 1000;
361  return sum / numActiveLandmarks;
362 }
363 
365 {
366  DataPtr fixedData = mServices->registration()->getFixedData();
367  if (!fixedData)
368  return 1000.0;
369 
370  Landmark masterLandmark = fixedData->getLandmarks()->getLandmarks()[uid];
371  Landmark targetLandmark = this->getTargetLandmarks()[uid];
372  if (masterLandmark.getUid().isEmpty() || targetLandmark.getUid().isEmpty())
373  return 1000.0;
374 
375  Vector3D p_master_master = masterLandmark.getCoord();
376  Vector3D p_target_target = targetLandmark.getCoord();
377  Transform3D rMmaster = fixedData->get_rMd();
378  Transform3D rMtarget = this->getTargetTransform();
379 
380  Vector3D p_target_r = rMtarget.coord(p_target_target);
381  Vector3D p_master_r = rMmaster.coord(p_master_master);
382 
383  return (p_target_r - p_master_r).length();
384 }
385 
386 }//namespace cx
QString qstring_cast(const T &val)
QString getUid() const
Definition: cxLandmark.cpp:54
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:61
Transform3D Transform3D
Transform3D is a representation of an affine 3D transform.
void landmarkAdded(QString uid)
QString getUid() const
Definition: cxLandmark.cpp:195
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:90
virtual void cellClickedSlot(int row, int column)
when a landmark is selected from the table
boost::shared_ptr< class RegServices > RegServicesPtr
Definition: cxRegServices.h:41
Transform3D createTransformTranslate(const Vector3D &translation)
virtual void prePaintEvent()
populates the table widget
Vector3D getCoord() const
Definition: cxLandmark.cpp:59
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:63
virtual LandmarkMap getTargetLandmarks() const =0
Superclass for all data objects.
Definition: cxData.h:109
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:205
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:200
void landmarkRemoved(QString uid)
void reportDebug(QString msg)
Definition: cxLogger.cpp:89
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