Medical Imaging Interaction Toolkit  2018.4.99-389bf124
Medical Imaging Interaction Toolkit
mitkToFDistanceImageToSurfaceFilter.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 
15 #include <mitkSurface.h>
16 #include "mitkImageReadAccessor.h"
17 
18 #include <itkImage.h>
19 
20 #include <vtkCellArray.h>
21 #include <vtkPoints.h>
22 #include <vtkPolyData.h>
23 #include <vtkPointData.h>
24 #include <vtkFloatArray.h>
25 #include <vtkSmartPointer.h>
26 #include <vtkIdList.h>
27 
28 #include <cmath>
29 #include <vtkMath.h>
30 
32  m_IplScalarImage(nullptr), m_CameraIntrinsics(), m_TextureImageWidth(0), m_TextureImageHeight(0), m_InterPixelDistance(), m_TextureIndex(0),
33  m_GenerateTriangularMesh(true), m_TriangulationThreshold(0.0)
34 {
35  m_InterPixelDistance.Fill(0.045);
37  m_CameraIntrinsics->SetFocalLength(273.138946533,273.485900879);
38  m_CameraIntrinsics->SetPrincipalPoint(107.867935181,98.3807373047);
39  m_CameraIntrinsics->SetDistorsionCoeffs(-0.486690014601f,0.553943634033f,0.00222016777843f,-0.00300851115026f);
41 }
42 
44 {
45 }
46 
47 void mitk::ToFDistanceImageToSurfaceFilter::SetInput( Image* distanceImage, mitk::CameraIntrinsics::Pointer cameraIntrinsics )
48 {
49  this->SetCameraIntrinsics(cameraIntrinsics);
50  this->SetInput(0,distanceImage);
51 }
52 
53 void mitk::ToFDistanceImageToSurfaceFilter::SetInput( unsigned int idx, Image* distanceImage, mitk::CameraIntrinsics::Pointer cameraIntrinsics )
54 {
55  this->SetCameraIntrinsics(cameraIntrinsics);
56  this->SetInput(idx,distanceImage);
57 }
58 
60 {
61  this->SetInput(0,distanceImage);
62 }
63 
64 void mitk::ToFDistanceImageToSurfaceFilter::SetInput( unsigned int idx, mitk::Image* distanceImage )
65 {
66  if ((distanceImage == nullptr) && (idx == this->GetNumberOfInputs() - 1)) // if the last input is set to nullptr, reduce the number of inputs by one
67  this->SetNumberOfIndexedInputs(this->GetNumberOfInputs() - 1);
68  else
69  this->ProcessObject::SetNthInput(idx, distanceImage); // Process object is not const-correct so the const_cast is required here
70 
72 }
73 
75 {
76  return this->GetInput(0);
77 }
78 
80 {
81  if (this->GetNumberOfInputs() < 1)
82  {
83  mitkThrow() << "No input given for ToFDistanceImageToSurfaceFilter";
84  }
85  return static_cast< mitk::Image*>(this->ProcessObject::GetInput(idx));
86 }
87 
89 {
90  mitk::Surface::Pointer output = this->GetOutput();
91  assert(output);
92  mitk::Image::Pointer input = this->GetInput();
93  assert(input);
94  // mesh points
95  int xDimension = input->GetDimension(0);
96  int yDimension = input->GetDimension(1);
97  unsigned int size = xDimension*yDimension; //size of the image-array
98  std::vector<bool> isPointValid;
99  isPointValid.resize(size);
100  vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();
101  points->SetDataTypeToDouble();
102  vtkSmartPointer<vtkCellArray> polys = vtkSmartPointer<vtkCellArray>::New();
103  vtkSmartPointer<vtkCellArray> vertices = vtkSmartPointer<vtkCellArray>::New();
104  vtkSmartPointer<vtkFloatArray> scalarArray = vtkSmartPointer<vtkFloatArray>::New();
105  vtkSmartPointer<vtkFloatArray> textureCoords = vtkSmartPointer<vtkFloatArray>::New();
106  textureCoords->SetNumberOfComponents(2);
107  textureCoords->Allocate(size);
108 
109  //Make a vtkIdList to save the ID's of the polyData corresponding to the image
110  //pixel ID's. See below for more documentation.
111  m_VertexIdList = vtkSmartPointer<vtkIdList>::New();
112  //Allocate the object once else it would automatically allocate new memory
113  //for every vertex and perform a copy which is expensive.
114  m_VertexIdList->Allocate(size);
115  m_VertexIdList->SetNumberOfIds(size);
116  for(unsigned int i = 0; i < size; ++i)
117  {
118  m_VertexIdList->SetId(i, 0);
119  }
120 
121  float* scalarFloatData = nullptr;
122 
123  if (this->m_IplScalarImage) // if scalar image is defined use it for texturing
124  {
125  scalarFloatData = (float*)this->m_IplScalarImage->imageData;
126  }
127  else if (this->GetInput(m_TextureIndex)) // otherwise use intensity image (input(2))
128  {
129  ImageReadAccessor inputAcc(this->GetInput(m_TextureIndex));
130  scalarFloatData = (float*)inputAcc.GetData();
131  }
132 
133  ImageReadAccessor inputAcc(input, input->GetSliceData(0,0,0));
134  float* inputFloatData = (float*)inputAcc.GetData();
135  //calculate world coordinates
136  mitk::ToFProcessingCommon::ToFPoint2D focalLengthInPixelUnits;
139  {
140  focalLengthInPixelUnits[0] = m_CameraIntrinsics->GetFocalLengthX();
141  focalLengthInPixelUnits[1] = m_CameraIntrinsics->GetFocalLengthY();
142  focalLengthInMm = 0.0;
143  }
145  {
146  //convert focallength from pixel to mm
147  focalLengthInPixelUnits[0] = 0.0;
148  focalLengthInPixelUnits[1] = 0.0;
149  focalLengthInMm = (m_CameraIntrinsics->GetFocalLengthX()*m_InterPixelDistance[0]+m_CameraIntrinsics->GetFocalLengthY()*m_InterPixelDistance[1])/2.0;
150  }
151  else
152  {
153  focalLengthInPixelUnits[0] = 0.0;
154  focalLengthInPixelUnits[1] = 0.0;
155  focalLengthInMm = 0.0;
156  }
157 
159  principalPoint[0] = m_CameraIntrinsics->GetPrincipalPointX();
160  principalPoint[1] = m_CameraIntrinsics->GetPrincipalPointY();
161 
162  mitk::Point3D origin = input->GetGeometry()->GetOrigin();
163  mitk::Vector3D spacing = input->GetGeometry()->GetSpacing();
164 
165  for (int j=0; j<yDimension; j++)
166  {
167  for (int i=0; i<xDimension; i++)
168  {
169  unsigned int pixelID = i+j*xDimension;
170 
171  mitk::ToFProcessingCommon::ToFScalarType distance = (double)inputFloatData[pixelID];
172 
176  unsigned int completeIndexX = i*spacing[0]+origin[0];
177  unsigned int completeIndexY = j*spacing[1]+origin[1];
178 
179  mitk::ToFProcessingCommon::ToFPoint3D cartesianCoordinates;
180  switch (m_ReconstructionMode)
181  {
183  {
184  cartesianCoordinates = mitk::ToFProcessingCommon::IndexToCartesianCoordinates(completeIndexX,completeIndexY,distance,focalLengthInPixelUnits,principalPoint);
185  break;
186  }
188  {
189  cartesianCoordinates = mitk::ToFProcessingCommon::IndexToCartesianCoordinatesWithInterpixdist(completeIndexX,completeIndexY,distance,focalLengthInMm,m_InterPixelDistance,principalPoint);
190  break;
191  }
192  case Kinect:
193  {
194  cartesianCoordinates = mitk::ToFProcessingCommon::KinectIndexToCartesianCoordinates(completeIndexX,completeIndexY,distance,focalLengthInPixelUnits,principalPoint);
195  break;
196  }
197  default:
198  {
199  MITK_ERROR << "Incorrect reconstruction mode!";
200  }
201  }
202  //Epsilon here, because we may have small float values like 0.00000001 which in fact represents 0.
203  if (distance<=mitk::eps)
204  {
205  isPointValid[pixelID] = false;
206  }
207  else
208  {
209  isPointValid[pixelID] = true;
210  //VTK would insert empty points into the polydata if we use
211  //points->InsertPoint(pixelID, cartesianCoordinates.GetDataPointer()).
212  //If we use points->InsertNextPoint(...) instead, the ID's do not
213  //correspond to the image pixel ID's. Thus, we have to save them
214  //in the vertexIdList.
215  m_VertexIdList->SetId(pixelID, points->InsertNextPoint(cartesianCoordinates.GetDataPointer()));
216 
218  {
219  if((i >= 1) && (j >= 1))
220  {
221  //This little piece of art explains the ID's:
222  //
223  // P(x_1y_1)---P(xy_1)
224  // | |
225  // | |
226  // | |
227  // P(x_1y)-----P(xy)
228  //
229  //We can only start triangulation if we are at vertex (1,1),
230  //because we need the other 3 vertices near this one.
231  //To go one pixel line back in the image array, we have to
232  //subtract 1x xDimension.
233  vtkIdType xy = pixelID;
234  vtkIdType x_1y = pixelID-1;
235  vtkIdType xy_1 = pixelID-xDimension;
236  vtkIdType x_1y_1 = xy_1-1;
237 
238  //Find the corresponding vertex ID's in the saved vertexIdList:
239  vtkIdType xyV = m_VertexIdList->GetId(xy);
240  vtkIdType x_1yV = m_VertexIdList->GetId(x_1y);
241  vtkIdType xy_1V = m_VertexIdList->GetId(xy_1);
242  vtkIdType x_1y_1V = m_VertexIdList->GetId(x_1y_1);
243 
244  if (isPointValid[xy]&&isPointValid[x_1y]&&isPointValid[x_1y_1]&&isPointValid[xy_1]) // check if points of cell are valid
245  {
246  double pointXY[3], pointX_1Y[3], pointXY_1[3], pointX_1Y_1[3];
247 
248  points->GetPoint(xyV, pointXY);
249  points->GetPoint(x_1yV, pointX_1Y);
250  points->GetPoint(xy_1V, pointXY_1);
251  points->GetPoint(x_1y_1V, pointX_1Y_1);
252 
253  if( (mitk::Equal(m_TriangulationThreshold, 0.0)) || ((vtkMath::Distance2BetweenPoints(pointXY, pointX_1Y) <= m_TriangulationThreshold)
254  && (vtkMath::Distance2BetweenPoints(pointXY, pointXY_1) <= m_TriangulationThreshold)
255  && (vtkMath::Distance2BetweenPoints(pointX_1Y, pointX_1Y_1) <= m_TriangulationThreshold)
256  && (vtkMath::Distance2BetweenPoints(pointXY_1, pointX_1Y_1) <= m_TriangulationThreshold)))
257  {
258  polys->InsertNextCell(3);
259  polys->InsertCellPoint(x_1yV);
260  polys->InsertCellPoint(xyV);
261  polys->InsertCellPoint(x_1y_1V);
262 
263  polys->InsertNextCell(3);
264  polys->InsertCellPoint(x_1y_1V);
265  polys->InsertCellPoint(xyV);
266  polys->InsertCellPoint(xy_1V);
267  }
268  else
269  {
270  //We dont want triangulation, but we want to keep the vertex
271  vertices->InsertNextCell(1);
272  vertices->InsertCellPoint(xyV);
273  }
274  }
275  }
276  }
277  else
278  {
279  //We dont want triangulation, we only want vertices
280  vertices->InsertNextCell(1);
281  vertices->InsertCellPoint(m_VertexIdList->GetId(pixelID));
282  }
283  //Scalar values are necessary for mapping colors/texture onto the surface
284  if (scalarFloatData)
285  {
286  scalarArray->InsertTuple1(m_VertexIdList->GetId(pixelID), scalarFloatData[pixelID]);
287  }
288  //These Texture Coordinates will map color pixel and vertices 1:1 (e.g. for Kinect).
289  float xNorm = (((float)i)/xDimension);// correct video texture scale for kinect
290  float yNorm = ((float)j)/yDimension; //don't flip. we don't need to flip.
291  textureCoords->InsertTuple2(m_VertexIdList->GetId(pixelID), xNorm, yNorm);
292  }
293  }
294  }
295 
296  vtkSmartPointer<vtkPolyData> mesh = vtkSmartPointer<vtkPolyData>::New();
297  mesh->SetPoints(points);
298  mesh->SetPolys(polys);
299  mesh->SetVerts(vertices);
300  //Pass the scalars to the polydata (if they were set).
301  if (scalarArray->GetNumberOfTuples()>0)
302  {
303  mesh->GetPointData()->SetScalars(scalarArray);
304  }
305  //Pass the TextureCoords to the polydata anyway (to save them).
306  mesh->GetPointData()->SetTCoords(textureCoords);
307  output->SetVtkPolyData(mesh);
308 }
309 
311 {
312  this->SetNumberOfIndexedOutputs(this->GetNumberOfInputs()); // create outputs for all inputs
313  for (unsigned int idx = 0; idx < this->GetNumberOfOutputs(); ++idx)
314  if (this->GetOutput(idx) == nullptr)
315  {
316  DataObjectPointer newOutput = this->MakeOutput(idx);
317  this->SetNthOutput(idx, newOutput);
318  }
319  this->Modified();
320 }
321 
323 {
324  this->GetOutput();
325  itkDebugMacro(<<"GenerateOutputInformation()");
326 
327 }
328 
330 {
331  this->m_IplScalarImage = iplScalarImage;
332  this->Modified();
333 }
334 
336 {
337  return this->m_IplScalarImage;
338 }
339 
341 {
342  this->m_TextureImageWidth = width;
343 }
344 
346 {
347  this->m_TextureImageHeight = height;
348 }
349 
350 
352 {
353  //vtkMath::Distance2BetweenPoints returns the squared distance between two points and
354  //hence we square m_TriangulationThreshold in order to save run-time.
355  this->m_TriangulationThreshold = triangulationThreshold*triangulationThreshold;
356 }
int m_TextureIndex
Index of the input used as texture image when no scalar image was set via SetIplScalarImage(). 0 = Distance, 1 = Amplitude, 2 = Intensity.
void CreateOutputsForAllInputs()
Create an output for each input.
static ToFPoint3D IndexToCartesianCoordinatesWithInterpixdist(unsigned int i, unsigned int j, ToFScalarType distance, ToFScalarType focalLength, ToFScalarType interPixelDistanceX, ToFScalarType interPixelDistanceY, ToFScalarType principalPointX, ToFScalarType principalPointY)
Convert index based distances to cartesian coordinates.
#define MITK_ERROR
Definition: mitkLogMacros.h:20
int m_TextureImageHeight
Height (y-dimension) of the texture image.
ToFProcessingCommon::ToFPoint2D m_InterPixelDistance
distance in mm between two adjacent pixels on the ToF camera chip
OutputType * GetOutput()
void GenerateData() override
Method generating the output of this filter. Called in the updated process of the pipeline...
void SetTextureImageHeight(int height)
Set height of the scalar image used for texturing the surface.
void SetScalarImage(IplImage *iplScalarImage)
Set scalar image used as texture of the surface.
virtual void SetInput(Image *distanceImage)
Sets the input of this filter.
ReconstructionModeType m_ReconstructionMode
The ReconstructionModeType enum: Defines the reconstruction mode, if using no interpixeldistances and...
mitk::CameraIntrinsics::Pointer m_CameraIntrinsics
Specifies the intrinsic parameters.
void SetTextureImageWidth(int width)
Set width of the scalar image used for texturing the surface.
int m_TextureImageWidth
Width (x-dimension) of the texture image.
Image * GetInput()
Returns the input of this filter.
#define mitkThrow()
itk::Point< ToFScalarType, 2 > ToFPoint2D
Image class for storing images.
Definition: mitkImage.h:72
static ToFProcessingCommon::ToFPoint3D KinectIndexToCartesianCoordinates(unsigned int i, unsigned int j, ToFScalarType distance, ToFScalarType focalLengthX, ToFScalarType focalLengthY, ToFScalarType principalPointX, ToFScalarType principalPointY)
KinectIndexToCartesianCoordinates Convert a pixel (i,j) with value d to a 3D world point...
itk::Point< ToFScalarType, 3 > ToFPoint3D
IplImage * m_IplScalarImage
Scalar image used for surface texturing.
~ToFDistanceImageToSurfaceFilter() override
Standard destructor.
static Pointer New()
MITKNEWMODULE_EXPORT bool Equal(mitk::ExampleDataStructure *leftHandSide, mitk::ExampleDataStructure *rightHandSide, mitk::ScalarType eps, bool verbose)
Returns true if the example data structures are considered equal.
itk::DataObject::Pointer MakeOutput(DataObjectPointerArraySizeType idx) override
void SetTriangulationThreshold(double triangulationThreshold)
SetTriangulationThreshold Sets a triangulation threshold in order to remove unusually huge faces from...
vtkSmartPointer< vtkIdList > m_VertexIdList
Make a vtkIdList to save the ID&#39;s of the polyData corresponding to the image pixel ID&#39;s...
MITKCORE_EXPORT const ScalarType eps
virtual void SetCameraIntrinsics(mitk::CameraIntrinsics::Pointer _arg)
ImageReadAccessor class to get locked read access for a particular image part.
IplImage * GetScalarImage()
Set scalar image used as texture of the surface.
const void * GetData() const
Gives const access to the data.
static ToFPoint3D IndexToCartesianCoordinates(unsigned int i, unsigned int j, ToFScalarType distance, ToFScalarType focalLengthX, ToFScalarType focalLengthY, ToFScalarType principalPointX, ToFScalarType principalPointY)
Convert index based distances to cartesian coordinates.