/*========================================================================= Program: vv http://www.creatis.insa-lyon.fr/rio/vv Authors belong to: - University of LYON http://www.universite-lyon.fr/ - Léon Bérard cancer center http://www.centreleonberard.fr - CREATIS CNRS laboratory http://www.creatis.insa-lyon.fr This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the copyright notices for more information. It is distributed under dual licence - BSD See included LICENSE.txt file - CeCILL-B http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html ===========================================================================**/ // vv #include "vvToolRigidReg.h" #include "vvSlicer.h" #include // vtk #include #include #include #include #include #include #include // itk #include // clitk #include "clitkTransformUtilities.h" #include "clitkMatrix.h" // qt #include #include #include //------------------------------------------------------------------------------ // Create the tool and automagically (I like this word) insert it in // the main window menu. ADD_TOOL(vvToolRigidReg); //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ vvToolRigidReg::vvToolRigidReg(vvMainWindowBase * parent, Qt::WindowFlags f): vvToolWidgetBase(parent, f), vvToolBase(parent), Ui::vvToolRigidReg() { // GUI Initialization Ui_vvToolRigidReg::setupUi(mToolWidget); // Set how many inputs are needed for this tool AddInputSelector("Select moving image"); QFont font = transformationLabel->font(); font.setStyleHint(QFont::TypeWriter); transformationLabel->setFont(font); mInitialMatrix = vtkSmartPointer::New(); // Set slider ranges, assume degrees, will not be changed for radians std::vector transSliders, rotSliders; std::vector transSBs, rotSBs; GetSlidersAndSpinBoxes(transSliders, rotSliders, transSBs, rotSBs); for(int i=0; i<3; i++) { transSliders[i]->setRange(-2000,2000); rotSliders[i]->setRange(-360,360); transSBs[i]->setRange(-2000,2000); transSBs[i]->setDecimals(3); rotSBs[i]->setRange(-360,360); rotSBs[i]->setDecimals(3); } } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ vvToolRigidReg::~vvToolRigidReg() { } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void vvToolRigidReg::Initialize() { SetToolName("Register"); SetToolMenuName("Register manually"); SetToolIconFilename(":/common/icons/register.png"); SetToolTip("Register manually."); SetToolExperimental(false); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void vvToolRigidReg::InputIsSelected(vvSlicerManager *input) { mInput = input; HideInputSelector(); QTabWidget * tab = dynamic_cast(mMainWindow)->GetTab(); move(tab->mapToGlobal(tab->pos())); resize(tab->width(), 0); //default image rotation center is the center of the image QString xcord,ycord,zcord; std::vector imageorigin; imageorigin=mInput->GetImage()->GetOrigin(); std::vector imageSize = mInput->GetImage()->GetSize(); std::vector imageSpacing = mInput->GetImage()->GetSpacing(); xcord=xcord.setNum(imageorigin[0]+(imageSize[0]-1)*imageSpacing[0]*0.5, 'g', 3); ycord=ycord.setNum(imageorigin[1]+(imageSize[1]-1)*imageSpacing[1]*0.5, 'g', 3); zcord=zcord.setNum(imageorigin[2]+(imageSize[2]-1)*imageSpacing[2]*0.5, 'g', 3); Xval->setText(xcord); Yval->setText(ycord); Zval->setText(zcord); //backup original matrix for(int j=0; j<4; j++) for(int i=0; i<4; i++) // TODO SR and BP: check on the list of transforms and not the first only mInitialMatrix->SetElement(i,j, mCurrentSlicerManager->GetImage()->GetTransform()[0]->GetMatrix()->GetElement(i,j)); QString origTransformString(clitk::Get4x4MatrixDoubleAsString(mInitialMatrix).c_str()); transformationLabel->setText(origTransformString); SetTransform(mInitialMatrix); //connect all sigs to slots connect(resetbutton, SIGNAL(pressed()), this, SLOT(ResetTransform())); connect(loadbutton, SIGNAL(pressed()), this, SLOT(LoadFile())); connect(savebutton, SIGNAL(pressed()), this, SLOT(SaveFile())); connect(xtrans_slider, SIGNAL(valueChanged(int)), this, SLOT(SliderChange(int))); connect(ytrans_slider, SIGNAL(valueChanged(int)), this, SLOT(SliderChange(int))); connect(ztrans_slider, SIGNAL(valueChanged(int)), this, SLOT(SliderChange(int))); connect(xrot_slider, SIGNAL(valueChanged(int)), this, SLOT(SliderChange(int))); connect(yrot_slider, SIGNAL(valueChanged(int)), this, SLOT(SliderChange(int))); connect(zrot_slider, SIGNAL(valueChanged(int)), this, SLOT(SliderChange(int))); connect(xtrans_sb, SIGNAL(valueChanged(double)), this, SLOT(SpinBoxChange(double))); connect(ytrans_sb, SIGNAL(valueChanged(double)), this, SLOT(SpinBoxChange(double))); connect(ztrans_sb, SIGNAL(valueChanged(double)), this, SLOT(SpinBoxChange(double))); connect(xrot_sb, SIGNAL(valueChanged(double)), this, SLOT(SpinBoxChange(double))); connect(yrot_sb, SIGNAL(valueChanged(double)), this, SLOT(SpinBoxChange(double))); connect(zrot_sb, SIGNAL(valueChanged(double)), this, SLOT(SpinBoxChange(double))); connect(stepTransSpinBox, SIGNAL(valueChanged(double)), this, SLOT(SetTranslationStep(double))); connect(stepRotSpinBox, SIGNAL(valueChanged(double)), this, SLOT(SetRotationStep(double))); connect(checkBoxDegrees, SIGNAL(stateChanged(int)), this, SLOT(ToggleSpinBoxAnglesUnit())); connect(Xval, SIGNAL(editingFinished()), this, SLOT(ChangeOfRotationCenter())); connect(Yval, SIGNAL(editingFinished()), this, SLOT(ChangeOfRotationCenter())); connect(Zval, SIGNAL(editingFinished()), this, SLOT(ChangeOfRotationCenter())); // Init step modifiers stepTransSpinBox->setValue(1.); stepRotSpinBox->setValue(1.); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void vvToolRigidReg::apply() { vvToolWidgetBase::close(); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ bool vvToolRigidReg::close() { QString warning = "Are you sure you want to reset the original transform?"; QMessageBox msgBox(QMessageBox::Warning, tr("Reset transform"),warning, 0, this); msgBox.addButton(tr("Yes"), QMessageBox::AcceptRole); msgBox.addButton(tr("No"), QMessageBox::RejectRole); if (msgBox.exec() == QMessageBox::AcceptRole) { if (mCurrentSlicerManager) SetTransform(mInitialMatrix); return vvToolWidgetBase::close(); } return false; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void vvToolRigidReg::reject() { return vvToolWidgetBase::reject(); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void vvToolRigidReg::SetTranslationStep(double v) { xtrans_sb->setSingleStep(v); ytrans_sb->setSingleStep(v); ztrans_sb->setSingleStep(v); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void vvToolRigidReg::SetRotationStep(double v) { xrot_sb->setSingleStep(v); yrot_sb->setSingleStep(v); zrot_sb->setSingleStep(v); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void vvToolRigidReg::SliderChange(int newVal) { std::vector transSliders, rotSliders; std::vector transSBs, rotSBs; GetSlidersAndSpinBoxes(transSliders, rotSliders, transSBs, rotSBs); for(int i=0; i<3; i++) { if(transSliders[i] == QObject::sender()) { transSBs[i]->setValue(newVal); } if(rotSliders[i] == QObject::sender()) { double rad = (checkBoxDegrees->checkState()==Qt::Unchecked)?itk::Math::pi/180.:1.; rotSBs[i]->setValue(newVal*rad); } } } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void vvToolRigidReg::SpinBoxChange(double newVal) { std::vector transSliders, rotSliders; std::vector transSBs, rotSBs; GetSlidersAndSpinBoxes(transSliders, rotSliders, transSBs, rotSBs); for(int i=0; i<3; i++) { if(transSBs[i] == QObject::sender()) { transSliders[i]->blockSignals(true); transSliders[i]->setValue(itk::Math::Round(newVal)); transSliders[i]->blockSignals(false); } if(rotSBs[i] == QObject::sender()) { double rad = (checkBoxDegrees->checkState()==Qt::Unchecked)?180./itk::Math::pi:1.; rotSliders[i]->blockSignals(true); rotSliders[i]->setValue(itk::Math::Round(newVal*rad)); rotSliders[i]->blockSignals(false); } } // Compute transform and set // TODO SR and BP: check on the list of transforms and not the first only vtkSmartPointer transform_final=mInput->GetImage()->GetTransform()[0]; transform_final->Identity(); transform_final->PostMultiply(); // Rotations double x=0, y=0 ,z=0; x= Xval->text().toDouble(); y= Yval->text().toDouble(); z= Zval->text().toDouble(); double rad = (checkBoxDegrees->checkState()==Qt::Unchecked)?180./itk::Math::pi:1.; transform_final->Translate(-x,-y,-z); transform_final->RotateY(yrot_sb->value()*rad); transform_final->RotateX(xrot_sb->value()*rad); transform_final->RotateZ(zrot_sb->value()*rad); transform_final->Translate(x,y,z); // Translation transform_final->Translate(xtrans_sb->value(), ytrans_sb->value(), ztrans_sb->value()); transform_final->Update(); SetTransform(transform_final->GetMatrix()); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void vvToolRigidReg::ToggleSpinBoxAnglesUnit() { double rad = (checkBoxDegrees->checkState()==Qt::Unchecked)?itk::Math::pi/180.:180./itk::Math::pi; std::vector transSliders, rotSliders; std::vector transSBs, rotSBs; GetSlidersAndSpinBoxes(transSliders, rotSliders, transSBs, rotSBs); for(int i=0; i<3; i++) { rotSBs[i]->blockSignals(true); rotSBs[i]->setValue(rotSBs[i]->value()*rad); rotSBs[i]->blockSignals(false); } } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void vvToolRigidReg::SaveFile() { //Write the Transformation Matrix std::string absPath = mCurrentSlicerManager->GetFileName(); absPath = itksys::SystemTools::GetFilenameWithoutExtension(absPath) + std::string(".mat"); QString filename = QFileDialog::getSaveFileName(this, tr("Save Transformation Matrix File"), absPath.c_str(), tr("Text (*.mat *.txt *.doc *.rtf)")); QFile file(filename); if (file.open(QFile::WriteOnly | QFile::Truncate)) { // TODO SR and BP: check on the list of transforms and not the first only vtkMatrix4x4* matrix = mCurrentSlicerManager->GetImage()->GetTransform()[0]->GetMatrix(); QString matrixStr = clitk::Get4x4MatrixDoubleAsString(matrix,16).c_str(); QTextStream out(&file); out << matrixStr; } else { QMessageBox::information(this,"Error","Unable to open file for writing"); } } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void vvToolRigidReg::LoadFile() { //Open File to read the transformation parameters QString file = QFileDialog::getOpenFileName( this, "Choose the filename for the transformation matrix", vtksys::SystemTools::GetFilenamePath(mCurrentSlicerManager->GetFileName()).c_str(), "Text (*.mat *.txt *.rtf *.doc)"); if (file.isEmpty()) return; itk::Matrix itkMat = clitk::ReadMatrix3D(file.toStdString()); vtkSmartPointer matrix = vtkSmartPointer::New(); matrix->Identity(); for(int j=0; j<4; j++) for(int i=0; i<4; i++) matrix->SetElement(j,i,itkMat[j][i]); SetTransform(matrix); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void vvToolRigidReg::ChangeOfRotationCenter() { // TODO SR and BP: check on the list of transforms and not the first only SetTransform(mCurrentSlicerManager->GetImage()->GetTransform()[0]->GetMatrix()); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void vvToolRigidReg::ResetTransform() { SetTransform(mInitialMatrix); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void vvToolRigidReg::SetTransform(vtkMatrix4x4 *matrix) { vtkSmartPointer transform=vtkSmartPointer::New(); // TODO SR and BP: check on the list of transforms and not the first only mCurrentSlicerManager->GetImage()->GetTransform()[0]->SetMatrix(matrix); transform->Update(); Render(); dynamic_cast(mMainWindow)->ImageInfoChanged(); // Compute parameters from transfer using itk Euler transform itk::Euler3DTransform::CenterType center; center[0] = Xval->text().toDouble(); center[1] = Yval->text().toDouble(); center[2] = Zval->text().toDouble(); itk::Euler3DTransform::MatrixType rotMat; itk::Euler3DTransform::OutputVectorType transVec; for(int i=0; i<3; i++) { transVec[i] = matrix->GetElement(i,3); for(int j=0; j<3; j++) rotMat[i][j] = matrix->GetElement(i,j); } itk::Euler3DTransform::Pointer euler; euler = itk::Euler3DTransform::New(); euler->SetCenter(center); try { #if ITK_VERSION_MAJOR > 4 || (ITK_VERSION_MAJOR == 4 && ITK_VERSION_MINOR > 6) euler->SetMatrix(rotMat,0.00001); #else euler->SetMatrix(rotMat); #endif } catch (itk::ExceptionObject) { QString warning = "The matrice is a non-orthogonal rotation matrix.\nThe manual registration doesn't work."; QMessageBox msgBox(QMessageBox::Warning, tr("Reset transform"),warning, 0, this); msgBox.addButton(tr("OK"), QMessageBox::AcceptRole); if (msgBox.exec() == QMessageBox::AcceptRole) { //SetTransform(mInitialMatrix); vvToolWidgetBase::close(); } } euler->SetOffset(transVec); // Modify GUI according to the new parameters std::vector transSliders, rotSliders; std::vector transSBs, rotSBs; GetSlidersAndSpinBoxes(transSliders, rotSliders, transSBs, rotSBs); for(int i=0; i<3; i++) { // Translations transSBs[i]->blockSignals(true); transSBs[i]->setValue( euler->GetParameters()[i+3] ); transSBs[i]->blockSignals(false); transSliders[i]->blockSignals(true); transSliders[i]->setValue( itk::Math::Round(euler->GetParameters()[i+3]) ); transSliders[i]->blockSignals(false); // Rotations double rad = (checkBoxDegrees->checkState()==Qt::Checked)?180./itk::Math::pi:1.; double angleDiff = euler->GetParameters()[i]-rotSBs[i]->value()/rad+2*itk::Math::pi; angleDiff = angleDiff - 2*itk::Math::pi*itk::Math::Round(angleDiff/(2*itk::Math::pi)); if(std::abs(angleDiff)>1.e-4) { rotSBs[i]->blockSignals(true); rotSBs[i]->setValue( euler->GetParameters()[i]*rad ); rotSBs[i]->blockSignals(false); } int iAngle = itk::Math::Round(euler->GetParameters()[i]*180./itk::Math::pi); if((iAngle-rotSliders[i]->value()+360)%360!=0) { rotSliders[i]->blockSignals(true); rotSliders[i]->setValue(iAngle); rotSliders[i]->blockSignals(false); } } } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ // Just an helper function to shorten the code with loops on sliders and spinboxes void vvToolRigidReg::GetSlidersAndSpinBoxes(std::vector&transSliders, std::vector&rotSliders, std::vector&transSBs, std::vector&rotSBs) { transSliders.push_back(xtrans_slider); transSliders.push_back(ytrans_slider); transSliders.push_back(ztrans_slider); rotSliders.push_back(xrot_slider); rotSliders.push_back(yrot_slider); rotSliders.push_back(zrot_slider); transSBs.push_back(xtrans_sb); transSBs.push_back(ytrans_sb); transSBs.push_back(ztrans_sb); rotSBs.push_back(xrot_sb); rotSBs.push_back(yrot_sb); rotSBs.push_back(zrot_sb); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void vvToolRigidReg::ExtentMax(const double pointExtent[8][4], double maxExtent[2][3]) { double max, min; for (int i=0; i<3; ++i) { max = pointExtent[0][i]; min = pointExtent[0][i]; for (int j=1; j<8; ++j) { if (pointExtent[j][i] > max) { max = pointExtent[j][i]; } if (pointExtent[j][i] < min) { min = pointExtent[j][i]; } } maxExtent[0][i] = min; maxExtent[1][i] = max; } } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void vvToolRigidReg::Render() { for (int i=0; iGetNumberOfSlicers(); i++) { mCurrentSlicerManager->GetSlicer(i)->ForceUpdateDisplayExtent(); mCurrentSlicerManager->GetSlicer(i)->Render(); } } //------------------------------------------------------------------------------