Medical Imaging Interaction Toolkit  2016.11.0
Medical Imaging Interaction Toolkit
mitkHeightFieldSurfaceClipImageFilter.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 #include "mitkImageTimeSelector.h"
19 #include "mitkProperties.h"
20 #include "mitkTimeHelper.h"
21 
22 #include "mitkImageAccessByItk.h"
23 #include "mitkImageToItk.h"
24 
25 #include <itkImageRegionConstIterator.h>
26 #include <itkImageRegionIteratorWithIndex.h>
27 #include <itkImageSliceConstIteratorWithIndex.h>
28 
29 #include <vtkCellLocator.h>
30 #include <vtkPolyData.h>
31 
32 #include <limits>
33 
34 namespace mitk
35 {
37  : m_ClippingMode(CLIPPING_MODE_CONSTANT),
38  m_ClippingConstant(0.0),
39  m_MultiplicationFactor(2.0),
40  m_MultiPlaneValue(2),
41  m_HeightFieldResolutionX(256),
42  m_HeightFieldResolutionY(256),
43  m_MaxHeight(1024.0)
44  {
45  this->SetNumberOfIndexedInputs(8);
46  this->SetNumberOfRequiredInputs(2);
47 
50  }
51 
54  {
55  this->SetNthInput(1, clippingSurface);
56  }
57 
59  {
60  if (planeList.size() > 7)
61  {
62  MITK_WARN << "Only 7 clipping planes are allowed!";
63  }
64 
65  for (unsigned int i = 0; i < planeList.size(); ++i)
66  {
67  this->SetNthInput(i + 1, planeList.at(i));
68  }
69  }
70 
72  {
73  return dynamic_cast<const Surface *>(itk::ProcessObject::GetInput(1));
74  }
75 
80  {
82  }
83 
85  {
87  }
88 
90  {
91  Image *outputImage = this->GetOutput();
92  Image *inputImage = const_cast<Image *>(this->GetInput(0));
93  const Surface *inputSurface = dynamic_cast<const Surface *>(this->GetInput(1));
94 
95  if (!outputImage->IsInitialized() || inputSurface == nullptr)
96  {
97  return;
98  }
99 
101 
102  GenerateTimeInInputRegion(outputImage, inputImage);
103  }
104 
106  {
107  const Image *inputImage = this->GetInput(0);
108  Image *outputImage = this->GetOutput();
109 
110  if (outputImage->IsInitialized() && (this->GetMTime() <= m_TimeOfHeaderInitialization.GetMTime()))
111  {
112  return;
113  }
114 
115  itkDebugMacro(<< "GenerateOutputInformation()");
116 
117  unsigned int i;
118  auto tmpDimensions = new unsigned int[inputImage->GetDimension()];
119 
120  for (i = 0; i < inputImage->GetDimension(); ++i)
121  {
122  tmpDimensions[i] = inputImage->GetDimension(i);
123  }
124 
125  outputImage->Initialize(
126  inputImage->GetPixelType(), inputImage->GetDimension(), tmpDimensions, inputImage->GetNumberOfChannels());
127 
128  delete[] tmpDimensions;
129 
130  outputImage->SetGeometry(static_cast<Geometry3D *>(inputImage->GetGeometry()->Clone().GetPointer()));
131 
132  outputImage->SetPropertyList(inputImage->GetPropertyList()->Clone());
133 
134  m_TimeOfHeaderInitialization.Modified();
135  }
136 
137  template <typename TPixel, unsigned int VImageDimension>
139  itk::Image<TPixel, VImageDimension> *inputItkImage,
140  HeightFieldSurfaceClipImageFilter *clipImageFilter,
141  vtkPolyData *clippingPolyData,
142  AffineTransform3D *imageToPlaneTransform)
143  {
144  typedef itk::Image<TPixel, VImageDimension> ItkInputImageType;
145  typedef itk::Image<TPixel, VImageDimension> ItkOutputImageType;
146 
147  typedef itk::ImageSliceConstIteratorWithIndex<ItkInputImageType> ItkInputImageIteratorType;
148  typedef itk::ImageRegionIteratorWithIndex<ItkOutputImageType> ItkOutputImageIteratorType;
149 
151  outputimagetoitk->SetInput(clipImageFilter->m_OutputTimeSelector->GetOutput());
152  outputimagetoitk->Update();
153 
154  typename ItkOutputImageType::Pointer outputItkImage = outputimagetoitk->GetOutput();
155  std::vector<double> test;
156 
157  // create the iterators
158  typename ItkInputImageType::RegionType inputRegionOfInterest = inputItkImage->GetLargestPossibleRegion();
159  ItkInputImageIteratorType inputIt(inputItkImage, inputRegionOfInterest);
160  ItkOutputImageIteratorType outputIt(outputItkImage, inputRegionOfInterest);
161 
162  // Get bounds of clipping data
163  clippingPolyData->ComputeBounds();
164  double *bounds = clippingPolyData->GetBounds();
165 
166  double xWidth = bounds[1] - bounds[0];
167  double yWidth = bounds[3] - bounds[2];
168 
169  // Create vtkCellLocator for clipping poly data
170  vtkCellLocator *cellLocator = vtkCellLocator::New();
171  cellLocator->SetDataSet(clippingPolyData);
172  cellLocator->CacheCellBoundsOn();
173  cellLocator->AutomaticOn();
174  cellLocator->BuildLocator();
175 
176  // Allocate memory for 2D image to hold the height field generated by
177  // projecting the clipping data onto the plane
178  auto heightField = new double[m_HeightFieldResolutionX * m_HeightFieldResolutionY];
179 
180  // Walk through height field and for each entry calculate height of the
181  // clipping poly data at this point by means of vtkCellLocator. The
182  // clipping data x/y bounds are used for converting from poly data space to
183  // image (height-field) space.
184  MITK_INFO << "Calculating Height Field..." << std::endl;
185  for (unsigned int y = 0; y < m_HeightFieldResolutionY; ++y)
186  {
187  for (unsigned int x = 0; x < m_HeightFieldResolutionX; ++x)
188  {
189  double p0[3], p1[3], surfacePoint[3], pcoords[3];
190  p0[0] = bounds[0] + xWidth * x / (double)m_HeightFieldResolutionX;
191  p0[1] = bounds[2] + yWidth * y / (double)m_HeightFieldResolutionY;
192  p0[2] = -m_MaxHeight;
193 
194  p1[0] = p0[0];
195  p1[1] = p0[1];
196  p1[2] = m_MaxHeight;
197 
198  double t, distance;
199  int subId;
200  if (cellLocator->IntersectWithLine(p0, p1, 0.1, t, surfacePoint, pcoords, subId))
201  {
202  distance = (2.0 * t - 1.0) * m_MaxHeight;
203  }
204  else
205  {
206  distance = -65536.0;
207  }
208  heightField[y * m_HeightFieldResolutionX + x] = distance;
209  itk::Image<double, 2>::IndexType index;
210  index[0] = x;
211  index[1] = y;
212  }
213  }
214 
215  // Walk through entire input image and for each point determine its distance
216  // from the x/y plane.
217  MITK_INFO << "Performing clipping..." << std::endl;
218 
219  TPixel factor = static_cast<TPixel>(clipImageFilter->m_MultiplicationFactor);
220  TPixel clippingConstant = clipImageFilter->m_ClippingConstant;
221 
222  inputIt.SetFirstDirection(0);
223  inputIt.SetSecondDirection(1);
224  // through all slices
225  for (inputIt.GoToBegin(), outputIt.GoToBegin(); !inputIt.IsAtEnd(); inputIt.NextSlice())
226  {
227  // through all lines of a slice
228  for (; !inputIt.IsAtEndOfSlice(); inputIt.NextLine())
229  {
230  // Transform the start(line) point from the image to the plane
231  Point3D imageP0, planeP0;
232  imageP0[0] = inputIt.GetIndex()[0];
233  imageP0[1] = inputIt.GetIndex()[1];
234  imageP0[2] = inputIt.GetIndex()[2];
235  planeP0 = imageToPlaneTransform->TransformPoint(imageP0);
236 
237  // Transform the end point (line) from the image to the plane
238  Point3D imageP1, planeP1;
239  imageP1[0] = imageP0[0] + inputRegionOfInterest.GetSize(0);
240  imageP1[1] = imageP0[1];
241  imageP1[2] = imageP0[2];
242  planeP1 = imageToPlaneTransform->TransformPoint(imageP1);
243 
244  // calculate the step size (if the plane is rotate, you go "crossway" through the image)
245  Vector3D step = (planeP1 - planeP0) / (double)inputRegionOfInterest.GetSize(0);
246 
247  // over all pixel
248  for (; !inputIt.IsAtEndOfLine(); ++inputIt, ++outputIt, planeP0 += step)
249  {
250  // Only ConstantMode: if image pixel value == constant mode value-->set output pixel value directly
251  if ((clipImageFilter->m_ClippingMode == CLIPPING_MODE_CONSTANT) &&
252  ((TPixel)inputIt.Get() == clippingConstant))
253  {
254  outputIt.Set(clippingConstant);
255  }
256 
257  else
258  {
259  int x0 = (int)((double)(m_HeightFieldResolutionX) * (planeP0[0] - bounds[0]) / xWidth);
260  int y0 = (int)((double)(m_HeightFieldResolutionY) * (planeP0[1] - bounds[2]) / yWidth);
261 
262  bool clip;
263 
264  // if the current point is outside of the plane region (RegionOfInterest)-->clip the pixel allways
265  if ((x0 < 0) || (x0 >= (int)m_HeightFieldResolutionX) || (y0 < 0) || (y0 >= (int)m_HeightFieldResolutionY))
266  {
267  clip = true;
268  }
269 
270  else
271  {
272  // Calculate bilinearly interpolated height field value at plane point
273  int x1 = x0 + 1;
274  int y1 = y0 + 1;
275  if (x1 >= (int)m_HeightFieldResolutionX)
276  {
277  x1 = x0;
278  }
279  if (y1 >= (int)m_HeightFieldResolutionY)
280  {
281  y1 = y0;
282  }
283 
284  // Get the neighbour points for the interpolation
285  ScalarType q00, q01, q10, q11;
286  q00 = heightField[y0 * m_HeightFieldResolutionX + x0];
287  q01 = heightField[y0 * m_HeightFieldResolutionX + x1];
288  q10 = heightField[y1 * m_HeightFieldResolutionX + x0];
289  q11 = heightField[y1 * m_HeightFieldResolutionX + x1];
290 
291  double p00 = ((double)(m_HeightFieldResolutionX) * (planeP0[0] - bounds[0]) / xWidth);
292  double p01 = ((double)(m_HeightFieldResolutionY) * (planeP0[1] - bounds[2]) / yWidth);
293 
294  ScalarType q =
295  q00 * ((double)x1 - p00) * ((double)y1 - p01) + q01 * (p00 - (double)x0) * ((double)y1 - p01) +
296  q10 * ((double)x1 - p00) * (p01 - (double)y0) + q11 * (p00 - (double)x0) * (p01 - (double)y0);
297 
298  if (q - planeP0[2] < 0)
299  {
300  clip = true;
301  }
302  else
303  {
304  clip = false;
305  }
306  }
307 
308  // different modes: differnt values for the clipped pixel
309  if (clip)
310  {
311  if (clipImageFilter->m_ClippingMode == CLIPPING_MODE_CONSTANT)
312  {
313  outputIt.Set(clipImageFilter->m_ClippingConstant);
314  }
315  else if (clipImageFilter->m_ClippingMode == CLIPPING_MODE_MULTIPLYBYFACTOR)
316  {
317  outputIt.Set(inputIt.Get() * factor);
318  }
319  else if (clipImageFilter->m_ClippingMode == CLIPPING_MODE_MULTIPLANE)
320  {
321  if (inputIt.Get() != 0)
322  outputIt.Set(inputIt.Get() + m_MultiPlaneValue);
323  else
324  outputIt.Set(inputIt.Get());
325  }
326  }
327  // the non-clipped pixel keeps his value
328  else
329  {
330  outputIt.Set(inputIt.Get());
331  }
332  }
333  }
334  }
335  }
336 
337  MITK_INFO << "DONE!" << std::endl;
338 
339  // Clean-up
340  cellLocator->Delete();
341  }
342 
344  {
345  const Image *inputImage = this->GetInput(0);
346 
347  const Image *outputImage = this->GetOutput();
348 
349  m_InputTimeSelector->SetInput(inputImage);
350  m_OutputTimeSelector->SetInput(outputImage);
351 
352  Image::RegionType outputRegion = outputImage->GetRequestedRegion();
353  const TimeGeometry *outputTimeGeometry = outputImage->GetTimeGeometry();
354  const TimeGeometry *inputTimeGeometry = inputImage->GetTimeGeometry();
355  ScalarType timeInMS;
356 
357  int timestep = 0;
358  int tstart = outputRegion.GetIndex(3);
359  int tmax = tstart + outputRegion.GetSize(3);
360 
361  for (unsigned int i = 1; i < this->GetNumberOfInputs(); ++i)
362  {
363  Surface *inputSurface = const_cast<Surface *>(dynamic_cast<Surface *>(itk::ProcessObject::GetInput(i)));
364 
365  if (!outputImage->IsInitialized() || inputSurface == nullptr)
366  return;
367 
368  MITK_INFO << "Plane: " << i;
369  MITK_INFO << "Clipping: Start\n";
370 
371  // const PlaneGeometry *clippingGeometryOfCurrentTimeStep = NULL;
372 
373  int t;
374  for (t = tstart; t < tmax; ++t)
375  {
376  timeInMS = outputTimeGeometry->TimeStepToTimePoint(t);
377  timestep = inputTimeGeometry->TimePointToTimeStep(timeInMS);
378 
379  m_InputTimeSelector->SetTimeNr(timestep);
380  m_InputTimeSelector->UpdateLargestPossibleRegion();
381  m_OutputTimeSelector->SetTimeNr(t);
382  m_OutputTimeSelector->UpdateLargestPossibleRegion();
383 
384  // Compose IndexToWorld transform of image with WorldToIndexTransform of
385  // clipping data for conversion from image index space to plane index space
386  AffineTransform3D::Pointer planeWorldToIndexTransform = AffineTransform3D::New();
387  inputSurface->GetGeometry(t)->GetIndexToWorldTransform()->GetInverse(planeWorldToIndexTransform);
388 
389  AffineTransform3D::Pointer imageToPlaneTransform = AffineTransform3D::New();
390  imageToPlaneTransform->SetIdentity();
391 
392  imageToPlaneTransform->Compose(inputTimeGeometry->GetGeometryForTimeStep(t)->GetIndexToWorldTransform());
393  imageToPlaneTransform->Compose(planeWorldToIndexTransform);
394 
395  MITK_INFO << "Accessing ITK function...\n";
396  if (i == 1)
397  {
398  AccessByItk_3(m_InputTimeSelector->GetOutput(),
400  this,
401  inputSurface->GetVtkPolyData(t),
402  imageToPlaneTransform);
403  }
404  else
405  {
406  mitk::Image::Pointer extensionImage = m_OutputTimeSelector->GetOutput()->Clone();
408  extensionImage, _InternalComputeClippedImage, this, inputSurface->GetVtkPolyData(t), imageToPlaneTransform);
409  }
412  }
413  }
414 
415  m_TimeOfHeaderInitialization.Modified();
416  }
417 
418 } // namespace
virtual void GenerateData() override
A version of GenerateData() specific for image processing filters.
virtual void SetRequestedRegionToLargestPossibleRegion() override
Class for storing surfaces (vtkPolyData).
Definition: mitkSurface.h:32
#define AccessByItk_3(mitkImage, itkImageTypeFunction, arg1, arg2, arg3)
itk::SmartPointer< Self > Pointer
#define MITK_INFO
Definition: mitkLogMacros.h:22
virtual const RegionType & GetRequestedRegion() const
virtual void Initialize(const mitk::PixelType &type, unsigned int dimension, const unsigned int *dimensions, unsigned int channels=1)
Definition: mitkImage.cpp:837
double ScalarType
virtual vtkPolyData * GetVtkPolyData(unsigned int t=0) const
Follow Up Storage - Class to facilitate loading/accessing structured follow-up data.
Definition: testcase.h:32
DataCollection - Class to facilitate loading/accessing structured data.
const mitk::TimeGeometry * GetTimeGeometry() const
Return the TimeGeometry of the data as const pointer.
Definition: mitkBaseData.h:52
void SetClippingMode(int mode)
Specifies whether clipped part of the image shall be replaced by a constant or multiplied by a user-s...
void _InternalComputeClippedImage(itk::Image< TPixel, VImageDimension > *itkImage, HeightFieldSurfaceClipImageFilter *clipImageFilter, vtkPolyData *clippingPolyData, AffineTransform3D *imageToPlaneTransform)
virtual void SetGeometry(BaseGeometry *aGeometry3D) override
Sets a geometry to an image.
Definition: mitkImage.cpp:1320
void SetPropertyList(PropertyList *propertyList)
Set the data's property list.
#define MITK_WARN
Definition: mitkLogMacros.h:23
void SetClippingSurface(Surface *clippingSurface)
Set/Get the surface defining a height field as a triangle mesh.
void SetClippingModeToMultiplyByFactor()
Specifies whether clipped part of the image shall be replaced by a constant or multiplied by a user-s...
void SetClippingModeToConstant()
Specifies whether clipped part of the image shall be replaced by a constant or multiplied by a user-s...
itk::ImageRegion< RegionDimension > RegionType
virtual TimeStepType TimePointToTimeStep(TimePointType timePoint) const =0
Converts a time point to the corresponding time step.
Image class for storing images.
Definition: mitkImage.h:76
mitk::PropertyList::Pointer GetPropertyList() const
Get the data's property list.
virtual TimePointType TimeStepToTimePoint(TimeStepType timeStep) const =0
Converts a time step to a time point.
int GetClippingMode()
Specifies whether clipped part of the image shall be replaced by a constant or multiplied by a user-s...
itk::AffineGeometryFrame< ScalarType, 3 >::TransformType AffineTransform3D
const mitk::PixelType GetPixelType(int n=0) const
Returns the PixelType of channel n.
Definition: mitkImage.cpp:105
void SetClippingSurfaces(ClippingPlaneList planeList)
Set/Get the surfaces defining a height field as a triangle mesh.
itk::TimeStamp m_TimeOfHeaderInitialization
Time when Header was last initialized.
void GenerateTimeInInputRegion(const mitk::TimeGeometry *outputTimeGeometry, const TOutputRegion &outputRegion, const mitk::TimeGeometry *inputTimeGeometry, TInputRegion &inputRegion)
static Pointer New()
InputImageType * GetInput(void)
virtual BaseGeometry::Pointer GetGeometryForTimeStep(TimeStepType timeStep) const =0
Returns the geometry which corresponds to the given time step.
virtual bool IsInitialized() const
Check whether the data has been initialized, i.e., at least the Geometry and other header data has be...
unsigned int GetNumberOfChannels() const
Get the number of channels.
OutputType * GetOutput()
Get the output data of this image source object.
const Surface * GetClippingSurface() const
Set/Get the surface defining a height field as a triangle mesh.
unsigned int GetDimension() const
Get dimension of the image.
Definition: mitkImage.cpp:110
mitk::BaseGeometry * GetGeometry(int t=0) const
Return the geometry, which is a TimeGeometry, of the data as non-const pointer.
Definition: mitkBaseData.h:129
Filter for clipping an image with an height-field represented by an mitk::Surface.
Pointer Clone() const
static Pointer New()
mitk::AffineTransform3D * GetIndexToWorldTransform()
Get the transformation used to convert from index to world coordinates.
static itkEventMacro(BoundingShapeInteractionEvent, itk::AnyEvent) class MITKBOUNDINGSHAPE_EXPORT BoundingShapeInteractor Pointer New()
Basic interaction methods for mitk::GeometryData.