1 /*=========================================================================
2 Program: vv http://www.creatis.insa-lyon.fr/rio/vv
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
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.
13 It is distributed under dual licence
15 - BSD See included LICENSE.txt file
16 - CeCILL-B http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
17 ===========================================================================**/
20 #include "vvToolRigidReg.h"
24 #include <vtkImageData.h>
25 #include <vtkSmartPointer.h>
26 #include <vtkTransform.h>
29 #include <itkEuler3DTransform.h>
32 #include "clitkTransformUtilities.h"
33 #include "clitkMatrix.h"
36 #include <QMessageBox>
37 #include <QFileDialog>
38 #include <QTextStream>
41 //------------------------------------------------------------------------------
42 // Create the tool and automagically (I like this word) insert it in
43 // the main window menu.
44 ADD_TOOL(vvToolRigidReg);
45 //------------------------------------------------------------------------------
47 //------------------------------------------------------------------------------
48 vvToolRigidReg::vvToolRigidReg(vvMainWindowBase * parent, Qt::WindowFlags f):
49 vvToolWidgetBase(parent, f),
50 vvToolBase<vvToolRigidReg>(parent),
54 Ui_vvToolRigidReg::setupUi(mToolWidget);
56 // Set how many inputs are needed for this tool
57 AddInputSelector("Select moving image");
59 QFont font = transformationLabel->font();
60 font.setStyleHint(QFont::TypeWriter);
61 transformationLabel->setFont(font);
63 mInitialMatrix = vtkSmartPointer<vtkMatrix4x4>::New();
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);
78 //------------------------------------------------------------------------------
80 //------------------------------------------------------------------------------
81 vvToolRigidReg::~vvToolRigidReg()
84 //------------------------------------------------------------------------------
86 //------------------------------------------------------------------------------
87 void vvToolRigidReg::Initialize()
89 SetToolName("Register");
90 SetToolMenuName("Register manually");
91 SetToolIconFilename(":/common/icons/register.png");
92 SetToolTip("Register manually.");
93 SetToolExperimental(false);
95 //------------------------------------------------------------------------------
97 //------------------------------------------------------------------------------
98 void vvToolRigidReg::InputIsSelected(vvSlicerManager *input)
102 QTabWidget * tab = dynamic_cast<vvMainWindow*>(mMainWindow)->GetTab();
103 move(tab->mapToGlobal(tab->pos()));
104 resize(tab->width(), 0);
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);
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);
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()));
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)));
146 connect(stepTransSpinBox, SIGNAL(valueChanged(double)), this, SLOT(SetTranslationStep(double)));
147 connect(stepRotSpinBox, SIGNAL(valueChanged(double)), this, SLOT(SetRotationStep(double)));
149 connect(checkBoxDegrees, SIGNAL(stateChanged(int)), this, SLOT(ToggleSpinBoxAnglesUnit()));
151 connect(Xval, SIGNAL(editingFinished()), this, SLOT(ChangeOfRotationCenter()));
152 connect(Yval, SIGNAL(editingFinished()), this, SLOT(ChangeOfRotationCenter()));
153 connect(Zval, SIGNAL(editingFinished()), this, SLOT(ChangeOfRotationCenter()));
155 // Init step modifiers
156 stepTransSpinBox->setValue(1.);
157 stepRotSpinBox->setValue(1.);
159 //------------------------------------------------------------------------------
161 //------------------------------------------------------------------------------
162 void vvToolRigidReg::apply()
164 vvToolWidgetBase::close();
166 //------------------------------------------------------------------------------
168 //------------------------------------------------------------------------------
169 bool vvToolRigidReg::close()
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();
181 //------------------------------------------------------------------------------
183 //------------------------------------------------------------------------------
184 void vvToolRigidReg::reject()
186 return vvToolWidgetBase::reject();
188 //------------------------------------------------------------------------------
190 //------------------------------------------------------------------------------
191 void vvToolRigidReg::SetTranslationStep(double v)
193 xtrans_sb->setSingleStep(v);
194 ytrans_sb->setSingleStep(v);
195 ztrans_sb->setSingleStep(v);
197 //------------------------------------------------------------------------------
199 //------------------------------------------------------------------------------
200 void vvToolRigidReg::SetRotationStep(double v)
202 xrot_sb->setSingleStep(v);
203 yrot_sb->setSingleStep(v);
204 zrot_sb->setSingleStep(v);
206 //------------------------------------------------------------------------------
208 //------------------------------------------------------------------------------
209 void vvToolRigidReg::SliderChange(int newVal)
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);
218 if(rotSliders[i] == QObject::sender()) {
219 double rad = (checkBoxDegrees->checkState()==Qt::Unchecked)?itk::Math::pi/180.:1.;
220 rotSBs[i]->setValue(newVal*rad);
224 //------------------------------------------------------------------------------
226 //------------------------------------------------------------------------------
227 void vvToolRigidReg::SpinBoxChange(double newVal)
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);
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);
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();
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);
265 transform_final->Translate(xtrans_sb->value(),
268 transform_final->Update();
269 SetTransform(transform_final->GetMatrix());
271 //------------------------------------------------------------------------------
273 //------------------------------------------------------------------------------
274 void vvToolRigidReg::ToggleSpinBoxAnglesUnit()
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);
286 //------------------------------------------------------------------------------
288 //------------------------------------------------------------------------------
289 void vvToolRigidReg::SaveFile()
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"),
296 tr("Text (*.mat *.txt *.doc *.rtf)"));
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);
308 QMessageBox::information(this,"Error","Unable to open file for writing");
311 //------------------------------------------------------------------------------
313 //------------------------------------------------------------------------------
314 void vvToolRigidReg::LoadFile()
316 //Open File to read the transformation parameters
317 QString file = QFileDialog::getOpenFileName(
319 "Choose the filename for the transformation matrix",
320 vtksys::SystemTools::GetFilenamePath(mCurrentSlicerManager->GetFileName()).c_str(),
321 "Text (*.mat *.txt *.rtf *.doc)");
326 itk::Matrix<double, 4, 4> itkMat = clitk::ReadMatrix3D(file.toStdString());
327 vtkSmartPointer<vtkMatrix4x4> matrix = vtkSmartPointer<vtkMatrix4x4>::New();
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);
334 //------------------------------------------------------------------------------
336 //------------------------------------------------------------------------------
337 void vvToolRigidReg::ChangeOfRotationCenter()
339 // TODO SR and BP: check on the list of transforms and not the first only
340 SetTransform(mCurrentSlicerManager->GetImage()->GetTransform()[0]->GetMatrix());
342 //------------------------------------------------------------------------------
344 //------------------------------------------------------------------------------
345 void vvToolRigidReg::ResetTransform()
347 SetTransform(mInitialMatrix);
349 //------------------------------------------------------------------------------
351 //------------------------------------------------------------------------------
352 void vvToolRigidReg::SetTransform(vtkMatrix4x4 *matrix)
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);
359 dynamic_cast<vvMainWindow*>(mMainWindow)->ImageInfoChanged();
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);
373 itk::Euler3DTransform<double>::Pointer euler;
374 euler = itk::Euler3DTransform<double>::New();
375 euler->SetCenter(center);
376 euler->SetMatrix(rotMat);
377 euler->SetOffset(transVec);
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++) {
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);
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);
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);
409 //------------------------------------------------------------------------------
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)
416 transSliders.push_back(xtrans_slider);
417 transSliders.push_back(ytrans_slider);
418 transSliders.push_back(ztrans_slider);
420 rotSliders.push_back(xrot_slider);
421 rotSliders.push_back(yrot_slider);
422 rotSliders.push_back(zrot_slider);
424 transSBs.push_back(xtrans_sb);
425 transSBs.push_back(ytrans_sb);
426 transSBs.push_back(ztrans_sb);
428 rotSBs.push_back(xrot_sb);
429 rotSBs.push_back(yrot_sb);
430 rotSBs.push_back(zrot_sb);
432 //------------------------------------------------------------------------------
434 //------------------------------------------------------------------------------
435 void vvToolRigidReg::Render()
437 for (int i=0; i<mCurrentSlicerManager->GetNumberOfSlicers(); i++)
439 mCurrentSlicerManager->GetSlicer(i)->ForceUpdateDisplayExtent();
440 mCurrentSlicerManager->GetSlicer(i)->Render();
443 //------------------------------------------------------------------------------