CustusX  16.5.0-rc9
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  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->applyPatientRegistration(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->applyPatientRegistration(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->applyPatientRegistration(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  Transform3D rMd;
361  delta = this->performLandmarkRegistration(p_moving_r, p_fixed_r, &ok);
362  idString = QString("Image to Image Landmark");
363  }
364 
365  if (!ok)
366  {
367  reportError("I-I Landmark registration: Failed to register: [" + qstring_cast(p_moving_r->GetNumberOfPoints()) + "p], "+ movingImage->getName());
368  return;
369  }
370 
371  this->applyImage2ImageRegistration(delta, idString);
372 }
373 
374 std::vector<Vector3D> RegistrationImplService::convertVtkPointsToPoints(vtkPointsPtr base)
375 {
376  std::vector<Vector3D> retval;
377 
378  for (int i=0; i<base->GetNumberOfPoints(); ++i)
379  {
380  Vector3D p(base->GetPoint(i));
381  retval.push_back(p);
382  }
383  return retval;
384 }
385 
389 Transform3D RegistrationImplService::performLandmarkRegistration(vtkPointsPtr source, vtkPointsPtr target, bool* ok) const
390 {
391  *ok = false;
392 
393  // too few data samples: ignore
394  if (source->GetNumberOfPoints() < 3)
395  {
396  return Transform3D::Identity();
397  }
398 
399  vtkLandmarkTransformPtr landmarktransform = vtkLandmarkTransformPtr::New();
400  landmarktransform->SetSourceLandmarks(source);
401  landmarktransform->SetTargetLandmarks(target);
402  landmarktransform->SetModeToRigidBody();
403  source->Modified();
404  target->Modified();
405  landmarktransform->Update();
406 
407  Transform3D tar_M_src(landmarktransform->GetMatrix());
408 
409  if (QString::number(tar_M_src(0,0))=="nan") // harry but quick way to check badness of transform...
410  {
411  return Transform3D::Identity();
412  }
413 
414  *ok = true;
415  return tar_M_src;
416 }
417 
419 {
420  this->performImage2ImageRegistration(delta_pre_rMd, description);
421 }
422 
424 {
425  this->performImage2ImageRegistration(delta_pre_rMd, description, true);
426 }
427 
428 void RegistrationImplService::performImage2ImageRegistration(Transform3D delta_pre_rMd, QString description, bool continuous)
429 {
430  RegistrationTransform regTrans(delta_pre_rMd, QDateTime::currentDateTime(), description);
431  regTrans.mFixed = mFixedData;
432  regTrans.mMoving = mMovingData;
433 
434  this->updateRegistration(mLastRegistrationTime, regTrans, this->getMovingData(), continuous);
435 
436  mLastRegistrationTime = regTrans.mTimestamp;
437  if(!continuous)
438  reportSuccess(QString("Image registration [%1] has been performed on %2").arg(description).arg(regTrans.mMoving) );
439 }
440 
442 {
443  this->performPatientRegistration(rMpr_new, description);
444 }
445 
447 {
448  this->performPatientRegistration(rMpr_new, description, true);
449 }
450 
451 void RegistrationImplService::performPatientRegistration(Transform3D rMpr_new, QString description, bool continuous)
452 {
453  RegistrationTransform regTrans(rMpr_new, QDateTime::currentDateTime(), description);
454  regTrans.mFixed = mFixedData;
455 
456  mPatientModelService->updateRegistration_rMpr(mLastRegistrationTime, regTrans, continuous);
457 
458  mLastRegistrationTime = regTrans.mTimestamp;
459  if(!continuous)
460  reportSuccess(QString("Patient registration [%1] has been performed.").arg(description));
461 }
462 
468 void RegistrationImplService::updateRegistration(QDateTime oldTime, RegistrationTransform deltaTransform, DataPtr data, bool continuous)
469 {
470  RegistrationApplicator applicator(mPatientModelService->getData());
471  applicator.updateRegistration(oldTime, deltaTransform, data, continuous);
472  if(!continuous)
473  mPatientModelService->autoSave();
474 }
475 
484 {
485  Transform3D rMpr = mPatientModelService->get_rMpr();
486 
487  //create a marked(m) space tm, which is related to tool space (t) as follows:
488  //the tool is defined in DICOM space such that
489  //the tool points toward the patients feet and the spheres faces the same
490  //direction as the nose
491  Transform3D tMpr = prMt.inv();
492 
493  // this is the new patient registration:
494  Transform3D tmMpr = tMtm * tMpr;
495  // the change in pat reg becomes:
496  Transform3D F = tmMpr * rMpr.inv();
497 
498  QString description("Patient Orientation");
499 
500  QDateTime oldTime = this->getLastRegistrationTime(); // time of previous reg
501  this->applyPatientRegistration(tmMpr, description);
502 
503  // now apply the inverse of F to all data,
504  // thus ensuring the total path from pr to d_i is unchanged:
505  Transform3D delta_pre_rMd = F;
506 
507 
508  // use the same registration time as generated in the applyPatientRegistration() above:
509  RegistrationTransform regTrans(delta_pre_rMd, this->getLastRegistrationTime(), description);
510 
511  std::map<QString,DataPtr> data = mPatientModelService->getData();
512  // update the transform on all target data:
513  for (std::map<QString,DataPtr>::iterator iter = data.begin(); iter!=data.end(); ++iter)
514  {
515  DataPtr current = iter->second;
516  RegistrationTransform newTransform = regTrans;
517  newTransform.mValue = regTrans.mValue * current->get_rMd();
518  current->get_rMd_History()->updateRegistration(oldTime, newTransform);
519 
520  report("Updated registration of data " + current->getName());
521  std::cout << "rMd_new\n" << newTransform.mValue << std::endl;
522  }
523 
524  this->setLastRegistrationTime(regTrans.mTimestamp);
525 
526  reportSuccess("Patient Orientation has been performed");
527 }
528 
529 void RegistrationImplService::writePreLandmarkRegistration(QString name, LandmarkMap landmarks)
530 {
531  QStringList lm;
532  for (LandmarkMap::iterator iter=landmarks.begin(); iter!=landmarks.end(); ++iter)
533  {
534  lm << mPatientModelService->getLandmarkProperties()[iter->second.getUid()].getName();
535  }
536 
537  QString msg = QString("Preparing to register [%1] containing the landmarks: [%2]").arg(name).arg(lm.join(","));
538  report(msg);
539 }
540 
547 vtkPointsPtr RegistrationImplService::convertTovtkPoints(const std::vector<QString>& uids, const LandmarkMap& data, Transform3D M)
548 {
549  vtkPointsPtr retval = vtkPointsPtr::New();
550 
551  for (unsigned i=0; i<uids.size(); ++i)
552  {
553  QString uid = uids[i];
554  Vector3D p = M.coord(data.find(uid)->second.getCoord());
555  retval->InsertNextPoint(p.begin());
556  }
557  return retval;
558 }
559 
561 {
562  return false;
563 }
564 
565 } /* 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
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 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 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 applyContinuousPatientRegistration(Transform3D rMpr_new, QString description)
virtual void applyPatientRegistration(Transform3D rMpr_new, QString description)
virtual void applyContinuousImage2ImageRegistration(Transform3D delta_pre_rMd, QString description)
virtual void applyImage2ImageRegistration(Transform3D delta_pre_rMd, QString description)
void isSaving(QDomElement &root)
xml storage is available
vtkSmartPointer< class vtkPoints > vtkPointsPtr