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 <vtkInformation.h>
27 #include <vtkTransform.h>
28 #include <vtkImageActor.h>
29 #include <vtkImageMapper3D.h>
30 #include <vtkOpenGLImageSliceMapper.h>
33 #include <itkEuler3DTransform.h>
36 #include "clitkTransformUtilities.h"
37 #include "clitkMatrix.h"
40 #include <QMessageBox>
41 #include <QFileDialog>
42 #include <QTextStream>
45 //------------------------------------------------------------------------------
46 // Create the tool and automagically (I like this word) insert it in
47 // the main window menu.
48 ADD_TOOL(vvToolRigidReg);
49 //------------------------------------------------------------------------------
51 //------------------------------------------------------------------------------
52 vvToolRigidReg::vvToolRigidReg(vvMainWindowBase * parent, Qt::WindowFlags f):
53 vvToolWidgetBase(parent, f),
54 vvToolBase<vvToolRigidReg>(parent),
58 Ui_vvToolRigidReg::setupUi(mToolWidget);
60 // Set how many inputs are needed for this tool
61 AddInputSelector("Select moving image");
63 QFont font = transformationLabel->font();
64 font.setStyleHint(QFont::TypeWriter);
65 transformationLabel->setFont(font);
67 mInitialMatrix = vtkSmartPointer<vtkMatrix4x4>::New();
69 // Set slider ranges, assume degrees, will not be changed for radians
70 std::vector<QSlider *> transSliders, rotSliders;
71 std::vector<QDoubleSpinBox *> transSBs, rotSBs;
72 GetSlidersAndSpinBoxes(transSliders, rotSliders, transSBs, rotSBs);
73 for(int i=0; i<3; i++) {
74 transSliders[i]->setRange(-2000,2000);
75 rotSliders[i]->setRange(-360,360);
76 transSBs[i]->setRange(-2000,2000);
77 transSBs[i]->setDecimals(3);
78 rotSBs[i]->setRange(-360,360);
79 rotSBs[i]->setDecimals(3);
82 //------------------------------------------------------------------------------
84 //------------------------------------------------------------------------------
85 vvToolRigidReg::~vvToolRigidReg()
88 //------------------------------------------------------------------------------
90 //------------------------------------------------------------------------------
91 void vvToolRigidReg::Initialize()
93 SetToolName("Register");
94 SetToolMenuName("Register manually");
95 SetToolIconFilename(":/common/icons/register.png");
96 SetToolTip("Register manually.");
97 SetToolExperimental(false);
99 //------------------------------------------------------------------------------
101 //------------------------------------------------------------------------------
102 void vvToolRigidReg::InputIsSelected(vvSlicerManager *input)
106 QTabWidget * tab = dynamic_cast<vvMainWindow*>(mMainWindow)->GetTab();
107 move(tab->mapToGlobal(tab->pos()));
108 resize(tab->width(), 0);
110 //default image rotation center is the center of the image
111 QString xcord,ycord,zcord;
112 std::vector<double> imageorigin;
113 imageorigin=mInput->GetImage()->GetOrigin();
114 std::vector<int> imageSize = mInput->GetImage()->GetSize();
115 std::vector<double> imageSpacing = mInput->GetImage()->GetSpacing();
116 xcord=xcord.setNum(imageorigin[0]+(imageSize[0]-1)*imageSpacing[0]*0.5, 'g', 3);
117 ycord=ycord.setNum(imageorigin[1]+(imageSize[1]-1)*imageSpacing[1]*0.5, 'g', 3);
118 zcord=zcord.setNum(imageorigin[2]+(imageSize[2]-1)*imageSpacing[2]*0.5, 'g', 3);
119 Xval->setText(xcord);
120 Yval->setText(ycord);
121 Zval->setText(zcord);
123 //backup original matrix
124 for(int j=0; j<4; j++)
125 for(int i=0; i<4; i++)
126 // TODO SR and BP: check on the list of transforms and not the first only
127 mInitialMatrix->SetElement(i,j, mCurrentSlicerManager->GetImage()->GetTransform()[0]->GetMatrix()->GetElement(i,j));
128 QString origTransformString(clitk::Get4x4MatrixDoubleAsString(mInitialMatrix).c_str());
129 transformationLabel->setText(origTransformString);
130 SetTransform(mInitialMatrix);
132 //connect all sigs to slots
133 connect(resetbutton, SIGNAL(pressed()), this, SLOT(ResetTransform()));
134 connect(loadbutton, SIGNAL(pressed()), this, SLOT(LoadFile()));
135 connect(savebutton, SIGNAL(pressed()), this, SLOT(SaveFile()));
137 connect(xtrans_slider, SIGNAL(valueChanged(int)), this, SLOT(SliderChange(int)));
138 connect(ytrans_slider, SIGNAL(valueChanged(int)), this, SLOT(SliderChange(int)));
139 connect(ztrans_slider, SIGNAL(valueChanged(int)), this, SLOT(SliderChange(int)));
140 connect(xrot_slider, SIGNAL(valueChanged(int)), this, SLOT(SliderChange(int)));
141 connect(yrot_slider, SIGNAL(valueChanged(int)), this, SLOT(SliderChange(int)));
142 connect(zrot_slider, SIGNAL(valueChanged(int)), this, SLOT(SliderChange(int)));
143 connect(xtrans_sb, SIGNAL(valueChanged(double)), this, SLOT(SpinBoxChange(double)));
144 connect(ytrans_sb, SIGNAL(valueChanged(double)), this, SLOT(SpinBoxChange(double)));
145 connect(ztrans_sb, SIGNAL(valueChanged(double)), this, SLOT(SpinBoxChange(double)));
146 connect(xrot_sb, SIGNAL(valueChanged(double)), this, SLOT(SpinBoxChange(double)));
147 connect(yrot_sb, SIGNAL(valueChanged(double)), this, SLOT(SpinBoxChange(double)));
148 connect(zrot_sb, SIGNAL(valueChanged(double)), this, SLOT(SpinBoxChange(double)));
150 connect(stepTransSpinBox, SIGNAL(valueChanged(double)), this, SLOT(SetTranslationStep(double)));
151 connect(stepRotSpinBox, SIGNAL(valueChanged(double)), this, SLOT(SetRotationStep(double)));
153 connect(checkBoxDegrees, SIGNAL(stateChanged(int)), this, SLOT(ToggleSpinBoxAnglesUnit()));
155 connect(Xval, SIGNAL(editingFinished()), this, SLOT(ChangeOfRotationCenter()));
156 connect(Yval, SIGNAL(editingFinished()), this, SLOT(ChangeOfRotationCenter()));
157 connect(Zval, SIGNAL(editingFinished()), this, SLOT(ChangeOfRotationCenter()));
159 // Init step modifiers
160 stepTransSpinBox->setValue(1.);
161 stepRotSpinBox->setValue(1.);
163 //------------------------------------------------------------------------------
165 //------------------------------------------------------------------------------
166 void vvToolRigidReg::apply()
168 vvToolWidgetBase::close();
170 //------------------------------------------------------------------------------
172 //------------------------------------------------------------------------------
173 bool vvToolRigidReg::close()
175 QString warning = "Are you sure you want to reset the original transform?";
176 QMessageBox msgBox(QMessageBox::Warning, tr("Reset transform"),warning, 0, this);
177 msgBox.addButton(tr("Yes"), QMessageBox::AcceptRole);
178 msgBox.addButton(tr("No"), QMessageBox::RejectRole);
179 if (msgBox.exec() == QMessageBox::AcceptRole) {
180 SetTransform(mInitialMatrix);
181 return vvToolWidgetBase::close();
185 //------------------------------------------------------------------------------
187 //------------------------------------------------------------------------------
188 void vvToolRigidReg::reject()
190 return vvToolWidgetBase::reject();
192 //------------------------------------------------------------------------------
194 //------------------------------------------------------------------------------
195 void vvToolRigidReg::SetTranslationStep(double v)
197 xtrans_sb->setSingleStep(v);
198 ytrans_sb->setSingleStep(v);
199 ztrans_sb->setSingleStep(v);
201 //------------------------------------------------------------------------------
203 //------------------------------------------------------------------------------
204 void vvToolRigidReg::SetRotationStep(double v)
206 xrot_sb->setSingleStep(v);
207 yrot_sb->setSingleStep(v);
208 zrot_sb->setSingleStep(v);
210 //------------------------------------------------------------------------------
212 //------------------------------------------------------------------------------
213 void vvToolRigidReg::SliderChange(int newVal)
215 std::vector<QSlider *> transSliders, rotSliders;
216 std::vector<QDoubleSpinBox *> transSBs, rotSBs;
217 GetSlidersAndSpinBoxes(transSliders, rotSliders, transSBs, rotSBs);
218 for(int i=0; i<3; i++) {
219 if(transSliders[i] == QObject::sender()) {
220 transSBs[i]->setValue(newVal);
222 if(rotSliders[i] == QObject::sender()) {
223 double rad = (checkBoxDegrees->checkState()==Qt::Unchecked)?itk::Math::pi/180.:1.;
224 rotSBs[i]->setValue(newVal*rad);
228 //------------------------------------------------------------------------------
230 //------------------------------------------------------------------------------
231 void vvToolRigidReg::SpinBoxChange(double newVal)
233 std::vector<QSlider *> transSliders, rotSliders;
234 std::vector<QDoubleSpinBox *> transSBs, rotSBs;
235 GetSlidersAndSpinBoxes(transSliders, rotSliders, transSBs, rotSBs);
236 for(int i=0; i<3; i++) {
237 if(transSBs[i] == QObject::sender()) {
238 transSliders[i]->blockSignals(true);
239 transSliders[i]->setValue(itk::Math::Round<double,double>(newVal));
240 transSliders[i]->blockSignals(false);
242 if(rotSBs[i] == QObject::sender()) {
243 double rad = (checkBoxDegrees->checkState()==Qt::Unchecked)?180./itk::Math::pi:1.;
244 rotSliders[i]->blockSignals(true);
245 rotSliders[i]->setValue(itk::Math::Round<double,double>(newVal*rad));
246 rotSliders[i]->blockSignals(false);
250 // Compute transform and set
251 // TODO SR and BP: check on the list of transforms and not the first only
252 vtkSmartPointer<vtkTransform> transform_final=mInput->GetImage()->GetTransform()[0];
253 transform_final->Identity();
254 transform_final->PostMultiply();
257 double x=0, y=0 ,z=0;
258 x= Xval->text().toDouble();
259 y= Yval->text().toDouble();
260 z= Zval->text().toDouble();
261 double rad = (checkBoxDegrees->checkState()==Qt::Unchecked)?180./itk::Math::pi:1.;
262 transform_final->Translate(-x,-y,-z);
263 transform_final->RotateY(yrot_sb->value()*rad);
264 transform_final->RotateX(xrot_sb->value()*rad);
265 transform_final->RotateZ(zrot_sb->value()*rad);
266 transform_final->Translate(x,y,z);
269 transform_final->Translate(xtrans_sb->value(),
272 transform_final->Update();
273 SetTransform(transform_final->GetMatrix());
275 //------------------------------------------------------------------------------
277 //------------------------------------------------------------------------------
278 void vvToolRigidReg::ToggleSpinBoxAnglesUnit()
280 double rad = (checkBoxDegrees->checkState()==Qt::Unchecked)?itk::Math::pi/180.:180./itk::Math::pi;
281 std::vector<QSlider *> transSliders, rotSliders;
282 std::vector<QDoubleSpinBox *> transSBs, rotSBs;
283 GetSlidersAndSpinBoxes(transSliders, rotSliders, transSBs, rotSBs);
284 for(int i=0; i<3; i++) {
285 rotSBs[i]->blockSignals(true);
286 rotSBs[i]->setValue(rotSBs[i]->value()*rad);
287 rotSBs[i]->blockSignals(false);
290 //------------------------------------------------------------------------------
292 //------------------------------------------------------------------------------
293 void vvToolRigidReg::SaveFile()
295 //Write the Transformation Matrix
296 std::string absPath = mCurrentSlicerManager->GetFileName();
297 absPath = itksys::SystemTools::GetFilenameWithoutExtension(absPath) + std::string(".mat");
298 QString filename = QFileDialog::getSaveFileName(this, tr("Save Transformation Matrix File"),
300 tr("Text (*.mat *.txt *.doc *.rtf)"));
302 QFile file(filename);
303 if (file.open(QFile::WriteOnly | QFile::Truncate)) {
304 // TODO SR and BP: check on the list of transforms and not the first only
305 vtkMatrix4x4* matrix = mCurrentSlicerManager->GetImage()->GetTransform()[0]->GetMatrix();
306 QString matrixStr = clitk::Get4x4MatrixDoubleAsString(matrix,16).c_str();
307 QTextStream out(&file);
312 QMessageBox::information(this,"Error","Unable to open file for writing");
315 //------------------------------------------------------------------------------
317 //------------------------------------------------------------------------------
318 void vvToolRigidReg::LoadFile()
320 //Open File to read the transformation parameters
321 QString file = QFileDialog::getOpenFileName(
323 "Choose the filename for the transformation matrix",
324 vtksys::SystemTools::GetFilenamePath(mCurrentSlicerManager->GetFileName()).c_str(),
325 "Text (*.mat *.txt *.rtf *.doc)");
330 itk::Matrix<double, 4, 4> itkMat = clitk::ReadMatrix3D(file.toStdString());
331 vtkSmartPointer<vtkMatrix4x4> matrix = vtkSmartPointer<vtkMatrix4x4>::New();
333 for(int j=0; j<4; j++)
334 for(int i=0; i<4; i++)
335 matrix->SetElement(j,i,itkMat[j][i]);
336 SetTransform(matrix);
338 //------------------------------------------------------------------------------
340 //------------------------------------------------------------------------------
341 void vvToolRigidReg::ChangeOfRotationCenter()
343 // TODO SR and BP: check on the list of transforms and not the first only
344 SetTransform(mCurrentSlicerManager->GetImage()->GetTransform()[0]->GetMatrix());
346 //------------------------------------------------------------------------------
348 //------------------------------------------------------------------------------
349 void vvToolRigidReg::ResetTransform()
351 SetTransform(mInitialMatrix);
353 //------------------------------------------------------------------------------
355 //------------------------------------------------------------------------------
356 void vvToolRigidReg::SetTransform(vtkMatrix4x4 *matrix)
358 vtkSmartPointer<vtkTransform> transform=vtkSmartPointer<vtkTransform>::New();
359 // TODO SR and BP: check on the list of transforms and not the first only
360 mCurrentSlicerManager->GetImage()->GetTransform()[0]->SetMatrix(matrix);
363 dynamic_cast<vvMainWindow*>(mMainWindow)->ImageInfoChanged();
365 // Compute parameters from transfer using itk Euler transform
366 itk::Euler3DTransform<double>::CenterType center;
367 center[0] = Xval->text().toDouble();
368 center[1] = Yval->text().toDouble();
369 center[2] = Zval->text().toDouble();
370 itk::Euler3DTransform<double>::MatrixType rotMat;
371 itk::Euler3DTransform<double>::OutputVectorType transVec;
372 for(int i=0; i<3; i++) {
373 transVec[i] = matrix->GetElement(i,3);
374 for(int j=0; j<3; j++)
375 rotMat[i][j] = matrix->GetElement(i,j);
377 itk::Euler3DTransform<double>::Pointer euler;
378 euler = itk::Euler3DTransform<double>::New();
379 euler->SetCenter(center);
380 euler->SetMatrix(rotMat);
381 euler->SetOffset(transVec);
383 // Modify GUI according to the new parameters
384 std::vector<QSlider *> transSliders, rotSliders;
385 std::vector<QDoubleSpinBox *> transSBs, rotSBs;
386 GetSlidersAndSpinBoxes(transSliders, rotSliders, transSBs, rotSBs);
387 for(int i=0; i<3; i++) {
389 transSBs[i]->blockSignals(true);
390 transSBs[i]->setValue( euler->GetParameters()[i+3] );
391 transSBs[i]->blockSignals(false);
392 transSliders[i]->blockSignals(true);
393 transSliders[i]->setValue( itk::Math::Round<double,double>(euler->GetParameters()[i+3]) );
394 transSliders[i]->blockSignals(false);
397 double rad = (checkBoxDegrees->checkState()==Qt::Checked)?180./itk::Math::pi:1.;
398 double angleDiff = euler->GetParameters()[i]-rotSBs[i]->value()/rad+2*itk::Math::pi;
399 angleDiff = angleDiff - 2*itk::Math::pi*itk::Math::Round<double,double>(angleDiff/(2*itk::Math::pi));
400 if(angleDiff>1.e-4) {
401 rotSBs[i]->blockSignals(true);
402 rotSBs[i]->setValue( euler->GetParameters()[i]*rad );
403 rotSBs[i]->blockSignals(false);
405 int iAngle = itk::Math::Round<int,double>(euler->GetParameters()[i]*180./itk::Math::pi);
406 if((iAngle-rotSliders[i]->value()+360)%360!=0) {
407 rotSliders[i]->blockSignals(true);
408 rotSliders[i]->setValue(iAngle);
409 rotSliders[i]->blockSignals(false);
413 //------------------------------------------------------------------------------
415 //------------------------------------------------------------------------------
416 // Just an helper function to shorten the code with loops on sliders and spinboxes
417 void vvToolRigidReg::GetSlidersAndSpinBoxes(std::vector<QSlider *>&transSliders, std::vector<QSlider *>&rotSliders,
418 std::vector<QDoubleSpinBox *>&transSBs, std::vector<QDoubleSpinBox *>&rotSBs)
420 transSliders.push_back(xtrans_slider);
421 transSliders.push_back(ytrans_slider);
422 transSliders.push_back(ztrans_slider);
424 rotSliders.push_back(xrot_slider);
425 rotSliders.push_back(yrot_slider);
426 rotSliders.push_back(zrot_slider);
428 transSBs.push_back(xtrans_sb);
429 transSBs.push_back(ytrans_sb);
430 transSBs.push_back(ztrans_sb);
432 rotSBs.push_back(xrot_sb);
433 rotSBs.push_back(yrot_sb);
434 rotSBs.push_back(zrot_sb);
436 //------------------------------------------------------------------------------
438 //------------------------------------------------------------------------------
439 void vvToolRigidReg::ExtentMax(const double pointExtent[8][4], double maxExtent[2][3])
442 for (int i=0; i<3; ++i) {
443 max = pointExtent[0][i];
444 min = pointExtent[0][i];
445 for (int j=1; j<8; ++j) {
446 if (pointExtent[j][i] > max) {
447 max = pointExtent[j][i];
449 if (pointExtent[j][i] < min) {
450 min = pointExtent[j][i];
453 maxExtent[0][i] = min;
454 maxExtent[1][i] = max;
457 //------------------------------------------------------------------------------
459 //------------------------------------------------------------------------------
460 void vvToolRigidReg::Render()
461 { //out << __func__ << endl;
462 #if VTK_MAJOR_VERSION > 5
463 vtkMatrix4x4* matrix = mCurrentSlicerManager->GetImage()->GetTransform()[0]->GetMatrix();
464 vtkMatrix4x4* matrixTranspose = matrix->NewInstance();
465 for (int i=0; i<3; ++i) {
466 for (int j=0; j<3; ++j)
468 matrixTranspose->SetElement(i,j,matrix->GetElement(j,i));
471 for (int i=0; i<4; ++i) {
472 matrixTranspose->SetElement(3,i,matrix->GetElement(3,i));
474 for (int i=0; i<4; ++i) {
475 matrixTranspose->SetElement(i,3,matrix->GetElement(i,3));
478 for (int i=0; i<mCurrentSlicerManager->GetNumberOfSlicers(); i++)
480 #if VTK_MAJOR_VERSION > 5
481 double pointExtent[8][4], pointExtentUpdate[8][4];
482 std::vector<int> w_ext;
483 w_ext=mCurrentSlicerManager->GetImage()->GetSize();
484 pointExtent[0][0] = 0.0;
485 pointExtent[0][1] = 0.0;
486 pointExtent[0][2] = 0.0;
487 pointExtent[0][3] = 1.0;
488 pointExtent[1][0] = w_ext[0]-1;
489 pointExtent[1][1] = w_ext[1]-1;
490 pointExtent[1][2] = w_ext[2]-1;
491 pointExtent[1][3] = 1.0;
492 pointExtent[2][0] = 0.0;
493 pointExtent[2][1] = w_ext[1]-1;
494 pointExtent[2][2] = w_ext[2]-1;
495 pointExtent[2][3] = 1.0;
496 pointExtent[3][0] = w_ext[0]-1;
497 pointExtent[3][1] = 0.0;
498 pointExtent[3][2] = w_ext[2]-1;
499 pointExtent[3][3] = 1.0;
500 pointExtent[4][0] = w_ext[0]-1;
501 pointExtent[4][1] = w_ext[1]-1;
502 pointExtent[4][2] = 0.0;
503 pointExtent[4][3] = 1.0;
504 pointExtent[5][0] = 0.0;
505 pointExtent[5][1] = 0.0;
506 pointExtent[5][2] = w_ext[2]-1;
507 pointExtent[5][3] = 1.0;
508 pointExtent[6][0] = 0.0;
509 pointExtent[6][1] = w_ext[1]-1;
510 pointExtent[6][2] = 0.0;
511 pointExtent[6][3] = 1.0;
512 pointExtent[7][0] = w_ext[0]-1;
513 pointExtent[7][1] = 0.0;
514 pointExtent[7][2] = 0.0;
515 pointExtent[7][3] = 1.0;
517 for (int k=0; k<8; ++k) {
518 for (int j=0; j<3; ++j)
520 pointExtent[k][j] = mCurrentSlicerManager->GetImage()->GetOrigin()[j] + mCurrentSlicerManager->GetImage()->GetSpacing()[j] * pointExtent[k][j];
522 matrixTranspose->MultiplyPoint(pointExtent[k], pointExtentUpdate[k]);
523 for (int j=0; j<3; ++j)
525 pointExtentUpdate[k][j] = (pointExtentUpdate[k][j] - mCurrentSlicerManager->GetImage()->GetOrigin()[j])/mCurrentSlicerManager->GetImage()->GetSpacing()[j];
528 double extUpdateTemp[2][3];
530 ExtentMax(pointExtentUpdate, extUpdateTemp);
531 for (int j=0; j<3; ++j) {
533 extUpdate[2*j+1] = itk::Math::Round<double>(extUpdateTemp[1][j] - extUpdateTemp[0][j]);
535 mCurrentSlicerManager->GetSlicer(i)->SetRegisterExtent(extUpdate);
536 extUpdate[2*mCurrentSlicerManager->GetSlicer(i)->GetOrientation()] = mCurrentSlicerManager->GetSlicer(i)->GetSlice();
537 extUpdate[2*mCurrentSlicerManager->GetSlicer(i)->GetOrientation()+1] = mCurrentSlicerManager->GetSlicer(i)->GetSlice();
539 vtkSmartPointer<vtkOpenGLImageSliceMapper> mapperOpenGL= vtkSmartPointer<vtkOpenGLImageSliceMapper>::New();
541 mapperOpenGL = dynamic_cast<vtkOpenGLImageSliceMapper*>(mCurrentSlicerManager->GetSlicer(i)->GetImageActor()->GetMapper());
542 } catch (const std::bad_cast& e) {
543 std::cerr << e.what() << std::endl;
544 std::cerr << "Conversion error" << std::endl;
547 mapperOpenGL->SetCroppingRegion(extUpdate);
549 mCurrentSlicerManager->GetSlicer(i)->ForceUpdateDisplayExtent();
550 mCurrentSlicerManager->GetSlicer(i)->Render();
553 //------------------------------------------------------------------------------