Medical Imaging Interaction Toolkit  2016.11.0
Medical Imaging Interaction Toolkit
mitkBinaryThresholdTool.cpp
Go to the documentation of this file.
1 /*===================================================================
2 
3 The Medical Imaging Interaction Toolkit (MITK)
4 
5 Copyright (c) German Cancer Research Center,
6 Division of Medical and Biological Informatics.
7 All rights reserved.
8 
9 This software is distributed WITHOUT ANY WARRANTY; without
10 even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 A PARTICULAR PURPOSE.
12 
13 See LICENSE.txt or http://www.mitk.org for details.
14 
15 ===================================================================*/
16 
18 
20 #include "mitkToolManager.h"
21 
22 #include "mitkColorProperty.h"
23 #include "mitkDataStorage.h"
25 #include "mitkOrganTypeProperty.h"
26 #include "mitkProperties.h"
27 #include "mitkRenderingManager.h"
29 #include <mitkCoreObjectFactory.h>
30 
31 #include "mitkImageAccessByItk.h"
32 #include "mitkImageCast.h"
34 #include "mitkImageTimeSelector.h"
35 #include "mitkLabelSetImage.h"
37 #include "mitkPadImageFilter.h"
38 #include <itkBinaryThresholdImageFilter.h>
39 #include <itkImageRegionIterator.h>
40 
41 // us
42 #include "usGetModuleContext.h"
43 #include "usModule.h"
44 #include "usModuleResource.h"
45 
46 namespace mitk
47 {
49 }
50 
52  : m_SensibleMinimumThresholdValue(-100),
53  m_SensibleMaximumThresholdValue(+100),
54  m_CurrentThresholdValue(0.0),
55  m_IsFloatImage(false)
56 {
58  m_ThresholdFeedbackNode->SetProperty("color", ColorProperty::New(0.0, 1.0, 0.0));
59  m_ThresholdFeedbackNode->SetProperty("name", StringProperty::New("Thresholding feedback"));
60  m_ThresholdFeedbackNode->SetProperty("opacity", FloatProperty::New(0.3));
61  m_ThresholdFeedbackNode->SetProperty("binary", BoolProperty::New(true));
62  m_ThresholdFeedbackNode->SetProperty("helper object", BoolProperty::New(true));
63 }
64 
66 {
67 }
68 
70 {
71  return NULL;
72 }
73 
75 {
77  us::ModuleResource resource = module->GetResource("Threshold_48x48.png");
78  return resource;
79 }
80 
82 {
83  return "Threshold";
84 }
85 
87 {
88  Superclass::Activated();
89 
90  m_ToolManager->RoiDataChanged +=
92 
93  m_OriginalImageNode = m_ToolManager->GetReferenceData(0);
94  m_NodeForThresholding = m_OriginalImageNode;
95 
96  if (m_NodeForThresholding.IsNotNull())
97  {
98  SetupPreviewNode();
99  }
100  else
101  {
102  m_ToolManager->ActivateTool(-1);
103  }
104 }
105 
107 {
108  m_ToolManager->RoiDataChanged -=
110  m_NodeForThresholding = NULL;
111  m_OriginalImageNode = NULL;
112  try
113  {
114  if (DataStorage *storage = m_ToolManager->GetDataStorage())
115  {
116  storage->Remove(m_ThresholdFeedbackNode);
118  }
119  }
120  catch (...)
121  {
122  // don't care
123  }
124  m_ThresholdFeedbackNode->SetData(NULL);
125 
126  Superclass::Deactivated();
127 }
128 
130 {
131  if (m_ThresholdFeedbackNode.IsNotNull())
132  {
133  m_CurrentThresholdValue = value;
134  // Bug 19250: The range of 0.01 is rather random. It was 0.001 before and probably due to rounding error propagation
135  // in VTK code
136  // it leads to strange banding effects on floating point images with a huge range (like -40000 - 40000). 0.01 lowers
137  // this effect
138  // enough to work with our images. Might not work on images with really huge ranges, though. Anyways, still seems to
139  // be low enough
140  // to work for floating point images with a range between 0 and 1. A better solution might be to dynamically
141  // calculate the value
142  // based on the value range of the current image (as big as possible, as small as necessary).
143  // m_ThresholdFeedbackNode->SetProperty( "levelwindow", LevelWindowProperty::New(
144  // LevelWindow(m_CurrentThresholdValue, 0.01) ) );
145  UpdatePreview();
146  }
147 }
148 
150 {
151  CreateNewSegmentationFromThreshold(m_NodeForThresholding);
152 
154  m_ToolManager->ActivateTool(-1);
155 }
156 
158 {
159  m_ToolManager->ActivateTool(-1);
160 }
161 
163 {
164  itk::RGBPixel<float> pixel;
165  pixel[0] = 0.0f;
166  pixel[1] = 1.0f;
167  pixel[2] = 0.0f;
168 
169  if (m_NodeForThresholding.IsNotNull())
170  {
171  Image::Pointer image = dynamic_cast<Image *>(m_NodeForThresholding->GetData());
172  Image::Pointer originalImage = dynamic_cast<Image *>(m_OriginalImageNode->GetData());
173 
174  if (image.IsNotNull())
175  {
176  mitk::LabelSetImage::Pointer workingImage =
177  dynamic_cast<mitk::LabelSetImage *>(m_ToolManager->GetWorkingData(0)->GetData());
178 
179  if (workingImage.IsNotNull())
180  {
181  m_ThresholdFeedbackNode->SetData(workingImage->Clone());
182  m_IsOldBinary = false;
183 
184  // Let's paint the feedback node green...
185  mitk::LabelSetImage::Pointer previewImage =
186  dynamic_cast<mitk::LabelSetImage *>(m_ThresholdFeedbackNode->GetData());
187 
188  if (previewImage.IsNull())
189  {
190  MITK_ERROR << "Cannot create helper objects.";
191  return;
192  }
193 
194  previewImage->GetActiveLabel()->SetColor(pixel);
195  previewImage->GetActiveLabelSet()->UpdateLookupTable(previewImage->GetActiveLabel()->GetValue());
196  }
197  else
198  {
199  mitk::Image::Pointer workingImageBin = dynamic_cast<mitk::Image *>(m_ToolManager->GetWorkingData(0)->GetData());
200  if (workingImageBin)
201  {
202  m_ThresholdFeedbackNode->SetData(workingImageBin->Clone());
203  m_IsOldBinary = true;
204  }
205  else
206  m_ThresholdFeedbackNode->SetData(mitk::Image::New());
207  }
208 
209  m_ThresholdFeedbackNode->SetColor(pixel);
210  m_ThresholdFeedbackNode->SetOpacity(0.5);
211 
212  int layer(50);
213  m_NodeForThresholding->GetIntProperty("layer", layer);
214  m_ThresholdFeedbackNode->SetIntProperty("layer", layer + 1);
215 
216  if (DataStorage *ds = m_ToolManager->GetDataStorage())
217  {
218  if (!ds->Exists(m_ThresholdFeedbackNode))
219  ds->Add(m_ThresholdFeedbackNode, m_OriginalImageNode);
220  }
221 
222  if (image.GetPointer() == originalImage.GetPointer())
223  {
224  Image::StatisticsHolderPointer statistics = originalImage->GetStatistics();
225  m_SensibleMinimumThresholdValue = static_cast<double>(statistics->GetScalarValueMin());
226  m_SensibleMaximumThresholdValue = static_cast<double>(statistics->GetScalarValueMax());
227  }
228 
229  if ((originalImage->GetPixelType().GetPixelType() == itk::ImageIOBase::SCALAR) &&
230  (originalImage->GetPixelType().GetComponentType() == itk::ImageIOBase::FLOAT ||
231  originalImage->GetPixelType().GetComponentType() == itk::ImageIOBase::DOUBLE))
232  m_IsFloatImage = true;
233  else
234  m_IsFloatImage = false;
235 
236  m_CurrentThresholdValue = (m_SensibleMaximumThresholdValue + m_SensibleMinimumThresholdValue) / 2.0;
237 
238  IntervalBordersChanged.Send(m_SensibleMinimumThresholdValue, m_SensibleMaximumThresholdValue, m_IsFloatImage);
239  ThresholdingValueChanged.Send(m_CurrentThresholdValue);
240  }
241  }
242 }
243 
244 template <typename TPixel, unsigned int VImageDimension>
245 static void ITKSetVolume(itk::Image<TPixel, VImageDimension> *originalImage,
246  mitk::Image *segmentation,
247  unsigned int timeStep)
248 {
249  segmentation->SetVolume((void *)originalImage->GetPixelContainer()->GetBufferPointer(), timeStep);
250 }
251 
253 {
254  if (node)
255  {
256  Image::Pointer feedBackImage = dynamic_cast<Image *>(m_ThresholdFeedbackNode->GetData());
257  if (feedBackImage.IsNotNull())
258  {
259  DataNode::Pointer emptySegmentation = GetTargetSegmentationNode();
260 
261  if (emptySegmentation)
262  {
263  // actually perform a thresholding and ask for an organ type
264  for (unsigned int timeStep = 0; timeStep < feedBackImage->GetTimeSteps(); ++timeStep)
265  {
266  try
267  {
269  timeSelector->SetInput(feedBackImage);
270  timeSelector->SetTimeNr(timeStep);
271  timeSelector->UpdateLargestPossibleRegion();
272  Image::Pointer image3D = timeSelector->GetOutput();
273 
274  if (image3D->GetDimension() == 2)
275  {
277  image3D, ITKSetVolume, 2, dynamic_cast<Image *>(emptySegmentation->GetData()), timeStep);
278  }
279  else
280  {
282  image3D, ITKSetVolume, 3, dynamic_cast<Image *>(emptySegmentation->GetData()), timeStep);
283  }
284  }
285  catch (...)
286  {
287  Tool::ErrorMessage("Error accessing single time steps of the original image. Cannot create segmentation.");
288  }
289  }
290 
291  if (m_OriginalImageNode.GetPointer() != m_NodeForThresholding.GetPointer())
292  {
294 
295  padFilter->SetInput(0, dynamic_cast<mitk::Image *>(emptySegmentation->GetData()));
296  padFilter->SetInput(1, dynamic_cast<mitk::Image *>(m_OriginalImageNode->GetData()));
297  padFilter->SetBinaryFilter(true);
298  padFilter->SetUpperThreshold(1);
299  padFilter->SetLowerThreshold(1);
300  padFilter->Update();
301 
302  emptySegmentation->SetData(padFilter->GetOutput());
303  }
304 
305  m_ToolManager->SetWorkingData(emptySegmentation);
306  m_ToolManager->GetWorkingData(0)->Modified();
307  }
308  }
309  }
310 }
311 
313 {
314  mitk::DataNode::Pointer node = m_ToolManager->GetRoiData(0);
315 
316  if (node.IsNotNull())
317  {
319  mitk::Image::Pointer image = dynamic_cast<mitk::Image *>(m_NodeForThresholding->GetData());
320 
321  if (image.IsNull())
322  return;
323 
324  roiFilter->SetInput(image);
325  roiFilter->SetRegionOfInterest(node->GetData());
326  roiFilter->Update();
327 
329  tmpNode->SetData(roiFilter->GetOutput());
330 
331  m_SensibleMaximumThresholdValue = static_cast<double>(roiFilter->GetMaxValue());
332  m_SensibleMinimumThresholdValue = static_cast<double>(roiFilter->GetMinValue());
333 
334  m_NodeForThresholding = tmpNode;
335  }
336  else
337  {
338  m_NodeForThresholding = m_OriginalImageNode;
339  }
340 
341  this->SetupPreviewNode();
342  this->UpdatePreview();
343 }
344 
345 template <typename TPixel, unsigned int VImageDimension>
346 void mitk::BinaryThresholdTool::ITKThresholding(itk::Image<TPixel, VImageDimension> *originalImage,
347  Image *segmentation,
348  double thresholdValue,
349  unsigned int timeStep)
350 {
351  typedef itk::Image<TPixel, VImageDimension> ImageType;
352  typedef itk::Image<mitk::Tool::DefaultSegmentationDataType, VImageDimension> SegmentationType;
353  typedef itk::BinaryThresholdImageFilter<ImageType, SegmentationType> ThresholdFilterType;
354 
356  filter->SetInput(originalImage);
357  filter->SetLowerThreshold(thresholdValue);
358  filter->SetUpperThreshold(m_SensibleMaximumThresholdValue);
359  filter->SetInsideValue(1);
360  filter->SetOutsideValue(0);
361  filter->Update();
362 
363  segmentation->SetVolume((void *)(filter->GetOutput()->GetPixelContainer()->GetBufferPointer()), timeStep);
364 }
365 
366 template <typename TPixel, unsigned int VImageDimension>
367 void mitk::BinaryThresholdTool::ITKThresholdingOldBinary(itk::Image<TPixel, VImageDimension> *originalImage,
368  Image *segmentation,
369  double thresholdValue,
370  unsigned int timeStep)
371 {
372  typedef itk::Image<TPixel, VImageDimension> ImageType;
373  typedef itk::Image<unsigned char, VImageDimension> SegmentationType;
374  typedef itk::BinaryThresholdImageFilter<ImageType, SegmentationType> ThresholdFilterType;
375 
377  filter->SetInput(originalImage);
378  filter->SetLowerThreshold(thresholdValue);
379  filter->SetUpperThreshold(m_SensibleMaximumThresholdValue);
380  filter->SetInsideValue(1);
381  filter->SetOutsideValue(0);
382  filter->Update();
383 
384  segmentation->SetVolume((void *)(filter->GetOutput()->GetPixelContainer()->GetBufferPointer()), timeStep);
385 }
386 
388 {
389  mitk::Image::Pointer thresholdImage = dynamic_cast<mitk::Image *>(m_NodeForThresholding->GetData());
390  mitk::Image::Pointer previewImage = dynamic_cast<mitk::Image *>(m_ThresholdFeedbackNode->GetData());
391  if (thresholdImage && previewImage)
392  {
393  for (unsigned int timeStep = 0; timeStep < thresholdImage->GetTimeSteps(); ++timeStep)
394  {
396  timeSelector->SetInput(thresholdImage);
397  timeSelector->SetTimeNr(timeStep);
398  timeSelector->UpdateLargestPossibleRegion();
399  Image::Pointer feedBackImage3D = timeSelector->GetOutput();
400 
401  if (m_IsOldBinary)
402  {
403  AccessByItk_n(feedBackImage3D, ITKThresholdingOldBinary, (previewImage, m_CurrentThresholdValue, timeStep));
404  }
405  else
406  {
407  AccessByItk_n(feedBackImage3D, ITKThresholding, (previewImage, m_CurrentThresholdValue, timeStep));
408  }
409  }
410 
412  }
413 }
Data management class that handles 'was created by' relations.
itk::SmartPointer< Self > Pointer
#define MITK_ERROR
Definition: mitkLogMacros.h:24
static Pointer New()
#define MITKSEGMENTATION_EXPORT
DataCollection - Class to facilitate loading/accessing structured data.
#define MITK_TOOL_MACRO(EXPORT_SPEC, CLASS_NAME, DESCRIPTION)
Message1< std::string > ErrorMessage
To send error messages (to be shown by some GUI)
Definition: mitkTool.h:105
#define AccessByItk_n(mitkImage, itkImageTypeFunction, va_tuple)
Access a MITK image by an ITK image with one or more parameters.
void CreateNewSegmentationFromThreshold(DataNode *node)
virtual bool SetVolume(const void *data, int t=0, int n=0)
Set data as volume at time t in channel n. It is in the responsibility of the caller to ensure that t...
Definition: mitkImage.cpp:673
virtual ScalarType GetScalarValueMin(int t=0, unsigned int component=0)
Get the minimum for scalar images. Recomputation performed only when necessary.
void ITKThresholding(itk::Image< TPixel, VImageDimension > *originalImage, mitk::Image *segmentation, double thresholdValue, unsigned int timeStep)
static Pointer New()
virtual void Activated() override
Called when the tool gets activated.
map::core::discrete::Elements< 3 >::InternalImageType ImageType
Calculates the segmented volumes for binary images.
static Pointer New()
#define AccessFixedDimensionByItk_2(mitkImage, itkImageTypeFunction, dimension, arg1, arg2)
static RenderingManager * GetInstance()
Module * GetModule() const
virtual ScalarType GetScalarValueMax(int t=0, unsigned int component=0)
Get the maximum for scalar images. Recomputation performed only when necessary.
static void ITKSetVolume(itk::Image< TPixel, VImageDimension > *originalImage, mitk::Image *segmentation, unsigned int timeStep)
Image class for storing images.
Definition: mitkImage.h:76
virtual const char * GetName() const override
Returns the name of this tool. Make it short!
void ITKThresholdingOldBinary(itk::Image< TPixel, VImageDimension > *originalImage, mitk::Image *segmentation, double thresholdValue, unsigned int timeStep)
virtual void SetThresholdValue(double value)
static Pointer New()
static Pointer New()
Class holding the statistics informations about a single mitk::Image.
us::ModuleResource GetIconResource() const override
Returns the tool button icon of the tool wrapped by a usModuleResource.
virtual const char ** GetXPM() const override
Returns an icon in the XPM format.
DataNode::Pointer m_ThresholdFeedbackNode
static Pointer New()
LabelSetImage class for handling labels and layers in a segmentation session.
static void ITKThresholding(itk::Image< TPixel, VImageDimension > *originalImage, mitk::Image *segmentation, double lower, double upper, unsigned int timeStep)
static void ITKThresholdingOldBinary(itk::Image< TPixel, VImageDimension > *originalImage, mitk::Image *segmentation, double lower, double upper, unsigned int timeStep)
ModuleResource GetResource(const std::string &path) const
Definition: usModule.cpp:267
static ModuleContext * GetModuleContext()
Returns the module context of the calling module.
static Pointer New()
void RequestUpdateAll(RequestType type=REQUEST_UPDATE_ALL)
Class for nodes of the DataTree.
Definition: mitkDataNode.h:66
static Pointer New()
virtual void Deactivated() override
Called when the tool gets deactivated.
static itkEventMacro(BoundingShapeInteractionEvent, itk::AnyEvent) class MITKBOUNDINGSHAPE_EXPORT BoundingShapeInteractor Pointer New()
Basic interaction methods for mitk::GeometryData.