forked from pixmeo/osirix
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathCenterline.mm
382 lines (305 loc) · 11.1 KB
/
Centerline.mm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
/*=========================================================================
Program: OsiriX
Copyright (c) OsiriX Team
All rights reserved.
Distributed under GNU - GPL
See http://www.osirix-viewer.com/copyright.html for details.
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.
=========================================================================*/
#import "Centerline.h"
#import "OSIVoxel.h"
#import "WaitRendering.h"
#define id Id
//#include "vtkSurfaceReconstructionFilter.h"
#include "vtkReverseSense.h"
#include "vtkShrinkFilter.h"
#include "vtkDelaunay3D.h"
#include "vtkDelaunay2D.h"
#include "vtkProperty.h"
#include "vtkActor.h"
#include "vtkOutlineFilter.h"
#include "vtkImageReader.h"
#include "vtkImageImport.h"
#include "vtkCamera.h"
#include "vtkStripper.h"
#include "vtkLookupTable.h"
#include "vtkImageDataGeometryFilter.h"
#include "vtkProperty.h"
#include "vtkPolyDataNormals.h"
#include "vtkContourFilter.h"
#include "vtkImageData.h"
#include "vtkExtractPolyDataGeometry.h"
#include "vtkPolyDataConnectivityFilter.h"
#include "vtkTransformPolyDataFilter.h"
#include "vtkImageResample.h"
#include "vtkDecimatePro.h"
#include "vtkSmoothPolyDataFilter.h"
#include "vtkPolyDataNormals.h"
#include "vtkTextureMapToSphere.h"
#include "vtkTransformTextureCoords.h"
#include "vtkPowerCrustSurfaceReconstruction.h"
#include "vtkTriangleFilter.h"
#undef id
@implementation Centerline
@synthesize wait = _wait, startingPoint = _startingPoint, endingPoint = _endingPoint, thinningIterations = _thinningIterations;
+ (id)centerline{
return [[[Centerline alloc] init] autorelease];
}
- (id)init {
if (self = [super init]) {
_thinningIterations = 750;
_wait = nil;
_startingPoint = nil;
_endingPoint = nil;
}
return self;
}
- (NSArray *)generateCenterline:(vtkPolyData *)polyData startingPoint:(OSIVoxel *)start endingPoint:(OSIVoxel *)end{
int oPoints = polyData->GetNumberOfPoints();
if (oPoints == 0) {
NSLog(@"No data to create centerline");
return nil;
}
NSMutableArray *connectedPoints = [NSMutableArray array];
NSMutableArray *stack = [NSMutableArray array];
NSMutableArray *centerlinePoints = [NSMutableArray array];
vtkDecimatePro *decimate = nil;
BOOL atEnd = NO;
OSIVoxel *endingPoint;
OSIVoxel *startingPoint;
float voxelWidth = start.voxelWidth;
float voxelHeight = start.voxelHeight;
float voxelDepth = start.voxelDepth;
// Never reach 0.8. Usually around 0.5, but we can hope.
[_wait setString:NSLocalizedString(@"Decimating Polygons", nil)];
float reduction = 0.8;
decimate = vtkDecimatePro::New();
decimate->SetInput(polyData);
decimate->SetTargetReduction(reduction);
decimate->SetPreserveTopology(YES);
decimate->BoundaryVertexDeletionOn();
decimate->SplittingOn();
decimate->SetMaximumError(VTK_DOUBLE_MAX);
decimate->Update();
vtkPolyData *data = decimate->GetOutput();
int nPoints = data->GetNumberOfPoints();
vtkPoints *medialPoints = data->GetPoints();
data->BuildLinks();
vtkIdType i;
int neighbors;
double x , y, z;
// get all cells around a point
data->BuildCells();
// Thinning Needs to be fast. Paper says iterate 1000
// point array will be thinnned
NSMutableArray *pointArray = [NSMutableArray array];
// originalPoints is unaltered so we can use the polygon points to calculate the centerline
NSMutableArray *originalPoints = [NSMutableArray array];
[_wait setString:NSLocalizedString(@"Building Points", nil)];
for (i = 0; i < nPoints; i++) {
double *position = medialPoints->GetPoint(i);
OSIVoxel *point3D = [OSIVoxel pointWithX:position[0] y:position[1] z:position[2] value:nil];
[point3D setUserInfo:[self connectedPointsForPoint:i fromPolyData:data]];
OSIVoxel *point2 = [[point3D copy] autorelease];
[pointArray addObject:point3D];
[originalPoints addObject:point2];
}
NSString *thinning = NSLocalizedString(@"Thinning", nil);
[_wait setString:thinning];
// Create NSArray from Polygon points
for (int a = 0; a < _thinningIterations ; a++){
for (OSIVoxel *point3D in pointArray) {
x = point3D.x;
y = point3D.y;
z = point3D.z;
NSSet *ptSet = [point3D userInfo];
for (NSNumber *number in ptSet) {
OSIVoxel *nextPoint = [pointArray objectAtIndex:[number intValue]];
x += nextPoint.x;
y += nextPoint.y;
z += nextPoint.z;
}
neighbors = [ptSet count] + 1;
// get average
x /= neighbors;
y /= neighbors;
z /= neighbors;
[point3D setX:(float)x y:(float)y z:(float)z];
//[_wait setString:[thinning stringByAppendingFormat:@" %d", a]];
}
}
x = [start x];
y = [start y];
z = [start z];
double minDistance = 1000000;
for (OSIVoxel *point3D in pointArray) {
double distance = sqrt( pow((x - point3D.x) * voxelWidth,2) + pow((y - point3D.y) * voxelHeight,2) + pow((z - point3D.z) * voxelDepth,2));
if (distance < minDistance) {
minDistance = distance;
startingPoint = point3D;
}
}
if (end) {
x = [end x];
y = [end y];
z = [end z];
for (OSIVoxel *point3D in pointArray) {
double distance = sqrt( pow((x - point3D.x) * voxelWidth,2) + pow((y - point3D.y) * voxelHeight,2) + pow((z - point3D.z) * voxelDepth,2));
if (distance < minDistance) {
minDistance = distance;
endingPoint = point3D;
}
}
}
int startIndex = [pointArray indexOfObject:startingPoint];
//set array to 0
unsigned char visited[nPoints];
for (int i = 0; i < nPoints; i++) visited[i] = 0;
visited[startIndex] = 1;
[stack addObject:[NSNumber numberWithInt:startIndex]];
vtkIdType currentPoint;
int currentModelIndex = startIndex;
OSIVoxel *currentModelPoint = startingPoint;
x = startingPoint.x;
y = startingPoint.y;
z = startingPoint.z;
[_wait setString:NSLocalizedString(@"Finding Index Points", nil)];
while (([stack count] > 0 ) && !atEnd) {
neighbors = 0;
currentPoint = [[stack lastObject] intValue];
[stack removeLastObject];
//Loop through neighbors to get avg neighbor position Go three connections out
NSSet *ptSet = [self connectedPointsForPoint:currentPoint fromPolyData:data];
NSMutableSet *neighbors = [NSMutableSet set];
[neighbors unionSet:ptSet];
for (int i = 0; i < 2; i++) {
NSMutableSet *newNeighbors = [NSMutableSet set];
for (NSNumber *number in neighbors) {
NSSet *neighborSet = (NSSet *)[[pointArray objectAtIndex:[number intValue]] userInfo];
[newNeighbors unionSet:neighborSet];
}
[neighbors unionSet:newNeighbors];
}
double modellingDistance = 5.0;
for (NSNumber *number in neighbors) {
int index = [number intValue];
OSIVoxel *nextPoint = [pointArray objectAtIndex:index];
if (visited[index] == 0) {
double distance = sqrt( pow((x - nextPoint.x) * voxelWidth,2) + pow((y - nextPoint.y) * voxelHeight,2) + pow((z - nextPoint.z) * voxelDepth,2));
if (distance > modellingDistance) {
// if point is within modelling distance of an existing point don't add
BOOL tooClose = NO;
for (OSIVoxel *existingPoint in connectedPoints) {
if (sqrt( pow((currentModelPoint.x - existingPoint.x) * voxelWidth,2)
+ pow((currentModelPoint.y - existingPoint.y) * voxelHeight,2)
+ pow((currentModelPoint.z - existingPoint.z) * voxelDepth,2)) < modellingDistance) tooClose = YES;
}
if (!tooClose) [connectedPoints addObject:currentModelPoint];
if ([currentModelPoint isEqual:endingPoint]) {
atEnd = YES;
break;
}
currentModelIndex = index;
currentModelPoint = nextPoint;
x = nextPoint.x;
y = nextPoint.y;
z = nextPoint.z;
}
[stack addObject:[NSNumber numberWithInt:index]];
visited[index] = 1;
}
}
// try and make sure visited most points
// Find next closest point
}
[_wait setString:NSLocalizedString(@"Arranging Points", nil)];
if ([connectedPoints count] > 0) {
// Arrange points from start to end based on proximity
NSMutableArray *arrangedPoints = [NSMutableArray array];
[arrangedPoints addObject:startingPoint];
[connectedPoints removeObject:startingPoint];
OSIVoxel *nextPoint;
currentModelPoint = startingPoint;
while ([connectedPoints count] > 1) {
minDistance = 1000000;
for (OSIVoxel *point3D in connectedPoints) {
double distance = sqrt( pow((currentModelPoint.x - point3D.x) * voxelWidth,2)
+ pow((currentModelPoint.y - point3D.y) * voxelHeight,2)
+ pow((currentModelPoint.z - point3D.z) * voxelDepth,2));
if (distance < minDistance) {
minDistance = distance;
nextPoint = point3D;
}
}
[arrangedPoints addObject:nextPoint];
[connectedPoints removeObject:nextPoint];
currentModelPoint = nextPoint;
}
[arrangedPoints addObject:[connectedPoints lastObject]];
// Get all points lying between our selected points.
//Get points from original surface.
//Get average for centerline
int pointCount = [arrangedPoints count] - 1;
[_wait setString:NSLocalizedString(@"Finding Centerline Points", nil)];
for (int i = 0; i < pointCount; i++) {
NSMutableSet *nearbyPoints = [NSMutableSet set];
OSIVoxel *firstPoint = [arrangedPoints objectAtIndex:i];
OSIVoxel *nextPoint = [arrangedPoints objectAtIndex: i+1];
double distance = sqrt( pow((firstPoint.x - nextPoint.x) * voxelWidth,2)
+ pow((firstPoint.y - nextPoint.y) * voxelHeight,2)
+ pow((firstPoint.z - nextPoint.z) * voxelDepth,2));
for (OSIVoxel *point3D in pointArray) {
double distance1 = sqrt( pow((firstPoint.x - point3D.x) * voxelWidth,2)
+ pow((firstPoint.y - point3D.y) * voxelHeight,2)
+ pow((firstPoint.z - point3D.z) * voxelDepth,2));
double distance2 = sqrt( pow((nextPoint.x - point3D.x) * voxelWidth,2)
+ pow((nextPoint.y - point3D.y) * voxelHeight,2)
+ pow((nextPoint.z - point3D.z) * voxelDepth,2));
if ((distance1 <= distance) && (distance2 <= distance)) {
int index = [pointArray indexOfObject:point3D];
if (index < [originalPoints count]);
[nearbyPoints addObject:[originalPoints objectAtIndex:index]];
}
}
double neighborsCount = (double)[nearbyPoints count];
double xPos, yPos, zPos;
for (OSIVoxel *point3D in nearbyPoints) {
xPos += point3D.x;
yPos += point3D.y;
zPos += point3D.z;
}
xPos /= neighborsCount;
yPos /= neighborsCount;
zPos /= neighborsCount;
[centerlinePoints addObject:[OSIVoxel pointWithX: xPos y:yPos z:zPos value:nil]];
}
}
decimate->Delete();
return centerlinePoints;
}
- (NSMutableSet *)connectedPointsForPoint:(vtkIdType)pt fromPolyData:(vtkPolyData *)data{
NSMutableSet *ptSet = [NSMutableSet set];
vtkIdType ncells;
vtkIdList *cellIds = vtkIdList::New();
// All cells for Point and number of cells
data->GetPointCells (pt, cellIds);
ncells = cellIds->GetNumberOfIds();
// loop through the cells
for (int j = 0; j < ncells; j++) {
vtkIdType numPoints;
vtkIdType *cellPoints ;
vtkIdType cellId = cellIds->GetId(j);
//get all points for the cell
data->GetCellPoints(cellId, numPoints, cellPoints);
// points may be duplicate
for (int k = 0; k < numPoints; k++) {
NSNumber *number = [NSNumber numberWithInt:cellPoints[k]];
[ptSet addObject:number];
}
}
cellIds -> Delete();
return ptSet;
}
@end