cimg.cxx 3.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,587 @@
1
+ /*
2
+ #
3
+ # File : skeleton.h
4
+ # ( C++ header file - CImg plug-in )
5
+ #
6
+ # Description : CImg plugin that implements the computation of the Hamilton-Jacobi skeletons
7
+ # using Siddiqi algorithm with the correction proposed by Torsello,
8
+ # as described in :
9
+ #
10
+ # [SBTZ02] K. Siddiqi, S. Bouix, A. Tannenbaum and S.W. Zucker. Hamilton-Jacobi Skeletons
11
+ # International Journal of Computer Vision, 48(3):215-231, 2002
12
+ #
13
+ # [TH03] A. Torsello and E. R. Hancock. Curvature Correction of the Hamilton-Jacobi Skeleton
14
+ # IEEE Computer Vision and Pattern Recognition, 2003
15
+ #
16
+ # [BST05] S. Bouix, K. Siddiqi and A. Tannenbaum. Flux driven automatic centerline
17
+ # extraction. Medical Image Analysis, 9:209-221, 2005
18
+ #
19
+ # IMPORTANT WARNING : You must include STL's <queue> before plugin inclusion to make it working !
20
+ #
21
+ # Copyright : Francois-Xavier Dupe
22
+ # ( http://www.greyc.ensicaen.fr/~fdupe/ )
23
+ #
24
+ # This software is governed by the CeCILL license under French law and
25
+ # abiding by the rules of distribution of free software. You can use,
26
+ # modify and/or redistribute the software under the terms of the CeCILL
27
+ # license as circulated by CEA, CNRS and INRIA at the following URL
28
+ # "http://www.cecill.info".
29
+ #
30
+ # As a counterpart to the access to the source code and rights to copy,
31
+ # modify and redistribute granted by the license, users are provided only
32
+ # with a limited warranty and the software's author, the holder of the
33
+ # economic rights, and the successive licensors have only limited
34
+ # liability.
35
+ #
36
+ # In this respect, the user's attention is drawn to the risks associated
37
+ # with loading, using, modifying and/or developing or reproducing the
38
+ # software by the user in light of its specific status of free software,
39
+ # that may mean that it is complicated to manipulate, and that also
40
+ # therefore means that it is reserved for developers and experienced
41
+ # professionals having in-depth computer knowledge. Users are therefore
42
+ # encouraged to load and test the software's suitability as regards their
43
+ # requirements in conditions enabling the security of their systems and/or
44
+ # data to be ensured and, more generally, to use and operate it in the
45
+ # same conditions as regards security.
46
+ #
47
+ # The fact that you are presently reading this means that you have had
48
+ # knowledge of the CeCILL license and that you accept its terms.
49
+ #
50
+ */
51
+ #ifndef cimg_plugin_skeleton
52
+ #define cimg_plugin_skeleton
53
+
54
+ /**
55
+ * Compute the flux of the gradient
56
+ * @param grad the gradient of the distance function
57
+ * @param sY the sampling size in Y
58
+ * @param sZ the sampling size in Z
59
+ * @return the flux
60
+ */
61
+ CImg<floatT> get_flux(const CImgList<floatT> & grad,
62
+ const float sY=1.0f, const float sZ=1.0f) const {
63
+
64
+ int stop = 0; // Stop flag
65
+ float f = 0; // The current flux
66
+ int count = 0; // Counter
67
+ CImg<floatT> flux(width(),height(),depth(),1,0);
68
+
69
+ cimg_forXYZ((*this),x,y,z) {
70
+ if (!(*this)(x,y,z)) continue; // If the point is the background
71
+
72
+ // Look at the neigthboorhound and compute the flux
73
+ stop = 0;
74
+ f = 0;
75
+ count = 0;
76
+
77
+ for (int k = -1; k<=1; ++k)
78
+ for (int l = -1; l<= 1; ++l)
79
+ for (int m = -1; m<= 1; ++m) {
80
+ if (stop==1) continue;
81
+
82
+ // Protection
83
+ if ((x + k<0) || (x + k>=width()) || (y + l<0) || (y + l>=height()) ||
84
+ (z + m<0) || (z + m>=depth()) || (k==0 && l==0 && m==0)) continue;
85
+ ++count;
86
+
87
+ // Test if the point is in the interior
88
+ if ((*this)(x + k,y + l,z + m)==0) { stop = 1; continue; }
89
+
90
+ // Compute the flux
91
+ f+=(grad(0,x + k,y + l,z + m)*k + grad(1,x + k,y + l,z + m)*l/sY + grad(2,x + k,y + l,z + m)*m/sZ)/
92
+ std::sqrt((float)(k*k + l*l + m*m));
93
+ }
94
+
95
+ // Update
96
+ if (stop==1 || count==0) flux(x,y,z) = 0;
97
+ else flux(x,y,z) = f/count;
98
+ }
99
+
100
+ return flux;
101
+ }
102
+
103
+ /**
104
+ * Definition of a point with his flux value
105
+ */
106
+ struct _PointFlux {
107
+ int pos [3];
108
+ float flux;
109
+ float dist;
110
+ };
111
+
112
+ /**
113
+ * Class for the priority queue
114
+ */
115
+ class _compare_point {
116
+ /**
117
+ * Create medial curves
118
+ */
119
+ bool curve;
120
+
121
+ public:
122
+ _compare_point(const bool _curve=false) { this->curve = _curve; }
123
+
124
+ bool operator()(const _PointFlux & p1, const _PointFlux & p2) const {
125
+ if (curve) {
126
+ if (p1.dist>p2.dist) return true;
127
+ else if (p1.dist==p2.dist && p1.flux<p2.flux) return true;
128
+ } else {
129
+ if (p1.flux<p2.flux) return true;
130
+ else if (p1.flux==p2.flux && p1.dist>p2.dist) return true;
131
+ }
132
+ return false;
133
+ }
134
+ };
135
+
136
+ /**
137
+ * Compute the log-density using the algorithm from Torsello
138
+ * @param dist the distance map
139
+ * @param grad the gradient of the distance map, e.g. the flux
140
+ * @param flux the divergence map
141
+ * @param delta the threshold for the division
142
+ * @return the logdensity \rho
143
+ */
144
+ CImg<floatT> get_logdensity(const CImg<floatT> & dist,
145
+ const CImgList<floatT> & grad,
146
+ const CImg<floatT> & flux, float delta = 0.1) const {
147
+ std::priority_queue< _PointFlux, std::vector<_PointFlux>, _compare_point > pqueue(true);
148
+ CImg<floatT> logdensity(width(),height(),depth(),1,0);
149
+
150
+ // 1 - Put all the pixel inside the priority queue
151
+ cimg_forXYZ(dist,x,y,z) if (dist(x,y,z)!=0) {
152
+ _PointFlux p;
153
+ p.pos[0] = x;
154
+ p.pos[1] = y;
155
+ p.pos[2] = z;
156
+ p.flux = 0;
157
+ p.dist = dist(x,y,z);
158
+ pqueue.push(p);
159
+ }
160
+
161
+ // 2 - Compute the logdensity
162
+ while (!pqueue.empty()) {
163
+ _PointFlux p = pqueue.top();
164
+ pqueue.pop();
165
+
166
+ const float
167
+ Fx = grad(0,p.pos[0],p.pos[1],p.pos[2]),
168
+ Fy = grad(1,p.pos[0],p.pos[1],p.pos[2]),
169
+ Fz = grad(2,p.pos[0],p.pos[1],p.pos[2]);
170
+
171
+ logdensity(p.pos[0],p.pos[1],p.pos[2]) = logdensity.linear_atXYZ(p.pos[0] - Fx,p.pos[1] - Fy,p.pos[2] - Fz)
172
+ - 0.5f * (flux(p.pos[0],p.pos[1],p.pos[2]) + flux.linear_atXYZ(p.pos[0] - Fx,p.pos[1] - Fy,p.pos[2] - Fz));
173
+
174
+ const float tmp = 1.0f - (1.0f - std::fabs(Fx)) * (1.0f - std::fabs(Fy)) * (1.0f - std::fabs(Fz));
175
+ if (tmp>delta) logdensity(p.pos[0],p.pos[1],p.pos[2])/=tmp;
176
+ else if (delta<1) logdensity(p.pos[0],p.pos[1],p.pos[2]) = 0;
177
+ }
178
+
179
+ return logdensity;
180
+ }
181
+
182
+ /**
183
+ * Computed the corrected divergence map using Torsello formula and idea
184
+ * @param logdensity the log density map
185
+ * @param grad the gradient of the distance map
186
+ * @param flux the flux using siddiqi formula
187
+ * @param delta the discrete step
188
+ * @return the corrected divergence map
189
+ */
190
+ CImg<floatT> get_corrected_flux(const CImg<floatT> & logdensity,
191
+ const CImgList<floatT> & grad,
192
+ const CImg<floatT> & flux,
193
+ float delta = 1.0) const {
194
+
195
+ CImg<floatT> corr_map(width(),height(),depth(),1,0);
196
+ cimg_forXYZ(corr_map,x,y,z) {
197
+ const float
198
+ Fx = grad(0,x,y,z),
199
+ Fy = grad(1,x,y,z),
200
+ Fz = grad(2,x,y,z);
201
+ corr_map(x,y,z) =
202
+ (logdensity(x,y,z) -
203
+ logdensity.linear_atXYZ(x - Fx,y - Fy,z - Fz)) * expf(logdensity(x,y,z) - 0.5f * delta) +
204
+ 0.5f * ( flux.linear_atXYZ(x - Fx,y - Fy,z - Fz)*expf(logdensity.linear_atXYZ(x - Fx,y - Fy,z - Fz)) +
205
+ flux(x,y,z)*expf(logdensity(x,y,z)));
206
+ }
207
+
208
+ return corr_map;
209
+ }
210
+
211
+ /**
212
+ * Test if a point is simple using Euler number for 2D case
213
+ * or using Malandain criterion for 3D case
214
+ * @param img the image
215
+ * @param x the x coordinate
216
+ * @param y the y coordinate
217
+ * @param z the z coordinate
218
+ * @return true if simple
219
+ */
220
+ bool _isSimple (const CImg<T> & img, int x, int y, int z ) const {
221
+ if (img.depth()==1) { // 2D case
222
+ int V = 0, E = 0; // Number of vertices and edges
223
+
224
+ for (int k = -1; k<=1; ++k)
225
+ for (int l = -1; l<=1; ++l) {
226
+ // Protection
227
+ if (x+k<0 || x+k>=img.width() || y+l<0 || y+l>=img.height()) continue;
228
+
229
+ // Count the number of vertices
230
+ if (img(x + k,y + l)!=0 && !(k==0 && l==0)) {
231
+ ++V;
232
+
233
+ // Count the number of edges
234
+ for (int k1 = -1; k1<=1; ++k1)
235
+ for (int l1 = -1; l1<=1; ++l1) {
236
+ // Protection
237
+ if (x + k + k1<0 || x + k + k1>=img.width() || y + l + l1<0 || y + l + l1>=img.height()) continue;
238
+
239
+ if (!(k1==0 && l1==0) && img(x + k + k1,y + l + l1)!=0 && k + k1>-2 && l + l1>-2 &&
240
+ k + k1<2 && l + l1<2 && !(k + k1==0 && l + l1==0))
241
+ ++E;
242
+ }
243
+ }
244
+ }
245
+
246
+ // Remove the corner if exists
247
+ if (x - 1>=0 && y - 1>=0 && img(x - 1,y - 1)!=0 && img(x,y - 1)!=0 && img(x - 1,y)!=0) E-=2;
248
+ if (x - 1>=0 && y + 1<img.height() && img(x - 1,y + 1)!=0 && img(x,y + 1)!=0 && img(x - 1,y)!=0) E-=2;
249
+ if (x + 1<img.width() && y - 1>=0 && img(x + 1,y - 1)!=0 && img(x,y - 1)!=0 && img(x + 1,y)!=0) E-=2;
250
+ if (x + 1<img.width() && y + 1<img.height() && img(x + 1,y + 1)!=0 && img(x,y + 1)!=0 && img(x + 1,y)!=0) E-=2;
251
+
252
+ // Final return true if it is a tree (eg euler number equal to 1)
253
+ if ((V - E/2)==1) return true;
254
+
255
+ } else { // 3D case
256
+ CImg<intT> visit(3,3,3,1,0); // Visitor table
257
+ int C_asterix = 0, C_bar = 0, count = 0;
258
+ visit(1,1,1) = -1;
259
+
260
+ // Compute C^*
261
+
262
+ // Seeking for a component
263
+ for (int k = -1; k<=1; ++k)
264
+ for (int l = -1; l<=1; ++l)
265
+ for (int m = -1; m<=1; ++m) {
266
+ int a_label = 0;
267
+
268
+ // Protection
269
+ if (x + k<0 || x + k>=img.width() ||
270
+ y + l<0 || y + l>=img.height() ||
271
+ z + m<0 || z + m>=img.depth() ||
272
+ (k==0 && l==0 && m==0)) continue;
273
+
274
+ if (visit(1 + k,1 + l,1 + m)==0 && img(x + k,y + l,z + m)!=0) {
275
+ // Look after the neightbor
276
+ for (int k1 = -1; k1<=1; ++k1)
277
+ for (int l1 = -1; l1<=1; ++l1)
278
+ for (int m1 = -1; m1<=1; ++m1) {
279
+ // Protection
280
+ if (x + k + k1<0 || x + k + k1>=img.width() ||
281
+ y + l + l1<0 || y + l + l1>=img.height() ||
282
+ z + m + m1<0 || z + m + m1>=img.depth() ||
283
+ k + k1>1 || k + k1<-1 ||
284
+ l + l1>1 || l + l1<-1 ||
285
+ m + m1>1 || m + m1<-1 ) continue;
286
+
287
+ // Search for a already knew component
288
+ if (visit(1 + k + k1,1 + l + l1,1 + m + m1)>0 &&
289
+ img(x + k + k1,y + l + l1,z + m + m1)!=0) {
290
+ if (a_label==0) a_label = visit(1 + k + k1,1 + l + l1,1 + m + m1);
291
+ else if (a_label!=visit(1 + k + k1,1 + l + l1,1 + m + m1)) {
292
+ // Meld component
293
+ --C_asterix;
294
+
295
+ int C = visit(1 + k + k1,1 + l + l1,1 + m + m1);
296
+ cimg_forXYZ(visit,a,b,c) if (visit(a,b,c)==C) visit(a,b,c) = a_label;
297
+ }
298
+ }
299
+ }
300
+
301
+ // Label the point
302
+ if (a_label==0) {
303
+ // Find a new component
304
+ ++C_asterix;
305
+ ++count;
306
+ visit(1 + k ,1 + l,1 + m) = count;
307
+ } else visit(1 + k,1 + l,1 + m) = a_label;
308
+ }
309
+ }
310
+
311
+ if (C_asterix!=1) return false;
312
+
313
+ // Compute \bar{C}
314
+
315
+ // Reinit visit
316
+ visit.fill(0);
317
+ visit(1,1,1) = -1;
318
+
319
+ // Seeking for a component
320
+
321
+ // Look at X-axis
322
+ for (int k = -1; k<=1; ++k) {
323
+ if (x + k<0 || x + k>=img.width()) continue;
324
+
325
+ if (img(x + k,y,z)==0 && visit(1 + k,1,1)==0) {
326
+ ++C_bar;
327
+ ++count;
328
+ visit(1 + k,1,1) = count;
329
+
330
+ // Follow component
331
+ for (int l = -1; l<=1; ++l) {
332
+ if (y + l<img.height() && y + l>=0 && img(x + k,y + l,z)==0 && visit(1 + k,1 + l,1)==0)
333
+ visit(1 + k,1 + l,1) = count;
334
+ if (z + l<img.depth() && z + l>=0 && img(x + k,y,z + l)==0 && visit(1 + k,1,1 + l)==0)
335
+ visit(1 + k,1,1 + l) = count;
336
+ }
337
+ }
338
+ }
339
+
340
+ // Look at Y-axis
341
+ for (int k = -1; k<=1; ++k) {
342
+ if (y + k<0 || y + k>=img.height()) continue;
343
+
344
+ if (img(x,y + k,z)==0 && visit(1,1 + k,1)==0) {
345
+ int a_label = 0;
346
+ ++C_bar;
347
+ ++count;
348
+ visit(1,1 + k,1) = count;
349
+ a_label = count;
350
+
351
+ // Follow component
352
+ for (int l = -1; l<=1; ++l) {
353
+ if (l==0) continue;
354
+
355
+ if (x + l<img.width() && x + l>=0 && img(x + l,y + k,z)==0) {
356
+ if (visit(1 + l,1 + k,1)!=0) {
357
+ if (a_label!=visit(1 + l,1 + k,1)) {
358
+ // Meld component
359
+ --C_bar;
360
+
361
+ int C = visit(1 + l,1 + k,1);
362
+ cimg_forXYZ(visit,a,b,c)
363
+ if (visit(a,b,c)==C) visit(a,b,c) = a_label;
364
+ }
365
+ } else visit(1 + l,1 + k,1) = a_label;
366
+ }
367
+
368
+ if (z + l<img.depth() && z + l>=0 && img(x,y + k,z + l)==0) {
369
+ if (visit(1,1 + k,1 + l)!=0) {
370
+ if (a_label!=visit(1,1 + k,1 + l)) {
371
+ // Meld component
372
+ --C_bar;
373
+
374
+ int C = visit(1,1 + k,1 + l);
375
+ cimg_forXYZ(visit,a,b,c)
376
+ if (visit(a,b,c)==C) visit(a,b,c) = a_label;
377
+ }
378
+ } else visit(1,1 + k,1 + l) = a_label;
379
+ }
380
+ }
381
+ }
382
+ }
383
+
384
+ // Look at Z-axis
385
+ for (int k = -1; k<=1; ++k) {
386
+ if (z + k<0 || z + k>=img.depth()) continue;
387
+
388
+ if (img(x,y,z + k)==0 && visit(1,1,1 + k)==0) {
389
+ int a_label = 0;
390
+ ++C_bar;
391
+ ++count;
392
+ visit(1,1,1 + k) = count;
393
+ a_label = count;
394
+
395
+ // Follow component
396
+ for (int l = -1; l<=1; ++l) {
397
+ if (l==0) continue;
398
+
399
+ if (x + l<img.width() && x + l>=0 && img(x + l,y,z + k)==0) {
400
+ if (visit(1 + l,1,1 + k)!=0) {
401
+ if (a_label!=visit(1 + l,1,1 + k)) {
402
+ // Meld component
403
+ --C_bar;
404
+
405
+ int C = visit(1 + l,1,1 + k);
406
+ cimg_forXYZ(visit,a,b,c)
407
+ if (visit(a,b,c)==C) visit(a,b,c) = a_label;
408
+ }
409
+ } else visit(1 + l,1,1 + k) = a_label;
410
+ }
411
+
412
+ if (y + l<img.height() && y + l>=0 && img(x,y + l,z + k)==0) {
413
+ if (visit(1,1 + l,1 + k)!=0) {
414
+ if (a_label!=visit(1,1 + l,1 + k)) {
415
+ // Meld component
416
+ --C_bar;
417
+
418
+ int C = visit(1,1 + l,1 + k);
419
+ cimg_forXYZ(visit,a,b,c)
420
+ if (visit(a,b,c)==C) visit(a,b,c) = a_label;
421
+ }
422
+ } else visit(1,1 + l,1 + k) = a_label;
423
+ }
424
+ }
425
+ }
426
+ }
427
+ if (C_bar==1) return true;
428
+ }
429
+
430
+ return false;
431
+ }
432
+
433
+ /**
434
+ * Test if a point is a end point
435
+ * @param img the image
436
+ * @param a_label the table of labels
437
+ * @param curve set it to true for having medial curve
438
+ * @param x the x coordinate
439
+ * @param y the y coordinate
440
+ * @param z the z coordinate
441
+ * @return true if simple
442
+ */
443
+ bool _isEndPoint(const CImg<T> & img, const CImg<T> & a_label,
444
+ const bool curve, const int x, const int y, const int z) const {
445
+ if (a_label(x,y,z)==1) return true;
446
+
447
+ if ((!curve) && (img.depth()!=1)) { // 3D case with medial surface
448
+ // Use Pudney specification with the 9 plans
449
+ const int plan9 [9][8][3] =
450
+ { { {-1,0,-1}, {0,0,-1}, {1,0,-1}, {-1,0,0}, {1,0,0}, {-1,0,1}, {0,0,1}, {1,0,1} }, // Plan 1
451
+ { {-1,1,0}, {0,1,0}, {1,1,0}, {-1,0,0}, {1,0,0}, {-1,-1,0}, {0,-1,0}, {1,-1,0} }, // Plan 2
452
+ { {0,-1,-1}, {0,0,-1}, {0,1,-1}, {0,-1,0}, {0,1,0}, {0,-1,1}, {0,0,1}, {0,1,1} }, // Plan 3
453
+ { {1,1,1}, {0,1,0}, {-1,1,-1}, {1,0,1}, {-1,0,-1}, {-1,-1,-1}, {0,-1,0}, {1,-1,1} }, // Plan 4
454
+ { {-1,1,1}, {0,1,0}, {1,1,-1}, {-1,0,1}, {1,0,-1}, {-1,-1,1}, {0,-1,0}, {1,-1,-1} }, // Plan 5
455
+ { {-1,1,1}, {0,1,1}, {1,1,1}, {-1,0,0}, {1,0,0}, {-1,-1,-1}, {0,-1,-1}, {1,-1,-1} }, // Plan 6
456
+ { {-1,1,-1}, {0,1,-1}, {1,1,-1}, {-1,0,0}, {1,0,0}, {-1,-1,1}, {0,-1,1}, {1,-1,1} }, // Plan 7
457
+ { {-1,1,-1}, {-1,1,0}, {-1,1,1}, {0,0,-1}, {0,0,1}, {1,-1,-1}, {1,-1,0}, {1,-1,1} }, // Plan 8
458
+ { {1,1,-1}, {1,1,0}, {1,1,1}, {0,0,-1}, {0,0,1}, {-1,-1,-1}, {-1,-1,0}, {-1,-1,1} } // Plan 9
459
+ };
460
+
461
+ // Count the number of neighbors on each plan
462
+ for (int k = 0; k<9; ++k) {
463
+ int count = 0;
464
+
465
+ for (int l = 0; l<8; ++l) {
466
+ if (x + plan9[k][l][0]<0 || x + plan9[k][l][0]>=img.width() ||
467
+ y + plan9[k][l][1]<0 || y + plan9[k][l][1]>=img.height() ||
468
+ z + plan9[k][l][2]<0 || z + plan9[k][l][2]>=img.depth()) continue;
469
+
470
+ if (img(x + plan9[k][l][0],y + plan9[k][l][1],z + plan9[k][l][2])!=0) ++count;
471
+ }
472
+
473
+ if (count<2) return true;
474
+ }
475
+ } else { // 2D or 3D case with medial curve
476
+ int isb = 0;
477
+
478
+ for (int k = -1; k<=1; ++k)
479
+ for (int l = -1; l<=1; ++l)
480
+ for (int m = -1; m<=1; ++m) {
481
+ // Protection
482
+ if (x + k<0 || x + k>=img.width() ||
483
+ y + l<0 || y + l>=img.height() ||
484
+ z + m<0 || z + m>=img.depth()) continue;
485
+
486
+ if (img(x + k,y + l,z + m)!=0) ++isb;
487
+ }
488
+
489
+ if (isb==2) return true; // The pixel with one neighbor
490
+ }
491
+
492
+ // Else it's not...
493
+ return false;
494
+ }
495
+
496
+ /**
497
+ * Compute the skeleton of the shape using Hamilton-Jacobi scheme
498
+ * @param flux the flux of the distance gradient
499
+ * @param dist the euclidean distance of the object
500
+ * @param curve create or not medial curve
501
+ * @param thres the threshold on the flux
502
+ * @return the skeleton
503
+ */
504
+ CImg<T> get_skeleton (const CImg<floatT> & flux,
505
+ const CImg<floatT> & dist, const bool curve, const float thres) const {
506
+ CImg<T>
507
+ skeleton(*this), // The skeleton
508
+ a_label(width(),height(),depth(),1,0), // Save label
509
+ count(width(),height(),depth(),1,0); // A counter for the queue
510
+ std::priority_queue< _PointFlux, std::vector<_PointFlux>, _compare_point > pqueue(curve);
511
+ int isb = 0;
512
+
513
+ // 1 - Init get the bound points
514
+ cimg_forXYZ(*this,x,y,z) {
515
+ if (skeleton(x,y,z)==0) continue;
516
+
517
+ // Test bound condition
518
+ isb = 0;
519
+ for (int k = -1; k<=1; ++k)
520
+ for (int l = -1; l<=1; ++l)
521
+ for (int m = -1; m<=1; ++m) {
522
+ // Protection
523
+ if (x + k<0 || x + k>=width() ||
524
+ y + l<0 || y + l>=height() ||
525
+ z + m<0 || z + m>=depth()) continue;
526
+ if (skeleton(x + k,y + l,z + m)==0) isb = 1;
527
+ }
528
+
529
+ if (isb==1 && _isSimple(skeleton,x,y,z)) {
530
+ _PointFlux p;
531
+ p.pos[0] = x;
532
+ p.pos[1] = y;
533
+ p.pos[2] = z;
534
+ p.flux = flux(x,y,z);
535
+ p.dist = dist(x,y,z);
536
+ pqueue.push(p);
537
+ count(x,y,z) = 1;
538
+ }
539
+ }
540
+
541
+ // 2 - Compute the skeleton
542
+ while (!pqueue.empty()) {
543
+ _PointFlux p = pqueue.top(); // Get the point with the max flux
544
+ pqueue.pop(); // Remove the point from the queue
545
+ count(p.pos[0],p.pos[1],p.pos[2]) = 0; // Reinit counter
546
+
547
+ // Test if the point is simple
548
+ if (_isSimple(skeleton,p.pos[0],p.pos[1],p.pos[2])) {
549
+ if ((! _isEndPoint(skeleton,a_label,curve,p.pos[0],p.pos[1],p.pos[2])) || p.flux>thres) {
550
+ skeleton(p.pos[0],p.pos[1],p.pos[2]) = 0; // Remove the point
551
+
552
+ for (int k = -1; k<=1; ++k)
553
+ for (int l = -1; l<=1; ++l)
554
+ for (int m = -1; m<=1; ++m) {
555
+ // Protection
556
+ if (p.pos[0] + k<0 || p.pos[0] + k>= width() ||
557
+ p.pos[1] + l<0 || p.pos[1] + l>= height() ||
558
+ p.pos[2] + m<0 || p.pos[2] + m>= depth()) continue;
559
+ if (skeleton(p.pos[0] + k,p.pos[1] + l,p.pos[2] + m)!=0 &&
560
+ count(p.pos[0] + k,p.pos[1] + l,p.pos[2] + m)<1 &&
561
+ _isSimple(skeleton,p.pos[0] + k,p.pos[1] + l,p.pos[2] + m)) {
562
+ _PointFlux p1;
563
+ p1.pos[0] = p.pos[0] + k;
564
+ p1.pos[1] = p.pos[1] + l;
565
+ p1.pos[2] = p.pos[2] + m;
566
+ p1.flux = flux(p.pos[0] + k,p.pos[1] + l,p.pos[2] + m);
567
+ p1.dist = dist(p.pos[0] + k,p.pos[1] + l,p.pos[2] + m);
568
+ pqueue.push(p1);
569
+ count(p.pos[0] + k,p.pos[1] + l,p.pos[2] + m) = 1;
570
+ }
571
+ }
572
+ } else a_label(p.pos[0],p.pos[1],p.pos[2]) = 1; // Mark the point as skeletal
573
+ }
574
+ }
575
+
576
+ return skeleton;
577
+ }
578
+
579
+ /**
580
+ * In place version of get_skeleton
581
+ */
582
+ CImg<T> skeleton(const CImg<floatT> & flux,
583
+ const CImg<floatT> & dist, bool curve ,float thres) {
584
+ return get_skeleton(flux,dist,curve,thres).move_to(*this);
585
+ }
586
+
587
+ #endif /* cimg_plugin_skeleton */