diff --git a/Modules/Bundles/org.mitk.gui.qt.segmentation/src/QmitkSegmentationPreferencePage.cpp b/Modules/Bundles/org.mitk.gui.qt.segmentation/src/QmitkSegmentationPreferencePage.cpp index c5e04c0193..60b942c30a 100644 --- a/Modules/Bundles/org.mitk.gui.qt.segmentation/src/QmitkSegmentationPreferencePage.cpp +++ b/Modules/Bundles/org.mitk.gui.qt.segmentation/src/QmitkSegmentationPreferencePage.cpp @@ -1,120 +1,170 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision: 18019 $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "QmitkSegmentationPreferencePage.h" #include #include #include #include #include #include #include +#include #include #include QmitkSegmentationPreferencePage::QmitkSegmentationPreferencePage() : m_MainControl(0) , m_Initializing(false) { } QmitkSegmentationPreferencePage::~QmitkSegmentationPreferencePage() { } void QmitkSegmentationPreferencePage::Init(berry::IWorkbench::Pointer ) { } void QmitkSegmentationPreferencePage::CreateQtControl(QWidget* parent) { m_Initializing = true; berry::IPreferencesService::Pointer prefService = berry::Platform::GetServiceRegistry() .GetServiceById(berry::IPreferencesService::ID); m_SegmentationPreferencesNode = prefService->GetSystemPreferences()->Node("/org.mitk.views.segmentation"); m_MainControl = new QWidget(parent); QVBoxLayout* displayOptionsLayout = new QVBoxLayout; m_RadioOutline = new QRadioButton( "Draw as outline", m_MainControl); displayOptionsLayout->addWidget( m_RadioOutline ); m_RadioOverlay = new QRadioButton( "Draw as transparent overlay", m_MainControl); displayOptionsLayout->addWidget( m_RadioOverlay ); QFormLayout *formLayout = new QFormLayout; + formLayout->setHorizontalSpacing(8); + formLayout->setVerticalSpacing(24); formLayout->addRow( "2D display", displayOptionsLayout ); m_VolumeRenderingCheckBox = new QCheckBox( "Show as volume rendering", m_MainControl ); formLayout->addRow( "3D display", m_VolumeRenderingCheckBox ); connect( m_VolumeRenderingCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnVolumeRenderingCheckboxChecked(int)) ); + + QFormLayout* surfaceLayout = new QFormLayout; + surfaceLayout->setSpacing(8); + + m_SmoothingCheckBox = new QCheckBox("Use image spacing as smoothing value hint", m_MainControl); + surfaceLayout->addRow(m_SmoothingCheckBox); + connect(m_SmoothingCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnSmoothingCheckboxChecked(int))); + + m_SmoothingSpinBox = new QDoubleSpinBox(m_MainControl); + m_SmoothingSpinBox->setMinimum(0.0); + m_SmoothingSpinBox->setSingleStep(0.5); + m_SmoothingSpinBox->setValue(1.0); + surfaceLayout->addRow("Smoothing value", m_SmoothingSpinBox); + + m_DecimationSpinBox = new QDoubleSpinBox(m_MainControl); + m_DecimationSpinBox->setMinimum(0.0); + m_DecimationSpinBox->setMaximum(0.99); + m_DecimationSpinBox->setSingleStep(0.1); + m_DecimationSpinBox->setValue(0.5); + surfaceLayout->addRow("Decimation rate", m_DecimationSpinBox); + + formLayout->addRow("Smoothed surface creation", surfaceLayout); m_MainControl->setLayout(formLayout); this->Update(); m_Initializing = false; } QWidget* QmitkSegmentationPreferencePage::GetQtControl() const { return m_MainControl; } bool QmitkSegmentationPreferencePage::PerformOk() { m_SegmentationPreferencesNode->PutBool("draw outline", m_RadioOutline->isChecked()); m_SegmentationPreferencesNode->PutBool("volume rendering", m_VolumeRenderingCheckBox->isChecked()); + m_SegmentationPreferencesNode->PutBool("smoothing hint", m_SmoothingCheckBox->isChecked()); + m_SegmentationPreferencesNode->PutDouble("smoothing value", m_SmoothingSpinBox->value()); + m_SegmentationPreferencesNode->PutDouble("decimation rate", m_DecimationSpinBox->value()); return true; } void QmitkSegmentationPreferencePage::PerformCancel() { } void QmitkSegmentationPreferencePage::Update() { //m_EnableSingleEditing->setChecked(m_SegmentationPreferencesNode->GetBool("Single click property editing", true)); if (m_SegmentationPreferencesNode->GetBool("draw outline", true) ) { m_RadioOutline->setChecked( true ); } else { m_RadioOverlay->setChecked( true ); } m_VolumeRenderingCheckBox->setChecked( m_SegmentationPreferencesNode->GetBool("volume rendering", false) ); + + if (m_SegmentationPreferencesNode->GetBool("smoothing hint", true)) + { + m_SmoothingCheckBox->setChecked(true); + m_SmoothingSpinBox->setDisabled(true); + } + else + { + m_SmoothingCheckBox->setChecked(false); + m_SmoothingSpinBox->setEnabled(true); + } + + m_SmoothingSpinBox->setValue(m_SegmentationPreferencesNode->GetDouble("smoothing value", 1.0)); + m_DecimationSpinBox->setValue(m_SegmentationPreferencesNode->GetDouble("decimation rate", 0.5)); } void QmitkSegmentationPreferencePage::OnVolumeRenderingCheckboxChecked(int state) { if (m_Initializing) return; if ( state != Qt::Unchecked ) { QMessageBox::information(NULL, "Memory warning", "Turning on volume rendering of segmentations will make the application more memory intensive (and potentially prone to crashes).\n\n" "If you encounter out-of-memory problems, try turning off volume rendering again."); } } + +void QmitkSegmentationPreferencePage::OnSmoothingCheckboxChecked(int state) +{ + if (state != Qt::Unchecked) + m_SmoothingSpinBox->setDisabled(true); + else + m_SmoothingSpinBox->setEnabled(true); +} diff --git a/Modules/Bundles/org.mitk.gui.qt.segmentation/src/QmitkSegmentationPreferencePage.h b/Modules/Bundles/org.mitk.gui.qt.segmentation/src/QmitkSegmentationPreferencePage.h index ad3443be2b..234d26a88d 100644 --- a/Modules/Bundles/org.mitk.gui.qt.segmentation/src/QmitkSegmentationPreferencePage.h +++ b/Modules/Bundles/org.mitk.gui.qt.segmentation/src/QmitkSegmentationPreferencePage.h @@ -1,83 +1,88 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision: 16224 $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef QmitkSegmentationPreferencePage_h_included #define QmitkSegmentationPreferencePage_h_included #include "berryIQtPreferencePage.h" #include "org_mitk_gui_qt_segmentation_Export.h" #include class QWidget; class QCheckBox; class QRadioButton; +class QDoubleSpinBox; class MITK_QT_SEGMENTATION QmitkSegmentationPreferencePage : public QObject, public berry::IQtPreferencePage { Q_OBJECT Q_INTERFACES(berry::IPreferencePage) public: QmitkSegmentationPreferencePage(); QmitkSegmentationPreferencePage(const QmitkSegmentationPreferencePage& other) { Q_UNUSED(other) throw std::runtime_error("Copy constructor not implemented"); } ~QmitkSegmentationPreferencePage(); void Init(berry::IWorkbench::Pointer workbench); void CreateQtControl(QWidget* widget); QWidget* GetQtControl() const; /// /// \see IPreferencePage::PerformOk() /// virtual bool PerformOk(); /// /// \see IPreferencePage::PerformCancel() /// virtual void PerformCancel(); /// /// \see IPreferencePage::Update() /// virtual void Update(); protected slots: void OnVolumeRenderingCheckboxChecked(int); + void OnSmoothingCheckboxChecked(int); protected: QWidget* m_MainControl; QRadioButton* m_RadioOutline; QRadioButton* m_RadioOverlay; QCheckBox* m_VolumeRenderingCheckBox; + QCheckBox* m_SmoothingCheckBox; + QDoubleSpinBox* m_SmoothingSpinBox; + QDoubleSpinBox* m_DecimationSpinBox; bool m_Initializing; berry::IPreferences::Pointer m_SegmentationPreferencesNode; }; #endif /* QMITKDATAMANAGERPREFERENCEPAGE_H_ */ diff --git a/Modules/Bundles/org.mitk.gui.qt.segmentation/src/internal/QmitkCreatePolygonModelAction.cpp b/Modules/Bundles/org.mitk.gui.qt.segmentation/src/internal/QmitkCreatePolygonModelAction.cpp index ed70a035bd..b92c3c2375 100644 --- a/Modules/Bundles/org.mitk.gui.qt.segmentation/src/internal/QmitkCreatePolygonModelAction.cpp +++ b/Modules/Bundles/org.mitk.gui.qt.segmentation/src/internal/QmitkCreatePolygonModelAction.cpp @@ -1,132 +1,161 @@ #include "QmitkCreatePolygonModelAction.h" #include "mitkShowSegmentationAsSmoothedSurface.h" #include "mitkShowSegmentationAsSurface.h" #include "mitkProgressBar.h" #include "mitkStatusBar.h" +#include +#include + QmitkCreatePolygonModelAction::QmitkCreatePolygonModelAction() { } QmitkCreatePolygonModelAction::~QmitkCreatePolygonModelAction() { } void QmitkCreatePolygonModelAction::Run(const std::vector& selectedNodes) { NodeList selection = selectedNodes; for ( NodeList::iterator iter = selection.begin(); iter != selection.end(); ++iter ) { mitk::DataNode* node = *iter; if (node) { mitk::Image::Pointer image = dynamic_cast( node->GetData() ); if (image.IsNull()) return; try { if (!m_IsSmoothed) { mitk::ShowSegmentationAsSurface::Pointer surfaceFilter = mitk::ShowSegmentationAsSurface::New(); // attach observer to get notified about result itk::SimpleMemberCommand::Pointer goodCommand = itk::SimpleMemberCommand::New(); goodCommand->SetCallbackFunction(this, &QmitkCreatePolygonModelAction::OnSurfaceCalculationDone); surfaceFilter->AddObserver(mitk::ResultAvailable(), goodCommand); itk::SimpleMemberCommand::Pointer badCommand = itk::SimpleMemberCommand::New(); badCommand->SetCallbackFunction(this, &QmitkCreatePolygonModelAction::OnSurfaceCalculationDone); surfaceFilter->AddObserver(mitk::ProcessingError(), badCommand); mitk::DataNode::Pointer nodepointer = node; surfaceFilter->SetPointerParameter("Input", image); surfaceFilter->SetPointerParameter("Group node", nodepointer); surfaceFilter->SetParameter("Show result", true ); surfaceFilter->SetParameter("Sync visibility", false ); surfaceFilter->SetDataStorage( *m_DataStorage ); /*if (this->m_IsSmoothed) { surfaceFilter->SetParameter("Smooth", true ); //surfaceFilter->SetParameter("Apply median", true ); surfaceFilter->SetParameter("Apply median", false ); // median makes the resulting surfaces look like lego models surfaceFilter->SetParameter("Median kernel size", 3u ); surfaceFilter->SetParameter("Gaussian SD", 2.5f ); surfaceFilter->SetParameter("Decimate mesh", m_IsDecimated ); surfaceFilter->SetParameter("Decimation rate", 0.8f ); } else {*/ surfaceFilter->SetParameter("Smooth", false ); surfaceFilter->SetParameter("Apply median", false ); surfaceFilter->SetParameter("Median kernel size", 3u ); surfaceFilter->SetParameter("Gaussian SD", 1.5f ); surfaceFilter->SetParameter("Decimate mesh", m_IsDecimated ); surfaceFilter->SetParameter("Decimation rate", 0.8f ); //} mitk::ProgressBar::GetInstance()->AddStepsToDo(10); mitk::ProgressBar::GetInstance()->Progress(2); mitk::StatusBar::GetInstance()->DisplayText("Surface creation started in background..."); surfaceFilter->StartAlgorithm(); } else { mitk::ShowSegmentationAsSmoothedSurface::Pointer filter = mitk::ShowSegmentationAsSmoothedSurface::New(); itk::SimpleMemberCommand::Pointer goodCommand = itk::SimpleMemberCommand::New(); goodCommand->SetCallbackFunction(this, &QmitkCreatePolygonModelAction::OnSurfaceCalculationDone); filter->AddObserver(mitk::ResultAvailable(), goodCommand); itk::SimpleMemberCommand::Pointer badCommand = itk::SimpleMemberCommand::New(); badCommand->SetCallbackFunction(this, &QmitkCreatePolygonModelAction::OnSurfaceCalculationDone); filter->AddObserver(mitk::ProcessingError(), badCommand); mitk::DataNode::Pointer nodepointer = node; filter->SetPointerParameter("Input", image); filter->SetPointerParameter("Group node", nodepointer); filter->SetDataStorage(*m_DataStorage); - // TODO: Set up progress bar and status bar text + berry::IPreferencesService::Pointer prefService = berry::Platform::GetServiceRegistry().GetServiceById(berry::IPreferencesService::ID); + berry::IPreferences::Pointer segPref = prefService->GetSystemPreferences()->Node("/org.mitk.views.segmentation"); + + bool smoothingHint = segPref->GetBool("smoothing hint", true); + float smoothing = (float)segPref->GetDouble("smoothing value", 1.0); + float decimation = (float)segPref->GetDouble("decimation rate", 0.5); + + if (smoothingHint) + { + smoothing = 0.0; + mitk::Vector3D spacing = image->GetGeometry()->GetSpacing(); + + for (mitk::Vector3D::Iterator iter = spacing.Begin(); iter != spacing.End(); ++iter) + smoothing = std::max(smoothing, *iter); + + filter->SetParameter("Smoothing", smoothing); + } + else + { + filter->SetParameter("Smoothing", smoothing); + } + + filter->SetParameter("Decimation", decimation); + + mitk::ProgressBar::GetInstance()->AddStepsToDo(10); + mitk::ProgressBar::GetInstance()->Progress(1); + mitk::StatusBar::GetInstance()->DisplayText("Smoothed surface creation started in background (this may take a while)..."); filter->StartAlgorithm(); } } catch(...) { MITK_ERROR << "surface creation filter had an error"; } } else { MITK_INFO << " a NULL node selected"; } } } void QmitkCreatePolygonModelAction::OnSurfaceCalculationDone() { mitk::ProgressBar::GetInstance()->Progress(8); } void QmitkCreatePolygonModelAction::SetSmoothed(bool smoothed) { this->m_IsSmoothed = smoothed; } void QmitkCreatePolygonModelAction::SetDecimated(bool decimated) { this->m_IsDecimated = decimated; } void QmitkCreatePolygonModelAction::SetDataStorage(mitk::DataStorage* dataStorage) { this->m_DataStorage = dataStorage; } void QmitkCreatePolygonModelAction::SetFunctionality(berry::QtViewPart *functionality) { //not needed } diff --git a/Modules/MitkExt/Algorithms/itkIntelligentBinaryClosingFilter.h b/Modules/MitkExt/Algorithms/itkIntelligentBinaryClosingFilter.h new file mode 100644 index 0000000000..84254142b3 --- /dev/null +++ b/Modules/MitkExt/Algorithms/itkIntelligentBinaryClosingFilter.h @@ -0,0 +1,139 @@ +#ifndef __itkIntelligentBinaryClosingFilter_h +#define __itkIntelligentBinaryClosingFilter_h + + +#if defined(_MSC_VER) +#pragma warning ( disable : 4786 ) +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +namespace itk +{ +/** \class itkIntelligentBinaryClosingFilter + * WARNING: This filtezr needs at least ITK version 3.2 + * or above to run and compile!!! + * */ + + +template +class ITK_EXPORT IntelligentBinaryClosingFilter : + public ImageToImageFilter< TInputImage, TOutputImage > +{ + +public: + /** Standard "Self" typedef. */ + typedef IntelligentBinaryClosingFilter Self; + + /** The type of input image. */ + typedef TInputImage InputImageType; + + /** Dimension of the input and output images. */ + itkStaticConstMacro (ImageDimension, unsigned int, + TInputImage::ImageDimension); + + /** The type of output image. */ + typedef TOutputImage OutputImageType; + + /** Standard super class typedef support. */ + typedef ImageToImageFilter< InputImageType, OutputImageType > Superclass; + + /** Smart pointer typedef support */ + typedef SmartPointer Pointer; + + /** Run-time type information (and related methods) */ + itkTypeMacro(IntelligentBinaryClosingFilter, ImageToImageFilter); + + /** Method for creation through the object factory. */ + itkNewMacro(Self); + + /** Standard process object method. This filter is not multithreaded. */ + void GenerateData(); + + /** Overloaded to link the input to this filter with the input of the + mini-pipeline */ + void SetInput(const InputImageType *input) + { + // processObject is not const-correct so a const_cast is needed here + this->ProcessObject::SetNthInput(0, + const_cast( input ) ); + m_DilateImageFilter->SetInput( const_cast( input ) ); + } + + virtual void SetInput( unsigned int i, const TInputImage * image) + { + if (i != 0) + { itkExceptionMacro(<< "Filter has only one input."); } + else + { this->SetInput(image); } + } + + itkGetMacro( ClosingRadius, float ); + itkSetMacro( ClosingRadius, float ); + + itkGetMacro( SurfaceRatio, float ); + itkSetMacro( SurfaceRatio, float ); + + +protected: + + IntelligentBinaryClosingFilter(); + virtual ~IntelligentBinaryClosingFilter() {} + IntelligentBinaryClosingFilter(const Self&) {} + void operator=(const Self&) {} + void PrintSelf(std::ostream& os, Indent indent) const; + + +private: + + typedef typename InputImageType::PixelType InputPixelType; + typedef BinaryBallStructuringElement + StructuringElementType; + typedef BinaryErodeImageFilter + BinaryErodeImageFilterType; + typedef BinaryDilateImageFilter + BinaryDilateImageFilterType; + typedef SubtractImageFilter + SubtractImageFilterType; + typedef ConnectedComponentImageFilter + ConnectedComponentImageFilterType; + typedef RelabelComponentImageFilter + RelabelComponentImageFilterType; + typedef GrayscaleDilateImageFilter + DilateComponentImageFilterType; + typedef ImageRegionIterator InputIteratorType; + typedef ImageRegionConstIterator ConstInputIteratorType; + typedef ImageRegionIterator OutputIteratorType; + + typename BinaryErodeImageFilterType::Pointer m_ErodeImageFilter; + typename BinaryDilateImageFilterType::Pointer m_DilateImageFilter; + typename SubtractImageFilterType::Pointer m_SubtractImageFilter; + typename ConnectedComponentImageFilterType::Pointer m_ConnectedComponentImageFilter; + typename RelabelComponentImageFilterType::Pointer m_RelabelComponentImageFilter; + typename DilateComponentImageFilterType::Pointer m_BorderDetectionDilateFilter; + //typename BinaryDilateImageFilterType::Pointer m_BorderAdjacencyDilateFilter; + float m_ClosingRadius; + float m_SurfaceRatio; + +}; + +} // end namespace itk + + +#ifndef ITK_MANUAL_INSTANTIATION +#include "itkIntelligentBinaryClosingFilter.txx" + +#endif + +#endif //IntelligentBinaryClosingFilter_h diff --git a/Modules/MitkExt/Algorithms/itkIntelligentBinaryClosingFilter.txx b/Modules/MitkExt/Algorithms/itkIntelligentBinaryClosingFilter.txx new file mode 100644 index 0000000000..8a121a31ac --- /dev/null +++ b/Modules/MitkExt/Algorithms/itkIntelligentBinaryClosingFilter.txx @@ -0,0 +1,134 @@ +#ifndef _itkIntelligentBinaryClosingFilter_txx +#define _itkIntelligentBinaryClosingFilter_txx + +#include "itkIntelligentBinaryClosingFilter.h" +#include + +namespace itk +{ + +template< class TInputImage, class TOutputImage > +IntelligentBinaryClosingFilter ::IntelligentBinaryClosingFilter() +{ + m_ErodeImageFilter = BinaryErodeImageFilterType::New(); + m_DilateImageFilter = BinaryDilateImageFilterType::New(); + m_SubtractImageFilter = SubtractImageFilterType::New(); + m_ConnectedComponentImageFilter = ConnectedComponentImageFilterType::New(); + m_RelabelComponentImageFilter = RelabelComponentImageFilterType::New(); + m_RelabelComponentImageFilter->SetInPlace( true ); + m_BorderDetectionDilateFilter = DilateComponentImageFilterType::New(); + m_ClosingRadius = 4.0; + m_SurfaceRatio = 70; +} + + +template< class TInputImage, class TOutputImage > +void IntelligentBinaryClosingFilter::GenerateData() +{ + const InputImageType *input = this->GetInput(); + // Allocate the output image. + typename OutputImageType::Pointer output = this->GetOutput(); + output->SetRequestedRegion( input->GetRequestedRegion() ); + output->SetBufferedRegion( input->GetBufferedRegion() ); + output->SetLargestPossibleRegion( input->GetLargestPossibleRegion() ); + output->Allocate(); + + // set up structuring element for closing + StructuringElementType seClosing; + unsigned long radius[ImageDimension]; + const typename InputImageType::SpacingType spacing = input->GetSpacing(); + for (unsigned int d=0; dSetInput( this->GetInput() ); + m_DilateImageFilter->SetKernel( seClosing ); + m_DilateImageFilter->SetDilateValue( 1 ); + m_ErodeImageFilter->SetInput( m_DilateImageFilter->GetOutput() ); + m_ErodeImageFilter->SetKernel( seClosing ); + m_ErodeImageFilter->SetErodeValue( 1 ); + + // subtraction + m_SubtractImageFilter->SetInput1( m_ErodeImageFilter->GetOutput() ); + m_SubtractImageFilter->SetInput2( this->GetInput() ); + + // connected components + m_ConnectedComponentImageFilter->SetInput( m_SubtractImageFilter->GetOutput() ); + m_RelabelComponentImageFilter->SetInput( m_ConnectedComponentImageFilter->GetOutput() ); + m_RelabelComponentImageFilter->Update(); + + // set up structuring element for border voxel detection + StructuringElementType seBorder; + for (unsigned int d=0; dSetInput( m_RelabelComponentImageFilter->GetOutput() ); + m_BorderDetectionDilateFilter->SetKernel( seBorder ); + m_BorderDetectionDilateFilter->Update(); + + // count volumes and border voxels for all components + OutputIteratorType itComp( m_RelabelComponentImageFilter->GetOutput(), + m_RelabelComponentImageFilter->GetOutput()->GetLargestPossibleRegion() ); + OutputIteratorType itBorder( m_BorderDetectionDilateFilter->GetOutput(), + m_BorderDetectionDilateFilter->GetOutput()->GetLargestPossibleRegion() ); + ConstInputIteratorType itIn( input, input->GetLargestPossibleRegion() ); + + std::vector volume( m_RelabelComponentImageFilter->GetNumberOfObjects()+1, 0 ); + std::vector border( m_RelabelComponentImageFilter->GetNumberOfObjects()+1, 0 ); + std::vector adjacent( m_RelabelComponentImageFilter->GetNumberOfObjects()+1, 0 ); + typename OutputImageType::ValueType borderId, compId; + for (itComp.GoToBegin(), itBorder.GoToBegin(), itIn.GoToBegin(); !itComp.IsAtEnd(); ++itComp, ++itBorder, ++itIn ) + { + borderId = itBorder.Get(); + if (borderId != 0) { + compId = itComp.Get(); + if (compId != 0) { + volume[compId]++; + } + else { + // this is border country + border[borderId]++; + if (itIn.Get() != 0) adjacent[borderId]++; + } + } + } + + // calculate ratios + std::vector ratio( m_RelabelComponentImageFilter->GetNumberOfObjects()+1, 0 ); + MITK_INFO << " " << m_RelabelComponentImageFilter->GetNumberOfObjects() << " components found" << std::endl; + for (unsigned int i=0; iGetLargestPossibleRegion() ); + for (itOut.GoToBegin(), itIn.GoToBegin(), itComp.GoToBegin(); !itOut.IsAtEnd(); ++itOut, ++itIn, ++itComp ) + { + if (itIn.Get() != 0) { + itOut.Set( 1 ); + } + else { + compId = itComp.Get(); + if (ratio[compId] > m_SurfaceRatio) itOut.Set( 1 ); + else itOut.Set( 0 ); + } + } +} + + +template +void IntelligentBinaryClosingFilter::PrintSelf(std::ostream& os, Indent indent) const +{ + Superclass::PrintSelf(os,indent); +} + +} // end namespace itk + +#endif diff --git a/Modules/MitkExt/Algorithms/mitkShowSegmentationAsSmoothedSurface.cpp b/Modules/MitkExt/Algorithms/mitkShowSegmentationAsSmoothedSurface.cpp index dc55fced6c..2669ebf6b5 100644 --- a/Modules/MitkExt/Algorithms/mitkShowSegmentationAsSmoothedSurface.cpp +++ b/Modules/MitkExt/Algorithms/mitkShowSegmentationAsSmoothedSurface.cpp @@ -1,65 +1,549 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "mitkShowSegmentationAsSmoothedSurface.h" +#include "mitkImageToItk.h" +#include "itkIntelligentBinaryClosingFilter.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include using namespace mitk; using namespace std; ShowSegmentationAsSmoothedSurface::ShowSegmentationAsSmoothedSurface() { } ShowSegmentationAsSmoothedSurface::~ShowSegmentationAsSmoothedSurface() { } void ShowSegmentationAsSmoothedSurface::Initialize(const NonBlockingAlgorithm *other) { Superclass::Initialize(other); - // TODO: Set up default parameters + bool syncVisibility = false; + + if (other != NULL) + other->GetParameter("Sync visibility", syncVisibility); + + SetParameter("Sync visibility", syncVisibility); + SetParameter("Wireframe", false); + + // The Smoothing value is used as variance for a Gauß filter. + // A reasonable default value equals the image spacing in mm. + SetParameter("Smoothing", 1.0f); + + // Valid range for dacimation value is ]0, 1[. High values + // increase decimation, especially when very close to 1. + SetParameter("Decimation", 0.5f); } bool ShowSegmentationAsSmoothedSurface::ReadyToRun() { try { - Image::Pointer image; + mitk::Image::Pointer image; GetPointerParameter("Input", image); return image.IsNotNull() && GetGroupNode(); } catch (const invalid_argument &) { return false; } } bool ShowSegmentationAsSmoothedSurface::ThreadedUpdateFunction() { - // TODO + Image::Pointer image; + GetPointerParameter("Input", image); + + float smoothing; + GetParameter("Smoothing", smoothing); + + float decimation; + GetParameter("Decimation", decimation); + + MITK_INFO << "CREATING SMOOTHED POLYGON MODEL" << endl; + MITK_INFO << " Smoothing = " << smoothing << endl; + MITK_INFO << " Decimation = " << decimation << endl; + + Geometry3D::Pointer geometry = image->GetGeometry(); + + // Make ITK image out of MITK image + + typedef itk::Image CharImageType; + typedef itk::Image ShortImageType; + typedef itk::Image FloatImageType; + + ImageToItk::Pointer imageToItkFilter = ImageToItk::New(); + imageToItkFilter->SetInput(image); + imageToItkFilter->Update(); + + CharImageType::Pointer itkImage = imageToItkFilter->GetOutput(); + + // Get bounding box and relabel + + MITK_INFO << "Extracting VOI..." << endl; + + int imageLabel = 1; + bool roiFound = false; + + CharImageType::IndexType minIndex; + minIndex.Fill(numeric_limits::max()); + + CharImageType::IndexType maxIndex; + maxIndex.Fill(numeric_limits::min()); + + itk::ImageRegionIteratorWithIndex iter(itkImage, itkImage->GetLargestPossibleRegion()); + + for (iter.GoToBegin(); !iter.IsAtEnd(); ++iter) + { + if (iter.Get() == imageLabel) + { + roiFound = true; + iter.Set(1); + + CharImageType::IndexType currentIndex = iter.GetIndex(); + + for (unsigned int dim = 0; dim < 3; ++dim) + { + minIndex[dim] = min(currentIndex[dim], minIndex[dim]); + maxIndex[dim] = max(currentIndex[dim], maxIndex[dim]); + } + } + else + { + iter.Set(0); + } + } + + if (!roiFound) + { + ProgressBar::GetInstance()->Progress(10); + MITK_ERROR << "Didn't found segmentation labeled with " << imageLabel << "!" << endl; + return false; + } + + ProgressBar::GetInstance()->Progress(1); + + // Extract and pad bounding box + + typedef itk::RegionOfInterestImageFilter ROIFilterType; + + ROIFilterType::Pointer roiFilter = ROIFilterType::New(); + CharImageType::RegionType region; + CharImageType::SizeType size; + + for (unsigned int dim = 0; dim < 3; ++dim) + { + size[dim] = maxIndex[dim] - minIndex[dim] + 1; + } + + region.SetIndex(minIndex); + region.SetSize(size); + + roiFilter->SetInput(itkImage); + roiFilter->SetRegionOfInterest(region); + roiFilter->ReleaseDataFlagOn(); + roiFilter->ReleaseDataBeforeUpdateFlagOn(); + + typedef itk::ConstantPadImageFilter PadFilterType; + + PadFilterType::Pointer padFilter = PadFilterType::New(); + const PadFilterType::SizeValueType pad[3] = { 10, 10, 10 }; + + padFilter->SetInput(roiFilter->GetOutput()); + padFilter->SetConstant(0); + padFilter->SetPadLowerBound(pad); + padFilter->SetPadUpperBound(pad); + padFilter->ReleaseDataFlagOn(); + padFilter->ReleaseDataBeforeUpdateFlagOn(); + padFilter->Update(); + + CharImageType::Pointer roiImage = padFilter->GetOutput(); + + roiImage->DisconnectPipeline(); + roiFilter = 0; + padFilter = 0; + + // Correct origin of real geometry (changed by cropping and padding) + + typedef AffineGeometryFrame3D::TransformType TransformType; + + TransformType::Pointer transform = TransformType::New(); + TransformType::OutputVectorType translation; + + for (unsigned int dim = 0; dim < 3; ++dim) + translation[dim] = (int)minIndex[dim] - (int)pad[dim]; + + transform->SetIdentity(); + transform->Translate(translation); + geometry->Compose(transform, true); + + ProgressBar::GetInstance()->Progress(1); + + // Median + + MITK_INFO << "Median..." << endl; + + typedef itk::BinaryMedianImageFilter MedianFilterType; + + MedianFilterType::Pointer medianFilter = MedianFilterType::New(); + CharImageType::SizeType radius = { 1, 1, 1 }; + + medianFilter->SetRadius(radius); + medianFilter->SetBackgroundValue(0); + medianFilter->SetForegroundValue(1); + medianFilter->SetInput(roiImage); + medianFilter->ReleaseDataFlagOn(); + medianFilter->ReleaseDataBeforeUpdateFlagOn(); + medianFilter->Update(); + + ProgressBar::GetInstance()->Progress(1); + + // Intelligent closing + + MITK_INFO << "Intelligent closing..." << endl; + + unsigned int surfaceRatio = 70; + + typedef itk::IntelligentBinaryClosingFilter ClosingFilterType; + + ClosingFilterType::Pointer closingFilter = ClosingFilterType::New(); + + closingFilter->SetInput(medianFilter->GetOutput()); + closingFilter->ReleaseDataFlagOn(); + closingFilter->ReleaseDataBeforeUpdateFlagOn(); + closingFilter->SetSurfaceRatio(surfaceRatio); + closingFilter->Update(); + + ShortImageType::Pointer closedImage = closingFilter->GetOutput(); + + closedImage->DisconnectPipeline(); + roiImage = 0; + medianFilter = 0; + closingFilter = 0; + + ProgressBar::GetInstance()->Progress(1); + + // Gaussian blur + + MITK_INFO << "Gauss..." << endl; + + typedef itk::BinaryThresholdImageFilter BinaryThresholdToFloatFilterType; + + BinaryThresholdToFloatFilterType::Pointer binThresToFloatFilter = BinaryThresholdToFloatFilterType::New(); + + binThresToFloatFilter->SetInput(closedImage); + binThresToFloatFilter->SetLowerThreshold(1); + binThresToFloatFilter->SetUpperThreshold(1); + binThresToFloatFilter->SetInsideValue(100); + binThresToFloatFilter->SetOutsideValue(0); + binThresToFloatFilter->ReleaseDataFlagOn(); + binThresToFloatFilter->ReleaseDataBeforeUpdateFlagOn(); + + typedef itk::DiscreteGaussianImageFilter GaussianFilterType; + + // From the following line on, IntelliSense (VS 2008) is broken. Any idea how to fix it? + GaussianFilterType::Pointer gaussFilter = GaussianFilterType::New(); + + gaussFilter->SetInput(binThresToFloatFilter->GetOutput()); + gaussFilter->SetUseImageSpacing(true); + gaussFilter->SetVariance(smoothing); + gaussFilter->ReleaseDataFlagOn(); + gaussFilter->ReleaseDataBeforeUpdateFlagOn(); + gaussFilter->Update(); + + FloatImageType::Pointer blurredImage = gaussFilter->GetOutput(); + + blurredImage->DisconnectPipeline(); + closedImage = 0; + binThresToFloatFilter = 0; + gaussFilter = 0; + + ProgressBar::GetInstance()->Progress(1); + + // Isolate largest component + + MITK_INFO << "Isolate largest component..." << endl; + + typedef itk::BinaryThresholdImageFilter BinaryThresholdFromFloatFilterType; + + BinaryThresholdFromFloatFilterType::Pointer binThresFromFloatFilter = BinaryThresholdFromFloatFilterType::New(); + + binThresFromFloatFilter->SetInput(blurredImage); + binThresFromFloatFilter->SetLowerThreshold(50); + binThresFromFloatFilter->SetUpperThreshold(255); + binThresFromFloatFilter->SetInsideValue(1); + binThresFromFloatFilter->SetOutsideValue(0); + binThresFromFloatFilter->ReleaseDataFlagOn(); + binThresFromFloatFilter->ReleaseDataBeforeUpdateFlagOn(); + + typedef itk::ConnectedComponentImageFilter ConnectedComponentFilterType; + + ConnectedComponentFilterType::Pointer connectedComponentFilter = ConnectedComponentFilterType::New(); + + connectedComponentFilter->SetInput(binThresFromFloatFilter->GetOutput()); + + typedef itk::RelabelComponentImageFilter RelabelComponentFilterType; + + RelabelComponentFilterType::Pointer relabelComponentFilter = RelabelComponentFilterType::New(); + + relabelComponentFilter->SetInput(connectedComponentFilter->GetOutput()); + relabelComponentFilter->ReleaseDataFlagOn(); + relabelComponentFilter->ReleaseDataBeforeUpdateFlagOn(); + relabelComponentFilter->Update(); + + MITK_INFO << " " << relabelComponentFilter->GetNumberOfObjects() << " components found" << endl; + + typedef itk::BinaryThresholdImageFilter BinaryThresholdFilterType; + + BinaryThresholdFilterType::Pointer binThresFilter = BinaryThresholdFilterType::New(); + + binThresFilter->SetInput(relabelComponentFilter->GetOutput()); + binThresFilter->SetLowerThreshold(2); + binThresFilter->SetUpperThreshold(255); + binThresFilter->SetInsideValue(0); + binThresFilter->SetOutsideValue(1); + binThresFilter->ReleaseDataFlagOn(); + binThresFilter->ReleaseDataBeforeUpdateFlagOn(); + binThresFilter->Update(); + + typedef itk::MultiplyImageFilter MultiplyFilterType; + + MultiplyFilterType::Pointer multiplyFilter = MultiplyFilterType::New(); + + multiplyFilter->SetInput1(blurredImage); + multiplyFilter->SetInput2(binThresFilter->GetOutput()); + multiplyFilter->ReleaseDataFlagOn(); + multiplyFilter->ReleaseDataBeforeUpdateFlagOn(); + multiplyFilter->Update(); + + CharImageType::Pointer singleComponentImage = multiplyFilter->GetOutput(); + + singleComponentImage->DisconnectPipeline(); + blurredImage = 0; + binThresFromFloatFilter = 0; + connectedComponentFilter = 0; + relabelComponentFilter = 0; + binThresFilter = 0; + multiplyFilter = 0; + + // Fill holes + + ProgressBar::GetInstance()->Progress(1); + + MITK_INFO << "Filling cavities..." << endl; + + binThresFilter = BinaryThresholdFilterType::New(); + + binThresFilter->SetInput(singleComponentImage); + binThresFilter->SetLowerThreshold(50); + binThresFilter->SetUpperThreshold(255); + binThresFilter->SetInsideValue(1); + binThresFilter->SetOutsideValue(0); + binThresFilter->ReleaseDataFlagOn(); + binThresFilter->ReleaseDataBeforeUpdateFlagOn(); + + typedef itk::ConnectedThresholdImageFilter ConnectedThresholdFilterType; + + ConnectedThresholdFilterType::Pointer connectedThresFilter = ConnectedThresholdFilterType::New(); + + CharImageType::IndexType corner; + + corner[0] = 0; + corner[1] = 0; + corner[2] = 0; + + connectedThresFilter->SetInput(binThresFilter->GetOutput()); + connectedThresFilter->SetSeed(corner); + connectedThresFilter->SetLower(0); + connectedThresFilter->SetUpper(0); + connectedThresFilter->SetReplaceValue(2); + connectedThresFilter->ReleaseDataFlagOn(); + connectedThresFilter->ReleaseDataBeforeUpdateFlagOn(); + + BinaryThresholdFilterType::Pointer binThresFilter2 = BinaryThresholdFilterType::New(); + + binThresFilter2->SetInput(connectedThresFilter->GetOutput()); + binThresFilter2->SetLowerThreshold(0); + binThresFilter2->SetUpperThreshold(0); + binThresFilter2->SetInsideValue(50); + binThresFilter2->SetOutsideValue(0); + binThresFilter2->ReleaseDataFlagOn(); + binThresFilter2->ReleaseDataBeforeUpdateFlagOn(); + + typedef itk::AddImageFilter AddFilterType; + + AddFilterType::Pointer addFilter = AddFilterType::New(); + + addFilter->SetInput1(singleComponentImage); + addFilter->SetInput2(binThresFilter2->GetOutput()); + addFilter->ReleaseDataFlagOn(); + addFilter->ReleaseDataBeforeUpdateFlagOn(); + addFilter->Update(); + + ProgressBar::GetInstance()->Progress(1); + + // Surface extraction + + MITK_INFO << "Surface extraction..." << endl; + + Image::Pointer filteredImage = Image::New(); + CastToMitkImage(addFilter->GetOutput(), filteredImage); + + filteredImage->SetGeometry(geometry); + + ImageToSurfaceFilter::Pointer imageToSurfaceFilter = ImageToSurfaceFilter::New(); + + imageToSurfaceFilter->SetInput(filteredImage); + imageToSurfaceFilter->SetThreshold(50); + imageToSurfaceFilter->SmoothOn(); + imageToSurfaceFilter->SetDecimate(ImageToSurfaceFilter::NoDecimation); + + m_Surface = imageToSurfaceFilter->GetOutput(); + + ProgressBar::GetInstance()->Progress(1); + + // Mesh decimation + + if (decimation > 0.0f && decimation < 1.0f) + { + MITK_INFO << "Quadric mesh decimation..." << endl; + + vtkQuadricDecimation *quadricDecimation = vtkQuadricDecimation::New(); + quadricDecimation->SetInput(m_Surface->GetVtkPolyData()); + quadricDecimation->SetTargetReduction(decimation); + quadricDecimation->AttributeErrorMetricOn(); + quadricDecimation->Update(); + + vtkCleanPolyData* cleaner = vtkCleanPolyData::New(); + cleaner->SetInput(quadricDecimation->GetOutput()); + cleaner->PieceInvariantOn(); + cleaner->ConvertLinesToPointsOn(); + cleaner->ConvertStripsToPolysOn(); + cleaner->PointMergingOn(); + cleaner->Update(); + + m_Surface->SetVtkPolyData(cleaner->GetOutput()); + } + + ProgressBar::GetInstance()->Progress(1); return true; } void ShowSegmentationAsSmoothedSurface::ThreadedUpdateSuccessful() { - // TODO + DataNode::Pointer node = LookForPointerTargetBelowGroupNode("Surface representation"); + bool addToTree = node.IsNull(); + + if (addToTree) + { + node = DataNode::New(); + + bool wireframe = false; + GetParameter("Wireframe", wireframe); + + if (wireframe) + { + VtkRepresentationProperty *representation = dynamic_cast( + node->GetProperty("material.representation")); + + if (representation != NULL) + representation->SetRepresentationToWireframe(); + } + + node->SetProperty("opacity", FloatProperty::New(0.3)); + node->SetProperty("line width", IntProperty::New(1)); + node->SetProperty("scalar visibility", BoolProperty::New(false)); + + UIDGenerator uidGenerator("Surface_"); + node->SetProperty("FILENAME", StringProperty::New(uidGenerator.GetUID() + ".vtk")); + + std::string groupNodeName = "surface"; + DataNode *groupNode = GetGroupNode(); + + if (groupNode != NULL) + groupNode->GetName(groupNodeName); + + node->SetProperty("name", StringProperty::New(groupNodeName)); + } + + node->SetData(m_Surface); + + if (addToTree) + { + DataNode* groupNode = GetGroupNode(); + + if (groupNode != NULL) + { + groupNode->SetProperty("Surface representation", SmartPointerProperty::New(node)); + + BaseProperty *colorProperty = groupNode->GetProperty("color"); + + if (colorProperty != NULL) + node->ReplaceProperty("color", colorProperty); + else + node->SetProperty("color", ColorProperty::New(1.0f, 1.0f, 0.0f)); + + bool showResult = true; + GetParameter("Show result", showResult); + + bool syncVisibility = false; + GetParameter("Sync visibility", syncVisibility); + + Image::Pointer image; + GetPointerParameter("Input", image); + + BaseProperty *organTypeProperty = image->GetProperty("organ type"); + + if (organTypeProperty != NULL) + m_Surface->SetProperty("organ type", organTypeProperty); + + BaseProperty *visibleProperty = groupNode->GetProperty("visible"); + + if (visibleProperty != NULL && syncVisibility) + node->ReplaceProperty("visible", visibleProperty); + else + node->SetProperty("visible", BoolProperty::New(showResult)); + } + + InsertBelowGroupNode(node); + } + + StatusBar::GetInstance()->Clear(); Superclass::ThreadedUpdateSuccessful(); } diff --git a/Modules/MitkExt/Algorithms/mitkShowSegmentationAsSmoothedSurface.h b/Modules/MitkExt/Algorithms/mitkShowSegmentationAsSmoothedSurface.h index 34e09ae416..d2779109b8 100644 --- a/Modules/MitkExt/Algorithms/mitkShowSegmentationAsSmoothedSurface.h +++ b/Modules/MitkExt/Algorithms/mitkShowSegmentationAsSmoothedSurface.h @@ -1,46 +1,46 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef MITK_SHOW_SEGMENTATION_AS_SMOOTHED_SURFACE_H #define MITK_SHOW_SEGMENTATION_AS_SMOOTHED_SURFACE_H #include "mitkSegmentationSink.h" -#include "mitkSurface.h" +#include namespace mitk { class MitkExt_EXPORT ShowSegmentationAsSmoothedSurface : public SegmentationSink { public: mitkClassMacro(ShowSegmentationAsSmoothedSurface, SegmentationSink) mitkAlgorithmNewMacro(ShowSegmentationAsSmoothedSurface) protected: void Initialize(const NonBlockingAlgorithm *other = NULL); bool ReadyToRun(); bool ThreadedUpdateFunction(); void ThreadedUpdateSuccessful(); private: ShowSegmentationAsSmoothedSurface(); ~ShowSegmentationAsSmoothedSurface(); Surface::Pointer m_Surface; }; } #endif