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