Invalid selection.
The number of selected nodes must be uneven! the current number is " << nodes.size() << ".
";
+ ss << "Invalid selection.
The number of selected nodes must be uneven! the current number is " << nodes.size() << ".
";
return ss.str();
}
return std::string();
};
multNodeSelectionWidget->SetSelectionCheckFunction(checkFunction);
\endcode
This check function will be passed to the QmitkNodeSelectionDialog and is used to print an error message, if an invalid selection is made inside the node selection dialog.
This will prevent the user from confirming an even selection. If a valid selection is made no error message will be printed and the user can confirm the selection.
\b QmitkNodeSelectionButton
The QmitkNodeSelectionButton is a customized QPushButton and is used for example inside the QmitkSingleNodeSelectionWidget: It displays the thumbnail or icon of the selected data node (if available) along with an HTML text of the data node info.
\section DataStorageViewerTestPlugin The DataStorageViewerTest plugin
The \a org.mitk.gui.qt.datastorageviewertest plugin can be enabled and used inside the MITK workbench. It serves as a test / demo plugin with a single view that shows different uses of the new selection concept and its related classes. The following classes are used:
-# Top row: A QmitkDataStorageListInspector
-# Top right: A QmitkDataStorageTreeInspector
-# Bottom left: A QmitkSingleNodeSelectionWidget
-# Bottom right: A QmitkMultiNodeSelectionWidget
The top row presents the two examples of an implementation of the QmitkAbstractDataStorageInspector. They display the whole content of the given data storage in two different ways. No node predicates are used in this case.
Both inspectors come with two checkboxes to set each inspectors as a selection provider and a selection listener.
With these checkboxes turned on the QmitkSelectionServiceConnector for each inspector is connected to the QmitkModelViewSelectionConnector of each inspector (as shown in \ref QmitkSelectionServiceConnector_ref "QmitkSelectionServiceConnector").
See how the selection also changes in the inspectors if nodes are selected in the mitk::DataManager and the Set as selection listener-checkbox is checked for the inspector.
The bottom row presents the two examples of an implementation of the QmitkAbstractNodeSelectionWidget: They display a specific data node selection that was made by the user.
Both widgets come with several checkboxes to modify the behavior of the selection widgets.
*/
diff --git a/Modules/AlgorithmsExt/include/mitkMaskAndCutRoiImageFilter.h b/Modules/AlgorithmsExt/include/mitkMaskAndCutRoiImageFilter.h
index be17583bbb..195fa6675b 100644
--- a/Modules/AlgorithmsExt/include/mitkMaskAndCutRoiImageFilter.h
+++ b/Modules/AlgorithmsExt/include/mitkMaskAndCutRoiImageFilter.h
@@ -1,77 +1,72 @@
/*============================================================================
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 mitkMaskAndCutRoiImageFilter_h
#define mitkMaskAndCutRoiImageFilter_h
#include "MitkAlgorithmsExtExports.h"
#include "mitkImageToImageFilter.h"
#include "itkRegionOfInterestImageFilter.h"
#include "mitkAutoCropImageFilter.h"
#include "mitkBoundingObject.h"
#include "mitkDataNode.h"
#include "mitkMaskImageFilter.h"
namespace mitk
{
/**
\brief Cuts a region of interest (ROI) out of an image
In the first step, this filter reduces the image region of the given ROI to a minimum. Using this region, a subvolume
ist cut out of the given input image.
The ROI is then used to mask the subvolume. Pixel inside the ROI will have their original value, pixel outside will be
replaced by m_OutsideValue
*/
class MITKALGORITHMSEXT_EXPORT MaskAndCutRoiImageFilter : public ImageToImageFilter
{
typedef itk::Image ItkImageType;
typedef itk::Image ItkMaskType;
typedef itk::ImageRegion<3> RegionType;
typedef itk::RegionOfInterestImageFilter ROIFilterType;
public:
mitkClassMacro(MaskAndCutRoiImageFilter, ImageToImageFilter);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
- itkGetMacro(MaxValue, mitk::ScalarType);
- itkGetMacro(MinValue, mitk::ScalarType);
void SetRegionOfInterest(mitk::BaseData *roi);
// void SetRegionOfInterest(Image::Pointer image);
// void SetRegionOfInterest(BoundingObject::Pointer boundingObject);
// void SetRegionOfInterestByNode(mitk::DataNode::Pointer node);
// temporary fix for bug #
mitk::Image::Pointer GetOutput();
protected:
MaskAndCutRoiImageFilter();
~MaskAndCutRoiImageFilter() override;
void GenerateData() override;
ROIFilterType::Pointer m_RoiFilter;
mitk::AutoCropImageFilter::Pointer m_CropFilter;
mitk::MaskImageFilter::Pointer m_MaskFilter;
// needed for temporary fix
mitk::Image::Pointer m_outputImage;
- mitk::ScalarType m_MaxValue;
- mitk::ScalarType m_MinValue;
-
}; // class
} // namespace
#endif
diff --git a/Modules/AlgorithmsExt/include/mitkMaskImageFilter.h b/Modules/AlgorithmsExt/include/mitkMaskImageFilter.h
index ced7d4cb2b..9a8e1b1482 100644
--- a/Modules/AlgorithmsExt/include/mitkMaskImageFilter.h
+++ b/Modules/AlgorithmsExt/include/mitkMaskImageFilter.h
@@ -1,111 +1,93 @@
/*============================================================================
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 mitkMaskImageFilter_h
#define mitkMaskImageFilter_h
#include "MitkAlgorithmsExtExports.h"
#include "mitkCommon.h"
-#include "mitkImageTimeSelector.h"
#include "mitkImageToImageFilter.h"
#include "itkImage.h"
namespace mitk
{
//##Documentation
//## @brief
//## @ingroup Process
class MITKALGORITHMSEXT_EXPORT MaskImageFilter : public ImageToImageFilter
{
public:
mitkClassMacro(MaskImageFilter, ImageToImageFilter);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
- void SetMask(const mitk::Image *mask);
- const mitk::Image *GetMask() const;
-
- /**
- * get/set the min Value of the original image in the masked area
- **/
- itkGetMacro(MinValue, mitk::ScalarType);
- itkSetMacro(MinValue, mitk::ScalarType);
-
- /**
- * get/set the max Value of the original image in the masked area
- **/
- itkGetMacro(MaxValue, mitk::ScalarType);
- itkSetMacro(MaxValue, mitk::ScalarType);
+ void SetMask(const mitk::Image *mask);
+ const Image *GetMask() const;
+ Image* GetMask();
/**
* This value is used as outside value. This only works
* if OverrideOutsideValue is set to true. Default is 0.
**/
itkSetMacro(OutsideValue, mitk::ScalarType);
/**
* This value is used as outside value. This only works
* if OverrideOutsideValue is set to true. Default is 0.
*/
itkGetMacro(OutsideValue, mitk::ScalarType);
/**
* If OverrideOutsideValue is set to false, this minimum
* of the pixel type of the output image is taken as outside
* value. If set to true, the value set via SetOutsideValue is
* used as background.
*/
itkSetMacro(OverrideOutsideValue, bool);
/**
* If OverrideOutsideValue is set to false, this minimum
* of the pixel type of the output image is taken as outside
* value. If set to true, the value set via SetOutsideValue is
* used as background.
*/
itkGetMacro(OverrideOutsideValue, bool);
itkBooleanMacro(OverrideOutsideValue);
protected:
MaskImageFilter();
~MaskImageFilter() override;
void GenerateInputRequestedRegion() override;
void GenerateOutputInformation() override;
void GenerateData() override;
- template
- void InternalComputeMask(itk::Image *itkImage);
-
- mitk::Image::Pointer m_Mask;
- mitk::ImageTimeSelector::Pointer m_InputTimeSelector;
- mitk::ImageTimeSelector::Pointer m_MaskTimeSelector;
- mitk::ImageTimeSelector::Pointer m_OutputTimeSelector;
+ template
+ void InternalComputeMask(itk::Image* itkInput, itk::Image* itkMask);
//##Description
//## @brief Time when Header was last initialized
itk::TimeStamp m_TimeOfHeaderInitialization;
mitk::ScalarType m_OutsideValue;
- mitk::ScalarType m_MinValue;
- mitk::ScalarType m_MaxValue;
+ TimeStepType m_CurrentOutputTS;
bool m_OverrideOutsideValue;
};
} // namespace mitk
#endif
diff --git a/Modules/AlgorithmsExt/src/mitkMaskAndCutRoiImageFilter.cpp b/Modules/AlgorithmsExt/src/mitkMaskAndCutRoiImageFilter.cpp
index 255d1316e1..eb3490c4a7 100644
--- a/Modules/AlgorithmsExt/src/mitkMaskAndCutRoiImageFilter.cpp
+++ b/Modules/AlgorithmsExt/src/mitkMaskAndCutRoiImageFilter.cpp
@@ -1,92 +1,90 @@
/*============================================================================
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 "mitkMaskAndCutRoiImageFilter.h"
#include "mitkBoundingObjectToSegmentationFilter.h"
#include "mitkImageCast.h"
mitk::MaskAndCutRoiImageFilter::MaskAndCutRoiImageFilter()
{
this->SetNumberOfRequiredInputs(2);
m_CropFilter = mitk::AutoCropImageFilter::New();
m_RoiFilter = ROIFilterType::New();
m_MaskFilter = mitk::MaskImageFilter::New();
}
mitk::MaskAndCutRoiImageFilter::~MaskAndCutRoiImageFilter()
{
}
void mitk::MaskAndCutRoiImageFilter::SetRegionOfInterest(mitk::BaseData *roi)
{
mitk::Image::Pointer image = dynamic_cast(roi);
if (image.IsNotNull())
{
this->SetInput(1, image);
return;
}
mitk::BoundingObject::Pointer boundingObject = dynamic_cast(roi);
if (boundingObject.IsNotNull() && this->GetInput(0) != nullptr)
{
mitk::BoundingObjectToSegmentationFilter::Pointer filter = mitk::BoundingObjectToSegmentationFilter::New();
filter->SetBoundingObject(boundingObject);
filter->SetInput(this->GetInput(0));
filter->Update();
this->SetInput(1, filter->GetOutput());
return;
}
}
mitk::Image::Pointer mitk::MaskAndCutRoiImageFilter::GetOutput()
{
return m_outputImage;
}
void mitk::MaskAndCutRoiImageFilter::GenerateData()
{
mitk::Image::ConstPointer inputImage = this->GetInput(0);
mitk::Image::ConstPointer maskImage = this->GetInput(1);
// mitk::Image::Pointer outputImage = this->GetOutput();
// temporary fix for bug #
m_outputImage = this->GetOutput();
ItkImageType::Pointer itkImage = ItkImageType::New();
mitk::Image::Pointer tmpImage = mitk::Image::New();
m_CropFilter->SetInput(maskImage);
m_CropFilter->SetBackgroundValue(0);
m_CropFilter->Update();
RegionType region = m_CropFilter->GetCroppingRegion();
mitk::CastToItkImage(inputImage, itkImage);
m_RoiFilter->SetInput(itkImage);
m_RoiFilter->SetRegionOfInterest(region);
m_RoiFilter->Update();
mitk::CastToMitkImage(m_RoiFilter->GetOutput(), tmpImage);
m_MaskFilter->SetInput(0, tmpImage);
m_MaskFilter->SetMask(m_CropFilter->GetOutput());
m_MaskFilter->SetOutsideValue(-32765);
m_MaskFilter->Update();
- m_MaxValue = m_MaskFilter->GetMaxValue();
- m_MinValue = m_MaskFilter->GetMinValue();
// temporary fix for bug #
m_outputImage = m_MaskFilter->GetOutput();
m_outputImage->DisconnectPipeline();
}
diff --git a/Modules/AlgorithmsExt/src/mitkMaskImageFilter.cpp b/Modules/AlgorithmsExt/src/mitkMaskImageFilter.cpp
index c4d13bff6c..070345dbf0 100644
--- a/Modules/AlgorithmsExt/src/mitkMaskImageFilter.cpp
+++ b/Modules/AlgorithmsExt/src/mitkMaskImageFilter.cpp
@@ -1,241 +1,129 @@
/*============================================================================
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 "mitkMaskImageFilter.h"
#include "mitkImageTimeSelector.h"
#include "mitkProperties.h"
#include "mitkTimeHelper.h"
+#include "mitkImageTimeSelector.h"
#include "mitkImageAccessByItk.h"
#include "mitkImageToItk.h"
-#include "itkImageRegionConstIterator.h"
-#include "itkImageRegionIteratorWithIndex.h"
+#include "itkMaskImageFilter.h"
#include
-mitk::MaskImageFilter::MaskImageFilter() : m_Mask(nullptr)
+mitk::MaskImageFilter::MaskImageFilter()
{
this->SetNumberOfIndexedInputs(2);
this->SetNumberOfRequiredInputs(2);
- m_InputTimeSelector = mitk::ImageTimeSelector::New();
- m_MaskTimeSelector = mitk::ImageTimeSelector::New();
- m_OutputTimeSelector = mitk::ImageTimeSelector::New();
m_OverrideOutsideValue = false;
m_OutsideValue = 0;
+ m_CurrentOutputTS = 0;
}
mitk::MaskImageFilter::~MaskImageFilter()
{
}
void mitk::MaskImageFilter::SetMask(const mitk::Image *mask)
{
// Process object is not const-correct so the const_cast is required here
- m_Mask = const_cast(mask);
- this->ProcessObject::SetNthInput(1, m_Mask);
+ auto nonconstMask = const_cast(mask);
+ this->ProcessObject::SetNthInput(1, nonconstMask);
}
const mitk::Image *mitk::MaskImageFilter::GetMask() const
{
- return m_Mask;
+ return this->GetInput(1);
+}
+
+mitk::Image* mitk::MaskImageFilter::GetMask()
+{
+ return this->GetInput(1);
}
void mitk::MaskImageFilter::GenerateInputRequestedRegion()
{
Superclass::GenerateInputRequestedRegion();
mitk::Image *output = this->GetOutput();
mitk::Image *input = this->GetInput();
- mitk::Image *mask = m_Mask;
+ mitk::Image *mask = this->GetMask();
if ((output->IsInitialized() == false) || (mask == nullptr) || (mask->GetTimeGeometry()->CountTimeSteps() == 0))
return;
input->SetRequestedRegionToLargestPossibleRegion();
mask->SetRequestedRegionToLargestPossibleRegion();
GenerateTimeInInputRegion(output, input);
- GenerateTimeInInputRegion(output, mask);
}
void mitk::MaskImageFilter::GenerateOutputInformation()
{
mitk::Image::ConstPointer input = this->GetInput();
mitk::Image::Pointer output = this->GetOutput();
if ((output->IsInitialized()) && (this->GetMTime() <= m_TimeOfHeaderInitialization.GetMTime()))
return;
itkDebugMacro(<< "GenerateOutputInformation()");
output->Initialize(input->GetPixelType(), *input->GetTimeGeometry());
output->SetPropertyList(input->GetPropertyList()->Clone());
m_TimeOfHeaderInitialization.Modified();
}
-template
-void mitk::MaskImageFilter::InternalComputeMask(itk::Image *inputItkImage)
+template
+void mitk::MaskImageFilter::InternalComputeMask(itk::Image* itkInput, itk::Image* itkMask)
{
- // dirty quick fix, duplicating code so both unsigned char and unsigned short are supported
- // this should be changed once unsigned char segmentations can be converted to unsigned short
- mitk::PixelType pixelType =
- m_MaskTimeSelector->GetOutput()->GetImageDescriptor()->GetChannelDescriptor().GetPixelType();
- if (pixelType.GetComponentType() == itk::IOComponentEnum::UCHAR)
- {
- typedef itk::Image ItkInputImageType;
- typedef itk::Image ItkMaskImageType;
- typedef itk::Image ItkOutputImageType;
-
- typedef itk::ImageRegionConstIterator ItkInputImageIteratorType;
- typedef itk::ImageRegionConstIterator ItkMaskImageIteratorType;
- typedef itk::ImageRegionIteratorWithIndex ItkOutputImageIteratorType;
-
- typename mitk::ImageToItk::Pointer maskimagetoitk = mitk::ImageToItk::New();
- maskimagetoitk->SetInput(m_MaskTimeSelector->GetOutput());
- maskimagetoitk->Update();
- typename ItkMaskImageType::Pointer maskItkImage = maskimagetoitk->GetOutput();
-
- typename mitk::ImageToItk::Pointer outputimagetoitk =
- mitk::ImageToItk::New();
- outputimagetoitk->SetInput(m_OutputTimeSelector->GetOutput());
- outputimagetoitk->Update();
- typename ItkOutputImageType::Pointer outputItkImage = outputimagetoitk->GetOutput();
-
- // create the iterators
- typename ItkInputImageType::RegionType inputRegionOfInterest = inputItkImage->GetLargestPossibleRegion();
- ItkInputImageIteratorType inputIt(inputItkImage, inputRegionOfInterest);
- ItkMaskImageIteratorType maskIt(maskItkImage, inputRegionOfInterest);
- ItkOutputImageIteratorType outputIt(outputItkImage, inputRegionOfInterest);
-
- // typename ItkOutputImageType::PixelType outsideValue = itk::NumericTraits::min();
- if (!m_OverrideOutsideValue)
- m_OutsideValue = itk::NumericTraits::min();
-
- m_MinValue = std::numeric_limits::max();
- m_MaxValue = std::numeric_limits::min();
-
- for (inputIt.GoToBegin(), maskIt.GoToBegin(), outputIt.GoToBegin(); !inputIt.IsAtEnd() && !maskIt.IsAtEnd();
- ++inputIt, ++maskIt, ++outputIt)
- {
- if (maskIt.Get() > itk::NumericTraits::Zero)
- {
- outputIt.Set(inputIt.Get());
- m_MinValue = std::min((float)inputIt.Get(), (float)m_MinValue);
- m_MaxValue = std::max((float)inputIt.Get(), (float)m_MaxValue);
- }
- else
- {
- outputIt.Set(m_OutsideValue);
- }
- }
- }
- else
- {
- {
- typedef itk::Image ItkInputImageType;
- typedef itk::Image ItkMaskImageType;
- typedef itk::Image ItkOutputImageType;
-
- typedef itk::ImageRegionConstIterator ItkInputImageIteratorType;
- typedef itk::ImageRegionConstIterator ItkMaskImageIteratorType;
- typedef itk::ImageRegionIteratorWithIndex ItkOutputImageIteratorType;
-
- typename mitk::ImageToItk::Pointer maskimagetoitk = mitk::ImageToItk::New();
- maskimagetoitk->SetInput(m_MaskTimeSelector->GetOutput());
- maskimagetoitk->Update();
- typename ItkMaskImageType::Pointer maskItkImage = maskimagetoitk->GetOutput();
-
- typename mitk::ImageToItk::Pointer outputimagetoitk =
- mitk::ImageToItk::New();
- outputimagetoitk->SetInput(m_OutputTimeSelector->GetOutput());
- outputimagetoitk->Update();
- typename ItkOutputImageType::Pointer outputItkImage = outputimagetoitk->GetOutput();
-
- // create the iterators
- typename ItkInputImageType::RegionType inputRegionOfInterest = inputItkImage->GetLargestPossibleRegion();
- ItkInputImageIteratorType inputIt(inputItkImage, inputRegionOfInterest);
- ItkMaskImageIteratorType maskIt(maskItkImage, inputRegionOfInterest);
- ItkOutputImageIteratorType outputIt(outputItkImage, inputRegionOfInterest);
-
- // typename ItkOutputImageType::PixelType outsideValue = itk::NumericTraits::min();
- if (!m_OverrideOutsideValue)
- m_OutsideValue = itk::NumericTraits::min();
-
- m_MinValue = std::numeric_limits::max();
- m_MaxValue = std::numeric_limits::min();
-
- for (inputIt.GoToBegin(), maskIt.GoToBegin(), outputIt.GoToBegin(); !inputIt.IsAtEnd() && !maskIt.IsAtEnd();
- ++inputIt, ++maskIt, ++outputIt)
- {
- if (maskIt.Get() > itk::NumericTraits::Zero)
- {
- outputIt.Set(inputIt.Get());
- m_MinValue = std::min((float)inputIt.Get(), (float)m_MinValue);
- m_MaxValue = std::max((float)inputIt.Get(), (float)m_MaxValue);
- }
- else
- {
- outputIt.Set(m_OutsideValue);
- }
- }
- }
- }
+ using MaskFilterType = itk::MaskImageFilter< itk::Image, itk::Image>;
+
+ auto maskFilter = MaskFilterType::New();
+ maskFilter->SetInput(itkInput);
+ maskFilter->SetMaskImage(itkMask);
+ maskFilter->SetOutsideValue(m_OutsideValue);
+ maskFilter->Update();
+
+ auto output = maskFilter->GetOutput();
+
+ // re-import to MITK
+ auto mitkOutput = this->GetOutput();
+ mitkOutput->SetVolume(output->GetBufferPointer(), m_CurrentOutputTS);
}
void mitk::MaskImageFilter::GenerateData()
{
- mitk::Image::ConstPointer input = this->GetInput();
- mitk::Image::Pointer mask = m_Mask;
+ auto input = this->GetInput();
+ auto mask = this->GetMask();
mitk::Image::Pointer output = this->GetOutput();
- if ((output->IsInitialized() == false) || (mask.IsNull()) || (mask->GetTimeGeometry()->CountTimeSteps() == 0))
+ if ((output->IsInitialized() == false) || (nullptr == mask) || (mask->GetTimeGeometry()->CountTimeSteps() == 0))
return;
- m_InputTimeSelector->SetInput(input);
- m_MaskTimeSelector->SetInput(mask);
- m_OutputTimeSelector->SetInput(this->GetOutput());
-
- mitk::Image::RegionType outputRegion = output->GetRequestedRegion();
- const mitk::TimeGeometry *outputTimeGeometry = output->GetTimeGeometry();
const mitk::TimeGeometry *inputTimeGeometry = input->GetTimeGeometry();
- const mitk::TimeGeometry *maskTimeGeometry = mask->GetTimeGeometry();
- ScalarType timeInMS;
- int timestep = 0;
- int tstart = outputRegion.GetIndex(3);
- int tmax = tstart + outputRegion.GetSize(3);
-
- int t;
- for (t = tstart; t < tmax; ++t)
+ for (TimeStepType t = 0; t < inputTimeGeometry->CountTimeSteps(); ++t)
{
- timeInMS = outputTimeGeometry->TimeStepToTimePoint(t);
-
- timestep = inputTimeGeometry->TimePointToTimeStep(timeInMS);
-
- m_InputTimeSelector->SetTimeNr(timestep);
- m_InputTimeSelector->UpdateLargestPossibleRegion();
- m_OutputTimeSelector->SetTimeNr(t);
- m_OutputTimeSelector->UpdateLargestPossibleRegion();
-
- timestep = maskTimeGeometry->TimePointToTimeStep(timeInMS);
- m_MaskTimeSelector->SetTimeNr(timestep);
- m_MaskTimeSelector->UpdateLargestPossibleRegion();
+ auto timeInMS = inputTimeGeometry->TimeStepToTimePoint(t);
- AccessByItk(m_InputTimeSelector->GetOutput(), InternalComputeMask);
+ m_CurrentOutputTS = t;
+ auto inputFrame = SelectImageByTimePoint(input, timeInMS);
+ auto maskFrame = SelectImageByTimePoint(mask, timeInMS);
+ AccessTwoImagesFixedDimensionByItk(inputFrame, maskFrame, InternalComputeMask, 3);
}
m_TimeOfHeaderInitialization.Modified();
}
diff --git a/Modules/Core/src/DataManagement/mitkTemporoSpatialStringProperty.cpp b/Modules/Core/src/DataManagement/mitkTemporoSpatialStringProperty.cpp
index 4764497413..ac53fc9dd6 100644
--- a/Modules/Core/src/DataManagement/mitkTemporoSpatialStringProperty.cpp
+++ b/Modules/Core/src/DataManagement/mitkTemporoSpatialStringProperty.cpp
@@ -1,426 +1,425 @@
/*============================================================================
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 "mitkTemporoSpatialStringProperty.h"
#include
using CondensedTimeKeyType = std::pair;
using CondensedTimePointsType = std::map;
using CondensedSliceKeyType = std::pair;
using CondensedSlicesType = std::map;
namespace
{
/** Helper function that checks if between an ID and a successive ID is no gap.*/
template
bool isGap(const TValue& value, const TValue& successor)
{
return value successor + 1;
}
template
void CheckAndCondenseElement(const TNewKey& newKeyMinID, const TNewValue& newValue, TMasterKey& masterKey, TMasterValue& masterValue, TCondensedContainer& condensedContainer)
{
if (newValue != masterValue
|| isGap(newKeyMinID, masterKey.second))
{
condensedContainer[masterKey] = masterValue;
masterValue = newValue;
masterKey.first = newKeyMinID;
}
masterKey.second = newKeyMinID;
}
/** Helper function that tries to condense the values of time points for a slice as much as possible and returns all slices with condensed timepoint values.*/
CondensedSlicesType CondenseTimePointValuesOfProperty(const mitk::TemporoSpatialStringProperty* tsProp)
{
CondensedSlicesType uncondensedSlices;
auto zs = tsProp->GetAvailableSlices();
for (const auto z : zs)
{
CondensedTimePointsType condensedTimePoints;
auto timePointIDs = tsProp->GetAvailableTimeSteps(z);
CondensedTimeKeyType condensedKey = { timePointIDs.front(),timePointIDs.front() };
auto refValue = tsProp->GetValue(timePointIDs.front(), z);
for (const auto timePointID : timePointIDs)
{
const auto& newVal = tsProp->GetValue(timePointID, z);
CheckAndCondenseElement(timePointID, newVal, condensedKey, refValue, condensedTimePoints);
}
condensedTimePoints[condensedKey] = refValue;
uncondensedSlices[{ z, z }] = condensedTimePoints;
}
return uncondensedSlices;
}
}
mitk::TemporoSpatialStringProperty::TemporoSpatialStringProperty(const char *s)
{
if (s)
{
SliceMapType slices{{0, s}};
m_Values.insert(std::make_pair(0, slices));
}
}
mitk::TemporoSpatialStringProperty::TemporoSpatialStringProperty(const std::string &s)
{
SliceMapType slices{{0, s}};
m_Values.insert(std::make_pair(0, slices));
}
mitk::TemporoSpatialStringProperty::TemporoSpatialStringProperty(const TemporoSpatialStringProperty &other)
: BaseProperty(other), m_Values(other.m_Values)
{
}
bool mitk::TemporoSpatialStringProperty::IsEqual(const BaseProperty &property) const
{
return this->m_Values == static_cast(property).m_Values;
}
bool mitk::TemporoSpatialStringProperty::Assign(const BaseProperty &property)
{
this->m_Values = static_cast(property).m_Values;
return true;
}
std::string mitk::TemporoSpatialStringProperty::GetValueAsString() const
{
return GetValue();
}
bool mitk::TemporoSpatialStringProperty::IsUniform() const
{
auto refValue = this->GetValue();
for (const auto& timeStep : m_Values)
{
auto finding = std::find_if_not(timeStep.second.begin(), timeStep.second.end(), [&refValue](const mitk::TemporoSpatialStringProperty::SliceMapType::value_type& val) { return val.second == refValue; });
if (finding != timeStep.second.end())
{
return false;
}
}
return true;
}
itk::LightObject::Pointer mitk::TemporoSpatialStringProperty::InternalClone() const
{
itk::LightObject::Pointer result(new Self(*this));
result->UnRegister();
return result;
}
mitk::TemporoSpatialStringProperty::ValueType mitk::TemporoSpatialStringProperty::GetValue() const
{
std::string result = "";
if (!m_Values.empty())
{
if (!m_Values.begin()->second.empty())
{
result = m_Values.begin()->second.begin()->second;
}
}
return result;
};
std::pair mitk::TemporoSpatialStringProperty::CheckValue(
const TimeStepType &timeStep, const IndexValueType &zSlice, bool allowCloseTime, bool allowCloseSlice) const
{
std::string value = "";
bool found = false;
auto timeIter = m_Values.find(timeStep);
auto timeEnd = m_Values.end();
if (timeIter == timeEnd && allowCloseTime)
{ // search for closest time step (earlier preverd)
timeIter = m_Values.upper_bound(timeStep);
if (timeIter != m_Values.begin())
{ // there is a key lower than time step
timeIter = std::prev(timeIter);
}
}
if (timeIter != timeEnd)
{
const SliceMapType &slices = timeIter->second;
auto sliceIter = slices.find(zSlice);
auto sliceEnd = slices.end();
if (sliceIter == sliceEnd && allowCloseSlice)
{ // search for closest slice (earlier preverd)
sliceIter = slices.upper_bound(zSlice);
if (sliceIter != slices.begin())
{ // there is a key lower than slice
sliceIter = std::prev(sliceIter);
}
}
if (sliceIter != sliceEnd)
{
value = sliceIter->second;
found = true;
}
}
return std::make_pair(found, value);
};
mitk::TemporoSpatialStringProperty::ValueType mitk::TemporoSpatialStringProperty::GetValue(const TimeStepType &timeStep,
const IndexValueType &zSlice,
bool allowCloseTime,
bool allowCloseSlice) const
{
return CheckValue(timeStep, zSlice, allowCloseTime, allowCloseSlice).second;
};
mitk::TemporoSpatialStringProperty::ValueType mitk::TemporoSpatialStringProperty::GetValueBySlice(
const IndexValueType &zSlice, bool allowClose) const
{
return GetValue(0, zSlice, true, allowClose);
};
mitk::TemporoSpatialStringProperty::ValueType mitk::TemporoSpatialStringProperty::GetValueByTimeStep(
const TimeStepType &timeStep, bool allowClose) const
{
return GetValue(timeStep, 0, allowClose, true);
};
bool mitk::TemporoSpatialStringProperty::HasValue() const
{
return !m_Values.empty();
};
bool mitk::TemporoSpatialStringProperty::HasValue(const TimeStepType &timeStep,
const IndexValueType &zSlice,
bool allowCloseTime,
bool allowCloseSlice) const
{
return CheckValue(timeStep, zSlice, allowCloseTime, allowCloseSlice).first;
};
bool mitk::TemporoSpatialStringProperty::HasValueBySlice(const IndexValueType &zSlice, bool allowClose) const
{
return HasValue(0, zSlice, true, allowClose);
};
bool mitk::TemporoSpatialStringProperty::HasValueByTimeStep(const TimeStepType &timeStep, bool allowClose) const
{
return HasValue(timeStep, 0, allowClose, true);
};
std::vector mitk::TemporoSpatialStringProperty::GetAvailableSlices() const
{
std::set uniqueSlices;
for (const auto& timeStep : m_Values)
{
for (const auto& slice : timeStep.second)
{
uniqueSlices.insert(slice.first);
}
}
return std::vector(std::begin(uniqueSlices), std::end(uniqueSlices));
}
std::vector mitk::TemporoSpatialStringProperty::GetAvailableSlices(
const TimeStepType &timeStep) const
{
std::vector result;
auto timeIter = m_Values.find(timeStep);
auto timeEnd = m_Values.end();
if (timeIter != timeEnd)
{
for (auto const &element : timeIter->second)
{
result.push_back(element.first);
}
}
return result;
};
std::vector mitk::TemporoSpatialStringProperty::GetAvailableTimeSteps() const
{
std::vector result;
for (auto const &element : m_Values)
{
result.push_back(element.first);
}
return result;
};
std::vector mitk::TemporoSpatialStringProperty::GetAvailableTimeSteps(const IndexValueType& slice) const
{
std::vector result;
for (const auto& timeStep : m_Values)
{
if (timeStep.second.find(slice) != std::end(timeStep.second))
{
result.push_back(timeStep.first);
}
}
return result;
}
void mitk::TemporoSpatialStringProperty::SetValue(const TimeStepType &timeStep,
const IndexValueType &zSlice,
const ValueType &value)
{
auto timeIter = m_Values.find(timeStep);
auto timeEnd = m_Values.end();
if (timeIter == timeEnd)
{
SliceMapType slices{{zSlice, value}};
m_Values.insert(std::make_pair(timeStep, slices));
}
else
{
timeIter->second[zSlice] = value;
}
this->Modified();
};
void mitk::TemporoSpatialStringProperty::SetValue(const ValueType &value)
{
- this->Modified();
m_Values.clear();
this->SetValue(0, 0, value);
};
bool mitk::TemporoSpatialStringProperty::ToJSON(nlohmann::json& j) const
{
// We condense the content of the property to have a compact serialization.
// We start with condensing time points and then slices (in difference to the
// internal layout). Reason: There is more entropy in slices (looking at DICOM)
// than across time points for one slice, so we can "compress" at a higher rate.
// We didn't want to change the internal structure of the property as it would
// introduce API inconvenience and subtle changes in behavior.
auto uncondensedSlices = CondenseTimePointValuesOfProperty(this);
CondensedSlicesType condensedSlices;
if(!uncondensedSlices.empty())
{
auto& masterSlice = uncondensedSlices.begin()->second;
auto masterSliceKey = uncondensedSlices.begin()->first;
for (const auto& uncondensedSlice : uncondensedSlices)
{
const auto& uncondensedSliceID = uncondensedSlice.first.first;
CheckAndCondenseElement(uncondensedSliceID, uncondensedSlice.second, masterSliceKey, masterSlice, condensedSlices);
}
condensedSlices[masterSliceKey] = masterSlice;
}
auto values = nlohmann::json::array();
for (const auto& z : condensedSlices)
{
for (const auto& t : z.second)
{
const auto& minSliceID = z.first.first;
const auto& maxSliceID = z.first.second;
const auto& minTimePointID = t.first.first;
const auto& maxTimePointID = t.first.second;
auto value = nlohmann::json::object();
value["z"] = minSliceID;
if (minSliceID != maxSliceID)
value["zmax"] = maxSliceID;
value["t"] = minTimePointID;
if (minTimePointID != maxTimePointID)
value["tmax"] = maxTimePointID;
value["value"] = t.second;
values.push_back(value);
}
}
j = nlohmann::json{{"values", values}};
return true;
}
bool mitk::TemporoSpatialStringProperty::FromJSON(const nlohmann::json& j)
{
for (const auto& element : j["values"])
{
auto value = element.value("value", "");
auto z = element.value("z", 0);
auto zmax = element.value("zmax", z);
auto t = element.value("t", 0);
auto tmax = element.value("tmax", t);
for (auto currentT = t; currentT <= tmax; ++currentT)
{
for (auto currentZ = z; currentZ <= zmax; ++currentZ)
{
this->SetValue(currentT, currentZ, value);
}
}
}
return true;
}
std::string mitk::PropertyPersistenceSerialization::serializeTemporoSpatialStringPropertyToJSON(const mitk::BaseProperty *prop)
{
const auto *tsProp = dynamic_cast(prop);
if (!tsProp)
mitkThrow() << "Cannot serialize properties of types other than TemporoSpatialStringProperty.";
nlohmann::json j;
tsProp->ToJSON(j);
return j.dump();
}
mitk::BaseProperty::Pointer mitk::PropertyPersistenceDeserialization::deserializeJSONToTemporoSpatialStringProperty(const std::string &value)
{
if (value.empty())
return nullptr;
auto prop = mitk::TemporoSpatialStringProperty::New();
auto root = nlohmann::json::parse(value);
prop->FromJSON(root);
return prop.GetPointer();
}
diff --git a/Modules/Multilabel/files.cmake b/Modules/Multilabel/files.cmake
index 3253bf602b..cc5f29c1e5 100644
--- a/Modules/Multilabel/files.cmake
+++ b/Modules/Multilabel/files.cmake
@@ -1,22 +1,23 @@
set(CPP_FILES
mitkLabel.cpp
mitkLabelSetImage.cpp
mitkLabelSetImageConverter.cpp
mitkLabelSetImageSource.cpp
mitkLabelSetImageHelper.cpp
mitkLabelSetImageSurfaceStampFilter.cpp
mitkLabelSetImageToSurfaceFilter.cpp
mitkLabelSetImageToSurfaceThreadedFilter.cpp
mitkLabelSetImageVtkMapper2D.cpp
mitkMultilabelObjectFactory.cpp
mitkMultiLabelIOHelper.cpp
mitkMultiLabelEvents.cpp
mitkMultiLabelPredicateHelper.cpp
mitkDICOMSegmentationPropertyHelper.cpp
mitkDICOMSegmentationConstants.cpp
mitkSegmentationTaskList.cpp
+ mitkMultiLabelSegmentationVtkMapper3D.cpp
)
set(RESOURCE_FILES
)
diff --git a/Modules/Multilabel/mitkLabelSetImage.cpp b/Modules/Multilabel/mitkLabelSetImage.cpp
index c53eaec19d..bfb1fdb3b3 100644
--- a/Modules/Multilabel/mitkLabelSetImage.cpp
+++ b/Modules/Multilabel/mitkLabelSetImage.cpp
@@ -1,1719 +1,1722 @@
/*============================================================================
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 "mitkLabelSetImage.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
namespace mitk
{
template
void ClearBufferProcessing(ImageType* itkImage)
{
itkImage->FillBuffer(0);
}
void ClearImageBuffer(mitk::Image* image)
{
if (image->GetDimension() == 4)
{ //remark: this extra branch was added, because LabelSetImage instances can be
//dynamic (4D), but AccessByItk by support only supports 2D and 3D.
//The option to change the CMake default dimensions for AccessByItk was
//dropped (for details see discussion in T28756)
AccessFixedDimensionByItk(image, ClearBufferProcessing, 4);
}
else
{
AccessByItk(image, ClearBufferProcessing);
}
}
}
const mitk::LabelSetImage::LabelValueType mitk::LabelSetImage::UNLABELED_VALUE = 0;
mitk::LabelSetImage::LabelSetImage()
: mitk::Image(), m_ActiveLabelValue(0), m_UnlabeledLabelLock(false), m_ActiveLayer(0), m_activeLayerInvalid(false)
{
m_LookupTable = mitk::LookupTable::New();
m_LookupTable->SetType(mitk::LookupTable::MULTILABEL);
// Add some DICOM Tags as properties to segmentation image
DICOMSegmentationPropertyHelper::DeriveDICOMSegmentationProperties(this);
}
mitk::LabelSetImage::LabelSetImage(const mitk::LabelSetImage &other)
: Image(other),
m_ActiveLabelValue(other.m_ActiveLabelValue),
m_LookupTable(other.m_LookupTable->Clone()),
m_UnlabeledLabelLock(other.m_UnlabeledLabelLock),
m_ActiveLayer(other.GetActiveLayer()),
m_activeLayerInvalid(false)
{
GroupIndexType i = 0;
for (auto groupImage : other.m_LayerContainer)
{
this->AddLayer(groupImage->Clone(), other.GetConstLabelsByValue(other.GetLabelValuesByGroup(i)));
i++;
}
m_Groups = other.m_Groups;
// Add some DICOM Tags as properties to segmentation image
DICOMSegmentationPropertyHelper::DeriveDICOMSegmentationProperties(this);
}
void mitk::LabelSetImage::Initialize(const mitk::Image *other)
{
mitk::PixelType pixelType(mitk::MakeScalarPixelType());
if (other->GetDimension() == 2)
{
const unsigned int dimensions[] = {other->GetDimension(0), other->GetDimension(1), 1};
Superclass::Initialize(pixelType, 3, dimensions);
}
else
{
Superclass::Initialize(pixelType, other->GetDimension(), other->GetDimensions());
}
auto originalGeometry = other->GetTimeGeometry()->Clone();
this->SetTimeGeometry(originalGeometry);
// initialize image memory to zero
ClearImageBuffer(this);
// Transfer some general DICOM properties from the source image to derived image (e.g. Patient information,...)
DICOMQIPropertyHelper::DeriveDICOMSourceProperties(other, this);
// Add a inital LabelSet ans corresponding image data to the stack
if (this->GetNumberOfLayers() == 0)
{
AddLayer();
}
}
mitk::LabelSetImage::~LabelSetImage()
{
for (auto [value, label] : m_LabelMap)
{
this->ReleaseLabel(label);
}
m_LabelMap.clear();
}
unsigned int mitk::LabelSetImage::GetActiveLayer() const
{
if (m_LayerContainer.size() == 0) mitkThrow() << "Cannot return active layer index. No layer is available.";
return m_ActiveLayer;
}
unsigned int mitk::LabelSetImage::GetNumberOfLayers() const
{
return m_LayerContainer.size();
}
void mitk::LabelSetImage::RemoveGroup(GroupIndexType indexToDelete)
{
if (!this->ExistGroup(indexToDelete)) mitkThrow() << "Cannot remove group. Group does not exist. Invalid group index: "<GetNumberOfLayers() == 1)
{ //last layer is about to be deleted
newActiveIndex = 0;
}
else
{
//we have to add/subtract one more because we have not removed the layer yet, thus the group count is to 1 high.
newActiveIndex = indexToDelete+1 < GetNumberOfLayers() ? indexToDelete : GetNumberOfLayers() - 2;
newActiveIndexBeforeDeletion = indexToDelete + 1 < GetNumberOfLayers() ? indexToDelete+1 : indexToDelete -1;
}
}
if (activeIndex == indexToDelete)
{
// we are deleting the active layer, it should not be copied back into the vector
m_activeLayerInvalid = true;
//copy the image content of the upcoming new active layer;
SetActiveLayer(newActiveIndexBeforeDeletion);
}
auto relevantLabels = m_GroupToLabelMap[indexToDelete];
{
std::lock_guard guard(m_LabelNGroupMapsMutex);
// remove labels of group
for (auto labelValue : relevantLabels)
{
auto label = m_LabelMap[labelValue];
this->ReleaseLabel(label);
m_LabelToGroupMap.erase(labelValue);
m_LabelMap.erase(labelValue);
this->InvokeEvent(LabelRemovedEvent(labelValue));
}
// remove the group entries in the maps and the image.
m_Groups.erase(m_Groups.begin() + indexToDelete);
m_GroupToLabelMap.erase(m_GroupToLabelMap.begin() + indexToDelete);
m_LayerContainer.erase(m_LayerContainer.begin() + indexToDelete);
}
//update old indexes in m_GroupToLabelMap to new layer indexes
for (auto& element : m_LabelToGroupMap)
{
if (element.second > indexToDelete) element.second = element.second -1;
}
//correct active layer index
m_ActiveLayer = newActiveIndex;
this->InvokeEvent(LabelsChangedEvent(relevantLabels));
this->InvokeEvent(GroupRemovedEvent(indexToDelete));
this->Modified();
}
mitk::LabelSetImage::LabelValueVectorType mitk::LabelSetImage::ExtractLabelValuesFromLabelVector(const LabelVectorType& labels)
{
LabelValueVectorType result;
for (auto label : labels)
{
result.emplace_back(label->GetValue());
}
return result;
}
mitk::LabelSetImage::LabelValueVectorType mitk::LabelSetImage::ExtractLabelValuesFromLabelVector(const ConstLabelVectorType& labels)
{
LabelValueVectorType result;
for (auto label : labels)
{
result.emplace_back(label->GetValue());
}
return result;
}
mitk::LabelSetImage::ConstLabelVectorType mitk::LabelSetImage::ConvertLabelVectorConst(const LabelVectorType& labels)
{
ConstLabelVectorType result(labels.begin(), labels.end());
return result;
};
const mitk::LabelSetImage::LabelValueVectorType mitk::LabelSetImage::GetAllLabelValues() const
{
LabelValueVectorType result;
for (auto [value, label] : m_LabelMap)
{
result.emplace_back(value);
}
return result;
}
mitk::LabelSetImage::LabelValueVectorType mitk::LabelSetImage::GetUsedLabelValues() const
{
LabelValueVectorType result = { UNLABELED_VALUE };
for (auto [value, label] : m_LabelMap)
{
result.emplace_back(value);
}
return result;
}
mitk::LabelSetImage::GroupIndexType mitk::LabelSetImage::AddLayer(ConstLabelVector labels)
{
mitk::Image::Pointer newImage = mitk::Image::New();
newImage->Initialize(this->GetPixelType(),
this->GetDimension(),
this->GetDimensions(),
this->GetImageDescriptor()->GetNumberOfChannels());
newImage->SetTimeGeometry(this->GetTimeGeometry()->Clone());
ClearImageBuffer(newImage);
return this->AddLayer(newImage, labels);
}
mitk::LabelSetImage::GroupIndexType mitk::LabelSetImage::AddLayer(mitk::Image* layerImage, ConstLabelVector labels)
{
GroupIndexType newGroupID = m_Groups.size();
if (nullptr == layerImage)
mitkThrow() << "Cannot add group. Passed group image is nullptr.";
bool equalGeometries = Equal(
*(this->GetTimeGeometry()),
*(layerImage->GetTimeGeometry()),
NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION,
NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_DIRECTION_PRECISION,
false);
if (!equalGeometries)
mitkThrow() << "Cannot add group. Passed group image has not the same geometry like segmentation.";
if (layerImage->GetPixelType() != MakePixelType())
mitkThrow() << "Cannot add group. Passed group image has incorrect pixel type. Only LabelValueType is supported. Invalid pixel type: "<< layerImage->GetPixelType().GetTypeAsString();
// push a new working image for the new layer
m_LayerContainer.push_back(layerImage);
m_Groups.push_back("");
m_GroupToLabelMap.push_back({});
for (auto label : labels)
{
if (m_LabelMap.end() != m_LabelMap.find(label->GetValue()))
{
mitkThrow() << "Cannot add layer. Labels that should be added with layer use at least one label value that is already in use. Conflicted label value: " << label->GetValue();
}
auto labelClone = label->Clone();
DICOMSegmentationPropertyHelper::SetDICOMSegmentProperties(labelClone);
this->AddLabelToMap(labelClone->GetValue(), labelClone, newGroupID);
this->RegisterLabel(labelClone);
}
this->Modified();
this->InvokeEvent(GroupAddedEvent(newGroupID));
return newGroupID;
}
void mitk::LabelSetImage::ReplaceGroupLabels(const GroupIndexType groupID, const ConstLabelVectorType& labelSet)
{
if (m_LayerContainer.size() <= groupID)
{
mitkThrow() << "Trying to replace labels of non-existing group. Invalid group id: "< guard(m_LabelNGroupMapsMutex);
oldLabels = this->m_GroupToLabelMap[groupID];
for (auto labelID : oldLabels)
{
this->RemoveLabelFromMap(labelID);
this->InvokeEvent(LabelRemovedEvent(labelID));
}
}
this->InvokeEvent(LabelsChangedEvent(oldLabels));
this->InvokeEvent(GroupModifiedEvent(groupID));
//add new labels to group
for (auto label : labelSet)
{
this->AddLabel(label->Clone(), groupID, true, false);
}
}
void mitk::LabelSetImage::ReplaceGroupLabels(const GroupIndexType groupID, const LabelVectorType& labelSet)
{
return ReplaceGroupLabels(groupID, ConvertLabelVectorConst(labelSet));
}
mitk::Image* mitk::LabelSetImage::GetGroupImage(GroupIndexType groupID)
{
if (!this->ExistGroup(groupID)) mitkThrow() << "Error, cannot return group image. Group ID is invalid. Invalid ID: " << groupID;
return groupID == this->GetActiveLayer() ? this : m_LayerContainer[groupID];
}
const mitk::Image* mitk::LabelSetImage::GetGroupImage(GroupIndexType groupID) const
{
if (!this->ExistGroup(groupID)) mitkThrow() << "Error, cannot return group image. Group ID is invalid. Invalid ID: " << groupID;
return groupID == this->GetActiveLayer() ? this : m_LayerContainer.at(groupID).GetPointer();
}
const mitk::Image* mitk::LabelSetImage::GetGroupImageWorkaround(GroupIndexType groupID) const
{
if (!this->ExistGroup(groupID))
mitkThrow() << "Error, cannot return group image. Group ID is invalid. Invalid ID: " << groupID;
if (groupID == this->GetActiveLayer() && this->GetMTime()> m_LayerContainer[groupID]->GetMTime())
{ //we have to transfer the content first into the group image
if (4 == this->GetDimension())
{
AccessFixedDimensionByItk_n(this, ImageToLayerContainerProcessing, 4, (groupID));
}
else
{
AccessByItk_1(this, ImageToLayerContainerProcessing, groupID);
}
}
return m_LayerContainer[groupID].GetPointer();
}
void mitk::LabelSetImage::SetActiveLayer(unsigned int layer)
{
try
{
if (4 == this->GetDimension())
{
if ((layer != GetActiveLayer() || m_activeLayerInvalid) && (layer < this->GetNumberOfLayers()))
{
BeforeChangeLayerEvent.Send();
if (m_activeLayerInvalid)
{
// We should not write the invalid layer back to the vector
m_activeLayerInvalid = false;
}
else
{
AccessFixedDimensionByItk_n(this, ImageToLayerContainerProcessing, 4, (GetActiveLayer()));
}
m_ActiveLayer = layer;
AccessFixedDimensionByItk_n(this, LayerContainerToImageProcessing, 4, (GetActiveLayer()));
AfterChangeLayerEvent.Send();
}
}
else
{
if ((layer != GetActiveLayer() || m_activeLayerInvalid) && (layer < this->GetNumberOfLayers()))
{
BeforeChangeLayerEvent.Send();
if (m_activeLayerInvalid)
{
// We should not write the invalid layer back to the vector
m_activeLayerInvalid = false;
}
else
{
AccessByItk_1(this, ImageToLayerContainerProcessing, GetActiveLayer());
}
m_ActiveLayer = layer;
AccessByItk_1(this, LayerContainerToImageProcessing, GetActiveLayer());
AfterChangeLayerEvent.Send();
}
}
}
catch (itk::ExceptionObject &e)
{
mitkThrow() << e.GetDescription();
}
this->Modified();
}
void mitk::LabelSetImage::SetActiveLabel(LabelValueType label)
{
m_ActiveLabelValue = label;
if (label != UNLABELED_VALUE)
{
auto groupID = this->GetGroupIndexOfLabel(label);
if (groupID!=this->GetActiveLayer()) this->SetActiveLayer(groupID);
}
Modified();
}
void mitk::LabelSetImage::ClearBuffer()
{
try
{
ClearImageBuffer(this);
this->Modified();
}
catch (itk::ExceptionObject &e)
{
mitkThrow() << e.GetDescription();
}
}
void mitk::LabelSetImage::MergeLabel(PixelType pixelValue, PixelType sourcePixelValue)
{
try
{
AccessByItk_2(this, MergeLabelProcessing, pixelValue, sourcePixelValue);
}
catch (itk::ExceptionObject &e)
{
mitkThrow() << e.GetDescription();
}
this->SetActiveLabel(pixelValue);
this->InvokeEvent(LabelModifiedEvent(sourcePixelValue));
this->InvokeEvent(LabelModifiedEvent(pixelValue));
this->InvokeEvent(LabelsChangedEvent({ sourcePixelValue, pixelValue }));
Modified();
}
void mitk::LabelSetImage::MergeLabels(PixelType pixelValue, const std::vector& vectorOfSourcePixelValues)
{
try
{
for (unsigned int idx = 0; idx < vectorOfSourcePixelValues.size(); idx++)
{
AccessByItk_2(this, MergeLabelProcessing, pixelValue, vectorOfSourcePixelValues[idx]);
this->InvokeEvent(LabelModifiedEvent(vectorOfSourcePixelValues[idx]));
}
}
catch (itk::ExceptionObject &e)
{
mitkThrow() << e.GetDescription();
}
this->SetActiveLabel(pixelValue);
this->InvokeEvent(LabelModifiedEvent(pixelValue));
auto modifiedValues = vectorOfSourcePixelValues;
modifiedValues.push_back(pixelValue);
this->InvokeEvent(LabelsChangedEvent(modifiedValues));
Modified();
}
void mitk::LabelSetImage::RemoveLabel(LabelValueType pixelValue)
{
GroupIndexType groupID = 0;
{
std::lock_guard guard(m_LabelNGroupMapsMutex);
if (m_LabelMap.find(pixelValue) == m_LabelMap.end()) return;
groupID = this->GetGroupIndexOfLabel(pixelValue);
//first erase the pixel content (also triggers a LabelModified event)
this->EraseLabel(pixelValue);
this->RemoveLabelFromMap(pixelValue);
if (m_ActiveLabelValue == pixelValue)
{
this->SetActiveLabel(0);
}
}
this->InvokeEvent(LabelRemovedEvent(pixelValue));
this->InvokeEvent(LabelsChangedEvent({ pixelValue }));
this->InvokeEvent(GroupModifiedEvent(groupID));
}
void mitk::LabelSetImage::RemoveLabelFromMap(LabelValueType pixelValue)
{
if (m_LabelMap.find(pixelValue) == m_LabelMap.end()) mitkThrow()<<"Invalid state of instance. RemoveLabelFromMap was called for unknown label id. invalid label id: "<GetGroupIndexOfLabel(pixelValue);
this->ReleaseLabel(m_LabelMap[pixelValue]);
//now remove the label entry itself
m_LabelMap.erase(pixelValue);
m_LabelToGroupMap.erase(pixelValue);
auto labelsInGroup = m_GroupToLabelMap[groupID];
labelsInGroup.erase(std::remove(labelsInGroup.begin(), labelsInGroup.end(), pixelValue), labelsInGroup.end());
m_GroupToLabelMap[groupID] = labelsInGroup;
}
void mitk::LabelSetImage::RemoveLabels(const LabelValueVectorType& vectorOfLabelPixelValues)
{
for (const auto labelValue : vectorOfLabelPixelValues)
{
this->RemoveLabel(labelValue);
}
this->InvokeEvent(LabelsChangedEvent(vectorOfLabelPixelValues));
}
void mitk::LabelSetImage::EraseLabel(LabelValueType pixelValue)
{
try
{
auto groupID = this->GetGroupIndexOfLabel(pixelValue);
mitk::Image* groupImage = this->GetGroupImage(groupID);
if (4 == this->GetDimension())
{
AccessFixedDimensionByItk_1(groupImage, EraseLabelProcessing, 4, pixelValue);
}
else
{
AccessByItk_1(groupImage, EraseLabelProcessing, pixelValue);
}
}
catch (const itk::ExceptionObject& e)
{
mitkThrow() << e.GetDescription();
}
this->InvokeEvent(LabelModifiedEvent(pixelValue));
this->InvokeEvent(LabelsChangedEvent({ pixelValue }));
Modified();
}
void mitk::LabelSetImage::EraseLabels(const LabelValueVectorType& labelValues)
{
for (auto labelValue : labelValues)
{
this->EraseLabel(labelValue);
}
}
mitk::LabelSetImage::LabelValueType mitk::LabelSetImage::GetUnusedLabelValue() const
{
auto usedValues = this->GetUsedLabelValues();
return usedValues.back() + 1;
}
mitk::Label* mitk::LabelSetImage::AddLabel(mitk::Label* label, GroupIndexType groupID, bool addAsClone, bool correctLabelValue)
{
if (nullptr == label) mitkThrow() << "Invalid use of AddLabel. label is not valid.";
mitk::Label::Pointer newLabel = label;
{
std::lock_guard guard(m_LabelNGroupMapsMutex);
unsigned int max_size = mitk::Label::MAX_LABEL_VALUE + 1;
if (m_LayerContainer.size() >= max_size)
return nullptr;
if (addAsClone) newLabel = label->Clone();
auto pixelValue = newLabel->GetValue();
auto usedValues = this->GetUsedLabelValues();
auto finding = std::find(usedValues.begin(), usedValues.end(), pixelValue);
if (!usedValues.empty() && usedValues.end() != finding)
{
if (correctLabelValue)
{
pixelValue = this->GetUnusedLabelValue();
newLabel->SetValue(pixelValue);
}
else
{
mitkThrow() << "Cannot add label due to conflicting label value that already exists in the MultiLabelSegmentation. Conflicting label value: " << pixelValue;
}
}
// add DICOM information of the label
DICOMSegmentationPropertyHelper::SetDICOMSegmentProperties(newLabel);
this->AddLabelToMap(pixelValue, newLabel, groupID);
this->RegisterLabel(newLabel);
}
this->InvokeEvent(LabelAddedEvent(newLabel->GetValue()));
m_ActiveLabelValue = newLabel->GetValue();
this->Modified();
return newLabel;
}
mitk::Label* mitk::LabelSetImage::AddLabelWithContent(Label* label, const Image* labelContent, GroupIndexType groupID, LabelValueType contentLabelValue, bool addAsClone, bool correctLabelValue)
{
if (nullptr == labelContent) mitkThrow() << "Invalid use of AddLabel. labelContent is not valid.";
if (!Equal(*(this->GetTimeGeometry()), *(labelContent->GetTimeGeometry()), mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_DIRECTION_PRECISION))
mitkThrow() << "Invalid use of AddLabel. labelContent has not the same geometry like the segmentation.";
auto newLabel = this->AddLabel(label, groupID, addAsClone, correctLabelValue);
mitk::TransferLabelContent(labelContent, this->GetGroupImage(groupID), this->GetConstLabelsByValue(this->GetLabelValuesByGroup(groupID)),
mitk::LabelSetImage::UNLABELED_VALUE, mitk::LabelSetImage::UNLABELED_VALUE, false, { {contentLabelValue, newLabel->GetValue()}},
mitk::MultiLabelSegmentation::MergeStyle::Replace, mitk::MultiLabelSegmentation::OverwriteStyle::RegardLocks);
this->Modified();
return newLabel;
}
mitk::Label* mitk::LabelSetImage::AddLabel(const std::string& name, const mitk::Color& color, GroupIndexType groupID)
{
mitk::Label::Pointer newLabel = mitk::Label::New();
newLabel->SetName(name);
newLabel->SetColor(color);
return AddLabel(newLabel,groupID,false);
}
void mitk::LabelSetImage::RenameLabel(LabelValueType pixelValue, const std::string& name, const mitk::Color& color)
{
std::shared_lock guard(m_LabelNGroupMapsMutex);
mitk::Label* label = GetLabel(pixelValue);
if (nullptr == label) mitkThrow() << "Cannot rename label.Unknown label value provided. Unknown label value:" << pixelValue;
label->SetName(name);
label->SetColor(color);
this->UpdateLookupTable(pixelValue);
// change DICOM information of the label
DICOMSegmentationPropertyHelper::SetDICOMSegmentProperties(label);
}
mitk::Label *mitk::LabelSetImage::GetActiveLabel()
{
if (m_ActiveLabelValue == UNLABELED_VALUE) return nullptr;
auto finding = m_LabelMap.find(m_ActiveLabelValue);
return finding == m_LabelMap.end() ? nullptr : finding->second;
}
const mitk::Label* mitk::LabelSetImage::GetActiveLabel() const
{
if (m_ActiveLabelValue == UNLABELED_VALUE) return nullptr;
auto finding = m_LabelMap.find(m_ActiveLabelValue);
return finding == m_LabelMap.end() ? nullptr : finding->second;
}
void mitk::LabelSetImage::UpdateCenterOfMass(PixelType pixelValue)
{
if (4 == this->GetDimension())
{
AccessFixedDimensionByItk_1(this->GetGroupImage(this->GetGroupIndexOfLabel(pixelValue)), CalculateCenterOfMassProcessing, 4, pixelValue);
}
else
{
AccessByItk_1(this->GetGroupImage(this->GetGroupIndexOfLabel(pixelValue)), CalculateCenterOfMassProcessing, pixelValue);
}
}
void mitk::LabelSetImage::SetLookupTable(mitk::LookupTable* lut)
{
m_LookupTable = lut;
this->Modified();
}
void mitk::LabelSetImage::UpdateLookupTable(PixelType pixelValue)
{
const mitk::Color& color = this->GetLabel(pixelValue)->GetColor();
double rgba[4];
m_LookupTable->GetTableValue(static_cast(pixelValue), rgba);
rgba[0] = color.GetRed();
rgba[1] = color.GetGreen();
rgba[2] = color.GetBlue();
if (GetLabel(pixelValue)->GetVisible())
rgba[3] = GetLabel(pixelValue)->GetOpacity();
else
rgba[3] = 0.0;
m_LookupTable->SetTableValue(static_cast(pixelValue), rgba);
}
unsigned int mitk::LabelSetImage::GetNumberOfLabels(unsigned int layer) const
{
if (layer >= m_Groups.size()) mitkThrow() << "Cannot get number of labels in group. Group is unknown. Invalid index:" << layer;
return m_GroupToLabelMap[layer].size();
}
unsigned int mitk::LabelSetImage::GetTotalNumberOfLabels() const
{
return m_LabelMap.size();
}
void mitk::LabelSetImage::MaskStamp(mitk::Image *mask, bool forceOverwrite)
{
try
{
mitk::PadImageFilter::Pointer padImageFilter = mitk::PadImageFilter::New();
padImageFilter->SetInput(0, mask);
padImageFilter->SetInput(1, this);
padImageFilter->SetPadConstant(0);
padImageFilter->SetBinaryFilter(false);
padImageFilter->SetLowerThreshold(0);
padImageFilter->SetUpperThreshold(1);
padImageFilter->Update();
mitk::Image::Pointer paddedMask = padImageFilter->GetOutput();
if (paddedMask.IsNull())
return;
AccessByItk_2(this, MaskStampProcessing, paddedMask, forceOverwrite);
}
catch (...)
{
mitkThrow() << "Could not stamp the provided mask on the selected label.";
}
}
void mitk::LabelSetImage::InitializeByLabeledImage(mitk::Image::Pointer image)
{
if (image.IsNull() || image->IsEmpty() || !image->IsInitialized())
mitkThrow() << "Invalid labeled image.";
try
{
this->Initialize(image);
unsigned int byteSize = sizeof(LabelSetImage::PixelType);
for (unsigned int dim = 0; dim < image->GetDimension(); ++dim)
{
byteSize *= image->GetDimension(dim);
}
mitk::ImageWriteAccessor *accessor = new mitk::ImageWriteAccessor(static_cast(this));
memset(accessor->GetData(), 0, byteSize);
delete accessor;
auto geometry = image->GetTimeGeometry()->Clone();
this->SetTimeGeometry(geometry);
if (image->GetDimension() == 3)
{
AccessTwoImagesFixedDimensionByItk(this, image, InitializeByLabeledImageProcessing, 3);
}
else if (image->GetDimension() == 4)
{
AccessTwoImagesFixedDimensionByItk(this, image, InitializeByLabeledImageProcessing, 4);
}
else
{
mitkThrow() << image->GetDimension() << "-dimensional label set images not yet supported";
}
}
catch (Exception& e)
{
mitkReThrow(e) << "Could not initialize by provided labeled image.";
}
catch (...)
{
mitkThrow() << "Could not initialize by provided labeled image due to unknown error.";
}
this->Modified();
}
template
void mitk::LabelSetImage::InitializeByLabeledImageProcessing(LabelSetImageType *labelSetImage, ImageType *image)
{
typedef itk::ImageRegionConstIteratorWithIndex SourceIteratorType;
typedef itk::ImageRegionIterator TargetIteratorType;
TargetIteratorType targetIter(labelSetImage, labelSetImage->GetRequestedRegion());
targetIter.GoToBegin();
SourceIteratorType sourceIter(image, image->GetRequestedRegion());
sourceIter.GoToBegin();
while (!sourceIter.IsAtEnd())
{
const auto originalSourceValue = sourceIter.Get();
const auto sourceValue = static_cast(originalSourceValue);
if (originalSourceValue > mitk::Label::MAX_LABEL_VALUE)
{
mitkThrow() << "Cannot initialize MultiLabelSegmentation by image. Image contains a pixel value that exceeds the label value range. Invalid pixel value:" << originalSourceValue;
}
targetIter.Set(sourceValue);
if (LabelSetImage::UNLABELED_VALUE!=sourceValue && !this->ExistLabel(sourceValue))
{
if (this->GetTotalNumberOfLabels() >= mitk::Label::MAX_LABEL_VALUE)
{
mitkThrow() << "Cannot initialize MultiLabelSegmentation by image. Image contains to many labels.";
}
std::stringstream name;
name << "object-" << sourceValue;
double rgba[4];
this->GetLookupTable()->GetTableValue(sourceValue, rgba);
mitk::Color color;
color.SetRed(rgba[0]);
color.SetGreen(rgba[1]);
color.SetBlue(rgba[2]);
auto label = mitk::Label::New();
label->SetName(name.str().c_str());
label->SetColor(color);
label->SetOpacity(rgba[3]);
label->SetValue(sourceValue);
this->AddLabel(label,0,false);
}
++sourceIter;
++targetIter;
}
}
template
void mitk::LabelSetImage::MaskStampProcessing(ImageType *itkImage, mitk::Image *mask, bool forceOverwrite)
{
typename ImageType::Pointer itkMask;
mitk::CastToItkImage(mask, itkMask);
typedef itk::ImageRegionConstIterator SourceIteratorType;
typedef itk::ImageRegionIterator TargetIteratorType;
SourceIteratorType sourceIter(itkMask, itkMask->GetLargestPossibleRegion());
sourceIter.GoToBegin();
TargetIteratorType targetIter(itkImage, itkImage->GetLargestPossibleRegion());
targetIter.GoToBegin();
const auto activeLabel = this->GetActiveLabel()->GetValue();
while (!sourceIter.IsAtEnd())
{
PixelType sourceValue = sourceIter.Get();
PixelType targetValue = targetIter.Get();
if ((sourceValue != UNLABELED_VALUE) &&
(forceOverwrite || !this->IsLabelLocked(targetValue))) // skip unlabeled pixels and locked labels
{
targetIter.Set(activeLabel);
}
++sourceIter;
++targetIter;
}
this->Modified();
}
template
void mitk::LabelSetImage::CalculateCenterOfMassProcessing(ImageType *itkImage, LabelValueType pixelValue)
{
if (ImageType::GetImageDimension() != 3)
{
return;
}
auto labelGeometryFilter = itk::LabelGeometryImageFilter::New();
labelGeometryFilter->SetInput(itkImage);
labelGeometryFilter->Update();
auto centroid = labelGeometryFilter->GetCentroid(pixelValue);
mitk::Point3D pos;
pos[0] = centroid[0];
pos[1] = centroid[1];
pos[2] = centroid[2];
this->GetLabel(pixelValue)->SetCenterOfMassIndex(pos);
this->GetSlicedGeometry()->IndexToWorld(pos, pos);
this->GetLabel(pixelValue)->SetCenterOfMassCoordinates(pos);
}
template
void mitk::LabelSetImage::LayerContainerToImageProcessing(itk::Image *target,
unsigned int layer)
{
typedef itk::Image ImageType;
typename ImageType::Pointer itkSource;
// mitk::CastToItkImage(m_LayerContainer[layer], itkSource);
itkSource = ImageToItkImage(m_LayerContainer[layer]);
typedef itk::ImageRegionConstIterator SourceIteratorType;
typedef itk::ImageRegionIterator TargetIteratorType;
SourceIteratorType sourceIter(itkSource, itkSource->GetLargestPossibleRegion());
sourceIter.GoToBegin();
TargetIteratorType targetIter(target, target->GetLargestPossibleRegion());
targetIter.GoToBegin();
while (!sourceIter.IsAtEnd())
{
targetIter.Set(sourceIter.Get());
++sourceIter;
++targetIter;
}
}
template
void mitk::LabelSetImage::ImageToLayerContainerProcessing(const itk::Image *source,
unsigned int layer) const
{
typedef itk::Image ImageType;
typename ImageType::Pointer itkTarget;
// mitk::CastToItkImage(m_LayerContainer[layer], itkTarget);
itkTarget = ImageToItkImage(m_LayerContainer[layer]);
typedef itk::ImageRegionConstIterator SourceIteratorType;
typedef itk::ImageRegionIterator TargetIteratorType;
SourceIteratorType sourceIter(source, source->GetLargestPossibleRegion());
sourceIter.GoToBegin();
TargetIteratorType targetIter(itkTarget, itkTarget->GetLargestPossibleRegion());
targetIter.GoToBegin();
while (!sourceIter.IsAtEnd())
{
targetIter.Set(sourceIter.Get());
++sourceIter;
++targetIter;
}
m_LayerContainer[layer]->Modified();
}
template
void mitk::LabelSetImage::EraseLabelProcessing(ImageType *itkImage, PixelType pixelValue)
{
typedef itk::ImageRegionIterator IteratorType;
IteratorType iter(itkImage, itkImage->GetLargestPossibleRegion());
iter.GoToBegin();
while (!iter.IsAtEnd())
{
PixelType value = iter.Get();
if (value == pixelValue)
{
iter.Set(0);
}
++iter;
}
}
template
void mitk::LabelSetImage::MergeLabelProcessing(ImageType *itkImage, PixelType pixelValue, PixelType index)
{
typedef itk::ImageRegionIterator IteratorType;
IteratorType iter(itkImage, itkImage->GetLargestPossibleRegion());
iter.GoToBegin();
while (!iter.IsAtEnd())
{
if (iter.Get() == index)
{
iter.Set(pixelValue);
}
++iter;
}
}
void mitk::LabelSetImage::AddLabelToMap(LabelValueType labelValue, mitk::Label* label, GroupIndexType groupID)
{
if (m_LabelMap.find(labelValue)!=m_LabelMap.end())
mitkThrow() << "Segmentation is in an invalid state: Label value collision. A label was added with a LabelValue already in use. LabelValue: " << labelValue;
if (!this->ExistGroup(groupID))
mitkThrow() << "Cannot add label. Defined group is unknown. Invalid group index: " << groupID;
m_LabelMap[labelValue] = label;
m_LabelToGroupMap[labelValue] = groupID;
auto groupFinding = std::find(m_GroupToLabelMap[groupID].begin(), m_GroupToLabelMap[groupID].end(), labelValue);
if (groupFinding == m_GroupToLabelMap[groupID].end())
{
m_GroupToLabelMap[groupID].push_back(labelValue);
}
}
void mitk::LabelSetImage::RegisterLabel(mitk::Label* label)
{
if (nullptr == label) mitkThrow() << "Invalid call of RegisterLabel with a nullptr.";
UpdateLookupTable(label->GetValue());
auto command = itk::MemberCommand::New();
command->SetCallbackFunction(this, &LabelSetImage::OnLabelModified);
m_LabelModEventGuardMap.emplace(label->GetValue(), ITKEventObserverGuard(label, itk::ModifiedEvent(), command));
}
void mitk::LabelSetImage::ReleaseLabel(Label* label)
{
if (nullptr == label) mitkThrow() << "Invalid call of ReleaseLabel with a nullptr.";
m_LabelModEventGuardMap.erase(label->GetValue());
}
void mitk::LabelSetImage::ApplyToLabels(const LabelValueVectorType& values, std::function&& lambda)
{
auto labels = this->GetLabelsByValue(values);
std::for_each(labels.begin(), labels.end(), lambda);
this->InvokeEvent(LabelsChangedEvent(values));
}
void mitk::LabelSetImage::VisitLabels(const LabelValueVectorType& values, std::function&& lambda) const
{
auto labels = this->GetConstLabelsByValue(values);
std::for_each(labels.begin(), labels.end(), lambda);
}
void mitk::LabelSetImage::OnLabelModified(const Object* sender, const itk::EventObject&)
{
auto label = dynamic_cast(sender);
if (nullptr == label)
mitkThrow() << "LabelSet is in wrong state. LabelModified event is not send by a label instance.";
Superclass::Modified();
this->InvokeEvent(LabelModifiedEvent(label->GetValue()));
}
bool mitk::LabelSetImage::ExistLabel(LabelValueType value) const
{
auto finding = m_LabelMap.find(value);
return m_LabelMap.end() != finding;
}
bool mitk::LabelSetImage::ExistLabel(LabelValueType value, GroupIndexType groupIndex) const
{
auto finding = m_LabelToGroupMap.find(value);
if (m_LabelToGroupMap.end() != finding)
{
return finding->second == groupIndex;
}
return false;
}
bool mitk::LabelSetImage::ExistGroup(GroupIndexType index) const
{
return index < m_LayerContainer.size();
}
mitk::LabelSetImage::GroupIndexType mitk::LabelSetImage::GetGroupIndexOfLabel(LabelValueType value) const
{
auto finding = m_LabelToGroupMap.find(value);
if (m_LabelToGroupMap.end() == finding)
{
mitkThrow()<< "Cannot deduce group index. Passed label value does not exist. Value: "<< value;
}
return finding->second;
}
const mitk::Label* mitk::LabelSetImage::GetLabel(LabelValueType value) const
{
auto finding = m_LabelMap.find(value);
if (m_LabelMap.end() != finding)
{
return finding->second;
}
return nullptr;
};
mitk::Label* mitk::LabelSetImage::GetLabel(LabelValueType value)
{
auto finding = m_LabelMap.find(value);
if (m_LabelMap.end() != finding)
{
return finding->second;
}
return nullptr;
};
bool mitk::LabelSetImage::IsLabelLocked(LabelValueType value) const
{
if (value == UNLABELED_VALUE)
{
return m_UnlabeledLabelLock;
}
const auto label = this->GetLabel(value);
return label->GetLocked();
}
const mitk::LabelSetImage::ConstLabelVectorType mitk::LabelSetImage::GetLabels() const
{
ConstLabelVectorType result;
for (auto [value, label] : m_LabelMap)
{
result.emplace_back(label);
}
return result;
}
const mitk::LabelSetImage::LabelVectorType mitk::LabelSetImage::GetLabels()
{
LabelVectorType result;
for (auto [value, label] : m_LabelMap)
{
result.emplace_back(label);
}
return result;
}
const mitk::LabelSetImage::LabelVectorType mitk::LabelSetImage::GetLabelsByValue(const LabelValueVectorType& labelValues, bool ignoreMissing)
{
LabelVectorType result;
for (const auto& labelValue : labelValues)
{
auto* label = this->GetLabel(labelValue);
if (label != nullptr)
{
result.emplace_back(label);
}
else if (!ignoreMissing) mitkThrow() << "Error cannot get labels by Value. At least one passed value is unknown. Unknown value: " << labelValue;
}
return result;
}
const mitk::LabelSetImage::ConstLabelVectorType mitk::LabelSetImage::GetConstLabelsByValue(const LabelValueVectorType& labelValues, bool ignoreMissing) const
{
ConstLabelVectorType result;
for (const auto& labelValue : labelValues)
{
const auto* label = this->GetLabel(labelValue);
if (label != nullptr)
{
result.emplace_back(label);
}
else if (!ignoreMissing) mitkThrow() << "Error cannot get labels by Value. At least one passed value is unknown. Unknown value: " << labelValue;
}
return result;
}
const mitk::LabelSetImage::LabelValueVectorType mitk::LabelSetImage::GetLabelValuesByGroup(GroupIndexType index) const
{
if (!this->ExistGroup(index))
mitkThrow() << "Cannot get labels of an invalid group. Invalid group index: " << index;
return m_GroupToLabelMap[index];
}
const mitk::LabelSetImage::LabelValueVectorType mitk::LabelSetImage::GetLabelValuesByName(GroupIndexType index, const std::string_view name) const
{
LabelValueVectorType result;
auto searchName = [&result, name](const Label* l) { if(l->GetName() == name) result.push_back(l->GetValue()); };
this->VisitLabels(this->GetLabelValuesByGroup(index), searchName);
return result;
}
std::vector mitk::LabelSetImage::GetLabelClassNames() const
{
std::set names;
auto searchName = [&names](const Label* l) { names.emplace(l->GetName()); };
this->VisitLabels(this->GetAllLabelValues(), searchName);
return std::vector(names.begin(), names.end());
}
std::vector mitk::LabelSetImage::GetLabelClassNamesByGroup(GroupIndexType index) const
{
std::set names;
auto searchName = [&names](const Label* l) { names.emplace(l->GetName()); };
this->VisitLabels(this->GetLabelValuesByGroup(index), searchName);
return std::vector(names.begin(), names.end());
}
void mitk::LabelSetImage::SetAllLabelsVisible(bool visible)
{
- auto setVisibility = [this, visible](Label* l)
+ auto setVisibility = [visible,this](Label* l)
{
l->SetVisible(visible);
this->UpdateLookupTable(l->GetValue());
};
this->ApplyToLabels(this->GetAllLabelValues(), setVisibility);
+ this->m_LookupTable->Modified();
}
void mitk::LabelSetImage::SetAllLabelsVisibleByGroup(GroupIndexType group, bool visible)
{
- auto setVisibility = [this, visible](Label* l)
+ auto setVisibility = [visible, this](Label* l)
{
l->SetVisible(visible);
this->UpdateLookupTable(l->GetValue());
};
this->ApplyToLabels(this->GetLabelValuesByGroup(group), setVisibility);
+ this->m_LookupTable->Modified();
}
void mitk::LabelSetImage::SetAllLabelsVisibleByName(GroupIndexType group, const std::string_view name, bool visible)
{
- auto setVisibility = [this, visible](Label* l)
+ auto setVisibility = [visible, this](Label* l)
{
l->SetVisible(visible);
this->UpdateLookupTable(l->GetValue());
};
this->ApplyToLabels(this->GetLabelValuesByName(group, name), setVisibility);
+ this->m_LookupTable->Modified();
}
void mitk::LabelSetImage::SetAllLabelsLocked(bool locked)
{
auto setLock = [locked](Label* l) { l->SetLocked(locked); };
this->ApplyToLabels(this->GetAllLabelValues(), setLock);
}
void mitk::LabelSetImage::SetAllLabelsLockedByGroup(GroupIndexType group, bool locked)
{
auto setLock = [locked](Label* l) { l->SetLocked(locked); };
this->ApplyToLabels(this->GetLabelValuesByGroup(group), setLock);
}
void mitk::LabelSetImage::SetAllLabelsLockedByName(GroupIndexType group, const std::string_view name, bool locked)
{
auto setLock = [locked](Label* l) { l->SetLocked(locked); };
this->ApplyToLabels(this->GetLabelValuesByName(group, name), setLock);
}
bool mitk::Equal(const mitk::LabelSetImage &leftHandSide,
const mitk::LabelSetImage &rightHandSide,
ScalarType eps,
bool verbose)
{
bool returnValue = true;
/* LabelSetImage members */
MITK_INFO(verbose) << "--- LabelSetImage Equal ---";
// m_LookupTable;
const mitk::LookupTable* lhsLUT = leftHandSide.GetLookupTable();
const mitk::LookupTable* rhsLUT = rightHandSide.GetLookupTable();
returnValue = *lhsLUT == *rhsLUT;
if (!returnValue)
{
MITK_INFO(verbose) << "Lookup tables not equal.";
return returnValue;
;
}
// number layers
returnValue = leftHandSide.GetNumberOfLayers() == rightHandSide.GetNumberOfLayers();
if (!returnValue)
{
MITK_INFO(verbose) << "Number of layers not equal.";
return false;
}
// total number labels
returnValue = leftHandSide.GetTotalNumberOfLabels() == rightHandSide.GetTotalNumberOfLabels();
if (!returnValue)
{
MITK_INFO(verbose) << "Total number of labels not equal.";
return false;
}
// active layer
returnValue = leftHandSide.GetActiveLayer() == rightHandSide.GetActiveLayer();
if (!returnValue)
{
MITK_INFO(verbose) << "Active layer not equal.";
return false;
}
if (4 == leftHandSide.GetDimension())
{
MITK_INFO(verbose) << "Can not compare image data for 4D images - skipping check.";
}
else
{
// working image data
returnValue = mitk::Equal((const mitk::Image &)leftHandSide, (const mitk::Image &)rightHandSide, eps, verbose);
if (!returnValue)
{
MITK_INFO(verbose) << "Working image data not equal.";
return false;
}
}
if (leftHandSide.GetTotalNumberOfLabels() != rightHandSide.GetTotalNumberOfLabels())
{
MITK_INFO(verbose) << "Number of labels are not equal.";
return false;
}
for (unsigned int layerIndex = 0; layerIndex < leftHandSide.GetNumberOfLayers(); layerIndex++)
{
if (4 == leftHandSide.GetDimension())
{
MITK_INFO(verbose) << "Can not compare image data for 4D images - skipping check.";
}
else
{
// layer image data
returnValue =
mitk::Equal(*leftHandSide.GetGroupImage(layerIndex), *rightHandSide.GetGroupImage(layerIndex), eps, verbose);
if (!returnValue)
{
MITK_INFO(verbose) << "Layer image data not equal.";
return false;
}
}
// label data
auto leftLabelsInGroup = leftHandSide.GetLabelValuesByGroup(layerIndex);
auto rightLabelsInGroup = rightHandSide.GetLabelValuesByGroup(layerIndex);
if (leftLabelsInGroup.size()!=rightLabelsInGroup.size())
{
MITK_INFO(verbose) << "Number of layer labels is not equal. Invalid layer:" <;
ConstLabelMapType ConvertLabelVectorToMap(const mitk::ConstLabelVector& labelV)
{
ConstLabelMapType result;
for (auto label : labelV)
{
const auto value = label->GetValue();
auto finding = result.find(value);
if (finding != result.end()) mitkThrow() << "Operation failed. Cannot convert label vector into label map, because at least one label value is not unique. Violating label value: " << value;
result.insert(std::make_pair(value, label));
}
return result;
}
/** Functor class that implements the label transfer and is used in conjunction with the itk::BinaryFunctorImageFilter.
* For details regarding the usage of the filter and the functor patterns, please see info of itk::BinaryFunctorImageFilter.
*/
template
class LabelTransferFunctor
{
public:
LabelTransferFunctor() {};
LabelTransferFunctor(const ConstLabelMapType& destinationLabels, mitk::Label::PixelType sourceBackground,
mitk::Label::PixelType destinationBackground, bool destinationBackgroundLocked,
mitk::Label::PixelType sourceLabel, mitk::Label::PixelType newDestinationLabel, mitk::MultiLabelSegmentation::MergeStyle mergeStyle,
mitk::MultiLabelSegmentation::OverwriteStyle overwriteStyle) :
m_DestinationLabels(destinationLabels), m_SourceBackground(sourceBackground),
m_DestinationBackground(destinationBackground), m_DestinationBackgroundLocked(destinationBackgroundLocked),
m_SourceLabel(sourceLabel), m_NewDestinationLabel(newDestinationLabel), m_MergeStyle(mergeStyle), m_OverwriteStyle(overwriteStyle)
{
};
~LabelTransferFunctor() {};
bool operator!=(const LabelTransferFunctor& other)const
{
return !(*this == other);
}
bool operator==(const LabelTransferFunctor& other) const
{
return this->m_SourceBackground == other.m_SourceBackground &&
this->m_DestinationBackground == other.m_DestinationBackground &&
this->m_DestinationBackgroundLocked == other.m_DestinationBackgroundLocked &&
this->m_SourceLabel == other.m_SourceLabel &&
this->m_NewDestinationLabel == other.m_NewDestinationLabel &&
this->m_MergeStyle == other.m_MergeStyle &&
this->m_OverwriteStyle == other.m_OverwriteStyle &&
this->m_DestinationLabels == other.m_DestinationLabels;
}
LabelTransferFunctor& operator=(const LabelTransferFunctor& other)
{
this->m_DestinationLabels = other.m_DestinationLabels;
this->m_SourceBackground = other.m_SourceBackground;
this->m_DestinationBackground = other.m_DestinationBackground;
this->m_DestinationBackgroundLocked = other.m_DestinationBackgroundLocked;
this->m_SourceLabel = other.m_SourceLabel;
this->m_NewDestinationLabel = other.m_NewDestinationLabel;
this->m_MergeStyle = other.m_MergeStyle;
this->m_OverwriteStyle = other.m_OverwriteStyle;
return *this;
}
inline TOutputpixel operator()(const TDestinationPixel& existingDestinationValue, const TSourcePixel& existingSourceValue)
{
if (existingSourceValue == this->m_SourceLabel)
{
if (mitk::MultiLabelSegmentation::OverwriteStyle::IgnoreLocks == this->m_OverwriteStyle)
{
return this->m_NewDestinationLabel;
}
else
{
if (existingDestinationValue == m_DestinationBackground)
{
if (!m_DestinationBackgroundLocked)
{
return this->m_NewDestinationLabel;
}
}
else
{
auto labelFinding = this->m_DestinationLabels.find(existingDestinationValue);
if (labelFinding==this->m_DestinationLabels.end() || !labelFinding->second->GetLocked())
{
return this->m_NewDestinationLabel;
}
}
}
}
else if (mitk::MultiLabelSegmentation::MergeStyle::Replace == this->m_MergeStyle
&& existingSourceValue == this->m_SourceBackground
&& existingDestinationValue == this->m_NewDestinationLabel
&& (mitk::MultiLabelSegmentation::OverwriteStyle::IgnoreLocks == this->m_OverwriteStyle
|| !this->m_DestinationBackgroundLocked))
{
return this->m_DestinationBackground;
}
return existingDestinationValue;
}
private:
ConstLabelMapType m_DestinationLabels;
mitk::Label::PixelType m_SourceBackground = 0;
mitk::Label::PixelType m_DestinationBackground = 0;
bool m_DestinationBackgroundLocked = false;
mitk::Label::PixelType m_SourceLabel = 1;
mitk::Label::PixelType m_NewDestinationLabel = 1;
mitk::MultiLabelSegmentation::MergeStyle m_MergeStyle = mitk::MultiLabelSegmentation::MergeStyle::Replace;
mitk::MultiLabelSegmentation::OverwriteStyle m_OverwriteStyle = mitk::MultiLabelSegmentation::OverwriteStyle::RegardLocks;
};
/**Helper function used by TransferLabelContentAtTimeStep to allow the templating over different image dimensions in conjunction of AccessFixedPixelTypeByItk_n.*/
template
void TransferLabelContentAtTimeStepHelper(const itk::Image* itkSourceImage, mitk::Image* destinationImage,
const mitk::ConstLabelVector& destinationLabels, mitk::Label::PixelType sourceBackground, mitk::Label::PixelType destinationBackground,
bool destinationBackgroundLocked, mitk::Label::PixelType sourceLabel, mitk::Label::PixelType newDestinationLabel, mitk::MultiLabelSegmentation::MergeStyle mergeStyle, mitk::MultiLabelSegmentation::OverwriteStyle overwriteStyle)
{
typedef itk::Image ContentImageType;
typename ContentImageType::Pointer itkDestinationImage;
mitk::CastToItkImage(destinationImage, itkDestinationImage);
auto sourceRegion = itkSourceImage->GetLargestPossibleRegion();
auto relevantRegion = itkDestinationImage->GetLargestPossibleRegion();
bool overlapping = relevantRegion.Crop(sourceRegion);
if (!overlapping)
{
mitkThrow() << "Invalid call of TransferLabelContentAtTimeStep; sourceImage and destinationImage seem to have no overlapping image region.";
}
typedef LabelTransferFunctor LabelTransferFunctorType;
typedef itk::BinaryFunctorImageFilter FilterType;
LabelTransferFunctorType transferFunctor(ConvertLabelVectorToMap(destinationLabels), sourceBackground, destinationBackground,
destinationBackgroundLocked, sourceLabel, newDestinationLabel, mergeStyle, overwriteStyle);
auto transferFilter = FilterType::New();
transferFilter->SetFunctor(transferFunctor);
transferFilter->InPlaceOn();
transferFilter->SetInput1(itkDestinationImage);
transferFilter->SetInput2(itkSourceImage);
transferFilter->GetOutput()->SetRequestedRegion(relevantRegion);
transferFilter->Update();
}
void mitk::TransferLabelContentAtTimeStep(
const Image* sourceImage, Image* destinationImage, const mitk::ConstLabelVector& destinationLabels, const TimeStepType timeStep, mitk::Label::PixelType sourceBackground,
mitk::Label::PixelType destinationBackground, bool destinationBackgroundLocked, LabelValueMappingVector labelMapping,
MultiLabelSegmentation::MergeStyle mergeStyle, MultiLabelSegmentation::OverwriteStyle overwriteStlye)
{
if (nullptr == sourceImage)
{
mitkThrow() << "Invalid call of TransferLabelContentAtTimeStep; sourceImage must not be null.";
}
if (nullptr == destinationImage)
{
mitkThrow() << "Invalid call of TransferLabelContentAtTimeStep; destinationImage must not be null.";
}
if (sourceImage == destinationImage && labelMapping.size() > 1)
{
MITK_DEBUG << "Warning. Using TransferLabelContentAtTimeStep or TransferLabelContent with equal source and destination and more then on label to transfer, can lead to wrong results. Please see documentation and verify that the usage is OK.";
}
Image::ConstPointer sourceImageAtTimeStep = SelectImageByTimeStep(sourceImage, timeStep);
Image::Pointer destinationImageAtTimeStep = SelectImageByTimeStep(destinationImage, timeStep);
if (nullptr == sourceImageAtTimeStep)
{
mitkThrow() << "Invalid call of TransferLabelContentAtTimeStep; sourceImage does not have the requested time step: " << timeStep;
}
if (nullptr == destinationImageAtTimeStep)
{
mitkThrow() << "Invalid call of TransferLabelContentAtTimeStep; destinationImage does not have the requested time step: " << timeStep;
}
if (!Equal(*(sourceImageAtTimeStep->GetGeometry()), *(destinationImageAtTimeStep->GetGeometry()), mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_DIRECTION_PRECISION))
{
if (IsSubGeometry(*(sourceImageAtTimeStep->GetGeometry()), *(destinationImageAtTimeStep->GetGeometry()), mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_DIRECTION_PRECISION))
{
//we have to pad the source image
//because ImageToImageFilters always check for origin matching even if
//the requested output region is fitting :(
auto padFilter = mitk::PadImageFilter::New();
padFilter->SetInput(0, sourceImageAtTimeStep);
padFilter->SetInput(1, destinationImageAtTimeStep);
padFilter->SetPadConstant(Label::UNLABELED_VALUE);
padFilter->SetBinaryFilter(false);
padFilter->Update();
sourceImageAtTimeStep = padFilter->GetOutput();
}
else
{
mitkThrow() << "Invalid call of TransferLabelContentAtTimeStep; source image has neither the same geometry than destination image nor has the source image a sub geometry.";
}
}
auto destLabelMap = ConvertLabelVectorToMap(destinationLabels);
for (const auto& [sourceLabel, newDestinationLabel] : labelMapping)
{
if (LabelSetImage::UNLABELED_VALUE!=newDestinationLabel && destLabelMap.end() == destLabelMap.find(newDestinationLabel))
{
mitkThrow() << "Invalid call of TransferLabelContentAtTimeStep. Defined destination label does not exist in destinationImage. newDestinationLabel: " << newDestinationLabel;
}
AccessFixedPixelTypeByItk_n(sourceImageAtTimeStep, TransferLabelContentAtTimeStepHelper, (Label::PixelType), (destinationImageAtTimeStep, destinationLabels, sourceBackground, destinationBackground, destinationBackgroundLocked, sourceLabel, newDestinationLabel, mergeStyle, overwriteStlye));
}
destinationImage->Modified();
}
void mitk::TransferLabelContent(
const Image* sourceImage, Image* destinationImage, const mitk::ConstLabelVector& destinationLabels, mitk::Label::PixelType sourceBackground,
mitk::Label::PixelType destinationBackground, bool destinationBackgroundLocked, LabelValueMappingVector labelMapping,
MultiLabelSegmentation::MergeStyle mergeStyle, MultiLabelSegmentation::OverwriteStyle overwriteStlye)
{
if (nullptr == sourceImage)
{
mitkThrow() << "Invalid call of TransferLabelContent; sourceImage must not be null.";
}
if (nullptr == destinationImage)
{
mitkThrow() << "Invalid call of TransferLabelContent; destinationImage must not be null.";
}
const auto sourceTimeStepCount = sourceImage->GetTimeGeometry()->CountTimeSteps();
if (sourceTimeStepCount != destinationImage->GetTimeGeometry()->CountTimeSteps())
{
mitkThrow() << "Invalid call of TransferLabelContent; mismatch between images in number of time steps.";
}
for (mitk::TimeStepType i = 0; i < sourceTimeStepCount; ++i)
{
TransferLabelContentAtTimeStep(sourceImage, destinationImage, destinationLabels, i, sourceBackground,
destinationBackground, destinationBackgroundLocked, labelMapping, mergeStyle, overwriteStlye);
}
}
void mitk::TransferLabelContentAtTimeStep(
const LabelSetImage* sourceImage, LabelSetImage* destinationImage, const TimeStepType timeStep,
LabelValueMappingVector labelMapping,
MultiLabelSegmentation::MergeStyle mergeStyle, MultiLabelSegmentation::OverwriteStyle overwriteStlye)
{
if (nullptr == sourceImage)
{
mitkThrow() << "Invalid call of TransferLabelContentAtTimeStep; sourceImage must not be null.";
}
auto destinationLabels = destinationImage->GetConstLabelsByValue(destinationImage->GetLabelValuesByGroup(destinationImage->GetActiveLayer()));
for (const auto& mappingElement : labelMapping)
{
if (LabelSetImage::UNLABELED_VALUE != mappingElement.first && !sourceImage->ExistLabel(mappingElement.first, sourceImage->GetActiveLayer()))
{
mitkThrow() << "Invalid call of TransferLabelContentAtTimeStep. Defined source label does not exist in sourceImage. SourceLabel: " << mappingElement.first;
}
}
TransferLabelContentAtTimeStep(sourceImage, destinationImage, destinationLabels, timeStep, LabelSetImage::UNLABELED_VALUE, LabelSetImage::UNLABELED_VALUE, destinationImage->GetUnlabeledLabelLock(),
labelMapping, mergeStyle, overwriteStlye);
}
void mitk::TransferLabelContent(
const LabelSetImage* sourceImage, LabelSetImage* destinationImage,
LabelValueMappingVector labelMapping,
MultiLabelSegmentation::MergeStyle mergeStyle, MultiLabelSegmentation::OverwriteStyle overwriteStlye)
{
if (nullptr == sourceImage)
{
mitkThrow() << "Invalid call of TransferLabelContent; sourceImage must not be null.";
}
if (nullptr == destinationImage)
{
mitkThrow() << "Invalid call of TransferLabelContent; destinationImage must not be null.";
}
const auto sourceTimeStepCount = sourceImage->GetTimeGeometry()->CountTimeSteps();
if (sourceTimeStepCount != destinationImage->GetTimeGeometry()->CountTimeSteps())
{
mitkThrow() << "Invalid call of TransferLabelContent; images have no equal number of time steps.";
}
for (mitk::TimeStepType i = 0; i < sourceTimeStepCount; ++i)
{
TransferLabelContentAtTimeStep(sourceImage, destinationImage, i, labelMapping, mergeStyle, overwriteStlye);
}
}
diff --git a/Modules/Multilabel/mitkLabelSetImageVtkMapper2D.cpp b/Modules/Multilabel/mitkLabelSetImageVtkMapper2D.cpp
index 936e561891..7a4210ee50 100644
--- a/Modules/Multilabel/mitkLabelSetImageVtkMapper2D.cpp
+++ b/Modules/Multilabel/mitkLabelSetImageVtkMapper2D.cpp
@@ -1,643 +1,755 @@
/*============================================================================
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 "mitkLabelSetImageVtkMapper2D.h"
// MITK
#include
#include
#include
-#include
-#include
-#include
-#include
-#include
#include
#include
-#include
-#include
-#include
+#include
// MITK Rendering
-#include "vtkMitkLevelWindowFilter.h"
-#include "vtkMitkThickSlicesFilter.h"
#include "vtkNeverTranslucentTexture.h"
// VTK
#include
-#include
#include
#include
#include
-#include
#include
-#include
#include
#include
-#include
-#include
-//#include
+#include
-// ITK
-#include
-#include
+namespace
+{
+ itk::ModifiedTimeType PropertyTimeStampIsNewer(const mitk::IPropertyProvider* provider, mitk::BaseRenderer* renderer, const std::string& propName, itk::ModifiedTimeType refMT)
+ {
+ const std::string context = renderer != nullptr ? renderer->GetName() : "";
+ auto prop = provider->GetConstProperty(propName, context);
+ if (prop != nullptr)
+ {
+ return prop->GetTimeStamp() > refMT;
+ }
+ return false;
+ }
+}
mitk::LabelSetImageVtkMapper2D::LabelSetImageVtkMapper2D()
{
}
mitk::LabelSetImageVtkMapper2D::~LabelSetImageVtkMapper2D()
{
}
vtkProp *mitk::LabelSetImageVtkMapper2D::GetVtkProp(mitk::BaseRenderer *renderer)
{
// return the actor corresponding to the renderer
return m_LSH.GetLocalStorage(renderer)->m_Actors;
}
mitk::LabelSetImageVtkMapper2D::LocalStorage *mitk::LabelSetImageVtkMapper2D::GetLocalStorage(
mitk::BaseRenderer *renderer)
{
return m_LSH.GetLocalStorage(renderer);
}
-void mitk::LabelSetImageVtkMapper2D::GenerateDataForRenderer(mitk::BaseRenderer *renderer)
+void mitk::LabelSetImageVtkMapper2D::GenerateLookupTable(mitk::BaseRenderer* renderer)
{
- LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer);
- mitk::DataNode *node = this->GetDataNode();
- auto *image = dynamic_cast(node->GetData());
+ LocalStorage* localStorage = m_LSH.GetLocalStorage(renderer);
+ mitk::DataNode* node = this->GetDataNode();
+ auto* image = dynamic_cast(node->GetData());
assert(image && image->IsInitialized());
- // check if there is a valid worldGeometry
- const PlaneGeometry *worldGeometry = renderer->GetCurrentWorldPlaneGeometry();
- if ((worldGeometry == nullptr) || (!worldGeometry->IsValid()) || (!worldGeometry->HasReferenceGeometry()))
- return;
+ localStorage->m_LabelLookupTable = image->GetLookupTable()->Clone();
+ const auto labelValues = image->GetAllLabelValues();
- image->Update();
+ std::string propertyName = "org.mitk.multilabel.labels.highlighted";
- int numberOfLayers = image->GetNumberOfLayers();
- int activeLayer = image->GetActiveLayer();
+ mitk::IntVectorProperty::Pointer prop = dynamic_cast(node->GetNonConstProperty(propertyName));
+ if (nullptr != prop)
+ {
+ const auto highlightedLabelValues = prop->GetValue();
- float opacity = 1.0f;
- node->GetOpacity(opacity, renderer, "opacity");
+ if (!highlightedLabelValues.empty())
+ {
+ auto lookUpTable = localStorage->m_LabelLookupTable->GetVtkLookupTable();
+ auto highlightEnd = highlightedLabelValues.cend();
+
+ double rgba[4];
+ for (const auto& value : labelValues)
+ {
+ lookUpTable->GetTableValue(value, rgba);
+ if (highlightEnd == std::find(highlightedLabelValues.begin(), highlightedLabelValues.end(), value))
+ { //make all none highlighted values more transparent
+ rgba[3] *= 0.3;
+ }
+ else
+ {
+ if (rgba[3] != 0)
+ { //if highlighted values are visible set them to opaque to pop out
+ rgba[3] = 1.;
+ }
+ else
+ { //if highlighted values are invisible the opacity is increased a bit
+ //to give a visual hint that the are highlighted but also invisible.
+ //e.g. needed to see a difference if you change the visibility of
+ //a highlighted label in the MultiLabelInspector
+ rgba[3] = 0.4;
+ }
+ }
+ lookUpTable->SetTableValue(value, rgba);
+ }
+ localStorage->m_LabelLookupTable->Modified();
+ }
+ }
+}
- if (numberOfLayers != localStorage->m_NumberOfLayers)
+namespace
+{
+ std::vector GetOutdatedGroups(const mitk::LabelSetImageVtkMapper2D::LocalStorage* ls, const mitk::LabelSetImage* seg)
{
- localStorage->m_NumberOfLayers = numberOfLayers;
- localStorage->m_ReslicedImageVector.clear();
- localStorage->m_ReslicerVector.clear();
- localStorage->m_LayerTextureVector.clear();
- localStorage->m_LevelWindowFilterVector.clear();
- localStorage->m_LayerMapperVector.clear();
- localStorage->m_LayerActorVector.clear();
-
- localStorage->m_Actors = vtkSmartPointer::New();
+ const auto nrOfGroups = seg->GetNumberOfLayers();
+ std::vector result;
- for (int lidx = 0; lidx < numberOfLayers; ++lidx)
+ for (mitk::LabelSetImage::GroupIndexType groupID = 0; groupID < nrOfGroups; ++groupID)
{
- localStorage->m_ReslicedImageVector.push_back(vtkSmartPointer::New());
- localStorage->m_ReslicerVector.push_back(mitk::ExtractSliceFilter::New());
- localStorage->m_LayerTextureVector.push_back(vtkSmartPointer::New());
- localStorage->m_LevelWindowFilterVector.push_back(vtkSmartPointer::New());
- localStorage->m_LayerMapperVector.push_back(vtkSmartPointer::New());
- localStorage->m_LayerActorVector.push_back(vtkSmartPointer::New());
+ const auto groupImage = seg->GetGroupImage(groupID);
+ if (groupImage->GetMTime() > ls->m_LastDataUpdateTime
+ || groupImage->GetPipelineMTime() > ls->m_LastDataUpdateTime
+ || ls->m_GroupImageIDs.size() <= groupID
+ || groupImage != ls->m_GroupImageIDs[groupID])
+ {
+ result.push_back(groupID);
+ }
+ }
+ return result;
+ }
+}
- // do not repeat the texture (the image)
- localStorage->m_LayerTextureVector[lidx]->RepeatOff();
+void mitk::LabelSetImageVtkMapper2D::GenerateDataForRenderer(mitk::BaseRenderer *renderer)
+{
+ LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer);
+ mitk::DataNode *node = this->GetDataNode();
+ auto *segmentation = dynamic_cast(node->GetData());
+ assert(segmentation && segmentation->IsInitialized());
- // set corresponding mappers for the actors
- localStorage->m_LayerActorVector[lidx]->SetMapper(localStorage->m_LayerMapperVector[lidx]);
+ bool isLookupModified = localStorage->m_LabelLookupTable.IsNull() ||
+ (localStorage->m_LabelLookupTable->GetMTime() < segmentation->GetLookupTable()->GetMTime()) ||
+ PropertyTimeStampIsNewer(node, renderer, "org.mitk.multilabel.labels.highlighted", localStorage->m_LabelLookupTable->GetMTime()) ||
+ PropertyTimeStampIsNewer(node, renderer, "opacity", localStorage->m_LabelLookupTable->GetMTime());
- localStorage->m_Actors->AddPart(localStorage->m_LayerActorVector[lidx]);
- }
+ if (isLookupModified)
+ {
+ this->GenerateLookupTable(renderer);
+ }
- localStorage->m_Actors->AddPart(localStorage->m_OutlineShadowActor);
- localStorage->m_Actors->AddPart(localStorage->m_OutlineActor);
+ auto outdatedGroups = GetOutdatedGroups(localStorage, segmentation);
+
+ bool isGeometryModified = (localStorage->m_LastDataUpdateTime < renderer->GetCurrentWorldPlaneGeometryUpdateTime()) ||
+ (localStorage->m_LastDataUpdateTime < renderer->GetCurrentWorldPlaneGeometry()->GetMTime());
+
+ bool rendererGeometryIsValid = true;
+
+ // check if there is a valid worldGeometry
+ if (isGeometryModified)
+ {
+ const PlaneGeometry* worldGeometry = renderer->GetCurrentWorldPlaneGeometry();
+ rendererGeometryIsValid = worldGeometry != nullptr
+ && worldGeometry->IsValid()
+ && worldGeometry->HasReferenceGeometry();
+
+ isGeometryModified = rendererGeometryIsValid
+ && localStorage->m_WorldPlane.IsNotNull() && !Equal(*worldGeometry, *(localStorage->m_WorldPlane.GetPointer()));
+
+ localStorage->m_WorldPlane = rendererGeometryIsValid ? worldGeometry->Clone() : nullptr;
}
- // early out if there is no intersection of the current rendering geometry
- // and the geometry of the image that is to be rendered.
- if (!RenderingGeometryIntersectsImage(worldGeometry, image->GetSlicedGeometry()))
+ bool hasValidContent = rendererGeometryIsValid && RenderingGeometryIntersectsImage(localStorage->m_WorldPlane, segmentation->GetSlicedGeometry());
+
+ if (!hasValidContent && localStorage->m_HasValidContent)
{
// set image to nullptr, to clear the texture in 3D, because
// the latest image is used there if the plane is out of the geometry
// see bug-13275
- for (int lidx = 0; lidx < numberOfLayers; ++lidx)
+ for (unsigned int lidx = 0; lidx < localStorage->m_NumberOfLayers; ++lidx)
{
localStorage->m_ReslicedImageVector[lidx] = nullptr;
localStorage->m_LayerMapperVector[lidx]->SetInputData(localStorage->m_EmptyPolyData);
localStorage->m_OutlineActor->SetVisibility(false);
localStorage->m_OutlineShadowActor->SetVisibility(false);
}
+ localStorage->m_LastDataUpdateTime.Modified();
+ }
+
+ if (isGeometryModified || (hasValidContent && !localStorage->m_HasValidContent))
+ {
+ //if geometry is outdated or we have valid content again
+ // -> all groups need regeneration
+ outdatedGroups.resize(segmentation->GetNumberOfLayers());
+ std::iota(outdatedGroups.begin(), outdatedGroups.end(), 0);
+ }
+
+ localStorage->m_HasValidContent = hasValidContent;
+
+ if (!hasValidContent)
+ {
+ // early out if there is no intersection of the current rendering geometry
+ // and the geometry of the image that is to be rendered.
return;
}
- for (int lidx = 0; lidx < numberOfLayers; ++lidx)
+ if (!outdatedGroups.empty())
{
- const auto layerImage = image->GetGroupImage(lidx);
+ this->GenerateImageSlice(renderer, outdatedGroups);
+ }
- localStorage->m_ReslicerVector[lidx]->SetInput(layerImage);
- localStorage->m_ReslicerVector[lidx]->SetWorldGeometry(worldGeometry);
- localStorage->m_ReslicerVector[lidx]->SetTimeStep(this->GetTimestep());
+ float opacity = 1.0f;
+ node->GetOpacity(opacity, renderer, "opacity");
+
+ if (isLookupModified)
+ {
+ //if lookup table is modified all groups need a new color mapping
+ outdatedGroups.resize(segmentation->GetNumberOfLayers());
+ std::iota(outdatedGroups.begin(), outdatedGroups.end(), 0);
+ }
+
+ for (const auto groupID: outdatedGroups)
+ {
+ localStorage->m_LayerImageMapToColors[groupID]->SetLookupTable(localStorage->m_LabelLookupTable->GetVtkLookupTable());
+ localStorage->m_LayerImageMapToColors[groupID]->SetInputData(localStorage->m_ReslicedImageVector[groupID]);
+ localStorage->m_LayerImageMapToColors[groupID]->Update();
+
+ // check for texture interpolation property
+ bool textureInterpolation = false;
+ node->GetBoolProperty("texture interpolation", textureInterpolation, renderer);
+
+ // set the interpolation modus according to the property
+ localStorage->m_LayerTextureVector[groupID]->SetInterpolate(textureInterpolation);
+
+ localStorage->m_LayerTextureVector[groupID]->SetInputConnection(
+ localStorage->m_LayerImageMapToColors[groupID]->GetOutputPort());
+ this->TransformActor(renderer);
+
+ // set the plane as input for the mapper
+ localStorage->m_LayerMapperVector[groupID]->SetInputConnection(localStorage->m_Plane->GetOutputPort());
+
+ // set the texture for the actor
+ localStorage->m_LayerActorVector[groupID]->SetTexture(localStorage->m_LayerTextureVector[groupID]);
+ localStorage->m_LayerActorVector[groupID]->GetProperty()->SetOpacity(opacity);
+ }
+
+ auto activeLayer = segmentation->GetActiveLayer();
+ bool activeGroupIsOutdated = std::find(outdatedGroups.begin(), outdatedGroups.end(), activeLayer) != outdatedGroups.end();
+
+ if (activeGroupIsOutdated
+ || PropertyTimeStampIsNewer(node, renderer, "opacity", localStorage->m_LastActiveLabelUpdateTime.GetMTime())
+ || PropertyTimeStampIsNewer(node, renderer, "labelset.contour.active", localStorage->m_LastActiveLabelUpdateTime.GetMTime())
+ || PropertyTimeStampIsNewer(node, renderer, "labelset.contour.width", localStorage->m_LastActiveLabelUpdateTime.GetMTime())
+ )
+ {
+ this->GenerateActiveLabelOutline(renderer);
+ }
+}
+
+void mitk::LabelSetImageVtkMapper2D::GenerateImageSlice(mitk::BaseRenderer* renderer, const std::vector& outdatedGroupIDs)
+{
+ LocalStorage* localStorage = m_LSH.GetLocalStorage(renderer);
+ mitk::DataNode* node = this->GetDataNode();
+ auto* segmentation = dynamic_cast(node->GetData());
+ assert(segmentation && segmentation->IsInitialized());
+
+ segmentation->Update();
+
+ const auto numberOfLayers = segmentation->GetNumberOfLayers();
+
+ if (numberOfLayers != localStorage->m_NumberOfLayers)
+ {
+ if (numberOfLayers > localStorage->m_NumberOfLayers)
+ {
+ for (unsigned int lidx = localStorage->m_NumberOfLayers; lidx < numberOfLayers; ++lidx)
+ {
+ localStorage->m_GroupImageIDs.push_back(nullptr);
+ localStorage->m_ReslicedImageVector.push_back(vtkSmartPointer::New());
+ localStorage->m_ReslicerVector.push_back(mitk::ExtractSliceFilter::New());
+ localStorage->m_LayerTextureVector.push_back(vtkSmartPointer::New());
+ localStorage->m_LayerMapperVector.push_back(vtkSmartPointer::New());
+ localStorage->m_LayerActorVector.push_back(vtkSmartPointer::New());
+ localStorage->m_LayerImageMapToColors.push_back(vtkSmartPointer::New());
+
+ // do not repeat the texture (the image)
+ localStorage->m_LayerTextureVector[lidx]->RepeatOff();
+ // set corresponding mappers for the actors
+ localStorage->m_LayerActorVector[lidx]->SetMapper(localStorage->m_LayerMapperVector[lidx]);
+ }
+ }
+ else
+ {
+ localStorage->m_GroupImageIDs.resize(numberOfLayers);
+ localStorage->m_ReslicedImageVector.resize(numberOfLayers);
+ localStorage->m_ReslicerVector.resize(numberOfLayers);
+ localStorage->m_LayerTextureVector.resize(numberOfLayers);
+ localStorage->m_LayerMapperVector.resize(numberOfLayers);
+ localStorage->m_LayerActorVector.resize(numberOfLayers);
+ localStorage->m_LayerImageMapToColors.resize(numberOfLayers);
+ }
+ localStorage->m_NumberOfLayers = numberOfLayers;
+
+ localStorage->m_Actors = vtkSmartPointer::New();
+
+ for (unsigned int lidx = 0; lidx < numberOfLayers; ++lidx)
+ {
+ localStorage->m_Actors->AddPart(localStorage->m_LayerActorVector[lidx]);
+ }
+ localStorage->m_Actors->AddPart(localStorage->m_OutlineShadowActor);
+ localStorage->m_Actors->AddPart(localStorage->m_OutlineActor);
+ }
+
+ for (const auto groupID : outdatedGroupIDs)
+ {
+ const auto groupImage = segmentation->GetGroupImage(groupID);
+ localStorage->m_GroupImageIDs[groupID] = groupImage;
+
+ localStorage->m_ReslicerVector[groupID]->SetInput(groupImage);
+ localStorage->m_ReslicerVector[groupID]->SetWorldGeometry(localStorage->m_WorldPlane);
+ localStorage->m_ReslicerVector[groupID]->SetTimeStep(this->GetTimestep());
// set the transformation of the image to adapt reslice axis
- localStorage->m_ReslicerVector[lidx]->SetResliceTransformByGeometry(
- layerImage->GetTimeGeometry()->GetGeometryForTimeStep(this->GetTimestep()));
+ localStorage->m_ReslicerVector[groupID]->SetResliceTransformByGeometry(
+ groupImage->GetTimeGeometry()->GetGeometryForTimeStep(this->GetTimestep()));
// is the geometry of the slice based on the image image or the worldgeometry?
bool inPlaneResampleExtentByGeometry = false;
node->GetBoolProperty("in plane resample extent by geometry", inPlaneResampleExtentByGeometry, renderer);
- localStorage->m_ReslicerVector[lidx]->SetInPlaneResampleExtentByGeometry(inPlaneResampleExtentByGeometry);
- localStorage->m_ReslicerVector[lidx]->SetInterpolationMode(ExtractSliceFilter::RESLICE_NEAREST);
- localStorage->m_ReslicerVector[lidx]->SetVtkOutputRequest(true);
+ localStorage->m_ReslicerVector[groupID]->SetInPlaneResampleExtentByGeometry(inPlaneResampleExtentByGeometry);
+ localStorage->m_ReslicerVector[groupID]->SetInterpolationMode(ExtractSliceFilter::RESLICE_NEAREST);
+ localStorage->m_ReslicerVector[groupID]->SetVtkOutputRequest(true);
// this is needed when thick mode was enabled before. These variables have to be reset to default values
- localStorage->m_ReslicerVector[lidx]->SetOutputDimensionality(2);
- localStorage->m_ReslicerVector[lidx]->SetOutputSpacingZDirection(1.0);
- localStorage->m_ReslicerVector[lidx]->SetOutputExtentZDirection(0, 0);
+ localStorage->m_ReslicerVector[groupID]->SetOutputDimensionality(2);
+ localStorage->m_ReslicerVector[groupID]->SetOutputSpacingZDirection(1.0);
+ localStorage->m_ReslicerVector[groupID]->SetOutputExtentZDirection(0, 0);
// Bounds information for reslicing (only required if reference geometry is present)
// this used for generating a vtkPLaneSource with the right size
double sliceBounds[6];
sliceBounds[0] = 0.0;
sliceBounds[1] = 0.0;
sliceBounds[2] = 0.0;
sliceBounds[3] = 0.0;
sliceBounds[4] = 0.0;
sliceBounds[5] = 0.0;
- localStorage->m_ReslicerVector[lidx]->GetClippedPlaneBounds(sliceBounds);
+ localStorage->m_ReslicerVector[groupID]->GetClippedPlaneBounds(sliceBounds);
// setup the textured plane
this->GeneratePlane(renderer, sliceBounds);
// get the spacing of the slice
- localStorage->m_mmPerPixel = localStorage->m_ReslicerVector[lidx]->GetOutputSpacing();
- localStorage->m_ReslicerVector[lidx]->Modified();
+ localStorage->m_mmPerPixel = localStorage->m_ReslicerVector[groupID]->GetOutputSpacing();
+ localStorage->m_ReslicerVector[groupID]->Modified();
// start the pipeline with updating the largest possible, needed if the geometry of the image has changed
- localStorage->m_ReslicerVector[lidx]->UpdateLargestPossibleRegion();
- localStorage->m_ReslicedImageVector[lidx] = localStorage->m_ReslicerVector[lidx]->GetVtkOutput();
-
- const auto *planeGeometry = dynamic_cast(worldGeometry);
-
- double textureClippingBounds[6];
- for (auto &textureClippingBound : textureClippingBounds)
- {
- textureClippingBound = 0.0;
- }
-
- // Calculate the actual bounds of the transformed plane clipped by the
- // dataset bounding box; this is required for drawing the texture at the
- // correct position during 3D mapping.
- mitk::PlaneClipping::CalculateClippedPlaneBounds(layerImage->GetGeometry(), planeGeometry, textureClippingBounds);
-
- textureClippingBounds[0] = static_cast(textureClippingBounds[0] / localStorage->m_mmPerPixel[0] + 0.5);
- textureClippingBounds[1] = static_cast(textureClippingBounds[1] / localStorage->m_mmPerPixel[0] + 0.5);
- textureClippingBounds[2] = static_cast(textureClippingBounds[2] / localStorage->m_mmPerPixel[1] + 0.5);
- textureClippingBounds[3] = static_cast(textureClippingBounds[3] / localStorage->m_mmPerPixel[1] + 0.5);
-
- // clipping bounds for cutting the imageLayer
- localStorage->m_LevelWindowFilterVector[lidx]->SetClippingBounds(textureClippingBounds);
-
- localStorage->m_LevelWindowFilterVector[lidx]->SetLookupTable(
- image->GetLookupTable()->GetVtkLookupTable());
-
- // do not use a VTK lookup table (we do that ourselves in m_LevelWindowFilter)
- localStorage->m_LayerTextureVector[lidx]->SetColorModeToDirectScalars();
-
- // connect the imageLayer with the levelwindow filter
- localStorage->m_LevelWindowFilterVector[lidx]->SetInputData(localStorage->m_ReslicedImageVector[lidx]);
- // connect the texture with the output of the levelwindow filter
-
- // check for texture interpolation property
- bool textureInterpolation = false;
- node->GetBoolProperty("texture interpolation", textureInterpolation, renderer);
-
- // set the interpolation modus according to the property
- localStorage->m_LayerTextureVector[lidx]->SetInterpolate(textureInterpolation);
-
- localStorage->m_LayerTextureVector[lidx]->SetInputConnection(
- localStorage->m_LevelWindowFilterVector[lidx]->GetOutputPort());
+ localStorage->m_ReslicerVector[groupID]->UpdateLargestPossibleRegion();
+ localStorage->m_ReslicedImageVector[groupID] = localStorage->m_ReslicerVector[groupID]->GetVtkOutput();
+ }
+ localStorage->m_LastDataUpdateTime.Modified();
+}
- this->TransformActor(renderer);
+void mitk::LabelSetImageVtkMapper2D::GenerateActiveLabelOutline(mitk::BaseRenderer* renderer)
+{
+ LocalStorage* localStorage = m_LSH.GetLocalStorage(renderer);
+ mitk::DataNode* node = this->GetDataNode();
+ auto* image = dynamic_cast(node->GetData());
- // set the plane as input for the mapper
- localStorage->m_LayerMapperVector[lidx]->SetInputConnection(localStorage->m_Plane->GetOutputPort());
+ int activeLayer = image->GetActiveLayer();
- // set the texture for the actor
- localStorage->m_LayerActorVector[lidx]->SetTexture(localStorage->m_LayerTextureVector[lidx]);
- localStorage->m_LayerActorVector[lidx]->GetProperty()->SetOpacity(opacity);
- }
+ float opacity = 1.0f;
+ node->GetOpacity(opacity, renderer, "opacity");
mitk::Label* activeLabel = image->GetActiveLabel();
- if (nullptr != activeLabel)
+ bool contourActive = false;
+ node->GetBoolProperty("labelset.contour.active", contourActive, renderer);
+ if (nullptr != activeLabel && contourActive && activeLabel->GetVisible())
{
- bool contourActive = false;
- node->GetBoolProperty("labelset.contour.active", contourActive, renderer);
- if (contourActive && activeLabel->GetVisible()) //contour rendering
- {
- //generate contours/outlines
- localStorage->m_OutlinePolyData =
- this->CreateOutlinePolyData(renderer, localStorage->m_ReslicedImageVector[activeLayer], activeLabel->GetValue());
- localStorage->m_OutlineActor->SetVisibility(true);
- localStorage->m_OutlineShadowActor->SetVisibility(true);
- const mitk::Color& color = activeLabel->GetColor();
- localStorage->m_OutlineActor->GetProperty()->SetColor(color.GetRed(), color.GetGreen(), color.GetBlue());
- localStorage->m_OutlineShadowActor->GetProperty()->SetColor(0, 0, 0);
-
- float contourWidth(2.0);
- node->GetFloatProperty("labelset.contour.width", contourWidth, renderer);
- localStorage->m_OutlineActor->GetProperty()->SetLineWidth(contourWidth);
- localStorage->m_OutlineShadowActor->GetProperty()->SetLineWidth(contourWidth * 1.5);
-
- localStorage->m_OutlineActor->GetProperty()->SetOpacity(opacity);
- localStorage->m_OutlineShadowActor->GetProperty()->SetOpacity(opacity);
-
- localStorage->m_OutlineMapper->SetInputData(localStorage->m_OutlinePolyData);
- return;
- }
+ //generate contours/outlines
+ localStorage->m_OutlinePolyData =
+ this->CreateOutlinePolyData(renderer, localStorage->m_ReslicedImageVector[activeLayer], activeLabel->GetValue());
+ localStorage->m_OutlineActor->SetVisibility(true);
+ localStorage->m_OutlineShadowActor->SetVisibility(true);
+ const mitk::Color& color = activeLabel->GetColor();
+ localStorage->m_OutlineActor->GetProperty()->SetColor(color.GetRed(), color.GetGreen(), color.GetBlue());
+ localStorage->m_OutlineShadowActor->GetProperty()->SetColor(0, 0, 0);
+
+ float contourWidth(2.0);
+ node->GetFloatProperty("labelset.contour.width", contourWidth, renderer);
+ localStorage->m_OutlineActor->GetProperty()->SetLineWidth(contourWidth);
+ localStorage->m_OutlineShadowActor->GetProperty()->SetLineWidth(contourWidth * 1.5);
+
+ localStorage->m_OutlineActor->GetProperty()->SetOpacity(opacity);
+ localStorage->m_OutlineShadowActor->GetProperty()->SetOpacity(opacity);
+
+ localStorage->m_OutlineMapper->SetInputData(localStorage->m_OutlinePolyData);
+ }
+ else
+ {
+ localStorage->m_OutlineActor->SetVisibility(false);
+ localStorage->m_OutlineShadowActor->SetVisibility(false);
}
- localStorage->m_OutlineActor->SetVisibility(false);
- localStorage->m_OutlineShadowActor->SetVisibility(false);
+ localStorage->m_LastActiveLabelUpdateTime.Modified();
}
+
bool mitk::LabelSetImageVtkMapper2D::RenderingGeometryIntersectsImage(const PlaneGeometry *renderingGeometry,
- SlicedGeometry3D *imageGeometry)
+ const BaseGeometry *imageGeometry) const
{
// if either one of the two geometries is nullptr we return true
// for safety reasons
if (renderingGeometry == nullptr || imageGeometry == nullptr)
return true;
// get the distance for the first cornerpoint
ScalarType initialDistance = renderingGeometry->SignedDistance(imageGeometry->GetCornerPoint(0));
for (int i = 1; i < 8; i++)
{
mitk::Point3D cornerPoint = imageGeometry->GetCornerPoint(i);
// get the distance to the other cornerpoints
ScalarType distance = renderingGeometry->SignedDistance(cornerPoint);
// if it has not the same signing as the distance of the first point
if (initialDistance * distance < 0)
{
// we have an intersection and return true
return true;
}
}
// all distances have the same sign, no intersection and we return false
return false;
}
vtkSmartPointer mitk::LabelSetImageVtkMapper2D::CreateOutlinePolyData(mitk::BaseRenderer *renderer,
vtkImageData *image,
int pixelValue)
{
LocalStorage *localStorage = this->GetLocalStorage(renderer);
// get the min and max index values of each direction
int *extent = image->GetExtent();
int xMin = extent[0];
int xMax = extent[1];
int yMin = extent[2];
int yMax = extent[3];
int *dims = image->GetDimensions(); // dimensions of the image
int line = dims[0]; // how many pixels per line?
int x = xMin; // pixel index x
int y = yMin; // pixel index y
// get the depth for each contour
float depth = this->CalculateLayerDepth(renderer);
vtkSmartPointer points = vtkSmartPointer::New(); // the points to draw
vtkSmartPointer lines = vtkSmartPointer::New(); // the lines to connect the points
// We take the pointer to the first pixel of the image
auto *currentPixel = static_cast(image->GetScalarPointer());
while (y <= yMax)
{
// if the current pixel value is set to something
if ((currentPixel) && (*currentPixel == pixelValue))
{
// check in which direction a line is necessary
// a line is added if the neighbor of the current pixel has the value 0
// and if the pixel is located at the edge of the image
// if vvvvv not the first line vvvvv
if (y > yMin && *(currentPixel - line) != pixelValue)
{ // x direction - bottom edge of the pixel
// add the 2 points
vtkIdType p1 =
points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth);
vtkIdType p2 =
points->InsertNextPoint((x + 1) * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth);
// add the line between both points
lines->InsertNextCell(2);
lines->InsertCellPoint(p1);
lines->InsertCellPoint(p2);
}
// if vvvvv not the last line vvvvv
if (y < yMax && *(currentPixel + line) != pixelValue)
{ // x direction - top edge of the pixel
vtkIdType p1 =
points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth);
vtkIdType p2 = points->InsertNextPoint(
(x + 1) * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth);
lines->InsertNextCell(2);
lines->InsertCellPoint(p1);
lines->InsertCellPoint(p2);
}
// if vvvvv not the first pixel vvvvv
if ((x > xMin || y > yMin) && *(currentPixel - 1) != pixelValue)
{ // y direction - left edge of the pixel
vtkIdType p1 =
points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth);
vtkIdType p2 =
points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth);
lines->InsertNextCell(2);
lines->InsertCellPoint(p1);
lines->InsertCellPoint(p2);
}
// if vvvvv not the last pixel vvvvv
if ((y < yMax || (x < xMax)) && *(currentPixel + 1) != pixelValue)
{ // y direction - right edge of the pixel
vtkIdType p1 =
points->InsertNextPoint((x + 1) * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth);
vtkIdType p2 = points->InsertNextPoint(
(x + 1) * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth);
lines->InsertNextCell(2);
lines->InsertCellPoint(p1);
lines->InsertCellPoint(p2);
}
/* now consider pixels at the edge of the image */
// if vvvvv left edge of image vvvvv
if (x == xMin)
{ // draw left edge of the pixel
vtkIdType p1 =
points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth);
vtkIdType p2 =
points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth);
lines->InsertNextCell(2);
lines->InsertCellPoint(p1);
lines->InsertCellPoint(p2);
}
// if vvvvv right edge of image vvvvv
if (x == xMax)
{ // draw right edge of the pixel
vtkIdType p1 =
points->InsertNextPoint((x + 1) * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth);
vtkIdType p2 = points->InsertNextPoint(
(x + 1) * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth);
lines->InsertNextCell(2);
lines->InsertCellPoint(p1);
lines->InsertCellPoint(p2);
}
// if vvvvv bottom edge of image vvvvv
if (y == yMin)
{ // draw bottom edge of the pixel
vtkIdType p1 =
points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth);
vtkIdType p2 =
points->InsertNextPoint((x + 1) * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth);
lines->InsertNextCell(2);
lines->InsertCellPoint(p1);
lines->InsertCellPoint(p2);
}
// if vvvvv top edge of image vvvvv
if (y == yMax)
{ // draw top edge of the pixel
vtkIdType p1 =
points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth);
vtkIdType p2 = points->InsertNextPoint(
(x + 1) * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth);
lines->InsertNextCell(2);
lines->InsertCellPoint(p1);
lines->InsertCellPoint(p2);
}
} // end if currentpixel is set
x++;
if (x > xMax)
{ // reached end of line
x = xMin;
y++;
}
// Increase the pointer-position to the next pixel.
// This is safe, as the while-loop and the x-reset logic above makes
// sure we do not exceed the bounds of the image
currentPixel++;
} // end of while
// Create a polydata to store everything in
vtkSmartPointer polyData = vtkSmartPointer::New();
// Add the points to the dataset
polyData->SetPoints(points);
// Add the lines to the dataset
polyData->SetLines(lines);
return polyData;
}
-void mitk::LabelSetImageVtkMapper2D::ApplyColor(mitk::BaseRenderer *renderer, const mitk::Color &color)
-{
- LocalStorage *localStorage = this->GetLocalStorage(renderer);
- localStorage->m_OutlineActor->GetProperty()->SetColor(color.GetRed(), color.GetGreen(), color.GetBlue());
- localStorage->m_OutlineShadowActor->GetProperty()->SetColor(0, 0, 0);
-}
-
-void mitk::LabelSetImageVtkMapper2D::ApplyOpacity(mitk::BaseRenderer *renderer, int layer)
-{
- LocalStorage *localStorage = this->GetLocalStorage(renderer);
- float opacity = 1.0f;
- this->GetDataNode()->GetOpacity(opacity, renderer, "opacity");
- localStorage->m_LayerActorVector[layer]->GetProperty()->SetOpacity(opacity);
- localStorage->m_OutlineActor->GetProperty()->SetOpacity(opacity);
- localStorage->m_OutlineShadowActor->GetProperty()->SetOpacity(opacity);
-}
-
-void mitk::LabelSetImageVtkMapper2D::ApplyLookuptable(mitk::BaseRenderer *renderer, int layer)
-{
- LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer);
- auto *input = dynamic_cast(this->GetDataNode()->GetData());
- localStorage->m_LevelWindowFilterVector[layer]->SetLookupTable(
- input->GetLookupTable()->GetVtkLookupTable());
-}
-
void mitk::LabelSetImageVtkMapper2D::Update(mitk::BaseRenderer *renderer)
{
bool visible = true;
const DataNode *node = this->GetDataNode();
node->GetVisibility(visible, renderer, "visible");
if (!visible)
return;
auto *image = dynamic_cast(node->GetData());
if (image == nullptr || image->IsInitialized() == false)
return;
// Calculate time step of the image data for the specified renderer (integer value)
this->CalculateTimeStep(renderer);
// Check if time step is valid
const TimeGeometry *dataTimeGeometry = image->GetTimeGeometry();
if ((dataTimeGeometry == nullptr) || (dataTimeGeometry->CountTimeSteps() == 0) ||
(!dataTimeGeometry->IsValidTimeStep(this->GetTimestep())))
{
return;
}
image->UpdateOutputInformation();
LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer);
// check if something important has changed and we need to re-render
- if ((localStorage->m_LastDataUpdateTime < image->GetMTime()) ||
+ if (localStorage->m_LabelLookupTable.IsNull() ||
+ (localStorage->m_LabelLookupTable->GetMTime() < image->GetLookupTable()->GetMTime()) ||
+ (localStorage->m_LastDataUpdateTime < image->GetMTime()) ||
(localStorage->m_LastDataUpdateTime < image->GetPipelineMTime()) ||
(localStorage->m_LastDataUpdateTime < renderer->GetCurrentWorldPlaneGeometryUpdateTime()) ||
- (localStorage->m_LastDataUpdateTime < renderer->GetCurrentWorldPlaneGeometry()->GetMTime()))
+ (localStorage->m_LastDataUpdateTime < renderer->GetCurrentWorldPlaneGeometry()->GetMTime()) ||
+ (localStorage->m_LastPropertyUpdateTime < node->GetPropertyList()->GetMTime()) ||
+ (localStorage->m_LastPropertyUpdateTime < node->GetPropertyList(renderer)->GetMTime()) ||
+ (localStorage->m_LastPropertyUpdateTime < image->GetPropertyList()->GetMTime()))
{
this->GenerateDataForRenderer(renderer);
- localStorage->m_LastDataUpdateTime.Modified();
+ localStorage->m_LastPropertyUpdateTime.Modified();
}
else if ((localStorage->m_LastPropertyUpdateTime < node->GetPropertyList()->GetMTime()) ||
(localStorage->m_LastPropertyUpdateTime < node->GetPropertyList(renderer)->GetMTime()) ||
(localStorage->m_LastPropertyUpdateTime < image->GetPropertyList()->GetMTime()))
{
this->GenerateDataForRenderer(renderer);
localStorage->m_LastPropertyUpdateTime.Modified();
}
}
// set the two points defining the textured plane according to the dimension and spacing
void mitk::LabelSetImageVtkMapper2D::GeneratePlane(mitk::BaseRenderer *renderer, double planeBounds[6])
{
LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer);
float depth = this->CalculateLayerDepth(renderer);
// Set the origin to (xMin; yMin; depth) of the plane. This is necessary for obtaining the correct
// plane size in crosshair rotation and swivel mode.
localStorage->m_Plane->SetOrigin(planeBounds[0], planeBounds[2], depth);
// These two points define the axes of the plane in combination with the origin.
// Point 1 is the x-axis and point 2 the y-axis.
// Each plane is transformed according to the view (axial, coronal and sagittal) afterwards.
localStorage->m_Plane->SetPoint1(planeBounds[1], planeBounds[2], depth); // P1: (xMax, yMin, depth)
localStorage->m_Plane->SetPoint2(planeBounds[0], planeBounds[3], depth); // P2: (xMin, yMax, depth)
}
float mitk::LabelSetImageVtkMapper2D::CalculateLayerDepth(mitk::BaseRenderer *renderer)
{
// get the clipping range to check how deep into z direction we can render images
double maxRange = renderer->GetVtkRenderer()->GetActiveCamera()->GetClippingRange()[1];
// Due to a VTK bug, we cannot use the whole clipping range. /100 is empirically determined
float depth = -maxRange * 0.01; // divide by 100
int layer = 0;
GetDataNode()->GetIntProperty("layer", layer, renderer);
// add the layer property for each image to render images with a higher layer on top of the others
depth += layer * 10; //*10: keep some room for each image (e.g. for ODFs in between)
if (depth > 0.0f)
{
depth = 0.0f;
MITK_WARN << "Layer value exceeds clipping range. Set to minimum instead.";
}
return depth;
}
void mitk::LabelSetImageVtkMapper2D::TransformActor(mitk::BaseRenderer *renderer)
{
LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer);
// get the transformation matrix of the reslicer in order to render the slice as axial, coronal or sagittal
vtkSmartPointer trans = vtkSmartPointer::New();
vtkSmartPointer matrix = localStorage->m_ReslicerVector[0]->GetResliceAxes(); // same for all layers
trans->SetMatrix(matrix);
- for (int lidx = 0; lidx < localStorage->m_NumberOfLayers; ++lidx)
+ for (unsigned int lidx = 0; lidx < localStorage->m_NumberOfLayers; ++lidx)
{
// transform the plane/contour (the actual actor) to the corresponding view (axial, coronal or sagittal)
localStorage->m_LayerActorVector[lidx]->SetUserTransform(trans);
// transform the origin to center based coordinates, because MITK is center based.
localStorage->m_LayerActorVector[lidx]->SetPosition(
-0.5 * localStorage->m_mmPerPixel[0], -0.5 * localStorage->m_mmPerPixel[1], 0.0);
}
// same for outline actor
localStorage->m_OutlineActor->SetUserTransform(trans);
localStorage->m_OutlineActor->SetPosition(
-0.5 * localStorage->m_mmPerPixel[0], -0.5 * localStorage->m_mmPerPixel[1], 0.0);
// same for outline shadow actor
localStorage->m_OutlineShadowActor->SetUserTransform(trans);
localStorage->m_OutlineShadowActor->SetPosition(
-0.5 * localStorage->m_mmPerPixel[0], -0.5 * localStorage->m_mmPerPixel[1], 0.0);
}
void mitk::LabelSetImageVtkMapper2D::SetDefaultProperties(mitk::DataNode *node,
mitk::BaseRenderer *renderer,
bool overwrite)
{
// add/replace the following properties
node->SetProperty("opacity", FloatProperty::New(1.0f), renderer);
node->SetProperty("binary", BoolProperty::New(false), renderer);
- mitk::RenderingModeProperty::Pointer renderingModeProperty =
- mitk::RenderingModeProperty::New(RenderingModeProperty::LOOKUPTABLE_LEVELWINDOW_COLOR);
- node->SetProperty("Image Rendering.Mode", renderingModeProperty, renderer);
-
- mitk::LevelWindow levelwindow(32767.5, 65535);
- mitk::LevelWindowProperty::Pointer levWinProp = mitk::LevelWindowProperty::New(levelwindow);
-
- levWinProp->SetLevelWindow(levelwindow);
- node->SetProperty("levelwindow", levWinProp, renderer);
-
node->SetProperty("labelset.contour.active", BoolProperty::New(true), renderer);
node->SetProperty("labelset.contour.width", FloatProperty::New(2.0), renderer);
Superclass::SetDefaultProperties(node, renderer, overwrite);
}
mitk::LabelSetImageVtkMapper2D::LocalStorage::~LocalStorage()
{
}
mitk::LabelSetImageVtkMapper2D::LocalStorage::LocalStorage()
{
// Do as much actions as possible in here to avoid double executions.
m_Plane = vtkSmartPointer::New();
m_Actors = vtkSmartPointer::New();
m_OutlinePolyData = vtkSmartPointer::New();
m_EmptyPolyData = vtkSmartPointer::New();
m_OutlineActor = vtkSmartPointer::New();
m_OutlineMapper = vtkSmartPointer::New();
m_OutlineShadowActor = vtkSmartPointer::New();
+ m_HasValidContent = false;
m_NumberOfLayers = 0;
m_mmPerPixel = nullptr;
m_OutlineActor->SetMapper(m_OutlineMapper);
m_OutlineShadowActor->SetMapper(m_OutlineMapper);
m_OutlineActor->SetVisibility(false);
m_OutlineShadowActor->SetVisibility(false);
}
diff --git a/Modules/Multilabel/mitkLabelSetImageVtkMapper2D.h b/Modules/Multilabel/mitkLabelSetImageVtkMapper2D.h
index a5363284fa..c213b7fff5 100644
--- a/Modules/Multilabel/mitkLabelSetImageVtkMapper2D.h
+++ b/Modules/Multilabel/mitkLabelSetImageVtkMapper2D.h
@@ -1,241 +1,231 @@
/*============================================================================
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 mitkLabelSetImageVtkMapper2D_h
#define mitkLabelSetImageVtkMapper2D_h
// MITK
#include "MitkMultilabelExports.h"
#include "mitkCommon.h"
// MITK Rendering
#include "mitkBaseRenderer.h"
#include "mitkExtractSliceFilter.h"
#include "mitkLabelSetImage.h"
#include "mitkVtkMapper.h"
// VTK
#include
class vtkActor;
class vtkPolyDataMapper;
class vtkPlaneSource;
class vtkImageData;
class vtkLookupTable;
class vtkImageReslice;
class vtkPoints;
class vtkMitkThickSlicesFilter;
class vtkPolyData;
-class vtkMitkLevelWindowFilter;
class vtkNeverTranslucentTexture;
+class vtkImageMapToColors;
namespace mitk
{
/** \brief Mapper to resample and display 2D slices of a 3D labelset image.
*
* Properties that can be set for labelset images and influence this mapper are:
*
* - \b "labelset.contour.active": (BoolProperty) whether to show only the active label as a contour or not
* - \b "labelset.contour.width": (FloatProperty) line width of the contour
* The default properties are:
* - \b "labelset.contour.active", mitk::BoolProperty::New( true ), renderer, overwrite )
* - \b "labelset.contour.width", mitk::FloatProperty::New( 2.0 ), renderer, overwrite )
* \ingroup Mapper
*/
class MITKMULTILABEL_EXPORT LabelSetImageVtkMapper2D : public VtkMapper
{
public:
/** Standard class typedefs. */
mitkClassMacro(LabelSetImageVtkMapper2D, VtkMapper);
/** Method for creation through the object factory. */
itkNewMacro(Self);
/** \brief Get the Image to map */
const mitk::Image *GetInput(void);
/** \brief Checks whether this mapper needs to update itself and generate
* data. */
void Update(mitk::BaseRenderer *renderer) override;
//### methods of MITK-VTK rendering pipeline
vtkProp *GetVtkProp(mitk::BaseRenderer *renderer) override;
//### end of methods of MITK-VTK rendering pipeline
/** \brief Internal class holding the mapper, actor, etc. for each of the 3 2D render windows */
/**
* To render axial, coronal, and sagittal, the mapper is called three times.
* For performance reasons, the corresponding data for each view is saved in the
* internal helper class LocalStorage. This allows rendering n views with just
* 1 mitkMapper using n vtkMapper.
* */
class MITKMULTILABEL_EXPORT LocalStorage : public mitk::Mapper::BaseLocalStorage
{
public:
vtkSmartPointer m_Actors;
+ /** Vector containing the pointer of the currently used group images.
+ * IMPORTANT: This member must not be used to access any data.
+ * Its purpose is to allow checking if the order of the groups has changed
+ * in order to adapt the pipe line accordingly*/
+ std::vector m_GroupImageIDs;
+
std::vector> m_LayerActorVector;
std::vector> m_LayerMapperVector;
std::vector> m_ReslicedImageVector;
+ std::vector> m_LayerImageMapToColors;
std::vector> m_LayerTextureVector;
vtkSmartPointer m_EmptyPolyData;
vtkSmartPointer m_Plane;
std::vector m_ReslicerVector;
vtkSmartPointer m_OutlinePolyData;
/** \brief An actor for the outline */
vtkSmartPointer m_OutlineActor;
/** \brief An actor for the outline shadow*/
vtkSmartPointer m_OutlineShadowActor;
/** \brief A mapper for the outline */
vtkSmartPointer m_OutlineMapper;
/** \brief Timestamp of last update of stored data. */
itk::TimeStamp m_LastDataUpdateTime;
-
/** \brief Timestamp of last update of a property. */
itk::TimeStamp m_LastPropertyUpdateTime;
+ /** \brief Timestamp of last update of a property. */
+ itk::TimeStamp m_LastActiveLabelUpdateTime;
/** \brief mmPerPixel relation between pixel and mm. (World spacing).*/
mitk::ScalarType *m_mmPerPixel;
- int m_NumberOfLayers;
+ /** look up table for label colors. */
+ mitk::LookupTable::Pointer m_LabelLookupTable;
+
+ mitk::PlaneGeometry::Pointer m_WorldPlane;
+ bool m_HasValidContent;
- /** \brief This filter is used to apply the level window to Grayvalue and RBG(A) images. */
- // vtkSmartPointer m_LevelWindowFilter;
- std::vector> m_LevelWindowFilterVector;
+ unsigned int m_NumberOfLayers;
/** \brief Default constructor of the local storage. */
LocalStorage();
- /** \brief Default deconstructor of the local storage. */
+ /** \brief Default destructor of the local storage. */
~LocalStorage() override;
};
/** \brief The LocalStorageHandler holds all (three) LocalStorages for the three 2D render windows. */
mitk::LocalStorageHandler m_LSH;
/** \brief Get the LocalStorage corresponding to the current renderer. */
LocalStorage *GetLocalStorage(mitk::BaseRenderer *renderer);
/** \brief Set the default properties for general image rendering. */
static void SetDefaultProperties(mitk::DataNode *node, mitk::BaseRenderer *renderer = nullptr, bool overwrite = false);
/** \brief This method switches between different rendering modes (e.g. use a lookup table or a transfer function).
* Detailed documentation about the modes can be found here: \link mitk::RenderingModeProperty \endlink
*/
void ApplyRenderingMode(mitk::BaseRenderer *renderer);
protected:
/** \brief Transforms the actor to the actual position in 3D.
* \param renderer The current renderer corresponding to the render window.
*/
void TransformActor(mitk::BaseRenderer *renderer);
/** \brief Generates a plane according to the size of the resliced image in milimeters.
*
* In VTK a vtkPlaneSource is defined through three points. The origin and two
* points defining the axes of the plane (see VTK documentation). The origin is
* set to (xMin; yMin; Z), where xMin and yMin are the minimal bounds of the
* resliced image in space. Z is relevant for blending and the layer property.
* The center of the plane (C) is also the center of the view plane (cf. the image above).
*
* \note For the standard MITK view with three 2D render windows showing three
* different slices, three such planes are generated. All these planes are generated
* in the XY-plane (even if they depict a YZ-slice of the volume).
*
*/
void GeneratePlane(mitk::BaseRenderer *renderer, double planeBounds[6]);
/** \brief Generates a vtkPolyData object containing the outline of a given binary slice.
\param renderer Pointer to the renderer containing the needed information
\param image
\param pixelValue
\note This code is based on code from the iil library.
*/
vtkSmartPointer CreateOutlinePolyData(mitk::BaseRenderer *renderer,
vtkImageData *image,
int pixelValue = 1);
/** Default constructor */
LabelSetImageVtkMapper2D();
/** Default deconstructor */
~LabelSetImageVtkMapper2D() override;
/** \brief Does the actual resampling, without rendering the image yet.
* All the data is generated inside this method. The vtkProp (or Actor)
* is filled with content (i.e. the resliced image).
*
* After generation, a 4x4 transformation matrix(t) of the current slice is obtained
* from the vtkResliceImage object via GetReslicesAxis(). This matrix is
* applied to each textured plane (actor->SetUserTransform(t)) to transform everything
* to the actual 3D position (cf. the following image).
*
* \image html cameraPositioning3D.png
*
*/
void GenerateDataForRenderer(mitk::BaseRenderer *renderer) override;
+ void GenerateImageSlice(mitk::BaseRenderer* renderer, const std::vector& outdatedGroupIDs);
+
+ void GenerateActiveLabelOutline(mitk::BaseRenderer* renderer);
+
+ /** \brief Generates the look up table that should be used.
+ */
+ void GenerateLookupTable(mitk::BaseRenderer* renderer);
+
/** \brief This method uses the vtkCamera clipping range and the layer property
* to calcualte the depth of the object (e.g. image or contour). The depth is used
* to keep the correct order for the final VTK rendering.*/
float CalculateLayerDepth(mitk::BaseRenderer *renderer);
- /** \brief This method applies (or modifies) the lookuptable for all types of images.
- * \warning To use the lookup table, the property 'Lookup Table' must be set and a 'Image Rendering.Mode'
- * which uses the lookup table must be set.
- */
- void ApplyLookuptable(mitk::BaseRenderer *renderer, int layer);
-
- /** \brief This method applies a color transfer function.
- * Internally, a vtkColorTransferFunction is used. This is usefull for coloring continous
- * images (e.g. float)
- * \warning To use the color transfer function, the property 'Image Rendering.Transfer Function' must be set and a
- * 'Image Rendering.Mode' which uses the color transfer function must be set.
- */
- void ApplyColorTransferFunction(mitk::BaseRenderer *renderer);
-
- /**
- * @brief ApplyLevelWindow Apply the level window for the given renderer.
- * \warning To use the level window, the property 'LevelWindow' must be set and a 'Image Rendering.Mode' which uses
- * the level window must be set.
- * @param renderer Level window for which renderer?
- */
- void ApplyLevelWindow(mitk::BaseRenderer *renderer);
-
- /** \brief Set the color of the image/polydata */
- void ApplyColor(mitk::BaseRenderer *renderer, const mitk::Color &color);
-
- /** \brief Set the opacity of the actor. */
- void ApplyOpacity(mitk::BaseRenderer *renderer, int layer);
-
/**
* \brief Calculates whether the given rendering geometry intersects the
* given SlicedGeometry3D.
*
* This method checks if the given Geometry2D intersects the given
* SlicedGeometry3D. It calculates the distance of the Geometry2D to all
* 8 cornerpoints of the SlicedGeometry3D. If all distances have the same
* sign (all positive or all negative) there is no intersection.
* If the distances have different sign, there is an intersection.
**/
- bool RenderingGeometryIntersectsImage(const PlaneGeometry *renderingGeometry, SlicedGeometry3D *imageGeometry);
+ bool RenderingGeometryIntersectsImage(const PlaneGeometry *renderingGeometry, const BaseGeometry* imageGeometry) const;
};
} // namespace mitk
#endif
diff --git a/Modules/Multilabel/mitkMultiLabelSegmentationVtkMapper3D.cpp b/Modules/Multilabel/mitkMultiLabelSegmentationVtkMapper3D.cpp
new file mode 100644
index 0000000000..27a5d47030
--- /dev/null
+++ b/Modules/Multilabel/mitkMultiLabelSegmentationVtkMapper3D.cpp
@@ -0,0 +1,350 @@
+/*============================================================================
+
+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 "mitkMultiLabelSegmentationVtkMapper3D.h"
+
+// MITK
+#include