Medical Imaging Interaction Toolkit  2016.11.0
Medical Imaging Interaction Toolkit
mitkSliceBasedInterpolationController.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 
19 #include "mitkExtractSliceFilter.h"
20 #include "mitkImageAccessByItk.h"
21 #include "mitkImageCast.h"
22 #include "mitkImageReadAccessor.h"
23 #include "mitkImageTimeSelector.h"
24 
26 
27 #include <itkCommand.h>
28 #include <itkImage.h>
29 #include <itkImageSliceConstIteratorWithIndex.h>
30 
33 
35  const Image *image)
36 {
37  InterpolatorMapType::iterator iter = s_InterpolatorForImage.find(image);
38  if (iter != s_InterpolatorForImage.end())
39  {
40  return iter->second;
41  }
42  else
43  {
44  return NULL;
45  }
46 }
47 
49  : m_WorkingImage(NULL), m_ReferenceImage(NULL)
50 {
51 }
52 
54 {
55  // remove this from the list of interpolators
56  for (InterpolatorMapType::iterator iter = s_InterpolatorForImage.begin(); iter != s_InterpolatorForImage.end();
57  ++iter)
58  {
59  if (iter->second == this)
60  {
61  s_InterpolatorForImage.erase(iter);
62  break;
63  }
64  }
65 }
66 
68 {
69  m_LabelCountInSlice.clear();
70  int numberOfLabels = m_WorkingImage->GetNumberOfLabels();
71  m_LabelCountInSlice.resize(m_WorkingImage->GetTimeSteps());
72 
73  for (unsigned int timeStep = 0; timeStep < m_WorkingImage->GetTimeSteps(); ++timeStep)
74  {
75  m_LabelCountInSlice[timeStep].resize(3);
76  for (unsigned int dim = 0; dim < 3; ++dim)
77  {
78  m_LabelCountInSlice[timeStep][dim].clear();
79  m_LabelCountInSlice[timeStep][dim].resize(m_WorkingImage->GetDimension(dim));
80  for (unsigned int slice = 0; slice < m_WorkingImage->GetDimension(dim); ++slice)
81  {
82  m_LabelCountInSlice[timeStep][dim][slice].clear();
83  m_LabelCountInSlice[timeStep][dim][slice].resize(numberOfLabels);
84  m_LabelCountInSlice[timeStep][dim][slice].assign(numberOfLabels, 0);
85  }
86  }
87  }
88 }
89 
91 {
92  if (m_WorkingImage != newImage)
93  {
94  // delete the current working image from the list of interpolators
95  InterpolatorMapType::iterator iter = s_InterpolatorForImage.find(m_WorkingImage);
96  if (iter != s_InterpolatorForImage.end())
97  {
98  s_InterpolatorForImage.erase(iter);
99  }
100 
101  m_WorkingImage = newImage;
102 
103  s_InterpolatorForImage.insert(std::make_pair(m_WorkingImage, this));
104  }
105 
106  this->ResetLabelCount();
107 
108  AccessFixedDimensionByItk_1(m_WorkingImage, ScanImageITKProcessing, 3, 0);
109 
110  // for all timesteps, scan whole image: TODO: enable this again for 3D+time
111  /*
112  for (unsigned int timeStep = 0; timeStep < m_WorkingImage->GetTimeSteps(); ++timeStep)
113  {
114  ImageTimeSelector::Pointer timeSelector = ImageTimeSelector::New();
115  timeSelector->SetInput( m_WorkingImage );
116  timeSelector->SetTimeNr( timeStep );
117  timeSelector->UpdateLargestPossibleRegion();
118  Image::Pointer segmentation3D = timeSelector->GetOutput();
119  this->SetChangedVolume( segmentation3D.GetPointer(), timeStep );
120  }
121  */
122  // this->Modified();
123 }
124 
126 {
127  if (!newImage)
128  return;
129 
130  m_ReferenceImage = newImage;
131 
132  // ensure the reference image has the same dimensionality and extents as the segmentation image
133  if (m_WorkingImage.IsNull() || m_ReferenceImage->GetDimension() != m_WorkingImage->GetDimension() ||
134  m_ReferenceImage->GetPixelType().GetNumberOfComponents() != 1 ||
135  m_WorkingImage->GetPixelType().GetNumberOfComponents() != 1)
136  {
137  MITK_WARN << "Segmentation image has different image characteristics than reference image." << std::endl;
138  m_ReferenceImage = NULL;
139  return;
140  }
141 
142  for (unsigned int dim = 0; dim < m_WorkingImage->GetDimension(); ++dim)
143  {
144  if (m_ReferenceImage->GetDimension(dim) != m_WorkingImage->GetDimension(dim))
145  {
146  MITK_WARN << "original patient image does not match segmentation (different extent in dimension " << dim
147  << "), ignoring patient image" << std::endl;
148  m_ReferenceImage = NULL;
149  return;
150  }
151  }
152 }
153 
155  unsigned int sliceDimension,
156  unsigned int sliceIndex,
157  unsigned int timeStep)
158 {
159  if (!slice)
160  return;
161  if (slice->GetDimension() != 2)
162  return;
163  if (sliceDimension > 2)
164  return;
165  if (m_WorkingImage.IsNull())
166  return;
167 
168  // check if the number of labels has changed
169  int numberOfLabels = m_WorkingImage->GetNumberOfLabels();
170  if (m_LabelCountInSlice[0][0][0].size() != numberOfLabels)
171  return;
172 
173  unsigned int dim0(0);
174  unsigned int dim1(1);
175 
176  // determine the other two dimensions
177  switch (sliceDimension)
178  {
179  default:
180  case 2:
181  dim0 = 0;
182  dim1 = 1;
183  break;
184  case 1:
185  dim0 = 0;
186  dim1 = 2;
187  break;
188  case 0:
189  dim0 = 1;
190  dim1 = 2;
191  break;
192  }
193 
195  slice, ScanSliceITKProcessing, 2, SetChangedSliceOptions(sliceDimension, sliceIndex, dim0, dim1, timeStep));
196 
197  // this->Modified();
198 }
199 
200 template <typename PixelType>
201 void mitk::SliceBasedInterpolationController::ScanSliceITKProcessing(const itk::Image<PixelType, 2> *input,
202  const SetChangedSliceOptions &options)
203 {
204  unsigned int timeStep = options.timeStep;
205  unsigned int sliceDimension = options.sliceDimension;
206  unsigned int sliceIndex = options.sliceIndex;
207 
208  if (sliceDimension > 2)
209  return;
210  if (sliceIndex >= m_LabelCountInSlice[timeStep][sliceDimension].size())
211  return;
212 
213  unsigned int dim0(options.dim0);
214  unsigned int dim1(options.dim1);
215 
216  std::vector<int> numberOfPixels; // number of pixels in the current slice that are equal to the active label
217  int numberOfLabels = m_WorkingImage->GetNumberOfLabels();
218  numberOfPixels.resize(numberOfLabels);
219 
220  typedef itk::Image<PixelType, 2> ImageType;
221  typedef itk::ImageRegionConstIteratorWithIndex<ImageType> IteratorType;
222 
223  IteratorType iter(input, input->GetLargestPossibleRegion());
224  iter.GoToBegin();
225 
226  typename IteratorType::IndexType index;
227 
228  while (!iter.IsAtEnd())
229  {
230  index = iter.GetIndex();
231  int value = static_cast<int>(iter.Get());
232  ++m_LabelCountInSlice[timeStep][dim0][index[0]][value];
233  ++m_LabelCountInSlice[timeStep][dim1][index[1]][value];
234  ++numberOfPixels[value];
235  ++iter;
236  }
237 
238  for (unsigned int label = 0; label < numberOfLabels; ++label)
239  {
240  m_LabelCountInSlice[timeStep][sliceDimension][sliceIndex][label] = numberOfPixels[label];
241  }
242 }
243 
244 template <typename TPixel, unsigned int VImageDimension>
245 void mitk::SliceBasedInterpolationController::ScanImageITKProcessing(itk::Image<TPixel, VImageDimension> *input,
246  unsigned int timeStep)
247 {
248  typedef itk::ImageSliceConstIteratorWithIndex<itk::Image<TPixel, VImageDimension>> IteratorType;
249 
250  IteratorType iter(input, input->GetLargestPossibleRegion());
251  iter.SetFirstDirection(0);
252  iter.SetSecondDirection(1);
253 
254  typename IteratorType::IndexType index;
255  unsigned int x = 0;
256  unsigned int y = 0;
257  unsigned int z = 0;
258 
259  std::vector<int> numberOfPixels; // number of pixels per slice that are equal to the active label
260  int numberOfLabels = m_WorkingImage->GetNumberOfLabels();
261  numberOfPixels.resize(numberOfLabels);
262 
263  iter.GoToBegin();
264  while (!iter.IsAtEnd())
265  {
266  while (!iter.IsAtEndOfSlice())
267  {
268  while (!iter.IsAtEndOfLine())
269  {
270  index = iter.GetIndex();
271  x = index[0];
272  y = index[1];
273  z = index[2];
274 
275  int value = static_cast<unsigned int>(iter.Get());
276  ++m_LabelCountInSlice[timeStep][0][x][value];
277  ++m_LabelCountInSlice[timeStep][1][y][value];
278  ++numberOfPixels[value];
279 
280  ++iter;
281  }
282  iter.NextLine();
283  }
284 
285  for (unsigned int label = 0; label < numberOfLabels; ++label)
286  m_LabelCountInSlice[timeStep][2][z][label] += numberOfPixels[label];
287 
288  // clear label counter
289  numberOfPixels.assign(numberOfLabels, 0);
290  iter.NextSlice();
291  }
292 }
293 
295  unsigned int sliceIndex,
296  const mitk::PlaneGeometry *currentPlane,
297  unsigned int timeStep)
298 {
299  if (m_WorkingImage.IsNull())
300  return NULL;
301  if (!currentPlane)
302  return NULL;
303  if (timeStep >= m_LabelCountInSlice.size())
304  return NULL;
305  if (sliceDimension > 2)
306  return NULL;
307  int upperLimit = m_LabelCountInSlice[timeStep][sliceDimension].size();
308  if (sliceIndex >= upperLimit - 1)
309  return NULL; // can't interpolate first and last slice
310  if (sliceIndex < 1)
311  return NULL;
312 
313  int pixelValue = m_WorkingImage->GetActiveLabel()->GetValue();
314 
315  // slice contains a segmentation, won't interpolate anything then
316  if (m_LabelCountInSlice[timeStep][sliceDimension][sliceIndex][pixelValue] > 0)
317  return NULL;
318 
319  int lowerBound(0);
320  int upperBound(0);
321  bool bounds(false);
322 
323  for (lowerBound = sliceIndex - 1; /*lowerBound >= 0*/; --lowerBound)
324  {
325  if (m_LabelCountInSlice[timeStep][sliceDimension][lowerBound][pixelValue] > 0)
326  {
327  bounds = true;
328  break;
329  }
330 
331  if (lowerBound == 0)
332  break;
333  }
334 
335  if (!bounds)
336  return NULL;
337 
338  bounds = false;
339  for (upperBound = sliceIndex + 1; upperBound < upperLimit; ++upperBound)
340  {
341  if (m_LabelCountInSlice[timeStep][sliceDimension][upperBound][pixelValue] > 0)
342  {
343  bounds = true;
344  break;
345  }
346  }
347 
348  if (!bounds)
349  return NULL;
350 
351  // ok, we have found two neighboring slices with the active label
352  // (and we made sure that the current slice does NOT contain the active label
353  // Setting up the ExtractSliceFilter
355  extractor->SetInput(m_WorkingImage);
356  extractor->SetTimeStep(timeStep);
357  extractor->SetResliceTransformByGeometry(m_WorkingImage->GetTimeGeometry()->GetGeometryForTimeStep(timeStep));
358  extractor->SetVtkOutputRequest(false);
359 
360  // Reslicing the current plane
361  extractor->SetWorldGeometry(currentPlane);
362  extractor->Modified();
363 
364  try
365  {
366  extractor->Update();
367  }
368  catch (const std::exception &e)
369  {
370  MITK_ERROR << "Error in 2D interpolation: " << e.what();
371  return NULL;
372  }
373 
374  mitk::Image::Pointer resultImage = extractor->GetOutput();
375  resultImage->DisconnectPipeline();
376 
377  // Creating PlaneGeometry for lower slice
378  mitk::PlaneGeometry::Pointer reslicePlane = currentPlane->Clone();
379 
380  // Transforming the current origin so that it matches the lower slice
381  mitk::Point3D origin = currentPlane->GetOrigin();
382  m_WorkingImage->GetSlicedGeometry()->WorldToIndex(origin, origin);
383  origin[sliceDimension] = lowerBound;
384  m_WorkingImage->GetSlicedGeometry()->IndexToWorld(origin, origin);
385  reslicePlane->SetOrigin(origin);
386 
387  // Extract the lower slice
388  extractor->SetWorldGeometry(reslicePlane);
389  extractor->Modified();
390 
391  try
392  {
393  extractor->Update();
394  }
395  catch (const std::exception &e)
396  {
397  MITK_ERROR << "Error in 2D interpolation: " << e.what();
398  return NULL;
399  }
400 
401  mitk::Image::Pointer lowerMITKSlice = extractor->GetOutput();
402  lowerMITKSlice->DisconnectPipeline();
403 
404  // Transforming the current origin so that it matches the upper slice
405  m_WorkingImage->GetSlicedGeometry()->WorldToIndex(origin, origin);
406  origin[sliceDimension] = upperBound;
407  m_WorkingImage->GetSlicedGeometry()->IndexToWorld(origin, origin);
408  reslicePlane->SetOrigin(origin);
409 
410  // Extract the upper slice
411  extractor->SetWorldGeometry(reslicePlane);
412  extractor->Modified();
413 
414  try
415  {
416  extractor->Update();
417  }
418  catch (const std::exception &e)
419  {
420  MITK_ERROR << "Error in 2D interpolation: " << e.what();
421  return NULL;
422  }
423 
424  mitk::Image::Pointer upperMITKSlice = extractor->GetOutput();
425  upperMITKSlice->DisconnectPipeline();
426 
427  if (lowerMITKSlice.IsNull() || upperMITKSlice.IsNull())
428  return NULL;
429 
430  // interpolation algorithm gets some inputs
431  // two segmentations (guaranteed to be of the same data type, but no special data type guaranteed)
432  // orientation (sliceDimension) of the segmentations
433  // position of the two slices (sliceIndices)
434  // one volume image (original patient image)
435  //
436  // interpolation algorithm can use e.g. itk::ImageSliceConstIteratorWithIndex to
437  // inspect the original patient image at appropriate positions
438 
441 
442  algorithm->Interpolate(
443  lowerMITKSlice.GetPointer(), lowerBound, upperMITKSlice.GetPointer(), upperBound, sliceIndex, 0, resultImage);
444 
445  return resultImage;
446 }
const Point3D GetOrigin() const
Get the origin, e.g. the upper-left corner of the plane.
void ResetLabelCount()
Initializes the internal container with the number of voxels per label.
void SetWorkingImage(LabelSetImage *image)
Initialize with a whole volume.
itk::SmartPointer< Self > Pointer
Image::Pointer Interpolate(unsigned int sliceDimension, unsigned int sliceIndex, const mitk::PlaneGeometry *currentPlane, unsigned int timeStep)
Update after changing the whole working image.
#define MITK_ERROR
Definition: mitkLogMacros.h:24
static SliceBasedInterpolationController * InterpolatorForImage(const Image *)
Find interpolator for a given image.
void SetChangedSlice(const Image *image, unsigned int sliceDimension, unsigned int sliceIndex, unsigned int timeStep)
Update after changing a single slice in the working image.
static Pointer New()
Pointer Clone() const
std::map< const Image *, SliceBasedInterpolationController * > InterpolatorMapType
map::core::discrete::Elements< 3 >::InternalImageType ImageType
#define MITK_WARN
Definition: mitkLogMacros.h:23
Image class for storing images.
Definition: mitkImage.h:76
void ScanImageITKProcessing(itk::Image< TPixel, VImageDimension > *, unsigned int timeStep)
internal scan of the whole image
void ScanSliceITKProcessing(const itk::Image< PixelType, 2 > *, const SetChangedSliceOptions &options)
internal scan of a single slice
#define AccessFixedDimensionByItk_1(mitkImage, itkImageTypeFunction, dimension, arg1)
LabelSetImage class for handling labels and layers in a segmentation session.
Describes a two-dimensional, rectangular plane.
unsigned int GetDimension() const
Get dimension of the image.
Definition: mitkImage.cpp:110
void SetReferenceImage(Image *image)
Set a reference image (original patient image) - optional.
Protected class of mitk::SliceBasedInterpolationController. Don't use (you shouldn't be able to do so...