]> Creatis software - clitk.git/blob - vv/vvToolRigidReg.cxx
Debug RTStruct conversion with empty struc
[clitk.git] / vv / vvToolRigidReg.cxx
1 /*=========================================================================
2   Program:   vv                     http://www.creatis.insa-lyon.fr/rio/vv
3
4   Authors belong to:
5   - University of LYON              http://www.universite-lyon.fr/
6   - Léon Bérard cancer center       http://www.centreleonberard.fr
7   - CREATIS CNRS laboratory         http://www.creatis.insa-lyon.fr
8
9   This software is distributed WITHOUT ANY WARRANTY; without even
10   the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
11   PURPOSE.  See the copyright notices for more information.
12
13   It is distributed under dual licence
14
15   - BSD        See included LICENSE.txt file
16   - CeCILL-B   http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
17 ===========================================================================**/
18
19 // vv
20 #include "vvToolRigidReg.h"
21 #include "vvSlicer.h"
22 #include <vvBlendImageActor.h>
23
24 // vtk
25 #include <vtkImageData.h>
26 #include <vtkSmartPointer.h>
27 #include <vtkInformation.h>
28 #include <vtkTransform.h>
29 #include <vtkImageActor.h>
30 #include <vtkImageMapper3D.h>
31 #include <vtkOpenGLImageSliceMapper.h>
32
33 // itk
34 #include <itkEuler3DTransform.h>
35
36 // clitk
37 #include "clitkTransformUtilities.h"
38 #include "clitkMatrix.h"
39
40 // qt
41 #include <QMessageBox>
42 #include <QFileDialog>
43 #include <QTextStream>
44
45
46 //------------------------------------------------------------------------------
47 // Create the tool and automagically (I like this word) insert it in
48 // the main window menu.
49 ADD_TOOL(vvToolRigidReg);
50 //------------------------------------------------------------------------------
51
52 //------------------------------------------------------------------------------
53 vvToolRigidReg::vvToolRigidReg(vvMainWindowBase * parent, Qt::WindowFlags f):
54     vvToolWidgetBase(parent, f),
55     vvToolBase<vvToolRigidReg>(parent),
56     Ui::vvToolRigidReg()
57 {
58   // GUI Initialization
59   Ui_vvToolRigidReg::setupUi(mToolWidget);
60   
61   // Set how many inputs are needed for this tool
62   AddInputSelector("Select moving image");
63
64   QFont font = transformationLabel->font();
65   font.setStyleHint(QFont::TypeWriter);
66   transformationLabel->setFont(font);
67
68   mInitialMatrix = vtkSmartPointer<vtkMatrix4x4>::New();
69
70   // Set slider ranges, assume degrees, will not be changed for radians
71   std::vector<QSlider *> transSliders, rotSliders;
72   std::vector<QDoubleSpinBox *> transSBs, rotSBs;
73   GetSlidersAndSpinBoxes(transSliders, rotSliders, transSBs, rotSBs);
74   for(int i=0; i<3; i++) {
75     transSliders[i]->setRange(-2000,2000);
76     rotSliders[i]->setRange(-360,360);
77     transSBs[i]->setRange(-2000,2000);
78     transSBs[i]->setDecimals(3);
79     rotSBs[i]->setRange(-360,360);
80     rotSBs[i]->setDecimals(3);
81   }
82 }
83 //------------------------------------------------------------------------------
84
85 //------------------------------------------------------------------------------
86 vvToolRigidReg::~vvToolRigidReg()
87 {
88 }
89 //------------------------------------------------------------------------------
90
91 //------------------------------------------------------------------------------
92 void vvToolRigidReg::Initialize()
93 {
94   SetToolName("Register");
95   SetToolMenuName("Register manually");
96   SetToolIconFilename(":/common/icons/register.png");
97   SetToolTip("Register manually.");
98   SetToolExperimental(false);
99 }
100 //------------------------------------------------------------------------------
101
102 //------------------------------------------------------------------------------
103 void vvToolRigidReg::InputIsSelected(vvSlicerManager *input)
104 {
105   mInput = input;
106   HideInputSelector();
107   QTabWidget * tab = dynamic_cast<vvMainWindow*>(mMainWindow)->GetTab();
108   move(tab->mapToGlobal(tab->pos()));
109   resize(tab->width(), 0);
110
111   //default image rotation center is the center of the image
112   QString xcord,ycord,zcord;
113   std::vector<double> imageorigin;
114   imageorigin=mInput->GetImage()->GetOrigin();
115   std::vector<int> imageSize = mInput->GetImage()->GetSize();
116   std::vector<double> imageSpacing = mInput->GetImage()->GetSpacing();
117   xcord=xcord.setNum(imageorigin[0]+(imageSize[0]-1)*imageSpacing[0]*0.5, 'g', 3);
118   ycord=ycord.setNum(imageorigin[1]+(imageSize[1]-1)*imageSpacing[1]*0.5, 'g', 3);
119   zcord=zcord.setNum(imageorigin[2]+(imageSize[2]-1)*imageSpacing[2]*0.5, 'g', 3);
120   Xval->setText(xcord);
121   Yval->setText(ycord);
122   Zval->setText(zcord);
123
124   //backup original matrix
125   for(int j=0; j<4; j++)
126     for(int i=0; i<4; i++)
127       // TODO SR and BP: check on the list of transforms and not the first only
128       mInitialMatrix->SetElement(i,j, mCurrentSlicerManager->GetImage()->GetTransform()[0]->GetMatrix()->GetElement(i,j));
129   QString origTransformString(clitk::Get4x4MatrixDoubleAsString(mInitialMatrix).c_str());
130   transformationLabel->setText(origTransformString);
131   SetTransform(mInitialMatrix);
132
133   //connect all sigs to slots
134   connect(resetbutton, SIGNAL(pressed()), this, SLOT(ResetTransform()));
135   connect(loadbutton, SIGNAL(pressed()), this, SLOT(LoadFile()));
136   connect(savebutton, SIGNAL(pressed()), this, SLOT(SaveFile()));
137
138   connect(xtrans_slider, SIGNAL(valueChanged(int)), this, SLOT(SliderChange(int)));
139   connect(ytrans_slider, SIGNAL(valueChanged(int)), this, SLOT(SliderChange(int)));
140   connect(ztrans_slider, SIGNAL(valueChanged(int)), this, SLOT(SliderChange(int)));
141   connect(xrot_slider, SIGNAL(valueChanged(int)), this, SLOT(SliderChange(int)));
142   connect(yrot_slider, SIGNAL(valueChanged(int)), this, SLOT(SliderChange(int)));
143   connect(zrot_slider, SIGNAL(valueChanged(int)), this, SLOT(SliderChange(int)));
144   connect(xtrans_sb, SIGNAL(valueChanged(double)), this, SLOT(SpinBoxChange(double)));
145   connect(ytrans_sb, SIGNAL(valueChanged(double)), this, SLOT(SpinBoxChange(double)));
146   connect(ztrans_sb, SIGNAL(valueChanged(double)), this, SLOT(SpinBoxChange(double)));
147   connect(xrot_sb,   SIGNAL(valueChanged(double)), this, SLOT(SpinBoxChange(double)));
148   connect(yrot_sb,   SIGNAL(valueChanged(double)), this, SLOT(SpinBoxChange(double)));
149   connect(zrot_sb,   SIGNAL(valueChanged(double)), this, SLOT(SpinBoxChange(double)));
150
151   connect(stepTransSpinBox, SIGNAL(valueChanged(double)), this, SLOT(SetTranslationStep(double)));
152   connect(stepRotSpinBox, SIGNAL(valueChanged(double)), this, SLOT(SetRotationStep(double)));
153
154   connect(checkBoxDegrees, SIGNAL(stateChanged(int)), this, SLOT(ToggleSpinBoxAnglesUnit()));
155
156   connect(Xval, SIGNAL(editingFinished()), this, SLOT(ChangeOfRotationCenter()));
157   connect(Yval, SIGNAL(editingFinished()), this, SLOT(ChangeOfRotationCenter()));
158   connect(Zval, SIGNAL(editingFinished()), this, SLOT(ChangeOfRotationCenter()));
159
160   // Init step modifiers
161   stepTransSpinBox->setValue(1.);
162   stepRotSpinBox->setValue(1.);
163 }
164 //------------------------------------------------------------------------------
165
166 //------------------------------------------------------------------------------
167 void vvToolRigidReg::apply()
168 {
169   vvToolWidgetBase::close();
170 }
171 //------------------------------------------------------------------------------
172
173 //------------------------------------------------------------------------------
174 bool vvToolRigidReg::close()
175 {
176   QString warning = "Are you sure you want to reset the original transform?";
177   QMessageBox msgBox(QMessageBox::Warning, tr("Reset transform"),warning, 0, this);
178   msgBox.addButton(tr("Yes"), QMessageBox::AcceptRole);
179   msgBox.addButton(tr("No"), QMessageBox::RejectRole);
180   if (msgBox.exec() == QMessageBox::AcceptRole) {
181     if (mCurrentSlicerManager)
182         SetTransform(mInitialMatrix);
183     return vvToolWidgetBase::close();
184   }
185   return false;
186 }
187 //------------------------------------------------------------------------------
188
189 //------------------------------------------------------------------------------
190 void vvToolRigidReg::reject()
191 {
192   return vvToolWidgetBase::reject();
193 }
194 //------------------------------------------------------------------------------
195
196 //------------------------------------------------------------------------------
197 void vvToolRigidReg::SetTranslationStep(double v)
198 {
199   xtrans_sb->setSingleStep(v);
200   ytrans_sb->setSingleStep(v);
201   ztrans_sb->setSingleStep(v);
202 }
203 //------------------------------------------------------------------------------
204
205 //------------------------------------------------------------------------------
206 void vvToolRigidReg::SetRotationStep(double v)
207 {
208   xrot_sb->setSingleStep(v);
209   yrot_sb->setSingleStep(v);
210   zrot_sb->setSingleStep(v);
211 }
212 //------------------------------------------------------------------------------
213
214 //------------------------------------------------------------------------------
215 void vvToolRigidReg::SliderChange(int newVal)
216 {
217   std::vector<QSlider *> transSliders, rotSliders;
218   std::vector<QDoubleSpinBox *> transSBs, rotSBs;
219   GetSlidersAndSpinBoxes(transSliders, rotSliders, transSBs, rotSBs);
220   for(int i=0; i<3; i++) {
221     if(transSliders[i] == QObject::sender()) {
222       transSBs[i]->setValue(newVal);
223     }
224     if(rotSliders[i] == QObject::sender()) {
225       double rad = (checkBoxDegrees->checkState()==Qt::Unchecked)?itk::Math::pi/180.:1.;
226       rotSBs[i]->setValue(newVal*rad);
227     }
228   }
229 }
230 //------------------------------------------------------------------------------
231
232 //------------------------------------------------------------------------------
233 void vvToolRigidReg::SpinBoxChange(double newVal)
234 {
235   std::vector<QSlider *> transSliders, rotSliders;
236   std::vector<QDoubleSpinBox *> transSBs, rotSBs;
237   GetSlidersAndSpinBoxes(transSliders, rotSliders, transSBs, rotSBs);
238   for(int i=0; i<3; i++) {
239     if(transSBs[i] == QObject::sender()) {
240       transSliders[i]->blockSignals(true);
241       transSliders[i]->setValue(itk::Math::Round<double,double>(newVal));
242       transSliders[i]->blockSignals(false);
243     }
244     if(rotSBs[i] == QObject::sender()) {
245       double rad = (checkBoxDegrees->checkState()==Qt::Unchecked)?180./itk::Math::pi:1.;
246       rotSliders[i]->blockSignals(true);
247       rotSliders[i]->setValue(itk::Math::Round<double,double>(newVal*rad));
248       rotSliders[i]->blockSignals(false);
249     }
250   }
251
252   // Compute transform and set
253   // TODO SR and BP: check on the list of transforms and not the first only
254   vtkSmartPointer<vtkTransform> transform_final=mInput->GetImage()->GetTransform()[0];
255   transform_final->Identity();
256   transform_final->PostMultiply();
257
258   // Rotations
259   double x=0, y=0 ,z=0;
260   x= Xval->text().toDouble();
261   y= Yval->text().toDouble();
262   z= Zval->text().toDouble();
263   double rad = (checkBoxDegrees->checkState()==Qt::Unchecked)?180./itk::Math::pi:1.;
264   transform_final->Translate(-x,-y,-z);
265   transform_final->RotateY(yrot_sb->value()*rad);
266   transform_final->RotateX(xrot_sb->value()*rad);
267   transform_final->RotateZ(zrot_sb->value()*rad);
268   transform_final->Translate(x,y,z);
269
270   // Translation
271   transform_final->Translate(xtrans_sb->value(),
272                              ytrans_sb->value(),
273                              ztrans_sb->value());
274   transform_final->Update();
275   SetTransform(transform_final->GetMatrix());
276 }
277 //------------------------------------------------------------------------------
278
279 //------------------------------------------------------------------------------
280 void vvToolRigidReg::ToggleSpinBoxAnglesUnit()
281 {
282   double rad = (checkBoxDegrees->checkState()==Qt::Unchecked)?itk::Math::pi/180.:180./itk::Math::pi;
283   std::vector<QSlider *> transSliders, rotSliders;
284   std::vector<QDoubleSpinBox *> transSBs, rotSBs;
285   GetSlidersAndSpinBoxes(transSliders, rotSliders, transSBs, rotSBs);
286   for(int i=0; i<3; i++) {
287     rotSBs[i]->blockSignals(true);
288     rotSBs[i]->setValue(rotSBs[i]->value()*rad);
289     rotSBs[i]->blockSignals(false);
290   }
291 }
292 //------------------------------------------------------------------------------
293
294 //------------------------------------------------------------------------------
295 void vvToolRigidReg::SaveFile()
296 {
297   //Write the Transformation Matrix
298   std::string absPath = mCurrentSlicerManager->GetFileName();
299   absPath = itksys::SystemTools::GetFilenameWithoutExtension(absPath) + std::string(".mat");
300   QString filename = QFileDialog::getSaveFileName(this, tr("Save Transformation Matrix File"),
301                                             absPath.c_str(),
302                                             tr("Text (*.mat *.txt *.doc *.rtf)"));
303
304   QFile file(filename);
305   if (file.open(QFile::WriteOnly | QFile::Truncate)) {
306     // TODO SR and BP: check on the list of transforms and not the first only
307     vtkMatrix4x4* matrix = mCurrentSlicerManager->GetImage()->GetTransform()[0]->GetMatrix();
308     QString matrixStr = clitk::Get4x4MatrixDoubleAsString(matrix,16).c_str();
309     QTextStream out(&file);
310     out << matrixStr;
311   }
312   else
313   {
314     QMessageBox::information(this,"Error","Unable to open file for writing");
315   }
316 }
317 //------------------------------------------------------------------------------
318
319 //------------------------------------------------------------------------------
320 void vvToolRigidReg::LoadFile()
321 {
322   //Open File to read the transformation parameters
323   QString file = QFileDialog::getOpenFileName(
324                    this,
325                    "Choose the filename for the transformation matrix",
326                    vtksys::SystemTools::GetFilenamePath(mCurrentSlicerManager->GetFileName()).c_str(),
327                    "Text (*.mat *.txt *.rtf *.doc)");
328    if (file.isEmpty())
329      return;
330
331
332   itk::Matrix<double, 4, 4> itkMat = clitk::ReadMatrix3D(file.toStdString());
333   vtkSmartPointer<vtkMatrix4x4> matrix = vtkSmartPointer<vtkMatrix4x4>::New();
334   matrix->Identity();
335   for(int j=0; j<4; j++)
336     for(int i=0; i<4; i++)
337       matrix->SetElement(j,i,itkMat[j][i]);
338   SetTransform(matrix);
339 }
340 //------------------------------------------------------------------------------
341   
342 //------------------------------------------------------------------------------
343 void vvToolRigidReg::ChangeOfRotationCenter()
344 {
345   // TODO SR and BP: check on the list of transforms and not the first only
346   SetTransform(mCurrentSlicerManager->GetImage()->GetTransform()[0]->GetMatrix());
347 }
348 //------------------------------------------------------------------------------
349
350 //------------------------------------------------------------------------------
351 void vvToolRigidReg::ResetTransform()
352 {
353   SetTransform(mInitialMatrix);
354 }
355 //------------------------------------------------------------------------------
356
357 //------------------------------------------------------------------------------
358 void vvToolRigidReg::SetTransform(vtkMatrix4x4 *matrix)
359 {
360   vtkSmartPointer<vtkTransform> transform=vtkSmartPointer<vtkTransform>::New();
361   // TODO SR and BP: check on the list of transforms and not the first only
362   mCurrentSlicerManager->GetImage()->GetTransform()[0]->SetMatrix(matrix);
363   transform->Update();
364   Render();
365   dynamic_cast<vvMainWindow*>(mMainWindow)->ImageInfoChanged();
366
367   // Compute parameters from transfer using itk Euler transform
368   itk::Euler3DTransform<double>::CenterType center;
369   center[0] = Xval->text().toDouble();
370   center[1] = Yval->text().toDouble();
371   center[2] = Zval->text().toDouble();
372   itk::Euler3DTransform<double>::MatrixType rotMat;
373   itk::Euler3DTransform<double>::OutputVectorType transVec;
374   for(int i=0; i<3; i++) {
375     transVec[i] = matrix->GetElement(i,3);
376     for(int j=0; j<3; j++)
377       rotMat[i][j] = matrix->GetElement(i,j);
378   }
379   itk::Euler3DTransform<double>::Pointer euler;
380   euler = itk::Euler3DTransform<double>::New();
381   euler->SetCenter(center);
382   try {
383 #if ITK_VERSION_MAJOR > 4 || (ITK_VERSION_MAJOR == 4 && ITK_VERSION_MINOR > 6)
384     euler->SetMatrix(rotMat,0.00001);
385 #else
386     euler->SetMatrix(rotMat);
387 #endif
388   } catch (itk::ExceptionObject) {
389     QString warning = "The matrice is a non-orthogonal rotation matrix.\nThe manual registration doesn't work.";
390     QMessageBox msgBox(QMessageBox::Warning, tr("Reset transform"),warning, 0, this);
391     msgBox.addButton(tr("OK"), QMessageBox::AcceptRole);
392     if (msgBox.exec() == QMessageBox::AcceptRole) {
393         //SetTransform(mInitialMatrix);
394         vvToolWidgetBase::close();
395     }
396   }
397   euler->SetOffset(transVec);
398
399   // Modify GUI according to the new parameters
400   std::vector<QSlider *> transSliders, rotSliders;
401   std::vector<QDoubleSpinBox *> transSBs, rotSBs;
402   GetSlidersAndSpinBoxes(transSliders, rotSliders, transSBs, rotSBs);
403   for(int i=0; i<3; i++) {
404     // Translations
405     transSBs[i]->blockSignals(true);
406     transSBs[i]->setValue( euler->GetParameters()[i+3] );
407     transSBs[i]->blockSignals(false);
408     transSliders[i]->blockSignals(true);
409     transSliders[i]->setValue( itk::Math::Round<double,double>(euler->GetParameters()[i+3]) );
410     transSliders[i]->blockSignals(false);
411
412     // Rotations
413     double rad = (checkBoxDegrees->checkState()==Qt::Checked)?180./itk::Math::pi:1.;
414     double angleDiff = euler->GetParameters()[i]-rotSBs[i]->value()/rad+2*itk::Math::pi;
415     angleDiff = angleDiff - 2*itk::Math::pi*itk::Math::Round<double,double>(angleDiff/(2*itk::Math::pi));
416     if(std::abs(angleDiff)>1.e-4) {
417       rotSBs[i]->blockSignals(true);
418       rotSBs[i]->setValue( euler->GetParameters()[i]*rad );
419       rotSBs[i]->blockSignals(false);
420     }
421     int iAngle = itk::Math::Round<int,double>(euler->GetParameters()[i]*180./itk::Math::pi);
422     if((iAngle-rotSliders[i]->value()+360)%360!=0) {
423       rotSliders[i]->blockSignals(true);
424       rotSliders[i]->setValue(iAngle);
425       rotSliders[i]->blockSignals(false);
426     }
427   }
428 }
429 //------------------------------------------------------------------------------
430
431 //------------------------------------------------------------------------------
432 // Just an helper function to shorten the code with loops on sliders and spinboxes
433 void vvToolRigidReg::GetSlidersAndSpinBoxes(std::vector<QSlider *>&transSliders, std::vector<QSlider *>&rotSliders,
434                                             std::vector<QDoubleSpinBox *>&transSBs, std::vector<QDoubleSpinBox *>&rotSBs)
435 {
436   transSliders.push_back(xtrans_slider);
437   transSliders.push_back(ytrans_slider);
438   transSliders.push_back(ztrans_slider);
439
440   rotSliders.push_back(xrot_slider);
441   rotSliders.push_back(yrot_slider);
442   rotSliders.push_back(zrot_slider);
443
444   transSBs.push_back(xtrans_sb);
445   transSBs.push_back(ytrans_sb);
446   transSBs.push_back(ztrans_sb);
447
448   rotSBs.push_back(xrot_sb);
449   rotSBs.push_back(yrot_sb);
450   rotSBs.push_back(zrot_sb);
451 }
452 //------------------------------------------------------------------------------
453
454 //------------------------------------------------------------------------------
455 void vvToolRigidReg::ExtentMax(const double pointExtent[8][4], double maxExtent[2][3])
456 {
457     double max, min;
458     for (int i=0; i<3; ++i) {
459         max = pointExtent[0][i];
460         min = pointExtent[0][i];
461         for (int j=1; j<8; ++j) {
462             if (pointExtent[j][i] > max) {
463                 max = pointExtent[j][i];
464             }
465             if (pointExtent[j][i] < min) {
466                 min = pointExtent[j][i];
467             }
468         }
469         maxExtent[0][i] = min;
470         maxExtent[1][i] = max;
471     }
472 }
473 //------------------------------------------------------------------------------
474
475 //------------------------------------------------------------------------------
476 void vvToolRigidReg::Render()
477 {
478 for (int i=0; i<mCurrentSlicerManager->GetNumberOfSlicers(); i++) {
479     mCurrentSlicerManager->GetSlicer(i)->ForceUpdateDisplayExtent();
480     mCurrentSlicerManager->GetSlicer(i)->Render();
481 }
482 }
483 //------------------------------------------------------------------------------
484