Medical Imaging Interaction Toolkit  2016.11.0
Medical Imaging Interaction Toolkit
mitkBoundingShapeCropper.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 "mitkGeometry3D.h"
19 #include "mitkImageAccessByItk.h"
20 #include "mitkImageCast.h"
21 #include "mitkImageToItk.h"
22 #include "mitkStatusBar.h"
23 #include "mitkTimeHelper.h"
24 
25 #include <math.h>
26 
27 #include "vtkMatrix4x4.h"
28 #include "vtkSmartPointer.h"
29 #include "vtkTransform.h"
30 
31 #include "itkImageRegionIteratorWithIndex.h"
32 #include <itkImageIOBase.h>
33 #include <itkImageRegionConstIterator.h>
34 #include <itkRGBAPixel.h>
35 #include <itkRGBPixel.h>
36 
37 namespace mitk
38 {
40  : m_Geometry(nullptr),
41  m_OutsideValue(0),
42  m_UseCropTimeStepOnly(false),
43  m_CurrentTimeStep(0),
44  m_UseWholeInputRegion(false),
45  m_InputTimeSelector(mitk::ImageTimeSelector::New()),
46  m_OutputTimeSelector(mitk::ImageTimeSelector::New())
47  {
48  this->SetNumberOfIndexedInputs(2);
49  this->SetNumberOfRequiredInputs(2);
50  }
51 
53 
54  template <typename TPixel, unsigned int VImageDimension>
55  void BoundingShapeCropper::CutImage(itk::Image<TPixel, VImageDimension> *inputItkImage, int timeStep)
56  {
57  MITK_INFO << "Scalar Pixeltype" << std::endl;
58 
59  typedef TPixel TOutputPixel;
60  typedef itk::Image<TPixel, VImageDimension> ItkInputImageType;
61  typedef itk::Image<TOutputPixel, VImageDimension> ItkOutputImageType;
62  typedef typename itk::ImageBase<VImageDimension>::RegionType ItkRegionType;
63  typedef itk::ImageRegionIteratorWithIndex<ItkInputImageType> ItkInputImageIteratorType;
64  typedef itk::ImageRegionIteratorWithIndex<ItkOutputImageType> ItkOutputImageIteratorType;
65 
66  TOutputPixel outsideValue = this->GetOutsideValue();
67  // currently 0 if not set in advance
68  // TODO: change default value to itk::NumericTraits<TOutputPixel>::min();
69 
70  if (this->m_Geometry.IsNull())
71  return;
72 
73  if (inputItkImage == nullptr)
74  {
76  "An internal error occurred. Can't convert Image. Please report to bugs@mitk.org");
77  std::cout << " image is NULL...returning" << std::endl;
78  return;
79  }
80 
81  // first convert the index
82  typename ItkRegionType::IndexType::IndexValueType tmpIndex[3];
83  itk2vtk(this->m_InputRequestedRegion.GetIndex(), tmpIndex);
84  typename ItkRegionType::IndexType index;
85  index.SetIndex(tmpIndex);
86 
87  // then convert the size
88  typename ItkRegionType::SizeType::SizeValueType tmpSize[3];
89  itk2vtk(this->m_InputRequestedRegion.GetSize(), tmpSize);
90  typename ItkRegionType::SizeType size;
91  size.SetSize(tmpSize);
92 
93  // create the ITK-image-region out of index and size
94  ItkRegionType inputRegionOfInterest(index, size);
95 
96  // Get access to the MITK output image via an ITK image
97  typename mitk::ImageToItk<ItkOutputImageType>::Pointer outputimagetoitk =
99  outputimagetoitk->SetInput(this->m_OutputTimeSelector->GetOutput());
100  outputimagetoitk->Update();
101  typename ItkOutputImageType::Pointer outputItkImage = outputimagetoitk->GetOutput();
102 
103  // create the iterators
104  ItkInputImageIteratorType inputIt(inputItkImage, inputRegionOfInterest);
105  ItkOutputImageIteratorType outputIt(outputItkImage, outputItkImage->GetLargestPossibleRegion());
106 
107  // Cut the boundingbox out of the image by iterating through all images
108  // TODO: use more efficient method by using the contour instead off all single pixels
109  mitk::Point3D p;
110  mitk::BaseGeometry *inputGeometry = this->GetInput()->GetGeometry(timeStep);
111 
112  // calculates translation based on offset+extent not on the transformation matrix
113  // NOTE: center of the box is
114  vtkSmartPointer<vtkMatrix4x4> imageTransform = this->m_Geometry->GetGeometry()->GetVtkTransform()->GetMatrix();
115  Point3D center = this->m_Geometry->GetGeometry()->GetCenter();
116  auto translation = vtkSmartPointer<vtkTransform>::New();
117  translation->Translate(center[0] - imageTransform->GetElement(0, 3),
118  center[1] - imageTransform->GetElement(1, 3),
119  center[2] - imageTransform->GetElement(2, 3));
120  auto transform = vtkSmartPointer<vtkTransform>::New();
121  transform->SetMatrix(imageTransform);
122  transform->PostMultiply();
123  transform->Concatenate(translation);
124  transform->Update();
125 
126  mitk::Vector3D extent;
127  for (unsigned int i = 0; i < 3; ++i)
128  extent[i] = (this->m_Geometry->GetGeometry()->GetExtent(i));
129 
130  for (inputIt.GoToBegin(), outputIt.GoToBegin(); !inputIt.IsAtEnd(); ++inputIt, ++outputIt)
131  {
132  vtk2itk(inputIt.GetIndex(), p);
133  inputGeometry->IndexToWorld(p, p);
134  ScalarType p2[4];
135  p2[0] = p[0];
136  p2[1] = p[1];
137  p2[2] = p[2];
138  p2[3] = 1;
139  // transform point from world to object coordinates
140  transform->GetInverse()->TransformPoint(p2, p2);
141  // check if the world point is within bounds
142  bool isInside = (p2[0] >= (-extent[0] / 2.0)) && (p2[0] <= (extent[0] / 2.0)) && (p2[1] >= (-extent[1] / 2.0)) &&
143  (p2[1] <= (extent[1] / 2.0)) && (p2[2] >= (-extent[2] / 2.0)) && (p2[2] <= (extent[2] / 2.0));
144 
145  if ((!this->m_UseCropTimeStepOnly && isInside) ||
146  (this->m_UseCropTimeStepOnly && timeStep == this->m_CurrentTimeStep && isInside))
147  {
148  outputIt.Set((TOutputPixel)inputIt.Value());
149  }
150  else
151  {
152  outputIt.Set(outsideValue);
153  }
154  }
155  }
156 
158  {
159  m_Geometry = const_cast<mitk::GeometryData *>(geometry);
160  // Process object is not const-correct so the const_cast is required here
161  this->ProcessObject::SetNthInput(1, const_cast<mitk::GeometryData *>(geometry));
162  }
163 
164  // const mitk::GeometryData* BoundingShapeCropper::GetGeometryData() const
165  //{
166  // return m_Geometry.GetPointer();
167  //}
168 
171  {
172  mitk::Image *output = this->GetOutput();
173  if ((output->IsInitialized() == false) || (m_Geometry.IsNull()) ||
174  (m_Geometry->GetTimeGeometry()->CountTimeSteps() == 0))
175  return;
176 
177  GenerateTimeInInputRegion(output, const_cast<mitk::Image *>(this->GetInput()));
178  }
179 
181  {
182  // Set Cropping region
183  mitk::Image::Pointer output = this->GetOutput();
184  if ((output->IsInitialized()) && (output->GetPipelineMTime() <= m_TimeOfHeaderInitialization.GetMTime()))
185  return;
186 
187  mitk::Image::Pointer input = const_cast<mitk::Image *>(this->GetInput());
188 
189  if (input.IsNull())
190  {
191  mitkThrow() << "Input is not a mitk::Image";
192  }
193  itkDebugMacro(<< "GenerateOutputInformation()");
194 
195  unsigned int dimension = input->GetDimension();
196  if (dimension < 3)
197  {
198  mitkThrow() << "ImageCropper cannot handle 1D or 2D Objects.";
199  }
200 
201  if ((m_Geometry.IsNull()) || (m_Geometry->GetTimeGeometry()->CountTimeSteps() == 0))
202  return;
203 
204  mitk::BaseGeometry *bsGeometry = m_Geometry->GetGeometry();
205  mitk::BaseGeometry *inputImageGeometry = input->GetSlicedGeometry();
206 
207  // calculate bounding box
208  mitk::BoundingBox::Pointer bsBoxRelativeToImage =
209  bsGeometry->CalculateBoundingBoxRelativeToTransform(inputImageGeometry->GetIndexToWorldTransform());
210 
211  // pre-initialize input-requested-region to largest-possible-region
212  m_InputRequestedRegion = input->GetLargestPossibleRegion();
213  // build region out of bounding-box of index and size of the bounding box
214  mitk::SlicedData::IndexType index = m_InputRequestedRegion.GetIndex(); // init times and channels
215  mitk::BoundingBox::PointType min = bsBoxRelativeToImage->GetMinimum();
216  mitk::SlicedData::SizeType size = m_InputRequestedRegion.GetSize(); // init times and channels
217  mitk::BoundingBox::PointType max = bsBoxRelativeToImage->GetMaximum();
218 
219  for (unsigned int i = 0; i < dimension; i++)
220  {
221  index[i] = (mitk::SlicedData::IndexType::IndexValueType)(std::ceil(min[i]));
222  size[i] = (mitk::SlicedData::SizeType::SizeValueType)(std::ceil(max[i]) - index[i]);
223  }
224  mitk::SlicedData::RegionType bsRegion(index, size);
225 
226  if (m_UseWholeInputRegion == false)
227  {
228  // crop input-requested-region with region of bounding-object
229  if (m_InputRequestedRegion.Crop(bsRegion) == false)
230  {
231  // crop not possible => do nothing: set time size to 0.
232  size.Fill(0);
233  m_InputRequestedRegion.SetSize(size);
234  bsRegion.SetSize(size);
235  mitkThrow() << "No overlap of the image and the cropping object.";
236  }
237  }
238 
239  // initialize output image
240  auto dimensions = new unsigned int[dimension];
241  if (dimension > 3 && !this->GetUseCropTimeStepOnly())
242  memcpy(dimensions + 3, input->GetDimensions() + 3, (dimension - 3) * sizeof(unsigned int));
243  else
244  dimension = 3; // set timeStep to zero if GetUseCropTimeStepOnly is true
245 
246  itk2vtk(m_InputRequestedRegion.GetSize(), dimensions);
247 
248  output->Initialize(mitk::PixelType(GetOutputPixelType()), dimension, dimensions);
249  delete[] dimensions;
250 
251  // Apply transform of the input image to the new generated output image
252  mitk::BoundingShapeCropper::RegionType outputRegion = output->GetRequestedRegion();
253 
254  m_TimeOfHeaderInitialization.Modified();
255  }
256 
257  void BoundingShapeCropper::ComputeData(mitk::Image *image, int boTimeStep)
258  {
259  // examine dimension and pixeltype
260  if ((image == nullptr) || (image->GetDimension() > 4) || (image->GetDimension() <= 2))
261  {
262  MITK_ERROR << "Filter cannot handle dimensions less than 2 and greater than 4" << std::endl;
263  itkExceptionMacro("Filter cannot handle dimensions less than 2 and greater than 4");
264  return;
265  }
266 
267  AccessByItk_1(image, CutImage, boTimeStep);
268  }
269 
271  {
272  MITK_INFO << "Generate Data" << std::endl;
273  mitk::Image::ConstPointer input = this->GetInput();
274  mitk::Image::Pointer output = this->GetOutput();
275 
276  if (input.IsNull())
277  return;
278 
279  if ((output->IsInitialized() == false) || (m_Geometry.IsNull()) ||
280  (m_Geometry->GetTimeGeometry()->CountTimeSteps() == 0))
281  return;
282 
283  m_InputTimeSelector->SetInput(input);
284  m_OutputTimeSelector->SetInput(this->GetOutput());
285 
286  mitk::BoundingShapeCropper::RegionType outputRegion = output->GetRequestedRegion();
287  mitk::BaseGeometry *inputImageGeometry = input->GetSlicedGeometry();
288 
289  // iterate over all time steps and perform cropping or masking on all or a specific timestep (perviously specified
290  // by UseCurrentTimeStepOnly)
291  int tstart = outputRegion.GetIndex(3);
292  int tmax = tstart + outputRegion.GetSize(3);
293 
294  if (this->m_UseCropTimeStepOnly)
295  {
296  mitk::SlicedGeometry3D *slicedGeometry = output->GetSlicedGeometry(tstart);
297  auto indexToWorldTransform = AffineTransform3D::New();
298  indexToWorldTransform->SetParameters(input->GetSlicedGeometry(tstart)->GetIndexToWorldTransform()->GetParameters());
299  slicedGeometry->SetIndexToWorldTransform(indexToWorldTransform);
300  const mitk::SlicedData::IndexType &start = m_InputRequestedRegion.GetIndex();
301  mitk::Point3D origin;
302  vtk2itk(start, origin);
303  inputImageGeometry->IndexToWorld(origin, origin);
304  slicedGeometry->SetOrigin(origin);
305  m_InputTimeSelector->SetTimeNr(m_CurrentTimeStep);
306  m_InputTimeSelector->UpdateLargestPossibleRegion();
307  m_OutputTimeSelector->SetTimeNr(tstart);
308  m_OutputTimeSelector->UpdateLargestPossibleRegion();
309  ComputeData(m_InputTimeSelector->GetOutput(), m_CurrentTimeStep);
310  }
311  else
312  {
313  int t;
314  for (t = tstart; t < tmax; ++t)
315  {
316  mitk::SlicedGeometry3D *slicedGeometry = output->GetSlicedGeometry(t);
317  auto indexToWorldTransform = AffineTransform3D::New();
318  indexToWorldTransform->SetParameters(input->GetSlicedGeometry(t)->GetIndexToWorldTransform()->GetParameters());
319  slicedGeometry->SetIndexToWorldTransform(indexToWorldTransform);
320  const mitk::SlicedData::IndexType &start = m_InputRequestedRegion.GetIndex();
321  mitk::Point3D origin;
322  vtk2itk(start, origin);
323  inputImageGeometry->IndexToWorld(origin, origin);
324  slicedGeometry->SetOrigin(origin);
325  m_InputTimeSelector->SetTimeNr(t);
326  m_InputTimeSelector->UpdateLargestPossibleRegion();
327  m_OutputTimeSelector->SetTimeNr(t);
328  m_OutputTimeSelector->UpdateLargestPossibleRegion();
329  ComputeData(m_InputTimeSelector->GetOutput(), t);
330  }
331  }
332  m_InputTimeSelector->SetInput(nullptr);
333  m_OutputTimeSelector->SetInput(nullptr);
334  m_TimeOfHeaderInitialization.Modified();
335  }
336 
337 } // of namespace mitk
mitk::Point3D PointType
itk::SmartPointer< Self > Pointer
virtual ScalarType GetOutsideValue()
void SetIndexToWorldTransform(mitk::AffineTransform3D *transform)
#define MITK_INFO
Definition: mitkLogMacros.h:22
#define MITK_ERROR
Definition: mitkLogMacros.h:24
double ScalarType
virtual void GenerateOutputInformation() override
DataCollection - Class to facilitate loading/accessing structured data.
virtual void GenerateData() override
A version of GenerateData() specific for image processing filters.
#define AccessByItk_1(mitkImage, itkImageTypeFunction, arg1)
itk::Size< RegionDimension > SizeType
void DisplayErrorText(const char *t)
virtual bool GetUseCropTimeStepOnly()
itk::ImageRegion< RegionDimension > RegionType
#define mitkThrow()
void SetOrigin(const Point3D &origin)
Set the origin, i.e. the upper-left corner of the plane.
Image class for storing images.
Definition: mitkImage.h:76
void CutImage(itk::Image< TPixel, VImageDimension > *inputItkImage, int timeStep)
Template Function for cropping and masking images with scalar pixel type.
Provides access to a volume at a specific time of the input image.
void vtk2itk(const Tin &in, Tout &out)
virtual void ComputeData(mitk::Image *input3D, int boTimeStep)
Process the image and create the output.
const mitk::PixelType GetPixelType(int n=0) const
Returns the PixelType of channel n.
Definition: mitkImage.cpp:105
static T max(T x, T y)
Definition: svm.cpp:70
Data class only having a BaseGeometry but not containing any specific data.
virtual void GenerateInputRequestedRegion() override
Reimplemented from ImageToImageFilter.
mitk::BoundingBox::Pointer CalculateBoundingBoxRelativeToTransform(const mitk::AffineTransform3D *transform) const
Calculates a bounding-box around the geometry relative to a coordinate system defined by a transform...
Describes the geometry of a data object consisting of slices.
void GenerateTimeInInputRegion(const mitk::TimeGeometry *outputTimeGeometry, const TOutputRegion &outputRegion, const mitk::TimeGeometry *inputTimeGeometry, TInputRegion &inputRegion)
static StatusBar * GetInstance()
static method to get the GUI dependent StatusBar-instance so the methods DisplayText, etc. can be called No reference counting, cause of decentral static use!
static T min(T x, T y)
Definition: svm.cpp:67
static Pointer New()
void itk2vtk(const Tin &in, Tout &out)
InputImageType * GetInput(void)
void IndexToWorld(const mitk::Vector3D &vec_units, mitk::Vector3D &vec_mm) const
Convert (continuous or discrete) index coordinates of a vector vec_units to world coordinates (in mm)...
void SetGeometry(const mitk::GeometryData *geometry)
Set geometry of the bounding object.
virtual const PixelType GetOutputPixelType()
virtual bool IsInitialized() const
Check whether the data has been initialized, i.e., at least the Geometry and other header data has be...
OutputType * GetOutput()
Get the output data of this image source object.
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
BaseGeometry Describes the geometry of a data object.
Class for defining the data type of pixels.
Definition: mitkPixelType.h:55
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.