00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017 #include <iostream>
00018
00019 #include "QmitkImageCropper.h"
00020 #include <QAction>
00021
00022 #include <QInputDialog>
00023 #include <QMessageBox>
00024 #include <QCheckBox>
00025 #include <QSpinBox>
00026 #include <QSlider>
00027 #include <vtkRenderWindow.h>
00028
00029
00030 #include <mitkEllipsoid.h>
00031 #include <mitkCylinder.h>
00032 #include <mitkCone.h>
00033 #include <mitkRenderingManager.h>
00034 #include <mitkProperties.h>
00035 #include <mitkGlobalInteraction.h>
00036 #include "mitkUndoController.h"
00037 #include "mitkBoundingObjectCutter.h"
00038 #include "mitkImageAccessByItk.h"
00039 #include "mitkITKImageImport.h"
00040 #include "mitkIDataStorageService.h"
00041 #include "mitkNodePredicateDataType.h"
00042
00043 #include <itkCommand.h>
00044
00045
00046 const mitk::OperationType QmitkImageCropper::OP_EXCHANGE = 717;
00047
00048
00049 QmitkImageCropper::opExchangeNodes::opExchangeNodes( mitk::OperationType type, mitk::DataNode* node, mitk::BaseData* oldData, mitk::BaseData* newData )
00050 :mitk::Operation(type),m_Node(node),m_OldData(oldData),m_NewData(newData), m_NodeDeletedObserverTag(0), m_OldDataDeletedObserverTag(0),
00051 m_NewDataDeletedObserverTag(0)
00052 {
00053
00054 itk::MemberCommand<opExchangeNodes>::Pointer nodeDeletedCommand = itk::MemberCommand<opExchangeNodes>::New();
00055 nodeDeletedCommand->SetCallbackFunction(this, &opExchangeNodes::NodeDeleted);
00056
00057 m_NodeDeletedObserverTag = m_Node->AddObserver(itk::DeleteEvent(), nodeDeletedCommand);
00058 m_OldDataDeletedObserverTag = m_OldData->AddObserver(itk::DeleteEvent(), nodeDeletedCommand);
00059 m_NewDataDeletedObserverTag = m_NewData->AddObserver(itk::DeleteEvent(), nodeDeletedCommand);
00060 }
00061
00062
00063 QmitkImageCropper::opExchangeNodes::~opExchangeNodes()
00064 {
00065 if (m_Node != NULL)
00066 {
00067 m_Node->RemoveObserver(m_NodeDeletedObserverTag);
00068 m_Node=NULL;
00069 }
00070
00071 if (m_OldData.IsNotNull())
00072 {
00073 m_OldData->RemoveObserver(m_OldDataDeletedObserverTag);
00074 m_OldData=NULL;
00075 }
00076
00077 if (m_NewData.IsNotNull())
00078 {
00079 m_NewData->RemoveObserver(m_NewDataDeletedObserverTag);
00080 m_NewData=NULL;
00081 }
00082 }
00083
00084 void QmitkImageCropper::opExchangeNodes::NodeDeleted(const itk::Object * , const itk::EventObject &)
00085 {
00086 m_Node = NULL;
00087 m_OldData = NULL;
00088 m_NewData = NULL;
00089 }
00090
00091 QmitkImageCropper::QmitkImageCropper(QObject *parent)
00092 : QObject(parent),
00093 m_Controls(NULL)
00094 {
00095 }
00096
00097
00098 QmitkImageCropper::~QmitkImageCropper()
00099 {
00100
00101 m_CroppingObjectNode = NULL;
00102 m_CroppingObject = NULL;
00103 }
00104
00105 void QmitkImageCropper::CreateQtPartControl(QWidget* parent)
00106 {
00107 if (!m_Controls)
00108 {
00109
00110 m_Controls = new Ui::QmitkImageCropperControls;
00111 m_Controls->setupUi(parent);
00112
00113
00114 m_Controls->groupInfo->hide();
00115 m_Controls->m_SurroundingSlider->hide();
00116 m_Controls->m_SurroundingSpin->hide();
00117 m_Controls->m_TLGrayvalue->hide();
00118 m_Controls->m_NewBoxButton->setEnabled(true);
00119
00120
00121 this->CreateConnections();
00122
00123 m_Controls->cmbImage->SetDataStorage(this->GetDefaultDataStorage());
00124 m_Controls->cmbImage->SetPredicate(mitk::NodePredicateDataType::New("Image"));
00125 }
00126
00127 }
00128
00129 void QmitkImageCropper::CreateConnections()
00130 {
00131 if ( m_Controls )
00132 {
00133 connect( m_Controls->btnCrop, SIGNAL(clicked()), this, SLOT(CropImage()));
00134 connect( m_Controls->m_NewBoxButton, SIGNAL(clicked()), this, SLOT(CreateNewBoundingObject()) );
00135 connect( m_Controls->m_EnableSurroundingCheckBox, SIGNAL(toggled(bool)), this, SLOT(SurroundingCheck(bool)) );
00136 connect( m_Controls->chkInformation, SIGNAL(toggled(bool)), this, SLOT(ChkInformationToggled(bool)) );
00137 connect( m_Controls->cmbImage, SIGNAL(OnSelectionChanged(const mitk::DataNode*)), this, SLOT(OnImageSelectionChanged(const mitk::DataNode*)) );
00138 }
00139 }
00140
00141 void QmitkImageCropper::Activated()
00142 {
00143 QmitkFunctionality::Activated();
00144 }
00145
00146
00147 void QmitkImageCropper::Deactivated()
00148 {
00149 RemoveBoundingObjectFromNode();
00150
00151 QmitkFunctionality::Deactivated();
00152
00153 mitk::RenderingManager::GetInstance()->RequestUpdateAll();
00154 }
00155
00158 void QmitkImageCropper::ExecuteOperation (mitk::Operation *operation)
00159 {
00160 if (!operation) return;
00161
00162 switch (operation->GetOperationType())
00163 {
00164 case OP_EXCHANGE:
00165 {
00166
00167 opExchangeNodes* op = static_cast<opExchangeNodes*>(operation);
00168 op->GetNode()->SetData(op->GetNewData());
00169 mitk::RenderingManager::GetInstance()->InitializeViews();
00170 mitk::RenderingManager::GetInstance()->RequestUpdateAll();
00171 break;
00172 }
00173 default:;
00174 }
00175
00176 }
00177
00178 void QmitkImageCropper::OnImageSelectionChanged(const mitk::DataNode* )
00179 {
00180 this->RemoveBoundingObjectFromNode();
00181 }
00182
00183 void QmitkImageCropper::CreateNewBoundingObject()
00184 {
00185
00186
00187
00188 if (this->IsVisible())
00189 {
00190 m_ImageNode = this->selectedImage();
00191 if (m_ImageNode.IsNotNull())
00192 {
00193 m_ImageToCrop = dynamic_cast<mitk::Image*>(m_ImageNode->GetData());
00194 if(m_ImageToCrop.IsNotNull())
00195 {
00196 if(m_CroppingObject.IsNull())
00197 CreateBoundingObject();
00198 if (m_CroppingObject.IsNull())
00199 return;
00200
00201 AddBoundingObjectToNode( m_ImageNode );
00202
00203 m_ImageNode->SetVisibility(true);
00204 mitk::RenderingManager::GetInstance()->InitializeViews();
00205 mitk::RenderingManager::GetInstance()->RequestUpdateAll();
00206 m_Controls->m_NewBoxButton->setEnabled(false);
00207 m_Controls->btnCrop->setEnabled(true);
00208 }
00209 }
00210 else
00211 QMessageBox::information(NULL, "Image cropping functionality", "Load an image first!");
00212 }
00213 }
00214
00215
00216 void QmitkImageCropper::SurroundingCheck(bool value)
00217 {
00218 if(value)
00219 {
00220 m_ImageNode = this->selectedImage();
00221 if(m_ImageNode.IsNotNull())
00222 {
00223 mitk::DataNode *imageNode = m_ImageNode.GetPointer();
00224 if (imageNode)
00225 {
00226 mitk::BaseData* data = imageNode->GetData();
00227 if (data)
00228 {
00229
00230 mitk::Image* image = dynamic_cast<mitk::Image*>( data );
00231 if (image)
00232 {
00233 float min = 10000.0;
00234 float max = -10000.0;
00235
00236 min = image->GetScalarValueMin();
00237 max = image->GetScalarValueMax();
00238
00239 m_Controls->m_SurroundingSlider->setRange((int)min,(int)max);
00240 m_Controls->m_SurroundingSpin->setRange((int)min,(int)max);
00241 }
00242 }
00243 }
00244 m_Controls->m_SurroundingSlider->show();
00245 m_Controls->m_SurroundingSpin->show();
00246 m_Controls->m_TLGrayvalue->show();
00247 }
00248 else
00249 m_Controls->m_EnableSurroundingCheckBox->setChecked(false);
00250 }
00251 else
00252 {
00253 m_Controls->m_SurroundingSlider->hide();
00254 m_Controls->m_SurroundingSpin->hide();
00255 m_Controls->m_TLGrayvalue->hide();
00256 }
00257 }
00258
00259 void QmitkImageCropper::CropImage()
00260 {
00261
00262 if (m_ImageToCrop.IsNull()) return;
00263
00264
00265 if (m_CroppingObjectNode.IsNull())
00266 {
00267 QMessageBox::information(NULL, "Image cropping functionality", "Generate a new bounding object first!");
00268 return;
00269 }
00270
00271
00272 mitk::BoundingObjectCutter::Pointer cutter = mitk::BoundingObjectCutter::New();
00273 cutter->SetBoundingObject( m_CroppingObject );
00274 cutter->SetInput( m_ImageToCrop );
00275 cutter->AutoOutsideValueOff();
00276
00277
00278 try
00279 {
00280 cutter->Update();
00281
00282 }
00283 catch(itk::ExceptionObject&)
00284 {
00285 QMessageBox::warning ( NULL,
00286 tr("Cropping not possible"),
00287 tr("Sorry, the bounding box has to be completely inside the image.\n\n"
00288 "The possibility to drag it larger than the image a bug and has to be fixed."),
00289 QMessageBox::Ok, QMessageBox::NoButton, QMessageBox::NoButton );
00290 return;
00291 }
00292
00293
00294 mitk::Image::Pointer resultImage = cutter->GetOutput();
00295 resultImage->DisconnectPipeline();
00296
00297 if(m_Controls->m_EnableSurroundingCheckBox->isChecked())
00298 {
00299 AccessByItk_1( resultImage, AddSurrounding, resultImage);
00300 resultImage = m_surrImage;
00301 }
00302
00303 RemoveBoundingObjectFromNode();
00304
00305 {
00306 opExchangeNodes* doOp = new opExchangeNodes(OP_EXCHANGE, m_ImageNode.GetPointer(),
00307 m_ImageNode->GetData(),
00308 resultImage);
00309 opExchangeNodes* undoOp = new opExchangeNodes(OP_EXCHANGE, m_ImageNode.GetPointer(),
00310 resultImage,
00311 m_ImageNode->GetData());
00312
00313
00314
00315
00316
00317 mitk::UndoController::GetCurrentUndoModel()->SetOperationEvent(
00318 new mitk::OperationEvent(this, doOp, undoOp, "Crop image") );
00319 ExecuteOperation(doOp);
00320 }
00321
00322 m_Controls->m_NewBoxButton->setEnabled(true);
00323 m_Controls->btnCrop->setEnabled(false);
00324 }
00325
00326 template < typename TPixel, unsigned int VImageDimension >
00327 void QmitkImageCropper::AddSurrounding( itk::Image< TPixel, VImageDimension >* itkImage, mitk::Image::Pointer image)
00328 {
00329 typedef itk::Image< TPixel, VImageDimension > InputImageType;
00330
00331 typename InputImageType::Pointer extended = InputImageType::New();
00332 typename InputImageType::IndexType start;
00333 start[0]=0;
00334 start[1]=0;
00335 start[2]=0;
00336
00337 unsigned int *dims = image->GetDimensions();
00338 typename InputImageType::SizeType size;
00339
00340 size[0]=dims[0];
00341 size[1]=dims[1];
00342 size[2]=dims[2];
00343
00344 typename InputImageType::RegionType region;
00345 region.SetSize(size);
00346 region.SetIndex(start);
00347
00348 extended->SetRegions(region);
00349 extended->SetDirection(itkImage->GetDirection());
00350 extended->SetOrigin(itkImage->GetOrigin());
00351 extended->Allocate();
00352
00353 extended->SetSpacing(itkImage->GetSpacing());
00354
00355 typename InputImageType::IndexType idx;
00356
00357 progress = new QProgressDialog( "Adding surrounding...", "Abort", 0, (size[0]-1), m_Parent);
00358 progress->setLabelText("Image cropper");
00359 progress->show();
00360
00361 for (unsigned int i=0;i<size[0];i++)
00362 {
00363 for (unsigned int j=0;j<size[1];j++)
00364 {
00365 for (unsigned int k=0;k<size[2];k++)
00366 {
00367 idx[0]=i;
00368 idx[1]=j;
00369 idx[2]=k;
00370
00371 if(i==0 || j==0 || k==0 || i==size[0]-1 || j==size[1]-1 || k==size[2]-1)
00372 {
00373 extended->SetPixel(idx, m_Controls->m_SurroundingSpin->value());
00374 }
00375 else
00376 {
00377 extended->SetPixel(idx, itkImage->GetPixel(idx));
00378 }
00379 }
00380 }
00381 progress->setValue(i);
00382 if ( progress->wasCanceled() )
00383 break;
00384 }
00385 m_surrImage = mitk::Image::New();
00386 m_surrImage = mitk::ImportItkImage(extended);
00387
00388 }
00389
00390 void QmitkImageCropper::CreateBoundingObject()
00391 {
00392 QStringList items;
00393 items << tr("Cuboid") << tr("Ellipsoid") << tr("Cylinder") << tr("Cone");
00394
00395 bool ok;
00396 QString item = QInputDialog::getItem(m_Parent, tr("Select Bounding Object"), tr("Type of Bounding Object:"), items, 0, false, &ok);
00397
00398 if (!ok)
00399 return;
00400
00401 if (item == "Ellipsoid")
00402 m_CroppingObject = mitk::Ellipsoid::New();
00403 else if(item == "Cylinder")
00404 m_CroppingObject = mitk::Cylinder::New();
00405 else if (item == "Cone")
00406 m_CroppingObject = mitk::Cone::New();
00407 else if (item == "Cuboid")
00408 m_CroppingObject = mitk::Cuboid::New();
00409 else
00410 return;
00411
00412 m_CroppingObjectNode = mitk::DataNode::New();
00413 m_CroppingObjectNode->SetData( m_CroppingObject );
00414 m_CroppingObjectNode->SetProperty( "name", mitk::StringProperty::New( "CroppingObject" ) );
00415 m_CroppingObjectNode->SetProperty( "color", mitk::ColorProperty::New(1.0, 1.0, 0.0) );
00416 m_CroppingObjectNode->SetProperty( "opacity", mitk::FloatProperty::New(0.4) );
00417 m_CroppingObjectNode->SetProperty( "layer", mitk::IntProperty::New(99) );
00418 m_CroppingObjectNode->SetProperty( "helper object", mitk::BoolProperty::New(true) );
00419
00420 m_AffineInteractor = mitk::AffineInteractor::New("AffineInteractions ctrl-drag", m_CroppingObjectNode);
00421 }
00422
00423
00424 void QmitkImageCropper::AddBoundingObjectToNode(mitk::DataNode* node)
00425 {
00426 m_ImageToCrop = dynamic_cast<mitk::Image*>(node->GetData());
00427
00428 if(!this->GetDefaultDataStorage()->Exists(m_CroppingObjectNode))
00429 {
00430 this->GetDefaultDataStorage()->Add(m_CroppingObjectNode, node);
00431 m_CroppingObject->FitGeometry(m_ImageToCrop->GetTimeSlicedGeometry());
00432
00433 mitk::GlobalInteraction::GetInstance()->AddInteractor( m_AffineInteractor );
00434 }
00435 m_CroppingObjectNode->SetVisibility(true);
00436 }
00437
00438 void QmitkImageCropper::RemoveBoundingObjectFromNode()
00439 {
00440 if (m_CroppingObjectNode.IsNotNull())
00441 {
00442 if(this->GetDefaultDataStorage()->Exists(m_CroppingObjectNode))
00443 {
00444 this->GetDefaultDataStorage()->Remove(m_CroppingObjectNode);
00445 mitk::GlobalInteraction::GetInstance()->RemoveInteractor(m_AffineInteractor);
00446 }
00447 }
00448 }
00449
00450 const mitk::DataNode::Pointer QmitkImageCropper::selectedImage()
00451 {
00452 return m_Controls->cmbImage->GetSelectedNode();
00453 }
00454
00455
00456 void QmitkImageCropper::ChkInformationToggled( bool on )
00457 {
00458 if (on)
00459 m_Controls->groupInfo->show();
00460 else
00461 m_Controls->groupInfo->hide();
00462 }
00463
00464 void QmitkImageCropper::StdMultiWidgetAvailable( QmitkStdMultiWidget& stdMultiWidget )
00465 {
00466 m_MultiWidget = &stdMultiWidget;
00467 }
00468
00469 void QmitkImageCropper::StdMultiWidgetNotAvailable()
00470 {
00471 m_MultiWidget = NULL;
00472 }
00473
00474 void QmitkImageCropper::NodeRemoved(const mitk::DataNode *node)
00475 {
00476 std::string name = node->GetName();
00477
00478 if (strcmp(name.c_str(), "CroppingObject")==0)
00479 {
00480 m_CroppingObjectNode=NULL;
00481 m_CroppingObject = NULL;
00482 m_Controls->btnCrop->setEnabled(false);
00483 m_Controls->m_NewBoxButton->setEnabled(true);
00484 }
00485 }