Fraxinus  17.12
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) 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  connect(mSession.get(), &SessionStorageService::cleared, this, &RegistrationImplService::clearSlot);
64  connect(mSession.get(), &SessionStorageService::isLoadingSecond, this, &RegistrationImplService::duringLoadPatientSlot);
65  connect(mSession.get(), &SessionStorageService::isSaving, this, &RegistrationImplService::duringSavePatientSlot);
66 }
67 
69 {
70 }
71 
72 void RegistrationImplService::duringSavePatientSlot(QDomElement& node)
73 {
74  XMLNodeAdder root(node);
75  QDomElement managerNode = root.descend("managers").node().toElement();
76  this->addXml(managerNode);
77 }
78 
79 void RegistrationImplService::duringLoadPatientSlot(QDomElement& node)
80 {
81  XMLNodeParser root(node);
82  QDomElement registrationManager = root.descend("managers/registrationManager").node().toElement();
83  if (!registrationManager.isNull())
84  this->parseXml(registrationManager);
85 }
86 
87 void RegistrationImplService::addXml(QDomNode& parentNode)
88 {
89  QDomDocument doc = parentNode.ownerDocument();
90  QDomElement base = doc.createElement("registrationManager");
91  parentNode.appendChild(base);
92 
93  QDomElement fixedDataNode = doc.createElement("fixedDataUid");
94  DataPtr fixedData = this->getFixedData();
95  if(fixedData)
96  {
97  fixedDataNode.appendChild(doc.createTextNode(fixedData->getUid()));
98  }
99  base.appendChild(fixedDataNode);
100 
101  QDomElement movingDataNode = doc.createElement("movingDataUid");
102  DataPtr movingData = this->getMovingData();
103  if(movingData)
104  {
105  movingDataNode.appendChild(doc.createTextNode(movingData->getUid()));
106  }
107  base.appendChild(movingDataNode);
108 }
109 
110 void RegistrationImplService::parseXml(QDomNode& dataNode)
111 {
112  QString fixedData = dataNode.namedItem("fixedDataUid").toElement().text();
113  this->setFixedData(fixedData);
114 
115  QString movingData = dataNode.namedItem("movingDataUid").toElement().text();
116  this->setMovingData(movingData);
117 }
118 
119 void RegistrationImplService::clearSlot()
120 {
121  this->setLastRegistrationTime(QDateTime());
122  this->setFixedData(DataPtr());
123 }
124 
126 {
127  this->setMovingData((data) ? data->getUid() : "");
128 }
129 
131 {
132  this->setFixedData((data) ? data->getUid() : "");
133 }
134 
136 {
137  if (uid==mMovingData)
138  return;
139  mMovingData = uid;
140  emit movingDataChanged(mMovingData);
141 }
142 
144 {
145  if (uid==mFixedData)
146  return;
147  mFixedData = uid;
148  emit fixedDataChanged(mFixedData);
149 }
150 
152 {
153  return mPatientModelService->getData(mMovingData);
154 }
155 
157 {
158  return mPatientModelService->getData(mFixedData);
159 }
160 
162 {
163  return mLastRegistrationTime;
164 }
165 
167 {
168  mLastRegistrationTime = time;
169 }
170 
172 {
173  DataPtr fixedImage = this->getFixedData();
174 
175  if(!fixedImage)
176  {
177  reportError("The fixed data is not set, cannot do patient registration!");
178  return;
179  }
180  LandmarkMap fixedLandmarks = fixedImage->getLandmarks()->getLandmarks();
181  LandmarkMap toolLandmarks = mPatientModelService->getPatientLandmarks()->getLandmarks();
182 
183  this->writePreLandmarkRegistration(fixedImage->getName(), fixedImage->getLandmarks()->getLandmarks());
184  this->writePreLandmarkRegistration("physical", toolLandmarks);
185 
186  std::vector<QString> landmarks = this->getUsableLandmarks(fixedLandmarks, toolLandmarks);
187 
188  vtkPointsPtr p_ref = this->convertTovtkPoints(landmarks, fixedLandmarks, fixedImage->get_rMd());
189  vtkPointsPtr p_pr = this->convertTovtkPoints(landmarks, toolLandmarks, Transform3D::Identity());
190 
191  // ignore if too few data.
192  if (p_ref->GetNumberOfPoints() < 3)
193  return;
194 
195  bool ok = false;
196  Transform3D rMpr = this->performLandmarkRegistration(p_pr, p_ref, &ok);
197  if (!ok)
198  {
199  reportError("P-I Landmark registration: Failed to register: [" + qstring_cast(p_pr->GetNumberOfPoints()) + "p]");
200  return;
201  }
202 
203  this->addPatientRegistration(rMpr, "Patient Landmark");
204 }
205 
207 {
208  DataPtr fixedImage = this->getFixedData();
209  if(!fixedImage)
210  {
211  reportError("The fixed data is not set, cannot do image registration!");
212  return;
213  }
214 
215  LandmarkMap fixedLandmarks = fixedImage->getLandmarks()->getLandmarks();
216  LandmarkMap toolLandmarks = mPatientModelService->getPatientLandmarks()->getLandmarks();
217 
218  this->writePreLandmarkRegistration(fixedImage->getName(), fixedImage->getLandmarks()->getLandmarks());
219  this->writePreLandmarkRegistration("physical", toolLandmarks);
220 
221  std::vector<QString> landmarks = this->getUsableLandmarks(fixedLandmarks, toolLandmarks);
222 
223  Transform3D rMd = fixedImage->get_rMd();
224  Transform3D rMpr_old = mPatientModelService->get_rMpr();
225  std::vector<Vector3D> p_pr_old = this->convertAndTransformToPoints(landmarks, fixedLandmarks, rMpr_old.inv()*rMd);
226  std::vector<Vector3D> p_pr_new = this->convertAndTransformToPoints(landmarks, toolLandmarks, Transform3D::Identity());
227 
228  // ignore if too few data.
229  if (p_pr_old.size() < 1)
230  return;
231 
232  LandmarkTranslationRegistration landmarkTransReg;
233  bool ok = false;
234  Transform3D pr_oldMpr_new = landmarkTransReg.registerPoints(p_pr_old, p_pr_new, &ok);
235  if (!ok)
236  {
237  reportError("Fast translation registration: Failed to register: [" + qstring_cast(p_pr_old.size()) + "points]");
238  return;
239  }
240 
241  this->addPatientRegistration(rMpr_old*pr_oldMpr_new, "Fast Translation");
242 }
243 
251 {
252  //create a marked(m) space tm, which is related to tool space (t) as follows:
253  //the tool is defined in DICOM space such that
254  //the tool points toward the patients feet and the spheres faces the same
255  //direction as the nose
256  Transform3D tMpr = prMt.inv();
257 
258  Transform3D tmMpr = tMtm * tMpr;
259 
260  this->addPatientRegistration(tmMpr, "Fast Orientation");
261 
262  // also apply the fast translation registration if any (this frees us form doing stuff in a well-defined order.)
264 }
265 
266 std::vector<Vector3D> RegistrationImplService::convertAndTransformToPoints(const std::vector<QString>& uids, const LandmarkMap& data, Transform3D M)
267 {
268  std::vector<Vector3D> retval;
269 
270  for (unsigned i=0; i<uids.size(); ++i)
271  {
272  QString uid = uids[i];
273  Vector3D p = M.coord(data.find(uid)->second.getCoord());
274  retval.push_back(p);
275  }
276  return retval;
277 }
278 
283 std::vector<QString> RegistrationImplService::getUsableLandmarks(const LandmarkMap& data_a, const LandmarkMap& data_b)
284 {
285  std::vector<QString> retval;
286  std::map<QString, LandmarkProperty> props = mPatientModelService->getLandmarkProperties();
287  std::map<QString, LandmarkProperty>::iterator iter;
288 
289  for (iter=props.begin(); iter!=props.end(); ++iter)
290  {
291  QString uid = iter->first;
292  if (data_a.count(uid) && data_b.count(uid) && iter->second.getActive())
293  retval.push_back(uid);
294  }
295  return retval;
296 }
297 
299 {
300  //check that the fixed data is set
301  DataPtr fixedImage = this->getFixedData();
302  if(!fixedImage)
303  {
304  reportError("The fixed data is not set, cannot do landmark image registration!");
305  return;
306  }
307 
308  //check that the moving data is set
309  DataPtr movingImage = this->getMovingData();
310  if(!movingImage)
311  {
312  reportError("The moving data is not set, cannot do landmark image registration!");
313  return;
314  }
315 
316  // ignore self-registration, this gives no effect bestcase, buggy behaviour worstcase (has been observed)
317  if(movingImage==fixedImage)
318  {
319  reportError("The moving and fixed are equal, ignoring landmark image registration!");
320  return;
321  }
322 
323  LandmarkMap fixedLandmarks = fixedImage->getLandmarks()->getLandmarks();
324  LandmarkMap imageLandmarks = movingImage->getLandmarks()->getLandmarks();
325 
326  this->writePreLandmarkRegistration(fixedImage->getName(), fixedImage->getLandmarks()->getLandmarks());
327  this->writePreLandmarkRegistration(movingImage->getName(), movingImage->getLandmarks()->getLandmarks());
328 
329  std::vector<QString> landmarks = getUsableLandmarks(fixedLandmarks, imageLandmarks);
330  vtkPointsPtr p_fixed_r = convertTovtkPoints(landmarks, fixedLandmarks, fixedImage->get_rMd());
331  vtkPointsPtr p_moving_r = convertTovtkPoints(landmarks, imageLandmarks, movingImage->get_rMd());
332 
333  int minNumberOfPoints = 3;
334  if (translationOnly)
335  minNumberOfPoints = 1;
336 
337  // ignore if too few data.
338  if (p_fixed_r->GetNumberOfPoints() < minNumberOfPoints)
339  {
340  reportError(
341  QString("Found %1 corresponding landmarks, need %2, cannot do landmark image registration!")
342  .arg(p_fixed_r->GetNumberOfPoints())
343  .arg(minNumberOfPoints)
344  );
345  return;
346  }
347 
348  bool ok = false;
349  QString idString;
350  Transform3D delta;
351 
352  if (translationOnly)
353  {
354  LandmarkTranslationRegistration landmarkTransReg;
355  delta = landmarkTransReg.registerPoints(convertVtkPointsToPoints(p_fixed_r), convertVtkPointsToPoints(p_moving_r), &ok);
356  idString = QString("Image to Image Landmark Translation");
357  }
358  else
359  {
360  delta = this->performLandmarkRegistration(p_moving_r, p_fixed_r, &ok);
361  idString = QString("Image to Image Landmark");
362  }
363 
364  if (!ok)
365  {
366  reportError("I-I Landmark registration: Failed to register: [" + qstring_cast(p_moving_r->GetNumberOfPoints()) + "p], "+ movingImage->getName());
367  return;
368  }
369 
370  this->addImage2ImageRegistration(delta, idString);
371 }
372 
373 std::vector<Vector3D> RegistrationImplService::convertVtkPointsToPoints(vtkPointsPtr base)
374 {
375  std::vector<Vector3D> retval;
376 
377  for (int i=0; i<base->GetNumberOfPoints(); ++i)
378  {
379  Vector3D p(base->GetPoint(i));
380  retval.push_back(p);
381  }
382  return retval;
383 }
384 
388 Transform3D RegistrationImplService::performLandmarkRegistration(vtkPointsPtr source, vtkPointsPtr target, bool* ok) const
389 {
390  *ok = false;
391 
392  // too few data samples: ignore
393  if (source->GetNumberOfPoints() < 3)
394  {
395  return Transform3D::Identity();
396  }
397 
398  vtkLandmarkTransformPtr landmarktransform = vtkLandmarkTransformPtr::New();
399  landmarktransform->SetSourceLandmarks(source);
400  landmarktransform->SetTargetLandmarks(target);
401  landmarktransform->SetModeToRigidBody();
402  source->Modified();
403  target->Modified();
404  landmarktransform->Update();
405 
406  Transform3D tar_M_src(landmarktransform->GetMatrix());
407 
408  if (QString::number(tar_M_src(0,0))=="nan") // harry but quick way to check badness of transform...
409  {
410  return Transform3D::Identity();
411  }
412 
413  *ok = true;
414  return tar_M_src;
415 }
416 
418 {
419  this->performImage2ImageRegistration(dMd, description);
420 }
421 
423 {
424  this->performImage2ImageRegistration(dMd, description, true);
425 }
426 
427 void RegistrationImplService::performImage2ImageRegistration(Transform3D dMd, QString description, bool temporaryRegistration)
428 {
429  RegistrationTransform regTrans(dMd, QDateTime::currentDateTime(), description, temporaryRegistration);
430  regTrans.mFixed = mFixedData;
431  regTrans.mMoving = mMovingData;
432 
433  this->updateRegistration_rMd(mLastRegistrationTime, regTrans, this->getMovingData());
434 
435  mLastRegistrationTime = regTrans.mTimestamp;
436  if(!temporaryRegistration)
437  reportSuccess(QString("Image registration [%1] has been performed on %2").arg(description).arg(regTrans.mMoving) );
438 }
439 
441 {
442  this->performPatientRegistration(rMpr_new, description);
443 }
444 
446 {
447  this->performPatientRegistration(rMpr_new, description, true);
448 }
449 
450 void RegistrationImplService::performPatientRegistration(Transform3D rMpr_new, QString description, bool temporaryRegistration)
451 {
452  RegistrationTransform regTrans(rMpr_new, QDateTime::currentDateTime(), description, temporaryRegistration);
453  regTrans.mFixed = mFixedData;
454 
455  mPatientModelService->updateRegistration_rMpr(mLastRegistrationTime, regTrans);
456 
457  mLastRegistrationTime = regTrans.mTimestamp;
458  if(!temporaryRegistration)
459  reportSuccess(QString("Patient registration [%1] has been performed.").arg(description));
460 }
461 
467 void RegistrationImplService::updateRegistration_rMd(QDateTime oldTime, RegistrationTransform dMd, DataPtr data)
468 {
469  RegistrationApplicator applicator(mPatientModelService->getDatas());
470  dMd.mMoving = data->getUid();
471  applicator.updateRegistration(oldTime, dMd);
472 
473  bool silent = dMd.mTemp;
474  if(!silent)
475  mPatientModelService->autoSave();
476 }
477 
486 {
487  Transform3D rMpr = mPatientModelService->get_rMpr();
488 
489  //create a marked(m) space tm, which is related to tool space (t) as follows:
490  //the tool is defined in DICOM space such that
491  //the tool points toward the patients feet and the spheres faces the same
492  //direction as the nose
493  Transform3D tMpr = prMt.inv();
494 
495  // this is the new patient registration:
496  Transform3D tmMpr = tMtm * tMpr;
497  // the change in pat reg becomes:
498  Transform3D F = tmMpr * rMpr.inv();
499 
500  QString description("Patient Orientation");
501 
502  QDateTime oldTime = this->getLastRegistrationTime(); // time of previous reg
503  this->addPatientRegistration(tmMpr, description);
504 
505  // now apply the inverse of F to all data,
506  // thus ensuring the total path from pr to d_i is unchanged:
507  Transform3D delta_pre_rMd = F;
508 
509 
510  // use the same registration time as generated in the applyPatientRegistration() above:
511  RegistrationTransform regTrans(delta_pre_rMd, this->getLastRegistrationTime(), description);
512 
513  std::map<QString,DataPtr> data = mPatientModelService->getDatas();
514  // update the transform on all target data:
515  for (std::map<QString,DataPtr>::iterator iter = data.begin(); iter!=data.end(); ++iter)
516  {
517  DataPtr current = iter->second;
518  RegistrationTransform newTransform = regTrans;
519  newTransform.mValue = regTrans.mValue * current->get_rMd();
520  current->get_rMd_History()->addOrUpdateRegistration(oldTime, newTransform);
521 
522  report("Updated registration of data " + current->getName());
523  std::cout << "rMd_new\n" << newTransform.mValue << std::endl;
524  }
525 
526  this->setLastRegistrationTime(regTrans.mTimestamp);
527 
528  reportSuccess("Patient Orientation has been performed");
529 }
530 
531 void RegistrationImplService::writePreLandmarkRegistration(QString name, LandmarkMap landmarks)
532 {
533  QStringList lm;
534  for (LandmarkMap::iterator iter=landmarks.begin(); iter!=landmarks.end(); ++iter)
535  {
536  lm << mPatientModelService->getLandmarkProperties()[iter->second.getUid()].getName();
537  }
538 
539  QString msg = QString("Preparing to register [%1] containing the landmarks: [%2]").arg(name).arg(lm.join(","));
540  report(msg);
541 }
542 
549 vtkPointsPtr RegistrationImplService::convertTovtkPoints(const std::vector<QString>& uids, const LandmarkMap& data, Transform3D M)
550 {
551  vtkPointsPtr retval = vtkPointsPtr::New();
552 
553  for (unsigned i=0; i<uids.size(); ++i)
554  {
555  QString uid = uids[i];
556  Vector3D p = M.coord(data.find(uid)->second.getCoord());
557  retval->InsertNextPoint(p.begin());
558  }
559  return retval;
560 }
561 
563 {
564  return false;
565 }
566 
567 } /* namespace cx */
QString qstring_cast(const T &val)
void reportError(QString msg)
Definition: cxLogger.cpp:92
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: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
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.