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