2
2
3
3
#include " cxAirwaysFromCenterline.h"
4
4
#include < vtkPolyData.h>
5
- // #include "cxDoubleDataAdapterXml.h"
6
5
#include " cxBranchList.h"
7
6
#include " cxBranch.h"
8
- #include < vtkCellArray.h>
9
7
#include " vtkCardinalSpline.h"
10
8
#include < vtkLine.h>
11
- #include < vtkCellArray.h>
12
9
#include < vtkTubeFilter.h>
13
10
#include < vtkLineSource.h>
14
- #include < vtkCleanPolyData.h>
15
- #include < vtkAppendPolyData.h>
16
- #include < vtkImplicitModeller.h>
17
11
#include < vtkContourFilter.h>
12
+ #include < vtkPolyDataToImageStencil.h>
13
+ #include < cxImage.h>
14
+ #include " cxContourFilter.h"
15
+ #include < vtkImageData.h>
16
+ #include < vtkSphereSource.h>
17
+ #include < vtkDataSetSurfaceFilter.h>
18
+ #include < vtkPointData.h>
19
+ #include < vtkImageStencil.h>
18
20
19
- typedef vtkSmartPointer<class vtkCardinalSpline > vtkCardinalSplinePtr;
20
21
21
22
namespace cx
22
23
{
@@ -36,12 +37,12 @@ Eigen::MatrixXd AirwaysFromCenterline::getCenterlinePositions(vtkPolyDataPtr cen
36
37
{
37
38
38
39
int N = centerline_r->GetNumberOfPoints ();
39
- Eigen::MatrixXd CLpoints (3 ,N);
40
- for (vtkIdType i = 0 ; i < N; i++)
41
- {
42
- double p[3 ];
40
+ Eigen::MatrixXd CLpoints (3 ,N);
41
+ for (vtkIdType i = 0 ; i < N; i++)
42
+ {
43
+ double p[3 ];
43
44
centerline_r->GetPoint (i,p);
44
- Eigen::Vector3d position;
45
+ Eigen::Vector3d position;
45
46
position (0 ) = p[0 ]; position (1 ) = p[1 ]; position (2 ) = p[2 ];
46
47
CLpoints.block (0 , i , 3 , 1 ) = position;
47
48
}
@@ -64,156 +65,159 @@ void AirwaysFromCenterline::processCenterline(vtkPolyDataPtr centerline_r)
64
65
std::cout << " Number of branches in CT centerline: " << mBranchListPtr ->getBranches ().size () << std::endl;
65
66
}
66
67
67
-
68
- /*
69
- RouteToTarget::smoothBranch is smoothing the positions of a centerline branch by using vtkCardinalSpline.
70
- The degree of smoothing is dependent on the branch radius and the shape of the branch.
71
- First, the method tests if a straight line from start to end of the branch is sufficient by the condition of
72
- all positions along the line being within the lumen of the airway (max distance from original centerline
73
- is set to branch radius).
74
- If this fails, one more control point is added to the spline at the time, until the condition is fulfilled.
75
- The control point added for each iteration is the position with the larges deviation from the original/unfiltered
76
- centerline.
77
- */
78
- std::vector< Eigen::Vector3d > AirwaysFromCenterline::smoothBranch (BranchPtr branchPtr, int startIndex, Eigen::MatrixXd startPosition)
68
+ vtkPolyDataPtr AirwaysFromCenterline::generateTubes ()
79
69
{
80
- vtkCardinalSplinePtr splineX = vtkSmartPointer<vtkCardinalSpline>::New ();
81
- vtkCardinalSplinePtr splineY = vtkSmartPointer<vtkCardinalSpline>::New ();
82
- vtkCardinalSplinePtr splineZ = vtkSmartPointer<vtkCardinalSpline>::New ();
83
-
84
- double branchRadius = branchPtr->findBranchRadius ();
70
+ std::vector<BranchPtr> branches = mBranchListPtr ->getBranches ();
85
71
86
- // add control points to spline
72
+ // ------- create image data ---------------
73
+ vtkImageDataPtr resultImage = vtkImageDataPtr::New ();
74
+ vtkPointsPtr pointsPtr = vtkPointsPtr::New ();
87
75
88
- // add first position
89
- Eigen::MatrixXd positions = branchPtr->getPositions ();
90
- splineX->AddPoint (0 ,startPosition (0 ));
91
- splineY->AddPoint (0 ,startPosition (1 ));
92
- splineZ->AddPoint (0 ,startPosition (2 ));
76
+ int numberOfPoints = 0 ;
77
+ for (int i = 0 ; i < branches.size (); i++)
78
+ numberOfPoints += branches[i]->getPositions ().cols ();
93
79
80
+ pointsPtr->SetNumberOfPoints (numberOfPoints);
94
81
95
- // Add last position if no parent branch, else add parents position closest to current branch.
96
- // Branch positions are stored in order from head to feet (e.g. first position is top of trachea),
97
- // while route-to-target is generated from target to top of trachea.
98
- if (!branchPtr->getParentBranch ())
82
+ int pointIndex = 0 ;
83
+ for (int i = 0 ; i < branches.size (); i++)
99
84
{
100
- splineX->AddPoint (startIndex,positions (0 ,0 ));
101
- splineY->AddPoint (startIndex,positions (1 ,0 ));
102
- splineZ->AddPoint (startIndex,positions (2 ,0 ));
85
+ Eigen::MatrixXd positions = branches[i]->getPositions ();
86
+ for (int j = 0 ; j < positions.cols (); j++)
87
+ {
88
+ pointsPtr->SetPoint (pointIndex, positions (0 ,j), positions (1 ,j), positions (2 ,j));
89
+ pointIndex += 1 ;
90
+ }
103
91
}
104
- else
105
- {
106
- Eigen::MatrixXd parentPositions = branchPtr->getParentBranch ()->getPositions ();
107
- splineX->AddPoint (startIndex,parentPositions (0 ,parentPositions.cols ()-1 ));
108
- splineY->AddPoint (startIndex,parentPositions (1 ,parentPositions.cols ()-1 ));
109
- splineZ->AddPoint (startIndex,parentPositions (2 ,parentPositions.cols ()-1 ));
110
92
111
- }
93
+ double bounds[6 ];
94
+ pointsPtr->GetBounds (bounds);
112
95
113
- // Add points until all filtered/smoothed postions are within branch radius distance of the unfiltered branch centerline posiition.
114
- // This is to make sure the smoothed centerline is within the lumen of the airways.
115
- double maxDistanceToOriginalPosition = branchRadius;
116
- int maxDistanceIndex = -1 ;
117
- std::vector< Eigen::Vector3d > smoothingResult;
96
+ // Extend bounds to make room for surface model extended from centerline
97
+ bounds[0 ] -= 10 ;
98
+ bounds[1 ] += 10 ;
99
+ bounds[2 ] -= 10 ;
100
+ bounds[3 ] += 10 ;
101
+ bounds[4 ] -= 10 ;
102
+ bounds[5 ] -= 2 ; // to make top of trachea open
118
103
119
- // add positions to spline
120
- while (maxDistanceToOriginalPosition >= branchRadius && splineX->GetNumberOfPoints () < startIndex)
121
- {
122
- if (maxDistanceIndex > 0 )
123
- {
124
- // add to spline the position with largest distance to original position
125
- splineX->AddPoint (maxDistanceIndex,positions (0 ,startIndex - maxDistanceIndex));
126
- splineY->AddPoint (maxDistanceIndex,positions (1 ,startIndex - maxDistanceIndex));
127
- splineZ->AddPoint (maxDistanceIndex,positions (2 ,startIndex - maxDistanceIndex));
128
- }
104
+ double spacing[3 ];
105
+ spacing[0 ] = 0.5 ; // Smaller spacing improves resolution but increases run-time
106
+ spacing[1 ] = 0.5 ;
107
+ spacing[2 ] = 0.5 ;
108
+ resultImage->SetSpacing (spacing);
129
109
130
- // evaluate spline - get smoothed positions
131
- maxDistanceToOriginalPosition = 0.0 ;
132
- smoothingResult.clear ();
133
- for (int j=0 ; j<=startIndex; j++)
134
- {
135
- double splineParameter = j;
136
- Eigen::Vector3d tempPoint;
137
- tempPoint (0 ) = splineX->Evaluate (splineParameter);
138
- tempPoint (1 ) = splineY->Evaluate (splineParameter);
139
- tempPoint (2 ) = splineZ->Evaluate (splineParameter);
140
- smoothingResult.push_back (tempPoint);
141
-
142
- // calculate distance to original (non-filtered) position
143
- double distance = findDistanceToLine (tempPoint, positions);
144
- // finding the index with largest distance
145
- if (distance > maxDistanceToOriginalPosition)
146
- {
147
- maxDistanceToOriginalPosition = distance;
148
- maxDistanceIndex = j;
149
- }
150
- }
151
- }
110
+ // compute dimensions
111
+ int dim[3 ];
112
+ for (int i = 0 ; i < 3 ; i++)
113
+ dim[i] = static_cast <int >(std::ceil ((bounds[i * 2 + 1 ] - bounds[i * 2 ]) / spacing[i]));
152
114
153
- return smoothingResult;
154
- }
155
115
156
- vtkPolyDataPtr AirwaysFromCenterline::generateTubes ()
157
- {
116
+ resultImage-> SetDimensions (dim);
117
+ resultImage-> SetExtent ( 0 , dim[ 0 ] - 1 , 0 , dim[ 1 ] - 1 , 0 , dim[ 2 ] - 1 );
158
118
159
- vtkPolyDataPtr output = vtkPolyDataPtr::New ();
160
- vtkLineSourcePtr lineSourcePtr = vtkLineSourcePtr::New ();
161
- std::vector<BranchPtr> branches = mBranchListPtr ->getBranches ();
119
+ double origin[3 ];
120
+ origin[0 ] = bounds[0 ] + spacing[0 ] / 2 ;
121
+ origin[1 ] = bounds[2 ] + spacing[1 ] / 2 ;
122
+ origin[2 ] = bounds[4 ] + spacing[2 ] / 2 ;
123
+ resultImage->SetOrigin (origin);
124
+
125
+ resultImage->AllocateScalars (VTK_UNSIGNED_CHAR,1 );
162
126
127
+
128
+ // -----Generate tumes and spheres for each branch----------
129
+ vtkLineSourcePtr lineSourcePtr = vtkLineSourcePtr::New ();
163
130
for (int i = 0 ; i < branches.size (); i++)
164
131
{
165
132
Eigen::MatrixXd positions = branches[i]->getPositions ();
166
- vtkPointsPtr pointsPtr = vtkPointsPtr::New ();;
167
- pointsPtr->SetNumberOfPoints (positions.cols ());
133
+ vtkPointsPtr pointsPtr = vtkPointsPtr::New ();
134
+ int numberOfBranchPositions = positions.cols ();
135
+ pointsPtr->SetNumberOfPoints (numberOfBranchPositions);
136
+ int displaceIndex = 0 ;
168
137
169
- for ( int j = 0 ; j < positions. cols (); j++)
138
+ if (branches[i]-> getParentBranch ()) // Add parents last position to get connected tubes
170
139
{
171
- pointsPtr->SetPoint (j, positions (0 ,j), positions (1 ,j), positions (2 ,j));
140
+ displaceIndex = 1 ;
141
+ pointsPtr->SetNumberOfPoints (numberOfBranchPositions + 1 );
142
+ Eigen::MatrixXd parentPositions = branches[i]->getParentBranch ()->getPositions ();
143
+ int numberOfParentBranchPositions = parentPositions.cols ();
144
+ pointsPtr->SetPoint (0 , parentPositions (0 ,numberOfParentBranchPositions-1 ),
145
+ parentPositions (1 ,numberOfParentBranchPositions-1 ),
146
+ parentPositions (2 ,numberOfParentBranchPositions-1 ));
172
147
}
148
+
149
+ for (int j = 0 ; j < numberOfBranchPositions; j++)
150
+ pointsPtr->SetPoint (j + displaceIndex, positions (0 ,j), positions (1 ,j), positions (2 ,j));
151
+
173
152
lineSourcePtr->SetPoints (pointsPtr);
174
153
175
154
// Create a tube (cylinder) around the line
176
- vtkSmartPointer<vtkTubeFilter> tubeFilterPtr = vtkSmartPointer<vtkTubeFilter> ::New ();
155
+ vtkTubeFilterPtr tubeFilterPtr = vtkTubeFilterPtr ::New ();
177
156
tubeFilterPtr->SetInputConnection (lineSourcePtr->GetOutputPort ());
178
157
tubeFilterPtr->SetRadius (branches[i]->findBranchRadius ()); // default is .5
179
158
tubeFilterPtr->SetNumberOfSides (50 );
180
159
tubeFilterPtr->Update ();
181
160
182
- // Append the new mesh
183
- vtkAppendPolyDataPtr appendFilterPtr = vtkSmartPointer<vtkAppendPolyData>::New ();
184
- appendFilterPtr->AddInputData (output);
185
- appendFilterPtr->AddInputData (tubeFilterPtr->GetOutput ());
186
- appendFilterPtr->Update ();
187
-
188
- // Remove any duplicate points.
189
- vtkSmartPointer<vtkCleanPolyData> cleanFilterPtr = vtkSmartPointer<vtkCleanPolyData>::New ();
190
- cleanFilterPtr->SetInputConnection (appendFilterPtr->GetOutputPort ());
191
- cleanFilterPtr->Update ();
192
-
193
- output = cleanFilterPtr->GetOutput ();
161
+ // Add mesh from tube to image
162
+ vtkPolyDataToImageStencilPtr pol2stencTube = vtkPolyDataToImageStencilPtr::New ();
163
+ pol2stencTube->SetInputData (tubeFilterPtr->GetOutput ());
164
+ pol2stencTube->SetOutputOrigin (origin);
165
+ pol2stencTube->SetOutputSpacing (spacing);
166
+ pol2stencTube->SetOutputWholeExtent (resultImage->GetExtent ());
167
+ pol2stencTube->Update ();
168
+
169
+ vtkImageStencilPtr imgstencTube = vtkImageStencilPtr::New ();
170
+ imgstencTube->SetInputData (resultImage);
171
+ imgstencTube->SetStencilData (pol2stencTube->GetOutput ());
172
+ imgstencTube->ReverseStencilOn ();
173
+ imgstencTube->Update ();
174
+
175
+ resultImage = imgstencTube->GetOutput ();
176
+
177
+
178
+ // Add sphere at end of branch
179
+ vtkSphereSourcePtr spherePtr = vtkSphereSourcePtr::New ();
180
+ spherePtr->SetCenter (positions (0 ,numberOfBranchPositions-1 ),
181
+ positions (1 ,numberOfBranchPositions-1 ),
182
+ positions (2 ,numberOfBranchPositions-1 ));
183
+ spherePtr->SetRadius (1.05 * branches[i]->findBranchRadius ()); // 5% larger radius to close holes
184
+ spherePtr->SetThetaResolution (50 );
185
+ spherePtr->SetPhiResolution (50 );
186
+ spherePtr->Update ();
187
+
188
+ // Add sphere to image
189
+ vtkPolyDataToImageStencilPtr pol2stencSphere = vtkPolyDataToImageStencilPtr::New ();
190
+ pol2stencSphere->SetInputData (spherePtr->GetOutput ());
191
+ pol2stencSphere->SetOutputOrigin (origin);
192
+ pol2stencSphere->SetOutputSpacing (spacing);
193
+ pol2stencSphere->SetOutputWholeExtent (resultImage->GetExtent ());
194
+ pol2stencSphere->Update ();
195
+
196
+
197
+ vtkImageStencilPtr imgstencSphere = vtkImageStencilPtr::New ();
198
+ imgstencSphere->SetInputData (resultImage);
199
+ imgstencSphere->SetStencilData (pol2stencSphere->GetOutput ());
200
+ imgstencSphere->ReverseStencilOn ();
201
+ imgstencSphere->Update ();
202
+
203
+ resultImage = imgstencSphere->GetOutput ();
194
204
}
195
205
196
- // Create the implicit modeller
197
- vtkSmartPointer<vtkImplicitModeller> blobbyLogoImp =vtkSmartPointer<vtkImplicitModeller>::New ();
198
- blobbyLogoImp->SetInputData (output);
199
- blobbyLogoImp->SetMaximumDistance (0.1 );
200
- blobbyLogoImp->SetSampleDimensions (256 , 256 , 256 );
201
- blobbyLogoImp->SetAdjustDistance (0.1 );
202
206
203
- // Extract an iso surface, i.e. the tube shell
204
- vtkContourFilterPtr blobbyLogoIso = vtkSmartPointer<vtkContourFilter>::New ();
205
- blobbyLogoIso->SetInputConnection (blobbyLogoImp->GetOutputPort ());
206
- blobbyLogoIso->SetValue (1 , 1.5 ); // orig
207
- blobbyLogoIso->Update ();
207
+ // create contour from image
208
+ vtkPolyDataPtr rawContour = ContourFilter::execute (
209
+ resultImage,
210
+ 1 , // treshold
211
+ false , // reduce resolution
212
+ true , // smoothing
213
+ true , // keep topology
214
+ 0 // target decimation
215
+ );
208
216
209
- output = blobbyLogoIso-> GetOutput () ;
217
+ return rawContour ;
210
218
211
- return output;
212
219
}
213
220
214
-
215
-
216
-
217
221
double findDistanceToLine (Eigen::MatrixXd point, Eigen::MatrixXd line)
218
222
{
219
223
double minDistance = findDistance (point, line.col (0 ));
0 commit comments