CustusX  15.4.0-beta
An IGT application
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
cxRegistrationImplService.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 <ctkPluginContext.h>
36 #include <ctkServiceTracker.h>
37 #include <vtkPoints.h>
38 #include <vtkLandmarkTransform.h>
39 #include <vtkMatrix4x4.h>
40 
41 #include "cxData.h"
42 #include "cxTypeConversions.h"
43 #include "cxLogger.h"
45 #include "cxFrameForest.h"
46 #include "cxPatientModelService.h"
48 #include "cxLandmark.h"
52 #include "cxXMLNodeWrapper.h"
53 
54 namespace cx
55 {
56 
58  mLastRegistrationTime(QDateTime::currentDateTime()),
59  mContext(context),
60  mPatientModelService(new PatientModelServiceProxy(context)),
61  mSession(SessionStorageServiceProxy::create(context))
62 {
63 // mLastRegistrationTime = QDateTime::currentDateTime();
64 
65  connect(mSession.get(), &SessionStorageService::cleared, this, &RegistrationImplService::clearSlot);
66  connect(mSession.get(), &SessionStorageService::isLoading, this, &RegistrationImplService::duringLoadPatientSlot);
67  connect(mSession.get(), &SessionStorageService::isSaving, this, &RegistrationImplService::duringSavePatientSlot);
68 
69 // connect(mPatientModelService.get(), &PatientModelService::isSaving, this, &RegistrationImplService::duringSavePatientSlot);
70 // connect(mPatientModelService.get(), &PatientModelService::isLoading, this, &RegistrationImplService::duringLoadPatientSlot);
71 // connect(mPatientModelService.get(), &PatientModelService::cleared, this, &RegistrationImplService::clearSlot);
72 }
73 
75 {
76 }
77 
78 void RegistrationImplService::duringSavePatientSlot(QDomElement& node)
79 {
80  XMLNodeAdder root(node);
81  QDomElement managerNode = root.descend("managers").node().toElement();
82  this->addXml(managerNode);
83 // QDomElement managerNode = mPatientModelService->getCurrentWorkingElement("managers");
84 // this->addXml(managerNode);
85 }
86 
87 void RegistrationImplService::duringLoadPatientSlot(QDomElement& node)
88 {
89  XMLNodeParser root(node);
90  QDomElement registrationManager = root.descend("managers/registrationManager").node().toElement();
91  if (!registrationManager.isNull())
92  this->parseXml(registrationManager);
93 
94 // QDomElement registrationManager = mPatientModelService->getCurrentWorkingElement("managers/registrationManager");
95 // this->parseXml(registrationManager);
96 }
97 
98 void RegistrationImplService::addXml(QDomNode& parentNode)
99 {
100  QDomDocument doc = parentNode.ownerDocument();
101  QDomElement base = doc.createElement("registrationManager");
102  parentNode.appendChild(base);
103 
104  QDomElement fixedDataNode = doc.createElement("fixedDataUid");
105  DataPtr fixedData = this->getFixedData();
106  if(fixedData)
107  {
108  fixedDataNode.appendChild(doc.createTextNode(fixedData->getUid()));
109  }
110  base.appendChild(fixedDataNode);
111 
112  QDomElement movingDataNode = doc.createElement("movingDataUid");
113  DataPtr movingData = this->getMovingData();
114  if(movingData)
115  {
116  movingDataNode.appendChild(doc.createTextNode(movingData->getUid()));
117  }
118  base.appendChild(movingDataNode);
119 }
120 
121 void RegistrationImplService::parseXml(QDomNode& dataNode)
122 {
123  QString fixedData = dataNode.namedItem("fixedDataUid").toElement().text();
124  this->setFixedData(mPatientModelService->getData(fixedData));
125 
126  QString movingData = dataNode.namedItem("movingDataUid").toElement().text();
127  this->setMovingData(mPatientModelService->getData(movingData));
128 }
129 
130 void RegistrationImplService::clearSlot()
131 {
132  this->setLastRegistrationTime(QDateTime());
133  this->setFixedData(DataPtr());
134 }
135 
136 
137 
139 {
140  mMovingData = movingData;
141  emit movingDataChanged(this->getMovingDataUid());
142 }
143 
145 {
146  if(mFixedData == fixedData)
147  return;
148 
149  mFixedData = fixedData;
150  if (mFixedData)
151  report("Registration fixed data set to "+mFixedData->getUid());
152  emit fixedDataChanged(this->getFixedDataUid());
153 }
154 
156 {
157  return mMovingData;
158 }
159 
161 {
162  return mFixedData;
163 }
164 
166 {
167  return mLastRegistrationTime;
168 }
169 
170 
172 {
173  mLastRegistrationTime = time;
174 }
175 
177 {
178  DataPtr fixedImage = this->getFixedData();
179 
180  if(!fixedImage)
181  {
182  reportError("The fixed data is not set, cannot do patient registration!");
183  return;
184  }
185  LandmarkMap fixedLandmarks = fixedImage->getLandmarks()->getLandmarks();
186  LandmarkMap toolLandmarks = mPatientModelService->getPatientLandmarks()->getLandmarks();
187 
188  this->writePreLandmarkRegistration(fixedImage->getName(), fixedImage->getLandmarks()->getLandmarks());
189  this->writePreLandmarkRegistration("physical", toolLandmarks);
190 
191  std::vector<QString> landmarks = this->getUsableLandmarks(fixedLandmarks, toolLandmarks);
192 
193  vtkPointsPtr p_ref = this->convertTovtkPoints(landmarks, fixedLandmarks, fixedImage->get_rMd());
194  vtkPointsPtr p_pr = this->convertTovtkPoints(landmarks, toolLandmarks, Transform3D::Identity());
195 
196  // ignore if too few data.
197  if (p_ref->GetNumberOfPoints() < 3)
198  return;
199 
200  bool ok = false;
201  Transform3D rMpr = this->performLandmarkRegistration(p_pr, p_ref, &ok);
202  if (!ok)
203  {
204  reportError("P-I Landmark registration: Failed to register: [" + qstring_cast(p_pr->GetNumberOfPoints()) + "p]");
205  return;
206  }
207 
208  this->applyPatientRegistration(rMpr, "Patient Landmark");
209 }
210 
212 {
213  DataPtr fixedImage = this->getFixedData();
214  if(!fixedImage)
215  {
216  reportError("The fixed data is not set, cannot do image registration!");
217  return;
218  }
219 
220  LandmarkMap fixedLandmarks = fixedImage->getLandmarks()->getLandmarks();
221  LandmarkMap toolLandmarks = mPatientModelService->getPatientLandmarks()->getLandmarks();
222 
223  this->writePreLandmarkRegistration(fixedImage->getName(), fixedImage->getLandmarks()->getLandmarks());
224  this->writePreLandmarkRegistration("physical", toolLandmarks);
225 
226  std::vector<QString> landmarks = this->getUsableLandmarks(fixedLandmarks, toolLandmarks);
227 
228  Transform3D rMd = fixedImage->get_rMd();
229  Transform3D rMpr_old = mPatientModelService->get_rMpr();
230  std::vector<Vector3D> p_pr_old = this->convertAndTransformToPoints(landmarks, fixedLandmarks, rMpr_old.inv()*rMd);
231  std::vector<Vector3D> p_pr_new = this->convertAndTransformToPoints(landmarks, toolLandmarks, Transform3D::Identity());
232 
233  // ignore if too few data.
234  if (p_pr_old.size() < 1)
235  return;
236 
237  LandmarkTranslationRegistration landmarkTransReg;
238  bool ok = false;
239  Transform3D pr_oldMpr_new = landmarkTransReg.registerPoints(p_pr_old, p_pr_new, &ok);
240  if (!ok)
241  {
242  reportError("Fast translation registration: Failed to register: [" + qstring_cast(p_pr_old.size()) + "points]");
243  return;
244  }
245 
246  this->applyPatientRegistration(rMpr_old*pr_oldMpr_new, "Fast Translation");
247 }
248 
256 {
257  //create a marked(m) space tm, which is related to tool space (t) as follows:
258  //the tool is defined in DICOM space such that
259  //the tool points toward the patients feet and the spheres faces the same
260  //direction as the nose
261  Transform3D tMpr = prMt.inv();
262 
263  Transform3D tmMpr = tMtm * tMpr;
264 
265  this->applyPatientRegistration(tmMpr, "Fast Orientation");
266 
267  // also apply the fast translation registration if any (this frees us form doing stuff in a well-defined order.)
269 }
270 
271 std::vector<Vector3D> RegistrationImplService::convertAndTransformToPoints(const std::vector<QString>& uids, const LandmarkMap& data, Transform3D M)
272 {
273  std::vector<Vector3D> retval;
274 
275  for (unsigned i=0; i<uids.size(); ++i)
276  {
277  QString uid = uids[i];
278  Vector3D p = M.coord(data.find(uid)->second.getCoord());
279  retval.push_back(p);
280  }
281  return retval;
282 }
283 
288 std::vector<QString> RegistrationImplService::getUsableLandmarks(const LandmarkMap& data_a, const LandmarkMap& data_b)
289 {
290  std::vector<QString> retval;
291  std::map<QString, LandmarkProperty> props = mPatientModelService->getLandmarkProperties();
292  std::map<QString, LandmarkProperty>::iterator iter;
293 
294  for (iter=props.begin(); iter!=props.end(); ++iter)
295  {
296  QString uid = iter->first;
297  if (data_a.count(uid) && data_b.count(uid) && iter->second.getActive())
298  retval.push_back(uid);
299  }
300  return retval;
301 }
302 
304 {
305  //check that the fixed data is set
306  DataPtr fixedImage = this->getFixedData();
307  if(!fixedImage)
308  {
309  reportError("The fixed data is not set, cannot do landmark image registration!");
310  return;
311  }
312 
313  //check that the moving data is set
314  DataPtr movingImage = this->getMovingData();
315  if(!movingImage)
316  {
317  reportError("The moving data is not set, cannot do landmark image registration!");
318  return;
319  }
320 
321  // ignore self-registration, this gives no effect bestcase, buggy behaviour worstcase (has been observed)
322  if(movingImage==fixedImage)
323  {
324  reportError("The moving and fixed are equal, ignoring landmark image registration!");
325  return;
326  }
327 
328  LandmarkMap fixedLandmarks = fixedImage->getLandmarks()->getLandmarks();
329  LandmarkMap imageLandmarks = movingImage->getLandmarks()->getLandmarks();
330 
331  this->writePreLandmarkRegistration(fixedImage->getName(), fixedImage->getLandmarks()->getLandmarks());
332  this->writePreLandmarkRegistration(movingImage->getName(), movingImage->getLandmarks()->getLandmarks());
333 
334  std::vector<QString> landmarks = getUsableLandmarks(fixedLandmarks, imageLandmarks);
335  vtkPointsPtr p_fixed_r = convertTovtkPoints(landmarks, fixedLandmarks, fixedImage->get_rMd());
336  vtkPointsPtr p_moving_r = convertTovtkPoints(landmarks, imageLandmarks, movingImage->get_rMd());
337 
338  int minNumberOfPoints = 3;
339  if (translationOnly)
340  minNumberOfPoints = 1;
341 
342  // ignore if too few data.
343  if (p_fixed_r->GetNumberOfPoints() < minNumberOfPoints)
344  {
345  reportError(
346  QString("Found %1 corresponding landmarks, need %2, cannot do landmark image registration!")
347  .arg(p_fixed_r->GetNumberOfPoints())
348  .arg(minNumberOfPoints)
349  );
350  return;
351  }
352 
353  bool ok = false;
354  QString idString;
355  Transform3D delta;
356 
357  if (translationOnly)
358  {
359  LandmarkTranslationRegistration landmarkTransReg;
360  delta = landmarkTransReg.registerPoints(convertVtkPointsToPoints(p_fixed_r), convertVtkPointsToPoints(p_moving_r), &ok);
361  idString = QString("Image to Image Landmark Translation");
362  }
363  else
364  {
365  Transform3D rMd;
366  delta = this->performLandmarkRegistration(p_moving_r, p_fixed_r, &ok);
367  idString = QString("Image to Image Landmark");
368  }
369 
370  if (!ok)
371  {
372  reportError("I-I Landmark registration: Failed to register: [" + qstring_cast(p_moving_r->GetNumberOfPoints()) + "p], "+ movingImage->getName());
373  return;
374  }
375 
376  this->applyImage2ImageRegistration(delta, idString);
377 }
378 
379 std::vector<Vector3D> RegistrationImplService::convertVtkPointsToPoints(vtkPointsPtr base)
380 {
381  std::vector<Vector3D> retval;
382 
383  for (int i=0; i<base->GetNumberOfPoints(); ++i)
384  {
385  Vector3D p(base->GetPoint(i));
386  retval.push_back(p);
387  }
388  return retval;
389 }
390 
394 Transform3D RegistrationImplService::performLandmarkRegistration(vtkPointsPtr source, vtkPointsPtr target, bool* ok) const
395 {
396  *ok = false;
397 
398  // too few data samples: ignore
399  if (source->GetNumberOfPoints() < 3)
400  {
401  return Transform3D::Identity();
402  }
403 
404  vtkLandmarkTransformPtr landmarktransform = vtkLandmarkTransformPtr::New();
405  landmarktransform->SetSourceLandmarks(source);
406  landmarktransform->SetTargetLandmarks(target);
407  landmarktransform->SetModeToRigidBody();
408  source->Modified();
409  target->Modified();
410  landmarktransform->Update();
411 
412  Transform3D tar_M_src(landmarktransform->GetMatrix());
413 
414  if (QString::number(tar_M_src(0,0))=="nan") // harry but quick way to check badness of transform...
415  {
416  return Transform3D::Identity();
417  }
418 
419  *ok = true;
420  return tar_M_src;
421 }
422 
424 {
425  RegistrationTransform regTrans(delta_pre_rMd, QDateTime::currentDateTime(), description);
426  regTrans.mFixed = mFixedData ? mFixedData->getUid() : "";
427  regTrans.mMoving = mMovingData ? mMovingData->getUid() : "";
428 
429  this->updateRegistration(mLastRegistrationTime, regTrans, mMovingData);
430 
431  mLastRegistrationTime = regTrans.mTimestamp;
432  reportSuccess(QString("Image registration [%1] has been performed on %2").arg(description).arg(regTrans.mMoving) );
433 }
434 
435 //PatientModelService* RegistrationImplService::getPatientModelService()
436 //{
437 // ctkServiceTracker<PatientModelService*> tracker(mContext);
438 // tracker.open();
439 // PatientModelService* service = tracker.getService(); // get arbitrary instance of this type
440 // if(!service)
441 // reportError("RegistrationImplService can't access PatientModelService");
442 // return service;
443 //}
444 
446 {
447  RegistrationTransform regTrans(rMpr_new, QDateTime::currentDateTime(), description);
448  regTrans.mFixed = mFixedData ? mFixedData->getUid() : "";
449 
450  mPatientModelService->updateRegistration_rMpr(mLastRegistrationTime, regTrans);
451 
452  mLastRegistrationTime = regTrans.mTimestamp;
453  reportSuccess(QString("Patient registration [%1] has been performed.").arg(description));
454 }
455 
461 void RegistrationImplService::updateRegistration(QDateTime oldTime, RegistrationTransform deltaTransform, DataPtr data)
462 {
463  RegistrationApplicator applicator(mPatientModelService->getData());
464  applicator.updateRegistration(oldTime, deltaTransform, data);
465  mPatientModelService->autoSave();
466 }
467 
476 {
477  Transform3D rMpr = mPatientModelService->get_rMpr();
478 
479  //create a marked(m) space tm, which is related to tool space (t) as follows:
480  //the tool is defined in DICOM space such that
481  //the tool points toward the patients feet and the spheres faces the same
482  //direction as the nose
483  Transform3D tMpr = prMt.inv();
484 
485  // this is the new patient registration:
486  Transform3D tmMpr = tMtm * tMpr;
487  // the change in pat reg becomes:
488  Transform3D F = tmMpr * rMpr.inv();
489 
490  QString description("Patient Orientation");
491 
492  QDateTime oldTime = this->getLastRegistrationTime(); // time of previous reg
493  this->applyPatientRegistration(tmMpr, description);
494 
495  // now apply the inverse of F to all data,
496  // thus ensuring the total path from pr to d_i is unchanged:
497  Transform3D delta_pre_rMd = F;
498 
499 
500  // use the same registration time as generated in the applyPatientRegistration() above:
501  RegistrationTransform regTrans(delta_pre_rMd, this->getLastRegistrationTime(), description);
502 
503  std::map<QString,DataPtr> data = mPatientModelService->getData();
504  // update the transform on all target data:
505  for (std::map<QString,DataPtr>::iterator iter = data.begin(); iter!=data.end(); ++iter)
506  {
507  DataPtr current = iter->second;
508  RegistrationTransform newTransform = regTrans;
509  newTransform.mValue = regTrans.mValue * current->get_rMd();
510  current->get_rMd_History()->updateRegistration(oldTime, newTransform);
511 
512  report("Updated registration of data " + current->getName());
513  std::cout << "rMd_new\n" << newTransform.mValue << std::endl;
514  }
515 
516  this->setLastRegistrationTime(regTrans.mTimestamp);
517 
518  reportSuccess("Patient Orientation has been performed");
519 }
520 
521 void RegistrationImplService::writePreLandmarkRegistration(QString name, LandmarkMap landmarks)
522 {
523  QStringList lm;
524  for (LandmarkMap::iterator iter=landmarks.begin(); iter!=landmarks.end(); ++iter)
525  {
526  lm << mPatientModelService->getLandmarkProperties()[iter->second.getUid()].getName();
527  }
528 
529  QString msg = QString("Preparing to register [%1] containing the landmarks: [%2]").arg(name).arg(lm.join(","));
530  report(msg);
531 }
532 
539 vtkPointsPtr RegistrationImplService::convertTovtkPoints(const std::vector<QString>& uids, const LandmarkMap& data, Transform3D M)
540 {
541  vtkPointsPtr retval = vtkPointsPtr::New();
542 
543  for (unsigned i=0; i<uids.size(); ++i)
544  {
545  QString uid = uids[i];
546  Vector3D p = M.coord(data.find(uid)->second.getCoord());
547  retval->InsertNextPoint(p.begin());
548  }
549  return retval;
550 }
551 
553 {
554  return false;
555 }
556 
557 } /* namespace cx */
QString qstring_cast(const T &val)
void reportError(QString msg)
Definition: cxLogger.cpp:92
void isLoading(QDomElement &root)
emitted while loading a session. Xml storage is available, getRootFolder() is set to loaded value...
RegistrationImplService(ctkPluginContext *context)
virtual void doFastRegistration_Orientation(const Transform3D &tMtm, const Transform3D &prMt)
vtkSmartPointer< class vtkLandmarkTransform > vtkLandmarkTransformPtr
Transform3D Transform3D
Transform3D is a representation of an affine 3D transform.
virtual void doImageRegistration(bool translationOnly)
virtual void setFixedData(DataPtr data)
virtual void doFastRegistration_Translation()
use the landmarks in master image and patient to perform a translation-only landmark registration ...
void fixedDataChanged(QString uid)
boost::shared_ptr< class Data > DataPtr
QDateTime mTimestamp
time the transform was registrated.
Transform3D registerPoints(std::vector< Vector3D > ref, std::vector< Vector3D > target, bool *ok)
Transform3D mValue
value of transform
virtual void updateRegistration(QDateTime oldTime, RegistrationTransform deltaTransform, DataPtr data)
virtual void setMovingData(DataPtr data)
virtual void doPatientRegistration()
registrates the fixed image to the patient
void reportSuccess(QString msg)
Definition: cxLogger.cpp:93
void cleared()
emitted when session is cleared, before isLoading is called
virtual void setLastRegistrationTime(QDateTime time)
Eigen::Vector3d Vector3D
Vector3D is a representation of a point or vector in 3D.
Definition: cxVector3D.h:63
A registration event and its transform.
void movingDataChanged(QString uid)
virtual void applyPatientOrientation(const Transform3D &tMtm, const Transform3D &prMt)
Identical to doFastRegistration_Orientation(), except data does not move.
void report(QString msg)
Definition: cxLogger.cpp:90
std::map< QString, class Landmark > LandmarkMap
Always provides a PatientModelService.
virtual void applyPatientRegistration(Transform3D rMpr_new, QString description)
virtual void applyImage2ImageRegistration(Transform3D delta_pre_rMd, QString description)
void isSaving(QDomElement &root)
xml storage is available
vtkSmartPointer< class vtkPoints > vtkPointsPtr