Medical Imaging Interaction Toolkit  2016.11.0
Medical Imaging Interaction Toolkit
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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.