NorMIT-nav  16.5
An IGT application
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
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) :
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))
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  mLandmarkTableWidget->setColumnCount(4);
158  QStringList headerItems(QStringList() << "Name" << "Status" << "Coordinates" << "Accuracy (mm)");
159  mLandmarkTableWidget->setHorizontalHeaderLabels(headerItems);
160  mLandmarkTableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
161  mLandmarkTableWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
162 
163  for (unsigned i = 0; i < landmarks.size(); ++i)
164  {
165  std::vector<QTableWidgetItem*> items(4); // name, status, coordinates, accuracy
166 
167  LandmarkProperty prop = mServices->patient()->getLandmarkProperties()[landmarks[i].getUid()];
168  Vector3D coord = landmarks[i].getCoord();
169  coord = rMtarget.coord(coord); // display coordinates in space r (in principle, this means all coords should be equal)
170 
171  items[0] = new QTableWidgetItem(qstring_cast(prop.getName()));
172  items[0]->setToolTip(QString("Landmark name. Double-click to rename."));
173 
174  items[1] = new QTableWidgetItem;
175 
176  if (prop.getActive())
177  items[1]->setCheckState(Qt::Checked);
178  else
179  items[1]->setCheckState(Qt::Unchecked);
180  items[1]->setToolTip(QString("Check to use landmark in current registration."));
181 
182  QString coordText = "Not sampled";
183  if (targetData.count(prop.getUid()))
184  {
185  int width = 5;
186  int prec = 1;
187  coordText = tr("(%1, %2, %3)").arg(coord[0], width, 'f', prec).arg(coord[1], width, 'f', prec).arg(
188  coord[2], width, 'f', prec);
189  }
190 
191  items[2] = new QTableWidgetItem(coordText);
192  items[2]->setToolTip(QString("Landmark coordinates of target [%1] in reference space.").arg(this->getTargetName()));
193 
194  items[3] = new QTableWidgetItem(tr("%1").arg(this->getAccuracy(landmarks[i].getUid())));
195  items[3]->setToolTip(QString("Distance from target [%1] to fixed [%2].").arg(this->getTargetName()).arg(fixedName));
196 
197  for (unsigned j = 0; j < items.size(); ++j)
198  {
199  items[j]->setData(Qt::UserRole, qstring_cast(prop.getUid()));
200  mLandmarkTableWidget->setItem(i, j, items[j]);
201  }
202 
203  //highlight selected row
204  if (prop.getUid() == mActiveLandmark)
205  {
206  mLandmarkTableWidget->setCurrentItem(items[2]);
207  }
208  }
209 
211  mLandmarkTableWidget->blockSignals(false);
212 }
213 
215 {
216  mActiveLandmark = uid;
217  this->setModified();
218 }
219 
224 {
225  std::vector<Landmark> lm = this->getAllLandmarks();
226 
227  for (int i=0; i<lm.size()-1; ++i)
228  {
229  if (lm[i].getUid()==mActiveLandmark)
230  {
231  return lm[i+1].getUid();
232  }
233  }
234 
235  return "";
236 }
237 
238 std::vector<Landmark> LandmarkRegistrationWidget::getAllLandmarks() const
239 {
240  std::vector<Landmark> retval;
241  LandmarkMap targetData = this->getTargetLandmarks();
242  std::map<QString, LandmarkProperty> dataData = mServices->patient()->getLandmarkProperties();
243  std::map<QString, LandmarkProperty>::iterator iter;
244 
245  for (iter = dataData.begin(); iter != dataData.end(); ++iter)
246  {
247  if (targetData.count(iter->first))
248  retval.push_back(targetData[iter->first]);
249  else
250  retval.push_back(Landmark(iter->first));
251  }
252 
253  std::sort(retval.begin(), retval.end());
254 
255  return retval;
256 }
257 
259 {
260  QTableWidgetItem* item = mLandmarkTableWidget->item(row, column);
261  QString uid = item->data(Qt::UserRole).toString();
262 
263  if (column == 0)
264  {
265  QString name = item->text();
266  mServices->patient()->setLandmarkName(uid, name);
267  }
268  if (column == 1)
269  {
270  Qt::CheckState state = item->checkState();
271  mServices->patient()->setLandmarkActive(uid, state == Qt::Checked);
272  this->performRegistration(); // automatic when changing active state (Mantis #0000674)s
273  }
274  if (column == 2)
275  {
276  QString val = item->text();
277  // remove formatting stuff:
278  val = val.replace('(', " ");
279  val = val.replace(')', " ");
280  val = val.replace(',', " ");
281 
282  Transform3D rMtarget = this->getTargetTransform();
283 
284  Vector3D p_r = Vector3D::fromString(val);
285  Vector3D p_target = rMtarget.inv().coord(p_r);
286  this->setTargetLandmark(uid, p_target);
287  }
288 }
289 
291 {
292 // - This has too many side effects when we use the landmarks for several different registrations,
293 // i.e. image2image, patient, fast... Rather register explicitly, and add it to the buttons where you
294 // want the automation, such as in the patient reg sampler. (Mantis #0000674)
295 // this->performRegistration();
296  this->setModified();
297 }
298 
300 {
301  QString fixedName;
302  DataPtr fixedData = boost::dynamic_pointer_cast<Data>(mServices->registration()->getFixedData());
303  if (fixedData)
304  fixedName = fixedData->getName();
305 
306  if(this->isAverageAccuracyValid())
307  {
308  mAvarageAccuracyLabel->setText(tr("Mean accuracy %1 mm").arg(this->getAverageAccuracy(), 0, 'f', 2));
309  mAvarageAccuracyLabel->setToolTip(QString("Average landmark accuracy from target [%1] to fixed [%2].").arg(this->getTargetName()).arg(fixedName));
310  }
311  else
312  {
313  mAvarageAccuracyLabel->setText(" ");
314  mAvarageAccuracyLabel->setToolTip("");
315  }
316 }
317 
318 bool LandmarkRegistrationWidget::isAverageAccuracyValid()
319 {
320  int numActiveLandmarks = 0;
321  this->getAverageAccuracy(numActiveLandmarks);
322  if(numActiveLandmarks < 3)
323  return false;
324  return true;
325 }
326 
328 {
329  int numActiveLandmarks = 0;
330  return this->getAverageAccuracy(numActiveLandmarks);
331 }
332 
333 double LandmarkRegistrationWidget::getAverageAccuracy(int& numActiveLandmarks)
334 {
335  std::map<QString, LandmarkProperty> props = mServices->patient()->getLandmarkProperties();
336 
337  double sum = 0;
338  numActiveLandmarks = 0;
339  std::map<QString, LandmarkProperty>::iterator it = props.begin();
340  for (; it != props.end(); ++it)
341  {
342  if (!it->second.getActive()) //we don't want to take into account not active landmarks
343  continue;
344  QString uid = it->first;
345  double val = this->getAccuracy(uid);
346  if (!similar(val, 1000.0))
347  {
348  sum = sum + val;
349  numActiveLandmarks++;
350  }
351  }
352  if (numActiveLandmarks == 0)
353  return 1000;
354  return sum / numActiveLandmarks;
355 }
356 
358 {
359  DataPtr fixedData = mServices->registration()->getFixedData();
360  if (!fixedData)
361  return 1000.0;
362 
363  Landmark masterLandmark = fixedData->getLandmarks()->getLandmarks()[uid];
364  Landmark targetLandmark = this->getTargetLandmarks()[uid];
365  if (masterLandmark.getUid().isEmpty() || targetLandmark.getUid().isEmpty())
366  return 1000.0;
367 
368  Vector3D p_master_master = masterLandmark.getCoord();
369  Vector3D p_target_target = targetLandmark.getCoord();
370  Transform3D rMmaster = fixedData->get_rMd();
371  Transform3D rMtarget = this->getTargetTransform();
372 
373  Vector3D p_target_r = rMtarget.coord(p_target_target);
374  Vector3D p_master_r = rMmaster.coord(p_master_master);
375 
376  return (p_target_r - p_master_r).length();
377 }
378 
379 }//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
boost::shared_ptr< class RegServices > RegServicesPtr
Definition: cxRegServices.h:41
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:196
QVBoxLayout * mVerticalLayout
vertical layout is used
bool similar(const DoubleBoundingBox3D &a, const DoubleBoundingBox3D &b, double tol)
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:89
virtual void cellClickedSlot(int row, int column)
when a landmark is selected from the table
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
QString mActiveLandmark
uid of surrently selected landmark.
QString getName() const
Definition: cxLandmark.cpp:206
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:201
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
boost::shared_ptr< class Tool > ToolPtr