diff --git a/Modules/Core/include/mitkNodePredicateGeometry.h b/Modules/Core/include/mitkNodePredicateGeometry.h index 7dc584b910..c4bbebb851 100644 --- a/Modules/Core/include/mitkNodePredicateGeometry.h +++ b/Modules/Core/include/mitkNodePredicateGeometry.h @@ -1,69 +1,69 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef MITKNODEPREDICATEGEOMETRY_H_HEADER_INCLUDED_ #define MITKNODEPREDICATEGEOMETRY_H_HEADER_INCLUDED_ #include "mitkNodePredicateBase.h" #include "mitkBaseGeometry.h" #include "mitkTimeGeometry.h" namespace mitk { class BaseData; /**Documentation @brief Predicate that evaluates if the given DataNode's data object has the same geometry (in terms of spacing, origin, orientation) like the reference geometry. One can either check the whole time geometry of the date node by defining a referenc time geometry or check against one given reference base geometry. If the predicate should check against a base geometry, you can specify the timepoint of the data's time geometry that should be checked. If no timepoint is defined the predicate will evaluate the data geometry in the first timestep. Evaluates to "false" for unsupported or undefined data objects/geometries. @ingroup DataStorage */ class MITKCORE_EXPORT NodePredicateGeometry : public NodePredicateBase { public: mitkClassMacro(NodePredicateGeometry, NodePredicateBase); mitkNewMacro1Param(NodePredicateGeometry, const TimeGeometry*); mitkNewMacro1Param(NodePredicateGeometry, const BaseGeometry*); mitkNewMacro2Param(NodePredicateGeometry, const BaseGeometry*, TimePointType); itkSetMacro(CheckPrecision, mitk::ScalarType); itkGetMacro(CheckPrecision, mitk::ScalarType); ~NodePredicateGeometry() override; bool CheckNode(const mitk::DataNode *node) const override; protected: - /**Constructor that is used configures the predicate to check against the the refernce geometry against the first data timepoint.*/ + /**Constructor that is used configures the predicate to check the refernce geometry against the first data timepoint.*/ NodePredicateGeometry(const BaseGeometry* refGeometry); /**Constructor allows to define the timepoint that should be evaluated against the reference.*/ NodePredicateGeometry(const BaseGeometry* refGeometry, TimePointType relevantTimePoint); /**Constructor that is used configures the predicate to check against the whole time geometry.*/ NodePredicateGeometry(const TimeGeometry* refGeometry); BaseGeometry::ConstPointer m_RefGeometry; TimeGeometry::ConstPointer m_RefTimeGeometry; TimePointType m_TimePoint; /**Indicates if m_TimePoint should be regarded or always the first timestep should be used.*/ bool m_UseTimePoint; /**Precision that should be used for the equal checks.*/ mitk::ScalarType m_CheckPrecision; }; } // namespace mitk #endif /* MITKNodePredicateGeometry_H_HEADER_INCLUDED_ */ diff --git a/Modules/ImageStatistics/mitkPlanarFigureMaskGenerator.cpp b/Modules/ImageStatistics/mitkPlanarFigureMaskGenerator.cpp index f3bae29514..422abf6fb2 100644 --- a/Modules/ImageStatistics/mitkPlanarFigureMaskGenerator.cpp +++ b/Modules/ImageStatistics/mitkPlanarFigureMaskGenerator.cpp @@ -1,554 +1,554 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include #include #include #include "mitkImageAccessByItk.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace mitk { void PlanarFigureMaskGenerator::SetPlanarFigure(mitk::PlanarFigure::Pointer planarFigure) { if ( planarFigure.IsNull() ) { throw std::runtime_error( "Error: planar figure empty!" ); } const PlaneGeometry *planarFigurePlaneGeometry = planarFigure->GetPlaneGeometry(); if ( planarFigurePlaneGeometry == nullptr ) { throw std::runtime_error( "Planar-Figure not yet initialized!" ); } const auto *planarFigureGeometry = dynamic_cast< const PlaneGeometry * >( planarFigurePlaneGeometry ); if ( planarFigureGeometry == nullptr ) { throw std::runtime_error( "Non-planar planar figures not supported!" ); } if (planarFigure != m_PlanarFigure) { this->Modified(); m_PlanarFigure = planarFigure; } } mitk::Image::ConstPointer PlanarFigureMaskGenerator::GetReferenceImage() { if (IsUpdateRequired()) { this->CalculateMask(); } return m_ReferenceImage; } template < typename TPixel, unsigned int VImageDimension > void PlanarFigureMaskGenerator::InternalCalculateMaskFromPlanarFigure( const itk::Image< TPixel, VImageDimension > *image, unsigned int axis ) { typedef itk::Image< unsigned short, 2 > MaskImage2DType; typename MaskImage2DType::Pointer maskImage = MaskImage2DType::New(); maskImage->SetOrigin(image->GetOrigin()); maskImage->SetSpacing(image->GetSpacing()); maskImage->SetLargestPossibleRegion(image->GetLargestPossibleRegion()); maskImage->SetBufferedRegion(image->GetBufferedRegion()); maskImage->SetDirection(image->GetDirection()); maskImage->SetNumberOfComponentsPerPixel(image->GetNumberOfComponentsPerPixel()); maskImage->Allocate(); maskImage->FillBuffer(1); // all PolylinePoints of the PlanarFigure are stored in a vtkPoints object. // These points are used by the vtkLassoStencilSource to create // a vtkImageStencil. const mitk::PlaneGeometry *planarFigurePlaneGeometry = m_PlanarFigure->GetPlaneGeometry(); const typename PlanarFigure::PolyLineType planarFigurePolyline = m_PlanarFigure->GetPolyLine( 0 ); const mitk::BaseGeometry *imageGeometry3D = m_inputImage->GetGeometry( 0 ); // If there is a second poly line in a closed planar figure, treat it as a hole. PlanarFigure::PolyLineType planarFigureHolePolyline; if (m_PlanarFigure->GetPolyLinesSize() == 2) planarFigureHolePolyline = m_PlanarFigure->GetPolyLine(1); // Determine x- and y-dimensions depending on principal axis // TODO use plane geometry normal to determine that automatically, then check whether the PF is aligned with one of the three principal axis int i0, i1; switch ( axis ) { case 0: i0 = 1; i1 = 2; break; case 1: i0 = 0; i1 = 2; break; case 2: default: i0 = 0; i1 = 1; break; } // store the polyline contour as vtkPoints object bool outOfBounds = false; vtkSmartPointer points = vtkSmartPointer::New(); typename PlanarFigure::PolyLineType::const_iterator it; for ( it = planarFigurePolyline.begin(); it != planarFigurePolyline.end(); ++it ) { Point3D point3D; // Convert 2D point back to the local index coordinates of the selected // image // Fabian: From PlaneGeometry documentation: // Converts a 2D point given in mm (pt2d_mm) relative to the upper-left corner of the geometry into the corresponding world-coordinate (a 3D point in mm, pt3d_mm). // To convert a 2D point given in units (e.g., pixels in case of an image) into a 2D point given in mm (as required by this method), use IndexToWorld. planarFigurePlaneGeometry->Map( *it, point3D ); // Polygons (partially) outside of the image bounds can not be processed // further due to a bug in vtkPolyDataToImageStencil if ( !imageGeometry3D->IsInside( point3D ) ) { outOfBounds = true; } imageGeometry3D->WorldToIndex( point3D, point3D ); points->InsertNextPoint( point3D[i0], point3D[i1], 0 ); } vtkSmartPointer holePoints = nullptr; if (!planarFigureHolePolyline.empty()) { holePoints = vtkSmartPointer::New(); Point3D point3D; PlanarFigure::PolyLineType::const_iterator end = planarFigureHolePolyline.end(); for (it = planarFigureHolePolyline.begin(); it != end; ++it) { // Fabian: same as above planarFigurePlaneGeometry->Map(*it, point3D); imageGeometry3D->WorldToIndex(point3D, point3D); holePoints->InsertNextPoint(point3D[i0], point3D[i1], 0); } } // mark a malformed 2D planar figure ( i.e. area = 0 ) as out of bounds // this can happen when all control points of a rectangle lie on the same line = two of the three extents are zero double bounds[6] = {0, 0, 0, 0, 0, 0}; points->GetBounds( bounds ); bool extent_x = (fabs(bounds[0] - bounds[1])) < mitk::eps; bool extent_y = (fabs(bounds[2] - bounds[3])) < mitk::eps; bool extent_z = (fabs(bounds[4] - bounds[5])) < mitk::eps; // throw an exception if a closed planar figure is deformed, i.e. has only one non-zero extent if ( m_PlanarFigure->IsClosed() && ((extent_x && extent_y) || (extent_x && extent_z) || (extent_y && extent_z))) { mitkThrow() << "Figure has a zero area and cannot be used for masking."; } if ( outOfBounds ) { throw std::runtime_error( "Figure at least partially outside of image bounds!" ); } // create a vtkLassoStencilSource and set the points of the Polygon vtkSmartPointer lassoStencil = vtkSmartPointer::New(); lassoStencil->SetShapeToPolygon(); lassoStencil->SetPoints( points ); vtkSmartPointer holeLassoStencil = nullptr; if (holePoints.GetPointer() != nullptr) { holeLassoStencil = vtkSmartPointer::New(); holeLassoStencil->SetShapeToPolygon(); holeLassoStencil->SetPoints(holePoints); } // Export from ITK to VTK (to use a VTK filter) typedef itk::VTKImageImport< MaskImage2DType > ImageImportType; typedef itk::VTKImageExport< MaskImage2DType > ImageExportType; typename ImageExportType::Pointer itkExporter = ImageExportType::New(); itkExporter->SetInput( maskImage ); // itkExporter->SetInput( castFilter->GetOutput() ); vtkSmartPointer vtkImporter = vtkSmartPointer::New(); this->ConnectPipelines( itkExporter, vtkImporter ); // Apply the generated image stencil to the input image vtkSmartPointer imageStencilFilter = vtkSmartPointer::New(); imageStencilFilter->SetInputConnection( vtkImporter->GetOutputPort() ); imageStencilFilter->SetStencilConnection(lassoStencil->GetOutputPort()); imageStencilFilter->ReverseStencilOff(); imageStencilFilter->SetBackgroundValue( 0 ); imageStencilFilter->Update(); vtkSmartPointer holeStencilFilter = nullptr; if (holeLassoStencil.GetPointer() != nullptr) { holeStencilFilter = vtkSmartPointer::New(); holeStencilFilter->SetInputConnection(imageStencilFilter->GetOutputPort()); holeStencilFilter->SetStencilConnection(holeLassoStencil->GetOutputPort()); holeStencilFilter->ReverseStencilOn(); holeStencilFilter->SetBackgroundValue(0); holeStencilFilter->Update(); } // Export from VTK back to ITK vtkSmartPointer vtkExporter = vtkSmartPointer::New(); vtkExporter->SetInputConnection( holeStencilFilter.GetPointer() == nullptr ? imageStencilFilter->GetOutputPort() : holeStencilFilter->GetOutputPort()); vtkExporter->Update(); typename ImageImportType::Pointer itkImporter = ImageImportType::New(); this->ConnectPipelines( vtkExporter, itkImporter ); itkImporter->Update(); typedef itk::ImageDuplicator< ImageImportType::OutputImageType > DuplicatorType; DuplicatorType::Pointer duplicator = DuplicatorType::New(); duplicator->SetInputImage( itkImporter->GetOutput() ); duplicator->Update(); // Store mask m_InternalITKImageMask2D = duplicator->GetOutput(); } template < typename TPixel, unsigned int VImageDimension > void PlanarFigureMaskGenerator::InternalCalculateMaskFromOpenPlanarFigure( const itk::Image< TPixel, VImageDimension > *image, unsigned int axis ) { typedef itk::Image< unsigned short, 2 > MaskImage2DType; typedef itk::LineIterator< MaskImage2DType > LineIteratorType; typedef MaskImage2DType::IndexType IndexType2D; typedef std::vector< IndexType2D > IndexVecType; typename MaskImage2DType::Pointer maskImage = MaskImage2DType::New(); maskImage->SetOrigin(image->GetOrigin()); maskImage->SetSpacing(image->GetSpacing()); maskImage->SetLargestPossibleRegion(image->GetLargestPossibleRegion()); maskImage->SetBufferedRegion(image->GetBufferedRegion()); maskImage->SetDirection(image->GetDirection()); maskImage->SetNumberOfComponentsPerPixel(image->GetNumberOfComponentsPerPixel()); maskImage->Allocate(); maskImage->FillBuffer(0); // all PolylinePoints of the PlanarFigure are stored in a vtkPoints object. const mitk::PlaneGeometry *planarFigurePlaneGeometry = m_PlanarFigure->GetPlaneGeometry(); const typename PlanarFigure::PolyLineType planarFigurePolyline = m_PlanarFigure->GetPolyLine( 0 ); const mitk::BaseGeometry *imageGeometry3D = m_inputImage->GetGeometry( 0 ); // Determine x- and y-dimensions depending on principal axis // TODO use plane geometry normal to determine that automatically, then check whether the PF is aligned with one of the three principal axis int i0, i1; switch ( axis ) { case 0: i0 = 1; i1 = 2; break; case 1: i0 = 0; i1 = 2; break; case 2: default: i0 = 0; i1 = 1; break; } int numPolyLines = m_PlanarFigure->GetPolyLinesSize(); for ( int lineId = 0; lineId < numPolyLines; ++lineId ) { // store the polyline contour as vtkPoints object bool outOfBounds = false; IndexVecType pointIndices; typename PlanarFigure::PolyLineType::const_iterator it; for ( it = planarFigurePolyline.begin(); it != planarFigurePolyline.end(); ++it ) { Point3D point3D; planarFigurePlaneGeometry->Map( *it, point3D ); if ( !imageGeometry3D->IsInside( point3D ) ) { outOfBounds = true; } imageGeometry3D->WorldToIndex( point3D, point3D ); IndexType2D index2D; index2D[0] = point3D[i0]; index2D[1] = point3D[i1]; pointIndices.push_back( index2D ); } if ( outOfBounds ) { throw std::runtime_error( "Figure at least partially outside of image bounds!" ); } for ( IndexVecType::const_iterator it = pointIndices.begin(); it != pointIndices.end()-1; ++it ) { IndexType2D ind1 = *it; IndexType2D ind2 = *(it+1); LineIteratorType lineIt( maskImage, ind1, ind2 ); while ( !lineIt.IsAtEnd() ) { lineIt.Set( 1 ); ++lineIt; } } } // Store mask m_InternalITKImageMask2D = maskImage; } bool PlanarFigureMaskGenerator::CheckPlanarFigureIsNotTilted(const PlaneGeometry* planarGeometry, const BaseGeometry *geometry) { if (!planarGeometry) return false; if (!geometry) return false; unsigned int axis; return GetPrincipalAxis(geometry,planarGeometry->GetNormal(), axis); -}; +} bool PlanarFigureMaskGenerator::GetPrincipalAxis( const BaseGeometry *geometry, Vector3D vector, unsigned int &axis ) { vector.Normalize(); for ( unsigned int i = 0; i < 3; ++i ) { Vector3D axisVector = geometry->GetAxisVector( i ); axisVector.Normalize(); //normal mitk::eps is to pedantic for this check. See e.g. T27122 - //therefor choose a larger epsilon. The value was set a) as small as + //therefore choose a larger epsilon. The value was set a) as small as //possible but b) still allowing to datasets like in (T27122) to pass //when floating rounding errors sum up. const double epsilon = 5e-5; if ( fabs( fabs( axisVector * vector ) - 1.0) < epsilon) { axis = i; return true; } } return false; } void PlanarFigureMaskGenerator::CalculateMask() { if (m_inputImage.IsNull()) { MITK_ERROR << "Image is not set."; } if (m_PlanarFigure.IsNull()) { MITK_ERROR << "PlanarFigure is not set."; } if (m_TimeStep != 0) { MITK_WARN << "Multiple TimeSteps are not supported in PlanarFigureMaskGenerator (yet)."; } const BaseGeometry *imageGeometry = m_inputImage->GetGeometry(); if ( imageGeometry == nullptr ) { throw std::runtime_error( "Image geometry invalid!" ); } if (m_inputImage->GetTimeSteps() > 0) { mitk::ImageTimeSelector::Pointer imgTimeSel = mitk::ImageTimeSelector::New(); imgTimeSel->SetInput(m_inputImage); imgTimeSel->SetTimeNr(m_TimeStep); imgTimeSel->UpdateLargestPossibleRegion(); m_InternalTimeSliceImage = imgTimeSel->GetOutput(); } else { m_InternalTimeSliceImage = m_inputImage; } m_InternalITKImageMask2D = nullptr; const PlaneGeometry *planarFigurePlaneGeometry = m_PlanarFigure->GetPlaneGeometry(); const auto *planarFigureGeometry = dynamic_cast< const PlaneGeometry * >( planarFigurePlaneGeometry ); //const BaseGeometry *imageGeometry = m_inputImage->GetGeometry(); // Find principal direction of PlanarFigure in input image unsigned int axis; if ( !this->GetPrincipalAxis( imageGeometry, planarFigureGeometry->GetNormal(), axis ) ) { throw std::runtime_error( "Non-aligned planar figures not supported!" ); } m_PlanarFigureAxis = axis; // Find slice number corresponding to PlanarFigure in input image itk::Image< unsigned short, 3 >::IndexType index; imageGeometry->WorldToIndex( planarFigureGeometry->GetOrigin(), index ); unsigned int slice = index[axis]; m_PlanarFigureSlice = slice; // extract image slice which corresponds to the planarFigure and store it in m_InternalImageSlice mitk::Image::ConstPointer inputImageSlice = extract2DImageSlice(axis, slice); //mitk::IOUtil::Save(inputImageSlice, "/home/fabian/inputSliceImage.nrrd"); // Compute mask from PlanarFigure // rastering for open planar figure: if ( !m_PlanarFigure->IsClosed() ) { AccessFixedDimensionByItk_1(inputImageSlice, InternalCalculateMaskFromOpenPlanarFigure, 2, axis) } else//for closed planar figure { AccessFixedDimensionByItk_1(inputImageSlice, InternalCalculateMaskFromPlanarFigure, 2, axis) } //convert itk mask to mitk::Image::Pointer and return it mitk::Image::Pointer planarFigureMaskImage; planarFigureMaskImage = mitk::GrabItkImageMemory(m_InternalITKImageMask2D); //mitk::IOUtil::Save(planarFigureMaskImage, "/home/fabian/planarFigureMaskImage.nrrd"); //Convert2Dto3DImageFilter::Pointer sliceTo3DImageConverter = Convert2Dto3DImageFilter::New(); //sliceTo3DImageConverter->SetInput(planarFigureMaskImage); //sliceTo3DImageConverter->Update(); //mitk::IOUtil::Save(sliceTo3DImageConverter->GetOutput(), "/home/fabian/3DsliceImage.nrrd"); m_ReferenceImage = inputImageSlice; //mitk::IOUtil::Save(m_ReferenceImage, "/home/fabian/referenceImage.nrrd"); m_InternalMask = planarFigureMaskImage; } void PlanarFigureMaskGenerator::SetTimeStep(unsigned int timeStep) { if (timeStep != m_TimeStep) { m_TimeStep = timeStep; } } mitk::Image::Pointer PlanarFigureMaskGenerator::GetMask() { if (IsUpdateRequired()) { this->CalculateMask(); this->Modified(); } m_InternalMaskUpdateTime = this->GetMTime(); return m_InternalMask; } mitk::Image::ConstPointer PlanarFigureMaskGenerator::extract2DImageSlice(unsigned int axis, unsigned int slice) { // Extract slice with given position and direction from image unsigned int dimension = m_InternalTimeSliceImage->GetDimension(); if (dimension == 3) { ExtractImageFilter::Pointer imageExtractor = ExtractImageFilter::New(); imageExtractor->SetInput( m_InternalTimeSliceImage ); imageExtractor->SetSliceDimension( axis ); imageExtractor->SetSliceIndex( slice ); imageExtractor->Update(); return imageExtractor->GetOutput(); } else if(dimension == 2) { return m_InternalTimeSliceImage; } else { MITK_ERROR << "Unsupported image dimension. Dimension is: " << dimension << ". Only 2D and 3D images are supported."; return nullptr; } } bool PlanarFigureMaskGenerator::IsUpdateRequired() const { unsigned long thisClassTimeStamp = this->GetMTime(); unsigned long internalMaskTimeStamp = m_InternalMask->GetMTime(); unsigned long planarFigureTimeStamp = m_PlanarFigure->GetMTime(); unsigned long inputImageTimeStamp = m_inputImage->GetMTime(); if (thisClassTimeStamp > m_InternalMaskUpdateTime) // inputs have changed { return true; } if (m_InternalMaskUpdateTime < planarFigureTimeStamp || m_InternalMaskUpdateTime < inputImageTimeStamp) // mask image has changed outside of this class { return true; } if (internalMaskTimeStamp > m_InternalMaskUpdateTime) // internal mask has been changed outside of this class { return true; } return false; } } diff --git a/Modules/ImageStatistics/mitkPlanarFigureMaskGenerator.h b/Modules/ImageStatistics/mitkPlanarFigureMaskGenerator.h index 01bcd744bb..c724cd6e81 100644 --- a/Modules/ImageStatistics/mitkPlanarFigureMaskGenerator.h +++ b/Modules/ImageStatistics/mitkPlanarFigureMaskGenerator.h @@ -1,149 +1,149 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef MITKPLANARFIGUREMASKGENERATOR #define MITKPLANARFIGUREMASKGENERATOR #include #include #include #include #include #include #include #include #include #include namespace mitk { /** * \class PlanarFigureMaskGenerator * \brief Derived from MaskGenerator. This class is used to convert a mitk::PlanarFigure into a binary image mask */ class MITKIMAGESTATISTICS_EXPORT PlanarFigureMaskGenerator : public MaskGenerator { public: /** Standard Self typedef */ typedef PlanarFigureMaskGenerator Self; typedef MaskGenerator Superclass; typedef itk::SmartPointer Pointer; typedef itk::SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkNewMacro(Self); /** Runtime information support. */ itkTypeMacro(PlanarFigureMaskGenerator, MaskGenerator); /** * @brief GetMask Computes and returns the mask * @return mitk::Image::Pointer of the generated mask */ mitk::Image::Pointer GetMask() override; void SetPlanarFigure(mitk::PlanarFigure::Pointer planarFigure); mitk::Image::ConstPointer GetReferenceImage() override; /** * @brief SetTimeStep is used to set the time step for which the mask is to be generated * @param timeStep */ void SetTimeStep(unsigned int timeStep) override; itkGetConstMacro(PlanarFigureAxis, unsigned int); itkGetConstMacro(PlanarFigureSlice, unsigned int); /** Helper function that indicates if a passed planar geometry is tilted regarding a given geometry and its main axis. - *@result If either planarGeometry or geometry is nullptr it will return false.*/ + *@pre If either planarGeometry or geometry is nullptr it will return false.*/ static bool CheckPlanarFigureIsNotTilted(const PlaneGeometry* planarGeometry, const BaseGeometry *geometry); protected: PlanarFigureMaskGenerator() : Superclass(), m_ReferenceImage(nullptr), m_PlanarFigureAxis(0), m_InternalMaskUpdateTime(0), m_PlanarFigureSlice(0) { m_InternalMask = mitk::Image::New(); } private: void CalculateMask(); template void InternalCalculateMaskFromPlanarFigure(const itk::Image *image, unsigned int axis); template void InternalCalculateMaskFromOpenPlanarFigure(const itk::Image *image, unsigned int axis); mitk::Image::ConstPointer extract2DImageSlice(unsigned int axis, unsigned int slice); /** Helper function that deduces if the passed vector is equal to one of the primary axis of the geometry.*/ static bool GetPrincipalAxis(const BaseGeometry *geometry, Vector3D vector, unsigned int &axis); /** Connection from ITK to VTK */ template void ConnectPipelines(ITK_Exporter exporter, vtkSmartPointer importer) { importer->SetUpdateInformationCallback(exporter->GetUpdateInformationCallback()); importer->SetPipelineModifiedCallback(exporter->GetPipelineModifiedCallback()); importer->SetWholeExtentCallback(exporter->GetWholeExtentCallback()); importer->SetSpacingCallback(exporter->GetSpacingCallback()); importer->SetOriginCallback(exporter->GetOriginCallback()); importer->SetScalarTypeCallback(exporter->GetScalarTypeCallback()); importer->SetNumberOfComponentsCallback(exporter->GetNumberOfComponentsCallback()); importer->SetPropagateUpdateExtentCallback(exporter->GetPropagateUpdateExtentCallback()); importer->SetUpdateDataCallback(exporter->GetUpdateDataCallback()); importer->SetDataExtentCallback(exporter->GetDataExtentCallback()); importer->SetBufferPointerCallback(exporter->GetBufferPointerCallback()); importer->SetCallbackUserData(exporter->GetCallbackUserData()); } /** Connection from VTK to ITK */ template void ConnectPipelines(vtkSmartPointer exporter, ITK_Importer importer) { importer->SetUpdateInformationCallback(exporter->GetUpdateInformationCallback()); importer->SetPipelineModifiedCallback(exporter->GetPipelineModifiedCallback()); importer->SetWholeExtentCallback(exporter->GetWholeExtentCallback()); importer->SetSpacingCallback(exporter->GetSpacingCallback()); importer->SetOriginCallback(exporter->GetOriginCallback()); importer->SetScalarTypeCallback(exporter->GetScalarTypeCallback()); importer->SetNumberOfComponentsCallback(exporter->GetNumberOfComponentsCallback()); importer->SetPropagateUpdateExtentCallback(exporter->GetPropagateUpdateExtentCallback()); importer->SetUpdateDataCallback(exporter->GetUpdateDataCallback()); importer->SetDataExtentCallback(exporter->GetDataExtentCallback()); importer->SetBufferPointerCallback(exporter->GetBufferPointerCallback()); importer->SetCallbackUserData(exporter->GetCallbackUserData()); } bool IsUpdateRequired() const; mitk::PlanarFigure::Pointer m_PlanarFigure; itk::Image::Pointer m_InternalITKImageMask2D; mitk::Image::ConstPointer m_InternalTimeSliceImage; mitk::Image::ConstPointer m_ReferenceImage; unsigned int m_PlanarFigureAxis; unsigned long m_InternalMaskUpdateTime; unsigned int m_PlanarFigureSlice; }; } // namespace mitk #endif // MITKPLANARFIGUREMASKGENERATOR diff --git a/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.cpp b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.cpp index 5524d450ed..cfbd81dab8 100644 --- a/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.cpp +++ b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.cpp @@ -1,603 +1,599 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkImageStatisticsView.h" #include // berry includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mitkImageStatisticsContainerManager.h" #include const std::string QmitkImageStatisticsView::VIEW_ID = "org.mitk.views.imagestatistics"; QmitkImageStatisticsView::~QmitkImageStatisticsView() { if (nullptr != m_selectedPlanarFigure) { m_selectedPlanarFigure->RemoveObserver(m_PlanarFigureObserverTag); } } void QmitkImageStatisticsView::CreateQtPartControl(QWidget *parent) { m_Controls.setupUi(parent); m_Controls.widget_histogram->SetTheme(GetColorTheme()); m_Controls.widget_intensityProfile->SetTheme(GetColorTheme()); m_Controls.groupBox_histogram->setVisible(true); m_Controls.groupBox_intensityProfile->setVisible(false); m_Controls.label_currentlyComputingStatistics->setVisible(false); m_Controls.sliderWidget_histogram->setPrefix("Time: "); m_Controls.sliderWidget_histogram->setDecimals(0); m_Controls.sliderWidget_histogram->setVisible(false); m_Controls.sliderWidget_intensityProfile->setPrefix("Time: "); m_Controls.sliderWidget_intensityProfile->setDecimals(0); m_Controls.sliderWidget_intensityProfile->setVisible(false); ResetGUI(); m_Controls.widget_statistics->SetDataStorage(GetDataStorage()); CreateConnections(); } void QmitkImageStatisticsView::CreateConnections() { connect(m_Controls.checkBox_ignoreZero, &QCheckBox::stateChanged, this, &QmitkImageStatisticsView::OnCheckBoxIgnoreZeroStateChanged); connect(m_Controls.buttonSelection, &QAbstractButton::clicked, this, &QmitkImageStatisticsView::OnButtonSelectionPressed); } void QmitkImageStatisticsView::CalculateOrGetMultiStatistics() { m_selectedImageNode = nullptr; m_selectedMaskNode = nullptr; if (m_selectedImageNodes.empty()) { // no images selected; reset everything ResetGUI(); } for (auto imageNode : m_selectedImageNodes) { m_selectedImageNode = imageNode; if (m_selectedMaskNodes.empty()) { CalculateOrGetStatistics(); } else { for (auto maskNode : m_selectedMaskNodes) { m_selectedMaskNode = maskNode; CalculateOrGetStatistics(); } } } } void QmitkImageStatisticsView::CalculateOrGetStatistics() { if (nullptr != m_selectedPlanarFigure) { m_selectedPlanarFigure->RemoveObserver(m_PlanarFigureObserverTag); m_selectedPlanarFigure = nullptr; } m_Controls.groupBox_intensityProfile->setVisible(false); m_Controls.widget_statistics->setEnabled(m_selectedImageNode.IsNotNull()); if (nullptr != m_selectedImageNode) { auto image = dynamic_cast(m_selectedImageNode->GetData()); mitk::Image *mask = nullptr; mitk::PlanarFigure *maskPlanarFigure = nullptr; if (image->GetDimension() == 4) { m_Controls.sliderWidget_histogram->setVisible(true); unsigned int maxTimestep = image->GetTimeSteps(); m_Controls.sliderWidget_histogram->setMaximum(maxTimestep - 1); } else { m_Controls.sliderWidget_histogram->setVisible(false); } if (nullptr != m_selectedMaskNode) { mask = dynamic_cast(m_selectedMaskNode->GetData()); if (nullptr == mask) { maskPlanarFigure = dynamic_cast(m_selectedMaskNode->GetData()); } } mitk::ImageStatisticsContainer::ConstPointer imageStatistics; if (nullptr != mask) { imageStatistics = mitk::ImageStatisticsContainerManager::GetImageStatistics(GetDataStorage(), image, mask); } else if (nullptr != maskPlanarFigure) { m_selectedPlanarFigure = maskPlanarFigure; ITKCommandType::Pointer changeListener = ITKCommandType::New(); changeListener->SetCallbackFunction(this, &QmitkImageStatisticsView::CalculateOrGetMultiStatistics); m_PlanarFigureObserverTag = m_selectedPlanarFigure->AddObserver(mitk::EndInteractionPlanarFigureEvent(), changeListener); if (!maskPlanarFigure->IsClosed()) { ComputeAndDisplayIntensityProfile(image, maskPlanarFigure); } imageStatistics = mitk::ImageStatisticsContainerManager::GetImageStatistics(GetDataStorage(), image, maskPlanarFigure); } else { imageStatistics = mitk::ImageStatisticsContainerManager::GetImageStatistics(GetDataStorage(), image); } bool imageStatisticsOlderThanInputs = false; if (nullptr != imageStatistics && ((nullptr != image && imageStatistics->GetMTime() < image->GetMTime()) || (nullptr != mask && imageStatistics->GetMTime() < mask->GetMTime()) || (nullptr != maskPlanarFigure && imageStatistics->GetMTime() < maskPlanarFigure->GetMTime()))) { imageStatisticsOlderThanInputs = true; } if (nullptr != imageStatistics) { // triggers re-computation when switched between images and the newest one has not 100 bins (default) if (imageStatistics->TimeStepExists(0)) { auto calculatedBins = imageStatistics->GetStatisticsForTimeStep(0).m_Histogram.GetPointer()->Size(); if (calculatedBins != 100) { OnRequestHistogramUpdate(m_Controls.widget_histogram->GetBins()); } } } // statistics need to be computed if (!imageStatistics || imageStatisticsOlderThanInputs || m_ForceRecompute) { CalculateStatistics(image, mask, maskPlanarFigure); } // statistics already computed else { // Not an open planar figure: show histogram (intensity profile already shown) if (!(maskPlanarFigure && !maskPlanarFigure->IsClosed())) { if (imageStatistics->TimeStepExists(0)) { auto histogram = imageStatistics->GetStatisticsForTimeStep(0).m_Histogram.GetPointer(); auto histogramLabelName = GenerateStatisticsNodeName(image, mask); FillHistogramWidget({ histogram }, { histogramLabelName }); } } } } } void QmitkImageStatisticsView::CalculateStatistics(const mitk::Image *image, const mitk::Image *mask, const mitk::PlanarFigure *maskPlanarFigure) { auto runnable = new QmitkImageStatisticsCalculationRunnable(); runnable->Initialize(image, mask, maskPlanarFigure); runnable->setAutoDelete(false); runnable->SetIgnoreZeroValueVoxel(m_IgnoreZeroValueVoxel); m_Runnables.push_back(runnable); connect(runnable, &QmitkImageStatisticsCalculationRunnable::finished, this, &QmitkImageStatisticsView::OnStatisticsCalculationEnds, Qt::QueuedConnection); try { // Compute statistics QThreadPool::globalInstance()->start(runnable); m_Controls.label_currentlyComputingStatistics->setVisible(true); } catch (const mitk::Exception &e) { mitk::StatusBar::GetInstance()->DisplayErrorText(e.GetDescription()); m_Controls.label_currentlyComputingStatistics->setVisible(false); } catch (const std::runtime_error &e) { mitk::StatusBar::GetInstance()->DisplayErrorText(e.what()); m_Controls.label_currentlyComputingStatistics->setVisible(false); } catch (const std::exception &e) { mitk::StatusBar::GetInstance()->DisplayErrorText(e.what()); m_Controls.label_currentlyComputingStatistics->setVisible(false); } } void QmitkImageStatisticsView::ComputeAndDisplayIntensityProfile(mitk::Image *image, mitk::PlanarFigure *maskPlanarFigure) { mitk::Image::Pointer inputImage; if (image->GetDimension() == 4) { m_Controls.sliderWidget_intensityProfile->setVisible(true); unsigned int maxTimestep = image->GetTimeSteps(); m_Controls.sliderWidget_intensityProfile->setMaximum(maxTimestep - 1); // Intensity profile can only be calculated on 3D, so extract if 4D mitk::ImageTimeSelector::Pointer timeSelector = mitk::ImageTimeSelector::New(); int currentTimestep = static_cast(m_Controls.sliderWidget_intensityProfile->value()); timeSelector->SetInput(image); timeSelector->SetTimeNr(currentTimestep); timeSelector->Update(); inputImage = timeSelector->GetOutput(); } else { m_Controls.sliderWidget_intensityProfile->setVisible(false); inputImage = image; } auto intensityProfile = mitk::ComputeIntensityProfile(inputImage, maskPlanarFigure); // Don't show histogram for intensity profiles m_Controls.groupBox_histogram->setVisible(false); m_Controls.groupBox_intensityProfile->setVisible(true); m_Controls.widget_intensityProfile->Reset(); m_Controls.widget_intensityProfile->SetIntensityProfile(intensityProfile.GetPointer(), "Intensity Profile of " + m_selectedImageNode->GetName()); } void QmitkImageStatisticsView::FillHistogramWidget(const std::vector &histogram, const std::vector &dataLabels) { m_Controls.groupBox_histogram->setVisible(true); m_Controls.widget_histogram->SetTheme(GetColorTheme()); m_Controls.widget_histogram->SetHistogram(histogram.front(), dataLabels.front()); connect(m_Controls.widget_histogram, &QmitkHistogramVisualizationWidget::RequestHistogramUpdate, this, &QmitkImageStatisticsView::OnRequestHistogramUpdate); } QmitkChartWidget::ColorTheme QmitkImageStatisticsView::GetColorTheme() const { ctkPluginContext *context = berry::WorkbenchPlugin::GetDefault()->GetPluginContext(); ctkServiceReference styleManagerRef = context->getServiceReference(); if (styleManagerRef) { auto styleManager = context->getService(styleManagerRef); if (styleManager->GetStyle().name == "Dark") { return QmitkChartWidget::ColorTheme::darkstyle; } else { return QmitkChartWidget::ColorTheme::lightstyle; } } return QmitkChartWidget::ColorTheme::darkstyle; } void QmitkImageStatisticsView::ResetGUI() { m_Controls.widget_statistics->Reset(); m_Controls.widget_statistics->setEnabled(false); m_Controls.widget_histogram->Reset(); m_Controls.widget_histogram->setEnabled(false); } void QmitkImageStatisticsView::ResetGUIDefault() { m_Controls.widget_histogram->ResetDefault(); m_Controls.checkBox_ignoreZero->setChecked(false); m_IgnoreZeroValueVoxel = false; } void QmitkImageStatisticsView::OnStatisticsCalculationEnds() { auto runnable = qobject_cast(sender()); if (nullptr == runnable) { return; } mitk::StatusBar::GetInstance()->Clear(); auto it = std::find(m_Runnables.begin(), m_Runnables.end(), runnable); if (it != m_Runnables.end()) { m_Runnables.erase(it); } auto image = runnable->GetStatisticsImage(); // get mask const mitk::BaseData* mask = nullptr; if (runnable->GetMaskImage()) { mask = runnable->GetMaskImage(); } else if (runnable->GetPlanarFigure()) { mask = runnable->GetPlanarFigure(); } // get current statistics auto currentImageStatistics = mitk::ImageStatisticsContainerManager::GetImageStatistics(GetDataStorage(), image, mask); if (runnable->GetStatisticsUpdateSuccessFlag()) // case: calculation was successful { auto statistic = runnable->GetStatisticsData(); SetupRelationRules(image, mask, statistic); // checks for existing statistic, and add it to data manager HandleExistingStatistics(image, mask, statistic); auto maskPlanarFigure = dynamic_cast(mask); if (!maskPlanarFigure || maskPlanarFigure->IsClosed()) { auto histogramLabelName = GenerateStatisticsNodeName(image, mask); FillHistogramWidget({ runnable->GetTimeStepHistogram() }, { histogramLabelName }); } } else // case: calculation was not successful { // handle histogram auto emptyHistogram = HistogramType::New(); auto histogramLabelName = GenerateStatisticsNodeName(image, mask); FillHistogramWidget({ emptyHistogram }, { histogramLabelName }); // handle statistics mitk::ImageStatisticsContainer::Pointer statistic = mitk::ImageStatisticsContainer::New(); statistic->SetTimeGeometry(const_cast(image->GetTimeGeometry())); // add empty histogram to statistics for all time steps for (unsigned int i = 0; i < image->GetTimeSteps(); ++i) { auto statisticObject = mitk::ImageStatisticsContainer::ImageStatisticsObject(); statisticObject.m_Histogram = emptyHistogram; statistic->SetStatisticsForTimeStep(i, statisticObject); } SetupRelationRules(image, mask, statistic); HandleExistingStatistics(image, mask, statistic); mitk::StatusBar::GetInstance()->DisplayErrorText(runnable->GetLastErrorMessage().c_str()); m_Controls.widget_histogram->setEnabled(false); } m_Controls.label_currentlyComputingStatistics->setVisible(false); } void QmitkImageStatisticsView::OnRequestHistogramUpdate(unsigned int) { // NEEDS TO BE IMPLEMENTED - SEE T27032 } void QmitkImageStatisticsView::OnCheckBoxIgnoreZeroStateChanged(int state) { m_ForceRecompute = true; m_IgnoreZeroValueVoxel = (state == Qt::Unchecked) ? false : true; CalculateOrGetMultiStatistics(); m_ForceRecompute = false; } void QmitkImageStatisticsView::OnButtonSelectionPressed() { QmitkNodeSelectionDialog* dialog = new QmitkNodeSelectionDialog(nullptr, "Select input for the statistic","You may select images and ROIs to compute their statistic. ROIs may be segmentations or planar figures."); dialog->SetDataStorage(GetDataStorage()); dialog->SetSelectionCheckFunction(CheckForSameGeometry()); // set predicates auto isPlanarFigurePredicate = mitk::GetImageStatisticsPlanarFigurePredicate(); auto isMaskPredicate = mitk::GetImageStatisticsMaskPredicate(); auto isImagePredicate = mitk::GetImageStatisticsImagePredicate(); auto isMaskOrPlanarFigurePredicate = mitk::NodePredicateOr::New(isPlanarFigurePredicate, isMaskPredicate); auto isImageOrMaskOrPlanarFigurePredicate = mitk::NodePredicateOr::New(isMaskOrPlanarFigurePredicate, isImagePredicate); dialog->SetNodePredicate(isImageOrMaskOrPlanarFigurePredicate); dialog->SetSelectionMode(QAbstractItemView::MultiSelection); dialog->SetCurrentSelection(m_SelectedNodeList); if (dialog->exec()) { m_selectedImageNodes.resize(0); m_selectedMaskNodes.resize(0); m_SelectedNodeList = dialog->GetSelectedNodes(); auto isImagePredicate = mitk::GetImageStatisticsImagePredicate(); for (const auto& node : m_SelectedNodeList) { if (isImagePredicate->CheckNode(node)) { m_selectedImageNodes.push_back(node.GetPointer()); } else { m_selectedMaskNodes.push_back(node.GetPointer()); } } m_Controls.widget_statistics->SetImageNodes(m_selectedImageNodes); m_Controls.widget_statistics->SetMaskNodes(m_selectedMaskNodes); CalculateOrGetMultiStatistics(); } delete dialog; } void QmitkImageStatisticsView::HandleExistingStatistics(mitk::Image::ConstPointer image, mitk::BaseData::ConstPointer mask, mitk::ImageStatisticsContainer::Pointer statistic) { auto imageStatistics = mitk::ImageStatisticsContainerManager::GetImageStatistics(GetDataStorage(), image, mask); // if statistics base data already exist: add to existing node if (imageStatistics) { auto node = GetNodeForStatisticsContainer(imageStatistics); node->SetData(statistic); } // statistics base data does not exist: add new node else { auto statisticsNodeName = GenerateStatisticsNodeName(image, mask); auto statisticsNode = mitk::CreateImageStatisticsNode(statistic, statisticsNodeName); GetDataStorage()->Add(statisticsNode); } } std::string QmitkImageStatisticsView::GenerateStatisticsNodeName(mitk::Image::ConstPointer image, mitk::BaseData::ConstPointer mask) { std::string statisticsNodeName = "no"; if (image.IsNotNull()) { statisticsNodeName = image->GetUID(); } if (mask.IsNotNull()) { statisticsNodeName += "_" + mask->GetUID(); } statisticsNodeName += "_statistics"; return statisticsNodeName; } void QmitkImageStatisticsView::SetupRelationRules(mitk::Image::ConstPointer image, mitk::BaseData::ConstPointer mask, mitk::ImageStatisticsContainer::Pointer statistic) { auto imageRule = mitk::StatisticsToImageRelationRule::New(); imageRule->Connect(statistic, image); if (nullptr != mask) { auto maskRule = mitk::StatisticsToMaskRelationRule::New(); maskRule->Connect(statistic, mask); } } mitk::DataNode::Pointer QmitkImageStatisticsView::GetNodeForStatisticsContainer(mitk::ImageStatisticsContainer::ConstPointer container) { if (container.IsNull()) { mitkThrow() << "Given container is null!"; } auto allDataNodes = GetDataStorage()->GetAll()->CastToSTLConstContainer(); for (auto node : allDataNodes) { auto nodeData = node->GetData(); if (nodeData && nodeData->GetUID() == container->GetUID()) { return node; } } mitkThrow() << "No DataNode is found which holds the given statistics container!"; } QmitkNodeSelectionDialog::SelectionCheckFunctionType QmitkImageStatisticsView::CheckForSameGeometry() const { auto lambda = [this](const QmitkNodeSelectionDialog::NodeList& nodes) { if (nodes.empty()) { return std::string(); } const mitk::Image* imageNodeData = nullptr; for (auto& node : nodes) { imageNodeData = dynamic_cast(node->GetData()); if (imageNodeData) { break; } } + if (imageNodeData == nullptr) + { + std::stringstream ss; + ss << "

