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