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