Medical Imaging Interaction Toolkit  2018.4.99-b7f3afaa
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 (DKFZ)
6 All rights reserved.
7 
8 Use of this source code is governed by a 3-clause BSD license that can be
9 found in the LICENSE file.
10 
11 ============================================================================*/
12 
14 
16 #include "mitkToolManager.h"
17 
18 #include "mitkColorProperty.h"
19 #include "mitkDataStorage.h"
21 #include "mitkProperties.h"
22 #include "mitkRenderingManager.h"
24 #include <mitkCoreObjectFactory.h>
25 
26 #include "mitkImageAccessByItk.h"
27 #include "mitkImageCast.h"
29 #include "mitkImageTimeSelector.h"
30 #include "mitkLabelSetImage.h"
32 #include "mitkPadImageFilter.h"
33 #include <itkBinaryThresholdImageFilter.h>
34 #include <itkImageRegionIterator.h>
35 
36 // us
37 #include "usGetModuleContext.h"
38 #include "usModule.h"
39 #include "usModuleResource.h"
40 
41 namespace mitk
42 {
44 }
45 
47  : m_SensibleMinimumThresholdValue(-100),
48  m_SensibleMaximumThresholdValue(+100),
49  m_CurrentThresholdValue(0.0),
50  m_IsFloatImage(false)
51 {
53  m_ThresholdFeedbackNode->SetProperty("color", ColorProperty::New(0.0, 1.0, 0.0));
54  m_ThresholdFeedbackNode->SetProperty("name", StringProperty::New("Thresholding feedback"));
55  m_ThresholdFeedbackNode->SetProperty("opacity", FloatProperty::New(0.3));
56  m_ThresholdFeedbackNode->SetProperty("binary", BoolProperty::New(true));
57  m_ThresholdFeedbackNode->SetProperty("helper object", BoolProperty::New(true));
58 }
59 
61 {
62 }
63 
65 {
66  return nullptr;
67 }
68 
70 {
72  us::ModuleResource resource = module->GetResource("Threshold_48x48.png");
73  return resource;
74 }
75 
77 {
78  return "Threshold";
79 }
80 
82 {
83  Superclass::Activated();
84 
87 
90 
91  if (m_NodeForThresholding.IsNotNull())
92  {
94  }
95  else
96  {
98  }
99 }
100 
102 {
105  m_NodeForThresholding = nullptr;
106  m_OriginalImageNode = nullptr;
107  try
108  {
109  if (DataStorage *storage = m_ToolManager->GetDataStorage())
110  {
111  storage->Remove(m_ThresholdFeedbackNode);
113  }
114  }
115  catch (...)
116  {
117  // don't care
118  }
119  m_ThresholdFeedbackNode->SetData(nullptr);
120 
121  Superclass::Deactivated();
122 }
123 
125 {
126  if (m_ThresholdFeedbackNode.IsNotNull())
127  {
128  /* If value is not in the min/max range, do nothing. In that case, this
129  method will be called again with a proper value right after. The only
130  known case where this happens is with an [0.0, 1.0[ image, where value
131  could be an epsilon greater than the max. */
134  {
135  return;
136  }
137 
138  m_CurrentThresholdValue = value;
139  // Bug 19250: The range of 0.01 is rather random. It was 0.001 before and probably due to rounding error propagation
140  // in VTK code
141  // it leads to strange banding effects on floating point images with a huge range (like -40000 - 40000). 0.01 lowers
142  // this effect
143  // enough to work with our images. Might not work on images with really huge ranges, though. Anyways, still seems to
144  // be low enough
145  // to work for floating point images with a range between 0 and 1. A better solution might be to dynamically
146  // calculate the value
147  // based on the value range of the current image (as big as possible, as small as necessary).
148  // m_ThresholdFeedbackNode->SetProperty( "levelwindow", LevelWindowProperty::New(
149  // LevelWindow(m_CurrentThresholdValue, 0.01) ) );
150  UpdatePreview();
151  }
152 }
153 
155 {
157 
160 }
161 
163 {
165 }
166 
168 {
169  itk::RGBPixel<float> pixel;
170  pixel[0] = 0.0f;
171  pixel[1] = 1.0f;
172  pixel[2] = 0.0f;
173 
174  if (m_NodeForThresholding.IsNotNull())
175  {
176  Image::Pointer image = dynamic_cast<Image *>(m_NodeForThresholding->GetData());
177  Image::Pointer originalImage = dynamic_cast<Image *>(m_OriginalImageNode->GetData());
178 
179  if (image.IsNotNull())
180  {
181  mitk::LabelSetImage::Pointer workingImage =
182  dynamic_cast<mitk::LabelSetImage *>(m_ToolManager->GetWorkingData(0)->GetData());
183 
184  if (workingImage.IsNotNull())
185  {
186  m_ThresholdFeedbackNode->SetData(workingImage->Clone());
187  m_IsOldBinary = false;
188 
189  // Let's paint the feedback node green...
190  mitk::LabelSetImage::Pointer previewImage =
191  dynamic_cast<mitk::LabelSetImage *>(m_ThresholdFeedbackNode->GetData());
192 
193  if (previewImage.IsNull())
194  {
195  MITK_ERROR << "Cannot create helper objects.";
196  return;
197  }
198 
199  previewImage->GetActiveLabel()->SetColor(pixel);
200  previewImage->GetActiveLabelSet()->UpdateLookupTable(previewImage->GetActiveLabel()->GetValue());
201  }
202  else
203  {
204  mitk::Image::Pointer workingImageBin = dynamic_cast<mitk::Image *>(m_ToolManager->GetWorkingData(0)->GetData());
205  if (workingImageBin)
206  {
207  m_ThresholdFeedbackNode->SetData(workingImageBin->Clone());
208  m_IsOldBinary = true;
209  }
210  else
212  }
213 
214  m_ThresholdFeedbackNode->SetColor(pixel);
215  m_ThresholdFeedbackNode->SetOpacity(0.5);
216 
217  int layer(50);
218  m_NodeForThresholding->GetIntProperty("layer", layer);
219  m_ThresholdFeedbackNode->SetIntProperty("layer", layer + 1);
220 
222  {
223  if (!ds->Exists(m_ThresholdFeedbackNode))
225  }
226 
227  if (image.GetPointer() == originalImage.GetPointer())
228  {
229  Image::StatisticsHolderPointer statistics = originalImage->GetStatistics();
230  m_SensibleMinimumThresholdValue = static_cast<double>(statistics->GetScalarValueMin());
231  m_SensibleMaximumThresholdValue = static_cast<double>(statistics->GetScalarValueMax());
232  }
233 
234  if ((originalImage->GetPixelType().GetPixelType() == itk::ImageIOBase::SCALAR) &&
235  (originalImage->GetPixelType().GetComponentType() == itk::ImageIOBase::FLOAT ||
236  originalImage->GetPixelType().GetComponentType() == itk::ImageIOBase::DOUBLE))
237  m_IsFloatImage = true;
238  else
239  m_IsFloatImage = false;
240 
242 
245  }
246  }
247 }
248 
249 template <typename TPixel, unsigned int VImageDimension>
250 static void ITKSetVolume(itk::Image<TPixel, VImageDimension> *originalImage,
251  mitk::Image *segmentation,
252  unsigned int timeStep)
253 {
254  segmentation->SetVolume((void *)originalImage->GetPixelContainer()->GetBufferPointer(), timeStep);
255 }
256 
258 {
259  if (node)
260  {
261  Image::Pointer feedBackImage = dynamic_cast<Image *>(m_ThresholdFeedbackNode->GetData());
262  if (feedBackImage.IsNotNull())
263  {
264  DataNode::Pointer emptySegmentation = GetTargetSegmentationNode();
265 
266  if (emptySegmentation)
267  {
268  // actually perform a thresholding and ask for an organ type
269  for (unsigned int timeStep = 0; timeStep < feedBackImage->GetTimeSteps(); ++timeStep)
270  {
271  try
272  {
274  timeSelector->SetInput(feedBackImage);
275  timeSelector->SetTimeNr(timeStep);
276  timeSelector->UpdateLargestPossibleRegion();
277  Image::Pointer image3D = timeSelector->GetOutput();
278 
279  if (image3D->GetDimension() == 2)
280  {
282  image3D, ITKSetVolume, 2, dynamic_cast<Image *>(emptySegmentation->GetData()), timeStep);
283  }
284  else
285  {
287  image3D, ITKSetVolume, 3, dynamic_cast<Image *>(emptySegmentation->GetData()), timeStep);
288  }
289  }
290  catch (...)
291  {
292  Tool::ErrorMessage("Error accessing single time steps of the original image. Cannot create segmentation.");
293  }
294  }
295 
296  if (m_OriginalImageNode.GetPointer() != m_NodeForThresholding.GetPointer())
297  {
299 
300  padFilter->SetInput(0, dynamic_cast<mitk::Image *>(emptySegmentation->GetData()));
301  padFilter->SetInput(1, dynamic_cast<mitk::Image *>(m_OriginalImageNode->GetData()));
302  padFilter->SetBinaryFilter(true);
303  padFilter->SetUpperThreshold(1);
304  padFilter->SetLowerThreshold(1);
305  padFilter->Update();
306 
307  emptySegmentation->SetData(padFilter->GetOutput());
308  }
309 
310  m_ToolManager->SetWorkingData(emptySegmentation);
311  m_ToolManager->GetWorkingData(0)->Modified();
312  }
313  }
314  }
315 }
316 
318 {
320 
321  if (node.IsNotNull())
322  {
324  mitk::Image::Pointer image = dynamic_cast<mitk::Image *>(m_NodeForThresholding->GetData());
325 
326  if (image.IsNull())
327  return;
328 
329  roiFilter->SetInput(image);
330  roiFilter->SetRegionOfInterest(node->GetData());
331  roiFilter->Update();
332 
334  tmpNode->SetData(roiFilter->GetOutput());
335 
336  m_SensibleMaximumThresholdValue = static_cast<double>(roiFilter->GetMaxValue());
337  m_SensibleMinimumThresholdValue = static_cast<double>(roiFilter->GetMinValue());
338 
339  m_NodeForThresholding = tmpNode;
340  }
341  else
342  {
344  }
345 
346  this->SetupPreviewNode();
347  this->UpdatePreview();
348 }
349 
350 template <typename TPixel, unsigned int VImageDimension>
351 void mitk::BinaryThresholdTool::ITKThresholding(itk::Image<TPixel, VImageDimension> *originalImage,
352  Image *segmentation,
353  double thresholdValue,
354  unsigned int timeStep)
355 {
356  typedef itk::Image<TPixel, VImageDimension> ImageType;
357  typedef itk::Image<mitk::Tool::DefaultSegmentationDataType, VImageDimension> SegmentationType;
358  typedef itk::BinaryThresholdImageFilter<ImageType, SegmentationType> ThresholdFilterType;
359 
360  typename ThresholdFilterType::Pointer filter = ThresholdFilterType::New();
361  filter->SetInput(originalImage);
362  filter->SetLowerThreshold(thresholdValue);
363  filter->SetUpperThreshold(m_SensibleMaximumThresholdValue);
364  filter->SetInsideValue(1);
365  filter->SetOutsideValue(0);
366  filter->Update();
367 
368  segmentation->SetVolume((void *)(filter->GetOutput()->GetPixelContainer()->GetBufferPointer()), timeStep);
369 }
370 
371 template <typename TPixel, unsigned int VImageDimension>
372 void mitk::BinaryThresholdTool::ITKThresholdingOldBinary(itk::Image<TPixel, VImageDimension> *originalImage,
373  Image *segmentation,
374  double thresholdValue,
375  unsigned int timeStep)
376 {
377  typedef itk::Image<TPixel, VImageDimension> ImageType;
378  typedef itk::Image<unsigned char, VImageDimension> SegmentationType;
379  typedef itk::BinaryThresholdImageFilter<ImageType, SegmentationType> ThresholdFilterType;
380 
381  typename ThresholdFilterType::Pointer filter = ThresholdFilterType::New();
382  filter->SetInput(originalImage);
383  filter->SetLowerThreshold(thresholdValue);
384  filter->SetUpperThreshold(m_SensibleMaximumThresholdValue);
385  filter->SetInsideValue(1);
386  filter->SetOutsideValue(0);
387  filter->Update();
388 
389  segmentation->SetVolume((void *)(filter->GetOutput()->GetPixelContainer()->GetBufferPointer()), timeStep);
390 }
391 
393 {
394  mitk::Image::Pointer thresholdImage = dynamic_cast<mitk::Image *>(m_NodeForThresholding->GetData());
395  mitk::Image::Pointer previewImage = dynamic_cast<mitk::Image *>(m_ThresholdFeedbackNode->GetData());
396  if (thresholdImage && previewImage)
397  {
398  for (unsigned int timeStep = 0; timeStep < thresholdImage->GetTimeSteps(); ++timeStep)
399  {
401  timeSelector->SetInput(thresholdImage);
402  timeSelector->SetTimeNr(timeStep);
403  timeSelector->UpdateLargestPossibleRegion();
404  Image::Pointer feedBackImage3D = timeSelector->GetOutput();
405 
406  if (m_IsOldBinary)
407  {
408  AccessByItk_n(feedBackImage3D, ITKThresholdingOldBinary, (previewImage, m_CurrentThresholdValue, timeStep));
409  }
410  else
411  {
412  AccessByItk_n(feedBackImage3D, ITKThresholding, (previewImage, m_CurrentThresholdValue, timeStep));
413  }
414  }
415 
417  }
418 }
Data management class that handles &#39;was created by&#39; relations.
DataStorage * GetDataStorage()
itk::Image< unsigned char, 3 > ImageType
#define MITK_ERROR
Definition: mitkLogMacros.h:20
static Pointer New()
void Send(T t)
Definition: mitkMessage.h:602
#define MITKSEGMENTATION_EXPORT
DataCollection - Class to facilitate loading/accessing structured data.
Message1< std::string > ErrorMessage
To send error messages (to be shown by some GUI)
Definition: mitkTool.h:99
DataNode::Pointer m_NodeForThresholding
DataVectorType GetRoiData()
#define AccessByItk_n(mitkImage, itkImageTypeFunction, va_tuple)
Access a MITK image by an ITK image with one or more parameters.
bool ActivateTool(int id)
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:669
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()
void Activated() override
Called when the tool gets activated.
MITK_TOOL_MACRO(MITKSEGMENTATION_EXPORT, LiveWireTool2D, "LiveWire tool")
Message3< double, double, bool > IntervalBordersChanged
Calculates the segmented volumes for binary images.
static Pointer New()
#define AccessFixedDimensionByItk_2(mitkImage, itkImageTypeFunction, dimension, arg1, arg2)
static RenderingManager * GetInstance()
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)
Module * GetModule() const
Image class for storing images.
Definition: mitkImage.h:72
const char * GetName() const override
Returns the name of this tool. Make it short!
ModuleResource GetResource(const std::string &path) const
Definition: usModule.cpp:267
void SetWorkingData(DataVectorType)
void ITKThresholdingOldBinary(itk::Image< TPixel, VImageDimension > *originalImage, mitk::Image *segmentation, double thresholdValue, unsigned int timeStep)
Message1< double > ThresholdingValueChanged
virtual mitk::DataNode * GetTargetSegmentationNode()
Depending on the selected mode either returns the currently selected segmentation or creates a new on...
mitk::Image::Pointer image
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.
const char ** GetXPM() const override
Returns an icon in the XPM format.
DataNode::Pointer m_ThresholdFeedbackNode
static Pointer New()
DataVectorType GetReferenceData()
LabelSetImage class for handling labels and layers in a segmentation session.
ToolManager * m_ToolManager
Definition: mitkTool.h:228
void Send(T t, U u, V v)
Definition: mitkMessage.h:658
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:57
static Pointer New()
DataVectorType GetWorkingData()
void Deactivated() override
Called when the tool gets deactivated.