Medical Imaging Interaction Toolkit  2018.4.99-389bf124
Medical Imaging Interaction Toolkit
mitkSegmentationInterpolationController.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 "mitkImageCast.h"
16 #include "mitkImageReadAccessor.h"
17 #include "mitkImageTimeSelector.h"
18 #include <mitkExtractSliceFilter.h>
19 #include <mitkImageAccessByItk.h>
20 //#include <mitkPlaneGeometry.h>
21 
23 
24 #include <itkCommand.h>
25 #include <itkImage.h>
26 #include <itkImageSliceConstIteratorWithIndex.h>
27 
30 
32  const Image *image)
33 {
34  auto iter = s_InterpolatorForImage.find(image);
35  if (iter != s_InterpolatorForImage.end())
36  {
37  return iter->second;
38  }
39  else
40  {
41  return nullptr;
42  }
43 }
44 
47 {
48 }
49 
51 {
53 }
54 
56 {
57  static mitk::SegmentationInterpolationController::Pointer m_Instance;
58 
59  if (m_Instance.IsNull())
60  {
62  }
63  return m_Instance;
64 }
65 
67 {
68  // remove this from the list of interpolators
69  for (auto iter = s_InterpolatorForImage.begin(); iter != s_InterpolatorForImage.end(); ++iter)
70  {
71  if (iter->second == this)
72  {
73  s_InterpolatorForImage.erase(iter);
74  break;
75  }
76  }
77 }
78 
80 {
82  {
84  }
85 }
86 
88 {
89  m_BlockModified = block;
90 }
91 
93 {
94  // clear old information (remove all time steps
96 
97  // delete this from the list of interpolators
98  auto iter = s_InterpolatorForImage.find(segmentation);
99  if (iter != s_InterpolatorForImage.end())
100  {
101  s_InterpolatorForImage.erase(iter);
102  }
103 
104  if (!segmentation)
105  return;
106  if (segmentation->GetDimension() > 4 || segmentation->GetDimension() < 3)
107  {
108  itkExceptionMacro("SegmentationInterpolationController needs a 3D-segmentation or 3D+t, not 2D.");
109  }
110 
111  if (m_Segmentation != segmentation)
112  {
113  // observe Modified() event of image
114  itk::ReceptorMemberCommand<SegmentationInterpolationController>::Pointer command =
115  itk::ReceptorMemberCommand<SegmentationInterpolationController>::New();
116  command->SetCallbackFunction(this, &SegmentationInterpolationController::OnImageModified);
117  segmentation->AddObserver(itk::ModifiedEvent(), command);
118  }
119 
120  m_Segmentation = segmentation;
121 
122  m_SegmentationCountInSlice.resize(m_Segmentation->GetTimeSteps());
123  for (unsigned int timeStep = 0; timeStep < m_Segmentation->GetTimeSteps(); ++timeStep)
124  {
125  m_SegmentationCountInSlice[timeStep].resize(3);
126  for (unsigned int dim = 0; dim < 3; ++dim)
127  {
128  m_SegmentationCountInSlice[timeStep][dim].clear();
129  m_SegmentationCountInSlice[timeStep][dim].resize(m_Segmentation->GetDimension(dim));
130  m_SegmentationCountInSlice[timeStep][dim].assign(m_Segmentation->GetDimension(dim), 0);
131  }
132  }
133 
134  s_InterpolatorForImage.insert(std::make_pair(m_Segmentation, this));
135 
136  // for all timesteps
137  // scan whole image
138  for (unsigned int timeStep = 0; timeStep < m_Segmentation->GetTimeSteps(); ++timeStep)
139  {
141  timeSelector->SetInput(m_Segmentation);
142  timeSelector->SetTimeNr(timeStep);
143  timeSelector->UpdateLargestPossibleRegion();
144  Image::Pointer segmentation3D = timeSelector->GetOutput();
145  AccessFixedDimensionByItk_2(segmentation3D, ScanWholeVolume, 3, m_Segmentation, timeStep);
146  }
147 
148  // PrintStatus();
149 
151 
152  Modified();
153 }
154 
156 {
157  m_ReferenceImage = referenceImage;
158 
159  if (m_ReferenceImage.IsNull())
160  return; // no image set - ignore it then
161  assert(m_Segmentation.IsNotNull()); // should never happen
162 
163  // ensure the reference image has the same dimensionality and extents as the segmentation image
164  if (m_ReferenceImage.IsNull() || m_Segmentation.IsNull() ||
165  m_ReferenceImage->GetDimension() != m_Segmentation->GetDimension() ||
166  m_ReferenceImage->GetPixelType().GetNumberOfComponents() != 1 ||
167  m_Segmentation->GetPixelType().GetNumberOfComponents() != 1)
168  {
169  MITK_WARN << "Segmentation image has different image characteristics than reference image." << std::endl;
170  m_ReferenceImage = nullptr;
171  return;
172  }
173 
174  for (unsigned int dim = 0; dim < m_Segmentation->GetDimension(); ++dim)
175  if (m_ReferenceImage->GetDimension(dim) != m_Segmentation->GetDimension(dim))
176  {
177  MITK_WARN << "original patient image does not match segmentation (different extent in dimension " << dim
178  << "), ignoring patient image" << std::endl;
179  m_ReferenceImage = nullptr;
180  return;
181  }
182 }
183 
184 void mitk::SegmentationInterpolationController::SetChangedVolume(const Image *sliceDiff, unsigned int timeStep)
185 {
186  if (!sliceDiff)
187  return;
188  if (sliceDiff->GetDimension() != 3)
189  return;
190 
191  AccessFixedDimensionByItk_1(sliceDiff, ScanChangedVolume, 3, timeStep);
192 
193  // PrintStatus();
194  Modified();
195 }
196 
198  unsigned int sliceDimension,
199  unsigned int sliceIndex,
200  unsigned int timeStep)
201 {
202  if (!sliceDiff)
203  return;
204  if (sliceDimension > 2)
205  return;
206  if (timeStep >= m_SegmentationCountInSlice.size())
207  return;
208  if (sliceIndex >= m_SegmentationCountInSlice[timeStep][sliceDimension].size())
209  return;
210 
211  unsigned int dim0(0);
212  unsigned int dim1(1);
213 
214  // determine the other two dimensions
215  switch (sliceDimension)
216  {
217  default:
218  case 2:
219  dim0 = 0;
220  dim1 = 1;
221  break;
222  case 1:
223  dim0 = 0;
224  dim1 = 2;
225  break;
226  case 0:
227  dim0 = 1;
228  dim1 = 2;
229  break;
230  }
231 
232  mitk::ImageReadAccessor readAccess(sliceDiff);
233  auto *rawSlice = (unsigned char *)readAccess.GetData();
234  if (!rawSlice)
235  return;
236 
238  sliceDiff, ScanChangedSlice, 2, SetChangedSliceOptions(sliceDimension, sliceIndex, dim0, dim1, timeStep, rawSlice));
239 
240  Modified();
241 }
242 
243 template <typename DATATYPE>
245  const SetChangedSliceOptions &options)
246 {
247  auto *pixelData((DATATYPE *)options.pixelData);
248 
249  unsigned int timeStep(options.timeStep);
250 
251  unsigned int sliceDimension(options.sliceDimension);
252  unsigned int sliceIndex(options.sliceIndex);
253 
254  if (sliceDimension > 2)
255  return;
256  if (sliceIndex >= m_SegmentationCountInSlice[timeStep][sliceDimension].size())
257  return;
258 
259  unsigned int dim0(options.dim0);
260  unsigned int dim1(options.dim1);
261 
262  int numberOfPixels(0); // number of pixels in this slice that are not 0
263 
264  unsigned int dim0max = m_SegmentationCountInSlice[timeStep][dim0].size();
265  unsigned int dim1max = m_SegmentationCountInSlice[timeStep][dim1].size();
266 
267  // scan the slice from two directions
268  // and set the flags for the two dimensions of the slice
269  for (unsigned int v = 0; v < dim1max; ++v)
270  {
271  for (unsigned int u = 0; u < dim0max; ++u)
272  {
273  DATATYPE value = *(pixelData + u + v * dim0max);
274 
275  assert((signed)m_SegmentationCountInSlice[timeStep][dim0][u] + (signed)value >=
276  0); // just for debugging. This must always be true, otherwise some counting is going wrong
277  assert((signed)m_SegmentationCountInSlice[timeStep][dim1][v] + (signed)value >= 0);
278 
279  m_SegmentationCountInSlice[timeStep][dim0][u] =
280  static_cast<unsigned int>(m_SegmentationCountInSlice[timeStep][dim0][u] + value);
281  m_SegmentationCountInSlice[timeStep][dim1][v] =
282  static_cast<unsigned int>(m_SegmentationCountInSlice[timeStep][dim1][v] + value);
283  numberOfPixels += static_cast<int>(value);
284  }
285  }
286 
287  // flag for the dimension of the slice itself
288  assert((signed)m_SegmentationCountInSlice[timeStep][sliceDimension][sliceIndex] + numberOfPixels >= 0);
289  m_SegmentationCountInSlice[timeStep][sliceDimension][sliceIndex] += numberOfPixels;
290 
291  // MITK_INFO << "scan t=" << timeStep << " from (0,0) to (" << dim0max << "," << dim1max << ") (" << pixelData << "-"
292  // << pixelData+dim0max*dim1max-1 << ") in slice " << sliceIndex << " found " << numberOfPixels << " pixels" <<
293  // std::endl;
294 }
295 
296 template <typename TPixel, unsigned int VImageDimension>
297 void mitk::SegmentationInterpolationController::ScanChangedVolume(const itk::Image<TPixel, VImageDimension> *diffImage,
298  unsigned int timeStep)
299 {
300  typedef itk::ImageSliceConstIteratorWithIndex<itk::Image<TPixel, VImageDimension>> IteratorType;
301 
302  IteratorType iter(diffImage, diffImage->GetLargestPossibleRegion());
303  iter.SetFirstDirection(0);
304  iter.SetSecondDirection(1);
305 
306  int numberOfPixels(0); // number of pixels in this slice that are not 0
307 
308  typename IteratorType::IndexType index;
309  unsigned int x = 0;
310  unsigned int y = 0;
311  unsigned int z = 0;
312 
313  iter.GoToBegin();
314  while (!iter.IsAtEnd())
315  {
316  while (!iter.IsAtEndOfSlice())
317  {
318  while (!iter.IsAtEndOfLine())
319  {
320  index = iter.GetIndex();
321 
322  x = index[0];
323  y = index[1];
324  z = index[2];
325 
326  TPixel value = iter.Get();
327 
328  assert((signed)m_SegmentationCountInSlice[timeStep][0][x] + (signed)value >=
329  0); // just for debugging. This must always be true, otherwise some counting is going wrong
330  assert((signed)m_SegmentationCountInSlice[timeStep][1][y] + (signed)value >= 0);
331 
332  m_SegmentationCountInSlice[timeStep][0][x] =
333  static_cast<unsigned int>(m_SegmentationCountInSlice[timeStep][0][x] + value);
334  m_SegmentationCountInSlice[timeStep][1][y] =
335  static_cast<unsigned int>(m_SegmentationCountInSlice[timeStep][1][y] + value);
336 
337  numberOfPixels += static_cast<int>(value);
338 
339  ++iter;
340  }
341  iter.NextLine();
342  }
343  assert((signed)m_SegmentationCountInSlice[timeStep][2][z] + numberOfPixels >= 0);
344  m_SegmentationCountInSlice[timeStep][2][z] += numberOfPixels;
345  numberOfPixels = 0;
346 
347  iter.NextSlice();
348  }
349 }
350 
351 template <typename DATATYPE>
353  const Image *volume,
354  unsigned int timeStep)
355 {
356  if (!volume)
357  return;
358  if (timeStep >= m_SegmentationCountInSlice.size())
359  return;
360 
361  ImageReadAccessor readAccess(volume, volume->GetVolumeData(timeStep));
362 
363  for (unsigned int slice = 0; slice < volume->GetDimension(2); ++slice)
364  {
365  const auto *rawVolume =
366  static_cast<const DATATYPE *>(readAccess.GetData()); // we again promise not to change anything, we'll just count
367  const DATATYPE *rawSlice = rawVolume + (volume->GetDimension(0) * volume->GetDimension(1) * slice);
368 
369  ScanChangedSlice<DATATYPE>(nullptr, SetChangedSliceOptions(2, slice, 0, 1, timeStep, rawSlice));
370  }
371 }
372 
374 {
375  unsigned int timeStep(0); // if needed, put a loop over time steps around everyting, but beware, output will be long
376 
377  MITK_INFO << "Interpolator status (timestep 0): dimensions " << m_SegmentationCountInSlice[timeStep][0].size() << " "
378  << m_SegmentationCountInSlice[timeStep][1].size() << " " << m_SegmentationCountInSlice[timeStep][2].size()
379  << std::endl;
380 
381  MITK_INFO << "Slice 0: " << m_SegmentationCountInSlice[timeStep][2][0] << std::endl;
382 
383  // row "x"
384  for (unsigned int index = 0; index < m_SegmentationCountInSlice[timeStep][0].size(); ++index)
385  {
386  if (m_SegmentationCountInSlice[timeStep][0][index] > 0)
387  MITK_INFO << "O";
388  else
389  MITK_INFO << ".";
390  }
391  MITK_INFO << std::endl;
392 
393  // rows "y" and "z" (diagonal)
394  for (unsigned int index = 1; index < m_SegmentationCountInSlice[timeStep][1].size(); ++index)
395  {
396  if (m_SegmentationCountInSlice[timeStep][1][index] > 0)
397  MITK_INFO << "O";
398  else
399  MITK_INFO << ".";
400 
401  if (m_SegmentationCountInSlice[timeStep][2].size() > index) // if we also have a z value here, then print it, too
402  {
403  for (unsigned int indent = 1; indent < index; ++indent)
404  MITK_INFO << " ";
405 
406  if (m_SegmentationCountInSlice[timeStep][2][index] > 0)
407  MITK_INFO << m_SegmentationCountInSlice[timeStep][2][index]; //"O";
408  else
409  MITK_INFO << ".";
410  }
411 
412  MITK_INFO << std::endl;
413  }
414 
415  // z indices that are larger than the biggest y index
416  for (unsigned int index = m_SegmentationCountInSlice[timeStep][1].size();
417  index < m_SegmentationCountInSlice[timeStep][2].size();
418  ++index)
419  {
420  for (unsigned int indent = 0; indent < index; ++indent)
421  MITK_INFO << " ";
422 
423  if (m_SegmentationCountInSlice[timeStep][2][index] > 0)
424  MITK_INFO << m_SegmentationCountInSlice[timeStep][2][index]; //"O";
425  else
426  MITK_INFO << ".";
427 
428  MITK_INFO << std::endl;
429  }
430 }
431 
433  unsigned int sliceIndex,
434  const mitk::PlaneGeometry *currentPlane,
435  unsigned int timeStep)
436 {
437  if (m_Segmentation.IsNull())
438  return nullptr;
439 
440  if (!currentPlane)
441  {
442  return nullptr;
443  }
444 
445  if (timeStep >= m_SegmentationCountInSlice.size())
446  return nullptr;
447  if (sliceDimension > 2)
448  return nullptr;
449  unsigned int upperLimit = m_SegmentationCountInSlice[timeStep][sliceDimension].size();
450  if (sliceIndex >= upperLimit - 1)
451  return nullptr; // can't interpolate first and last slice
452  if (sliceIndex < 1)
453  return nullptr;
454 
455  if (m_SegmentationCountInSlice[timeStep][sliceDimension][sliceIndex] > 0)
456  return nullptr; // slice contains a segmentation, won't interpolate anything then
457 
458  unsigned int lowerBound(0);
459  unsigned int upperBound(0);
460  bool bounds(false);
461 
462  for (lowerBound = sliceIndex - 1; /*lowerBound >= 0*/; --lowerBound)
463  {
464  if (m_SegmentationCountInSlice[timeStep][sliceDimension][lowerBound] > 0)
465  {
466  bounds = true;
467  break;
468  }
469 
470  if (lowerBound == 0)
471  break; // otherwise overflow and start at something like 4294967295
472  }
473 
474  if (!bounds)
475  return nullptr;
476 
477  bounds = false;
478  for (upperBound = sliceIndex + 1; upperBound < upperLimit; ++upperBound)
479  {
480  if (m_SegmentationCountInSlice[timeStep][sliceDimension][upperBound] > 0)
481  {
482  bounds = true;
483  break;
484  }
485  }
486 
487  if (!bounds)
488  return nullptr;
489 
490  // ok, we have found two neighboring slices with segmentations (and we made sure that the current slice does NOT
491  // contain anything
492  // MITK_INFO << "Interpolate in timestep " << timeStep << ", dimension " << sliceDimension << ": estimate slice " <<
493  // sliceIndex << " from slices " << lowerBound << " and " << upperBound << std::endl;
494 
495  mitk::Image::Pointer lowerMITKSlice;
496  mitk::Image::Pointer upperMITKSlice;
497  mitk::Image::Pointer resultImage;
498 
499  try
500  {
501  // Setting up the ExtractSliceFilter
503  extractor->SetInput(m_Segmentation);
504  extractor->SetTimeStep(timeStep);
505  extractor->SetResliceTransformByGeometry(m_Segmentation->GetTimeGeometry()->GetGeometryForTimeStep(timeStep));
506  extractor->SetVtkOutputRequest(false);
507 
508  // Reslicing the current plane
509  extractor->SetWorldGeometry(currentPlane);
510  extractor->Modified();
511  extractor->Update();
512  resultImage = extractor->GetOutput();
513  resultImage->DisconnectPipeline();
514 
515  // Creating PlaneGeometry for lower slice
516  mitk::PlaneGeometry::Pointer reslicePlane = currentPlane->Clone();
517 
518  // Transforming the current origin so that it matches the lower slice
519  mitk::Point3D origin = currentPlane->GetOrigin();
520  m_Segmentation->GetSlicedGeometry(timeStep)->WorldToIndex(origin, origin);
521  origin[sliceDimension] = lowerBound;
522  m_Segmentation->GetSlicedGeometry(timeStep)->IndexToWorld(origin, origin);
523  reslicePlane->SetOrigin(origin);
524 
525  // Extract the lower slice
526  extractor = ExtractSliceFilter::New();
527  extractor->SetInput(m_Segmentation);
528  extractor->SetTimeStep(timeStep);
529  extractor->SetResliceTransformByGeometry(m_Segmentation->GetTimeGeometry()->GetGeometryForTimeStep(timeStep));
530  extractor->SetVtkOutputRequest(false);
531 
532  extractor->SetWorldGeometry(reslicePlane);
533  extractor->Modified();
534  extractor->Update();
535  lowerMITKSlice = extractor->GetOutput();
536  lowerMITKSlice->DisconnectPipeline();
537 
538  // Transforming the current origin so that it matches the upper slice
539  m_Segmentation->GetSlicedGeometry(timeStep)->WorldToIndex(origin, origin);
540  origin[sliceDimension] = upperBound;
541  m_Segmentation->GetSlicedGeometry(timeStep)->IndexToWorld(origin, origin);
542  reslicePlane->SetOrigin(origin);
543 
544  // Extract the upper slice
545  extractor = ExtractSliceFilter::New();
546  extractor->SetInput(m_Segmentation);
547  extractor->SetTimeStep(timeStep);
548  extractor->SetResliceTransformByGeometry(m_Segmentation->GetTimeGeometry()->GetGeometryForTimeStep(timeStep));
549  extractor->SetVtkOutputRequest(false);
550 
551  extractor->SetWorldGeometry(reslicePlane);
552  extractor->Modified();
553  extractor->Update();
554  upperMITKSlice = extractor->GetOutput();
555  upperMITKSlice->DisconnectPipeline();
556 
557  if (lowerMITKSlice.IsNull() || upperMITKSlice.IsNull())
558  return nullptr;
559  }
560  catch (const std::exception &e)
561  {
562  MITK_ERROR << "Error in 2D interpolation: " << e.what();
563  return nullptr;
564  }
565 
566  // interpolation algorithm gets some inputs
567  // two segmentations (guaranteed to be of the same data type, but no special data type guaranteed)
568  // orientation (sliceDimension) of the segmentations
569  // position of the two slices (sliceIndices)
570  // one volume image (original patient image)
571  //
572  // interpolation algorithm can use e.g. itk::ImageSliceConstIteratorWithIndex to
573  // inspect the original patient image at appropriate positions
574 
575  mitk::SegmentationInterpolationAlgorithm::Pointer algorithm =
577  return algorithm->Interpolate(lowerMITKSlice.GetPointer(),
578  lowerBound,
579  upperMITKSlice.GetPointer(),
580  upperBound,
581  sliceIndex,
582  sliceDimension,
583  resultImage,
584  timeStep,
586 }
#define MITK_INFO
Definition: mitkLogMacros.h:18
#define MITK_ERROR
Definition: mitkLogMacros.h:20
void SetChangedSlice(const Image *sliceDiff, unsigned int sliceDimension, unsigned int sliceIndex, unsigned int timeStep)
Update after changing a single slice.
static Pointer New()
Pointer Clone() const
void BlockModified(bool)
Block reaction to an images Modified() events.
static SegmentationInterpolationController * GetInstance()
Get existing instance or create a new one.
void ScanChangedVolume(const itk::Image< TPixel, VImageDimension > *, unsigned int timeStep)
#define MITK_WARN
Definition: mitkLogMacros.h:19
unsigned int GetDimension() const
Get dimension of the image.
Definition: mitkImage.cpp:106
Protected class of mitk::SegmentationInterpolationController. Don&#39;t use (you shouldn&#39;t be able to do ...
void SetChangedVolume(const Image *sliceDiff, unsigned int timeStep)
#define AccessFixedDimensionByItk_2(mitkImage, itkImageTypeFunction, dimension, arg1, arg2)
virtual ImageDataItemPointer GetVolumeData(int t=0, int n=0, void *data=nullptr, ImportMemoryManagementType importMemoryManagement=CopyMemory) const
Definition: mitkImage.cpp:326
Image class for storing images.
Definition: mitkImage.h:72
void SetSegmentationVolume(const Image *segmentation)
Initialize with a whole volume.
static SegmentationInterpolationController * InterpolatorForImage(const Image *)
Find interpolator for a given image.
Image::Pointer Interpolate(unsigned int sliceDimension, unsigned int sliceIndex, const mitk::PlaneGeometry *currentPlane, unsigned int timeStep)
Generates an interpolated image for the given slice.
mitk::Image::Pointer image
const Point3D GetOrigin() const
Get the origin, e.g. the upper-left corner of the plane.
void ScanWholeVolume(const itk::Image< DATATYPE, 3 > *, const Image *volume, unsigned int timeStep)
std::map< const Image *, SegmentationInterpolationController * > InterpolatorMapType
#define AccessFixedDimensionByItk_1(mitkImage, itkImageTypeFunction, dimension, arg1)
void SetReferenceVolume(const Image *segmentation)
Set a reference image (original patient image) - optional.
void ScanChangedSlice(const itk::Image< DATATYPE, 2 > *, const SetChangedSliceOptions &options)
internal scan of a single slice
Describes a two-dimensional, rectangular plane.
ImageReadAccessor class to get locked read access for a particular image part.
const void * GetData() const
Gives const access to the data.
static Pointer New()