MITK-IGT
IGT Extension of MITK
Loading...
Searching...
No Matches
mitkToFDistanceImageToSurfaceFilter.cpp
Go to the documentation of this file.
1/*============================================================================
2
3The Medical Imaging Interaction Toolkit (MITK)
4
5Copyright (c) German Cancer Research Center (DKFZ)
6All rights reserved.
7
8Use of this source code is governed by a 3-clause BSD license that can be
9found in the LICENSE file.
10
11============================================================================*/
12
14#include <mitkInstantiateAccessFunctions.h>
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);
36 m_CameraIntrinsics = mitk::CameraIntrinsics::New();
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
46
47void mitk::ToFDistanceImageToSurfaceFilter::SetInput( Image* distanceImage, mitk::CameraIntrinsics::Pointer cameraIntrinsics )
48{
49 this->SetCameraIntrinsics(cameraIntrinsics);
50 this->SetInput(0,distanceImage);
51}
52
53void mitk::ToFDistanceImageToSurfaceFilter::SetInput( unsigned int idx, Image* distanceImage, mitk::CameraIntrinsics::Pointer cameraIntrinsics )
54{
55 this->SetCameraIntrinsics(cameraIntrinsics);
56 this->SetInput(idx,distanceImage);
57}
58
59void mitk::ToFDistanceImageToSurfaceFilter::SetInput( mitk::Image* distanceImage )
60{
61 this->SetInput(0,distanceImage);
62}
63
64void 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
71 this->CreateOutputsForAllInputs();
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);
101 points->SetDataTypeToDouble();
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;
138 if((m_ReconstructionMode == WithOutInterPixelDistance) || (m_ReconstructionMode == Kinect))
139 {
140 focalLengthInPixelUnits[0] = m_CameraIntrinsics->GetFocalLengthX();
141 focalLengthInPixelUnits[1] = m_CameraIntrinsics->GetFocalLengthY();
142 focalLengthInMm = 0.0;
143 }
144 else if( m_ReconstructionMode == WithInterPixelDistance)
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 {
182 case WithOutInterPixelDistance:
183 {
184 cartesianCoordinates = mitk::ToFProcessingCommon::IndexToCartesianCoordinates(completeIndexX,completeIndexY,distance,focalLengthInPixelUnits,principalPoint);
185 break;
186 }
187 case WithInterPixelDistance:
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
217 if (m_GenerateTriangularMesh)
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
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}
ToFProcessingCommon::ToFPoint2D m_InterPixelDistance
distance in mm between two adjacent pixels on the ToF camera chip
void SetScalarImage(IplImage *iplScalarImage)
Set scalar image used as texture of the surface.
void SetTriangulationThreshold(double triangulationThreshold)
SetTriangulationThreshold Sets a triangulation threshold in order to remove unusually huge faces from...
void SetTextureImageWidth(int width)
Set width of the scalar image used for texturing the surface.
ReconstructionModeType m_ReconstructionMode
The ReconstructionModeType enum: Defines the reconstruction mode, if using no interpixeldistances and...
void GenerateData() override
Method generating the output of this filter. Called in the updated process of the pipeline....
Image * GetInput()
Returns the input of this filter.
void CreateOutputsForAllInputs()
Create an output for each input.
void SetTextureImageHeight(int height)
Set height of the scalar image used for texturing the surface.
IplImage * GetScalarImage()
Set scalar image used as texture of the surface.
virtual void SetInput(Image *distanceImage)
Sets the input of this filter.
mitk::CameraIntrinsics::Pointer m_CameraIntrinsics
Specifies the intrinsic parameters.
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 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.
itk::Point< ToFScalarType, 2 > ToFPoint2D
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
MITKIGTBASE_EXPORT bool Equal(const mitk::NavigationData &leftHandSide, const mitk::NavigationData &rightHandSide, ScalarType eps=mitk::eps, bool verbose=false)
Equal A function comparing two navigation data objects for beeing equal in meta- and imagedata.