Select at least one image.

"; + return ss.str(); + } + + auto imageGeoPredicate = mitk::NodePredicateGeometry::New(imageNodeData->GetGeometry()); + for (auto& rightNode : nodes) { - if (imageNodeData == rightNode->GetData()) + if (imageNodeData != rightNode->GetData()) { - bool sameGeometry = CheckForSameGeometry(imageNodeData, rightNode->GetData()); + bool sameGeometry = true; + + if (dynamic_cast(rightNode->GetData())) + { + sameGeometry = imageGeoPredicate->CheckNode(rightNode); + } + else + { + const mitk::PlanarFigure* planar2 = dynamic_cast(rightNode->GetData()); + if (planar2) + { + sameGeometry = mitk::PlanarFigureMaskGenerator::CheckPlanarFigureIsNotTilted(planar2->GetPlaneGeometry(), imageNodeData->GetGeometry()); + } + } + if (!sameGeometry) { std::stringstream ss; - ss << "

Invalid selection: All selected nodes must have the same geometry.

"; + ss << "

Invalid selection: All selected nodes must have the same geometry.

Differing node i.a.: \""; + ss << rightNode->GetName() <<"\"

"; return ss.str(); } } } return std::string(); }; return lambda; } - -bool QmitkImageStatisticsView::CheckForSameGeometry(const mitk::Image* image, const mitk::BaseData* data) const -{ - bool isSameGeometry(true); - - const mitk::Image* image2 = dynamic_cast(data); - const mitk::PlanarFigure* planar2 = dynamic_cast(data); - if (image2) - { - const mitk::BaseGeometry* geo1 = image->GetGeometry(); - const mitk::BaseGeometry* geo2 = image2->GetGeometry(); - - isSameGeometry = isSameGeometry && mitk::Equal(geo1->GetOrigin(), geo2->GetOrigin()); - isSameGeometry = isSameGeometry && mitk::Equal(geo1->GetExtent(0), geo2->GetExtent(0)); - isSameGeometry = isSameGeometry && mitk::Equal(geo1->GetExtent(1), geo2->GetExtent(1)); - isSameGeometry = isSameGeometry && mitk::Equal(geo1->GetExtent(2), geo2->GetExtent(2)); - isSameGeometry = isSameGeometry && mitk::Equal(geo1->GetSpacing(), geo2->GetSpacing()); - isSameGeometry = isSameGeometry && mitk::MatrixEqualElementWise(geo1->GetIndexToWorldTransform()->GetMatrix(), geo2->GetIndexToWorldTransform()->GetMatrix()); - - return isSameGeometry; - } - else if (planar2) - { - return mitk::PlanarFigureMaskGenerator::CheckPlanarFigureIsNotTilted(planar2->GetPlaneGeometry(), image->GetGeometry()); - } - - return false; -} diff --git a/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.h b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.h index ed8c310996..23d456eb19 100644 --- a/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.h +++ b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.h @@ -1,108 +1,107 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef QMITKIMAGESTATISTICSVIEW_H #define QMITKIMAGESTATISTICSVIEW_H #include "ui_QmitkImageStatisticsViewControls.h" #include #include #include #include #include /*! \brief QmitkImageStatisticsView is a bundle that allows statistics calculation from images. Three modes are supported: 1. Statistics of one image, 2. Statistics of an image and a segmentation, 3. Statistics of an image and a Planar Figure. The statistics calculation is realized in a separate thread to keep the gui accessible during calculation. \ingroup Plugins/org.mitk.gui.qt.measurementtoolbox */ class QmitkImageStatisticsView : public QmitkAbstractView { Q_OBJECT public: static const std::string VIEW_ID; /*! \brief default destructor */ ~QmitkImageStatisticsView() override; /*! \brief Creates the widget containing the application controls, like sliders, buttons etc.*/ void CreateQtPartControl(QWidget *parent) override; protected: using HistogramType = mitk::ImageStatisticsContainer::HistogramType; void SetFocus() override { }; virtual void CreateConnections(); void CalculateOrGetMultiStatistics(); void CalculateOrGetStatistics(); void CalculateStatistics(const mitk::Image* image, const mitk::Image* mask = nullptr, const mitk::PlanarFigure* maskPlanarFigure = nullptr); void ComputeAndDisplayIntensityProfile(mitk::Image * image, mitk::PlanarFigure* maskPlanarFigure); void FillHistogramWidget(const std::vector &histogram, const std::vector &dataLabels); QmitkChartWidget::ColorTheme GetColorTheme() const; void ResetGUI(); void ResetGUIDefault(); void OnStatisticsCalculationEnds(); void OnRequestHistogramUpdate(unsigned int); void OnCheckBoxIgnoreZeroStateChanged(int state); void OnButtonSelectionPressed(); // member variable Ui::QmitkImageStatisticsViewControls m_Controls; private: void HandleExistingStatistics(mitk::Image::ConstPointer, mitk::BaseData::ConstPointer, mitk::ImageStatisticsContainer::Pointer); std::string GenerateStatisticsNodeName(mitk::Image::ConstPointer, mitk::BaseData::ConstPointer); void SetupRelationRules(mitk::Image::ConstPointer, mitk::BaseData::ConstPointer, mitk::ImageStatisticsContainer::Pointer); mitk::DataNode::Pointer GetNodeForStatisticsContainer(mitk::ImageStatisticsContainer::ConstPointer container); QmitkNodeSelectionDialog::SelectionCheckFunctionType CheckForSameGeometry() const; - bool CheckForSameGeometry(const mitk::Image* image, const mitk::BaseData* data) const; typedef itk::SimpleMemberCommand ITKCommandType; mitk::DataNode::ConstPointer m_selectedImageNode = nullptr; mitk::DataNode::ConstPointer m_selectedMaskNode = nullptr; mitk::PlanarFigure::Pointer m_selectedPlanarFigure = nullptr; long m_PlanarFigureObserverTag; bool m_ForceRecompute = false; bool m_IgnoreZeroValueVoxel = false; std::vector m_selectedMaskNodes; std::vector m_selectedImageNodes; QmitkNodeSelectionDialog::NodeList m_SelectedNodeList; std::vector m_StatisticsForSelection; std::vector m_Runnables; }; #endif // QMITKIMAGESTATISTICSVIEW_H