CustusX  2023.01.05-dev+develop.0da12
An IGT application
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) 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 <ctkPluginContext.h>
15 #include <ctkServiceTracker.h>
16 #include <vtkPoints.h>
17 #include <vtkLandmarkTransform.h>
18 #include <vtkMatrix4x4.h>
19 
20 #include "cxData.h"
21 #include "cxTypeConversions.h"
22 #include "cxLogger.h"
24 #include "cxFrameForest.h"
25 #include "cxPatientModelService.h"
27 #include "cxLandmark.h"
31 #include "cxXMLNodeWrapper.h"
32 
33 namespace cx
34 {
35 
37  mLastRegistrationTime(QDateTime::currentDateTime()),
38  mContext(context),
39  mPatientModelService(new PatientModelServiceProxy(context)),
40  mSession(SessionStorageServiceProxy::create(context))
41 {
42  connect(mSession.get(), &SessionStorageService::cleared, this, &RegistrationImplService::clearSlot);
43  connect(mSession.get(), &SessionStorageService::isLoadingSecond, this, &RegistrationImplService::duringLoadPatientSlot);
44  connect(mSession.get(), &SessionStorageService::isSaving, this, &RegistrationImplService::duringSavePatientSlot);
45 }
46 
48 {
49 }
50 
51 void RegistrationImplService::duringSavePatientSlot(QDomElement& node)
52 {
53  XMLNodeAdder root(node);
54  QDomElement managerNode = root.descend("managers").node().toElement();
55  this->addXml(managerNode);
56 }
57 
58 void RegistrationImplService::duringLoadPatientSlot(QDomElement& node)
59 {
60  XMLNodeParser root(node);
61  QDomElement registrationManager = root.descend("managers/registrationManager").node().toElement();
62  if (!registrationManager.isNull())
63  this->parseXml(registrationManager);
64 }
65 
66 void RegistrationImplService::addXml(QDomNode& parentNode)
67 {
68  QDomDocument doc = parentNode.ownerDocument();
69  QDomElement base = doc.createElement("registrationManager");
70  parentNode.appendChild(base);
71 
72  QDomElement fixedDataNode = doc.createElement("fixedDataUid");
73  DataPtr fixedData = this->getFixedData();
74  if(fixedData)
75  {
76  fixedDataNode.appendChild(doc.createTextNode(fixedData->getUid()));
77  }
78  base.appendChild(fixedDataNode);
79 
80  QDomElement movingDataNode = doc.createElement("movingDataUid");
81  DataPtr movingData = this->getMovingData();
82  if(movingData)
83  {
84  movingDataNode.appendChild(doc.createTextNode(movingData->getUid()));
85  }
86  base.appendChild(movingDataNode);
87 }
88 
89 void RegistrationImplService::parseXml(QDomNode& dataNode)
90 {
91  QString fixedData = dataNode.namedItem("fixedDataUid").toElement().text();
92  this->setFixedData(fixedData);
93 
94  QString movingData = dataNode.namedItem("movingDataUid").toElement().text();
95  this->setMovingData(movingData);
96 }
97 
98 void RegistrationImplService::clearSlot()
99 {
100  this->setLastRegistrationTime(QDateTime());
101  this->setFixedData(DataPtr());
102 }
103 
105 {
106  this->setMovingData((data) ? data->getUid() : "");
107 }
108 
110 {
111  this->setFixedData((data) ? data->getUid() : "");
112 }
113 
115 {
116  if (uid==mMovingData)
117  return;
118  mMovingData = uid;
119  emit movingDataChanged(mMovingData);
120 }
121 
123 {
124  if (uid==mFixedData)
125  return;
126  mFixedData = uid;
127  emit fixedDataChanged(mFixedData);
128 }
129 
131 {
132  return mPatientModelService->getData(mMovingData);
133 }
134 
136 {
137  return mPatientModelService->getData(mFixedData);
138 }
139 
141 {
142  return mLastRegistrationTime;
143 }
144 
146 {
147  mLastRegistrationTime = time;
148 }
149 
151 {
152  DataPtr fixedImage = this->getFixedData();
153 
154  if(!fixedImage)
155  {
156  reportError("The fixed data is not set, cannot do patient registration!");
157  return;
158  }
159  LandmarkMap fixedLandmarks = fixedImage->getLandmarks()->getLandmarks();
160  LandmarkMap toolLandmarks = mPatientModelService->getPatientLandmarks()->getLandmarks();
161 
162  this->writePreLandmarkRegistration(fixedImage->getName(), fixedImage->getLandmarks()->getLandmarks());
163  this->writePreLandmarkRegistration("physical", toolLandmarks);
164 
165  std::vector<QString> landmarks = this->getUsableLandmarks(fixedLandmarks, toolLandmarks);
166 
167  vtkPointsPtr p_ref = this->convertTovtkPoints(landmarks, fixedLandmarks, fixedImage->get_rMd());
168  vtkPointsPtr p_pr = this->convertTovtkPoints(landmarks, toolLandmarks, Transform3D::Identity());
169 
170  // ignore if too few data.
171  if (p_ref->GetNumberOfPoints() < 3)
172  return;
173 
174  bool ok = false;
175  Transform3D rMpr = this->performLandmarkRegistration(p_pr, p_ref, &ok);
176  if (!ok)
177  {
178  reportError("P-I Landmark registration: Failed to register: [" + qstring_cast(p_pr->GetNumberOfPoints()) + "p]");
179  return;
180  }
181 
182  this->addPatientRegistration(rMpr, "Patient Landmark");
183 }
184 
186 {
187  DataPtr fixedImage = this->getFixedData();
188  if(!fixedImage)
189  {
190  reportError("The fixed data is not set, cannot do image registration!");
191  return;
192  }
193 
194  LandmarkMap fixedLandmarks = fixedImage->getLandmarks()->getLandmarks();
195  LandmarkMap toolLandmarks = mPatientModelService->getPatientLandmarks()->getLandmarks();
196 
197  this->writePreLandmarkRegistration(fixedImage->getName(), fixedImage->getLandmarks()->getLandmarks());
198  this->writePreLandmarkRegistration("physical", toolLandmarks);
199 
200  std::vector<QString> landmarks = this->getUsableLandmarks(fixedLandmarks, toolLandmarks);
201 
202  Transform3D rMd = fixedImage->get_rMd();
203  Transform3D rMpr_old = mPatientModelService->get_rMpr();
204  std::vector<Vector3D> p_pr_old = this->convertAndTransformToPoints(landmarks, fixedLandmarks, rMpr_old.inv()*rMd);
205  std::vector<Vector3D> p_pr_new = this->convertAndTransformToPoints(landmarks, toolLandmarks, Transform3D::Identity());
206 
207  // ignore if too few data.
208  if (p_pr_old.size() < 1)
209  return;
210 
211  LandmarkTranslationRegistration landmarkTransReg;
212  bool ok = false;
213  Transform3D pr_oldMpr_new = landmarkTransReg.registerPoints(p_pr_old, p_pr_new, &ok);
214  if (!ok)
215  {
216  reportError("Fast translation registration: Failed to register: [" + qstring_cast(p_pr_old.size()) + "points]");
217  return;
218  }
219 
220  this->addPatientRegistration(rMpr_old*pr_oldMpr_new, "Fast Translation");
221 }
222 
230 {
231  //create a marked(m) space tm, which is related to tool space (t) as follows:
232  //the tool is defined in DICOM space such that
233  //the tool points toward the patients feet and the spheres faces the same
234  //direction as the nose
235  Transform3D tMpr = prMt.inv();
236 
237  Transform3D tmMpr = tMtm * tMpr;
238 
239  this->addPatientRegistration(tmMpr, "Fast Orientation");
240 
241  // also apply the fast translation registration if any (this frees us form doing stuff in a well-defined order.)
243 }
244 
245 std::vector<Vector3D> RegistrationImplService::convertAndTransformToPoints(const std::vector<QString>& uids, const LandmarkMap& data, Transform3D M)
246 {
247  std::vector<Vector3D> retval;
248 
249  for (unsigned i=0; i<uids.size(); ++i)
250  {
251  QString uid = uids[i];
252  Vector3D p = M.coord(data.find(uid)->second.getCoord());
253  retval.push_back(p);
254  }
255  return retval;
256 }
257 
262 std::vector<QString> RegistrationImplService::getUsableLandmarks(const LandmarkMap& data_a, const LandmarkMap& data_b)
263 {
264  std::vector<QString> retval;
265  std::map<QString, LandmarkProperty> props = mPatientModelService->getLandmarkProperties();
266  std::map<QString, LandmarkProperty>::iterator iter;
267 
268  for (iter=props.begin(); iter!=props.end(); ++iter)
269  {
270  QString uid = iter->first;
271  if (data_a.count(uid) && data_b.count(uid) && iter->second.getActive())
272  retval.push_back(uid);
273  }
274  return retval;
275 }
276 
278 {
279  //check that the fixed data is set
280  DataPtr fixedImage = this->getFixedData();
281  if(!fixedImage)
282  {
283  reportError("The fixed data is not set, cannot do landmark image registration!");
284  return;
285  }
286 
287  //check that the moving data is set
288  DataPtr movingImage = this->getMovingData();
289  if(!movingImage)
290  {
291  reportError("The moving data is not set, cannot do landmark image registration!");
292  return;
293  }
294 
295  // ignore self-registration, this gives no effect bestcase, buggy behaviour worstcase (has been observed)
296  if(movingImage==fixedImage)
297  {
298  reportError("The moving and fixed are equal, ignoring landmark image registration!");
299  return;
300  }
301 
302  LandmarkMap fixedLandmarks = fixedImage->getLandmarks()->getLandmarks();
303  LandmarkMap imageLandmarks = movingImage->getLandmarks()->getLandmarks();
304 
305  this->writePreLandmarkRegistration(fixedImage->getName(), fixedImage->getLandmarks()->getLandmarks());
306  this->writePreLandmarkRegistration(movingImage->getName(), movingImage->getLandmarks()->getLandmarks());
307 
308  std::vector<QString> landmarks = getUsableLandmarks(fixedLandmarks, imageLandmarks);
309  vtkPointsPtr p_fixed_r = convertTovtkPoints(landmarks, fixedLandmarks, fixedImage->get_rMd());
310  vtkPointsPtr p_moving_r = convertTovtkPoints(landmarks, imageLandmarks, movingImage->get_rMd());
311 
312  int minNumberOfPoints = 3;
313  if (translationOnly)
314  minNumberOfPoints = 1;
315 
316  // ignore if too few data.
317  if (p_fixed_r->GetNumberOfPoints() < minNumberOfPoints)
318  {
319  reportError(
320  QString("Found %1 corresponding landmarks, need %2, cannot do landmark image registration!")
321  .arg(p_fixed_r->GetNumberOfPoints())
322  .arg(minNumberOfPoints)
323  );
324  return;
325  }
326 
327  bool ok = false;
328  QString idString;
329  Transform3D delta;
330 
331  if (translationOnly)
332  {
333  LandmarkTranslationRegistration landmarkTransReg;
334  delta = landmarkTransReg.registerPoints(convertVtkPointsToPoints(p_fixed_r), convertVtkPointsToPoints(p_moving_r), &ok);
335  idString = QString("Image to Image Landmark Translation");
336  }
337  else
338  {
339  delta = this->performLandmarkRegistration(p_moving_r, p_fixed_r, &ok);
340  idString = QString("Image to Image Landmark");
341  }
342 
343  if (!ok)
344  {
345  reportError("I-I Landmark registration: Failed to register: [" + qstring_cast(p_moving_r->GetNumberOfPoints()) + "p], "+ movingImage->getName());
346  return;
347  }
348 
349  this->addImage2ImageRegistration(delta, idString);
350 }
351 
352 std::vector<Vector3D> RegistrationImplService::convertVtkPointsToPoints(vtkPointsPtr base)
353 {
354  std::vector<Vector3D> retval;
355 
356  for (int i=0; i<base->GetNumberOfPoints(); ++i)
357  {
358  Vector3D p(base->GetPoint(i));
359  retval.push_back(p);
360  }
361  return retval;
362 }
363 
367 Transform3D RegistrationImplService::performLandmarkRegistration(vtkPointsPtr source, vtkPointsPtr target, bool* ok) const
368 {
369  *ok = false;
370 
371  // too few data samples: ignore
372  if (source->GetNumberOfPoints() < 3)
373  {
374  return Transform3D::Identity();
375  }
376 
377  vtkLandmarkTransformPtr landmarktransform = vtkLandmarkTransformPtr::New();
378  landmarktransform->SetSourceLandmarks(source);
379  landmarktransform->SetTargetLandmarks(target);
380  landmarktransform->SetModeToRigidBody();
381  source->Modified();
382  target->Modified();
383  landmarktransform->Update();
384 
385  Transform3D tar_M_src(landmarktransform->GetMatrix());
386 
387  if (QString::number(tar_M_src(0,0))=="nan") // harry but quick way to check badness of transform...
388  {
389  return Transform3D::Identity();
390  }
391 
392  *ok = true;
393  return tar_M_src;
394 }
395 
397 {
398  this->performImage2ImageRegistration(dMd, description);
399 }
400 
402 {
403  this->performImage2ImageRegistration(dMd, description, true);
404 }
405 
406 void RegistrationImplService::performImage2ImageRegistration(Transform3D dMd, QString description, bool temporaryRegistration)
407 {
408  RegistrationTransform regTrans(dMd, QDateTime::currentDateTime(), description, temporaryRegistration);
409  regTrans.mFixed = mFixedData;
410  regTrans.mMoving = mMovingData;
411 
412  this->updateRegistration_rMd(mLastRegistrationTime, regTrans, this->getMovingData());
413 
414  mLastRegistrationTime = regTrans.mTimestamp;
415  if(!temporaryRegistration)
416  reportSuccess(QString("Image registration [%1] has been performed on %2").arg(description).arg(regTrans.mMoving) );
417 }
418 
420 {
421  this->performPatientRegistration(rMpr_new, description);
422 }
423 
425 {
426  this->performPatientRegistration(rMpr_new, description, true);
427 }
428 
429 void RegistrationImplService::performPatientRegistration(Transform3D rMpr_new, QString description, bool temporaryRegistration)
430 {
431  RegistrationTransform regTrans(rMpr_new, QDateTime::currentDateTime(), description, temporaryRegistration);
432  regTrans.mFixed = mFixedData;
433 
434  mPatientModelService->updateRegistration_rMpr(mLastRegistrationTime, regTrans);
435 
436  mLastRegistrationTime = regTrans.mTimestamp;
437  if(!temporaryRegistration)
438  reportSuccess(QString("Patient registration [%1] has been performed.").arg(description));
439 }
440 
446 void RegistrationImplService::updateRegistration_rMd(QDateTime oldTime, RegistrationTransform dMd, DataPtr data)
447 {
448  RegistrationApplicator applicator(mPatientModelService->getDatas());
449  dMd.mMoving = data->getUid();
450  applicator.updateRegistration(oldTime, dMd);
451 
452  bool silent = dMd.mTemp;
453  if(!silent)
454  mPatientModelService->autoSave();
455 }
456 
465 {
466  Transform3D rMpr = mPatientModelService->get_rMpr();
467 
468  //create a marked(m) space tm, which is related to tool space (t) as follows:
469  //the tool is defined in DICOM space such that
470  //the tool points toward the patients feet and the spheres faces the same
471  //direction as the nose
472  Transform3D tMpr = prMt.inv();
473 
474  // this is the new patient registration:
475  Transform3D tmMpr = tMtm * tMpr;
476  // the change in pat reg becomes:
477  Transform3D F = tmMpr * rMpr.inv();
478 
479  QString description("Patient Orientation");
480 
481  QDateTime oldTime = this->getLastRegistrationTime(); // time of previous reg
482  this->addPatientRegistration(tmMpr, description);
483 
484  // now apply the inverse of F to all data,
485  // thus ensuring the total path from pr to d_i is unchanged:
486  Transform3D delta_pre_rMd = F;
487 
488 
489  // use the same registration time as generated in the applyPatientRegistration() above:
490  RegistrationTransform regTrans(delta_pre_rMd, this->getLastRegistrationTime(), description);
491 
492  std::map<QString,DataPtr> data = mPatientModelService->getDatas();
493  // update the transform on all target data:
494  for (std::map<QString,DataPtr>::iterator iter = data.begin(); iter!=data.end(); ++iter)
495  {
496  DataPtr current = iter->second;
497  RegistrationTransform newTransform = regTrans;
498  newTransform.mValue = regTrans.mValue * current->get_rMd();
499  current->get_rMd_History()->addOrUpdateRegistration(oldTime, newTransform);
500 
501  report("Updated registration of data " + current->getName());
502  std::cout << "rMd_new\n" << newTransform.mValue << std::endl;
503  }
504 
505  this->setLastRegistrationTime(regTrans.mTimestamp);
506 
507  reportSuccess("Patient Orientation has been performed");
508 }
509 
510 void RegistrationImplService::writePreLandmarkRegistration(QString name, LandmarkMap landmarks)
511 {
512  QStringList lm;
513  for (LandmarkMap::iterator iter=landmarks.begin(); iter!=landmarks.end(); ++iter)
514  {
515  lm << mPatientModelService->getLandmarkProperties()[iter->second.getUid()].getName();
516  }
517 
518  QString msg = QString("Preparing to register [%1] containing the landmarks: [%2]").arg(name).arg(lm.join(","));
519  report(msg);
520 }
521 
528 vtkPointsPtr RegistrationImplService::convertTovtkPoints(const std::vector<QString>& uids, const LandmarkMap& data, Transform3D M)
529 {
530  vtkPointsPtr retval = vtkPointsPtr::New();
531 
532  for (unsigned i=0; i<uids.size(); ++i)
533  {
534  QString uid = uids[i];
535  Vector3D p = M.coord(data.find(uid)->second.getCoord());
536  retval->InsertNextPoint(p.begin());
537  }
538  return retval;
539 }
540 
542 {
543  return false;
544 }
545 
546 } /* namespace cx */
QString qstring_cast(const T &val)
void reportError(QString msg)
Definition: cxLogger.cpp:71
RegistrationImplService(ctkPluginContext *context)
virtual void doFastRegistration_Orientation(const Transform3D &tMtm, const Transform3D &prMt)
vtkSmartPointer< class vtkLandmarkTransform > vtkLandmarkTransformPtr
XMLNodeAdder descend(QString path)
Transform3D Transform3D
Transform3D is a representation of an affine 3D transform.
void isLoadingSecond(QDomElement &root)
Emitted after the isLoading signal, to allow for structures that must be loaded after core structures...
virtual void addImage2ImageRegistration(Transform3D dMd, QString description)
virtual void doImageRegistration(bool translationOnly)
vtkSmartPointer< vtkPoints > vtkPointsPtr
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)
virtual void updateImage2ImageRegistration(Transform3D dMd, QString description)
boost::shared_ptr< class Data > DataPtr
virtual void addPatientRegistration(Transform3D rMpr_new, QString description)
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 setMovingData(DataPtr data)
virtual void doPatientRegistration()
registrates the fixed image to the patient
XMLNodeParser descend(QString path)
void reportSuccess(QString msg)
Definition: cxLogger.cpp:72
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:42
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:69
virtual void updatePatientRegistration(Transform3D rMpr_new, QString description)
std::map< QString, class Landmark > LandmarkMap
Always provides a PatientModelService.
void isSaving(QDomElement &root)
xml storage is available
Namespace for all CustusX production code.