1 #include <stdio.h>
2 #include <dirent.h>
3
4 // not supported on standard C, MacOS especially
5 // include <malloc.h>
6
7 #include <string.h>
8 #include <unistd.h>
9 #include <stdbool.h>
10 #include "mrilib.h"
11 // #include "distanceField.h"
12 #include <float.h>
13 #include "thd_depth.h"
14
getDistanceFields(char * inputFileName,char * outputFileName,int metric,bool do_sqrt,bool edges_are_zero_for_nz,bool debugMode)15 ERROR_NUMBER getDistanceFields(char *inputFileName, char *outputFileName, int metric, bool do_sqrt,
16 bool edges_are_zero_for_nz, bool debugMode){
17
18 ERROR_NUMBER errorNumber;
19 THD_3dim_dataset *din = NULL, *dout = NULL;
20
21 // Open dataset
22 if ( (errorNumber=open_input_dset(&din, inputFileName))!=ERROR_NONE ){
23 Cleanup(inputFileName, outputFileName, din);
24 return errorNumber;
25 }
26
27 if ( (errorNumber=getDistanceFieldDataSet(din, &dout, metric, do_sqrt, edges_are_zero_for_nz, debugMode))!=ERROR_NONE ){
28 Cleanup(inputFileName, outputFileName, din);
29 return errorNumber;
30 }
31
32 if (!outputFileName){ // Default output filename
33 char *prefix=DSET_PREFIX(din);
34 char *searchPath=DSET_DIRNAME(din);
35 char appendage[256];
36
37 // Set appendage
38 switch (metric){
39 case MARCHING_PARABOLAS:
40 sprintf(appendage, "MarParab");
41 break;
42 case EROSION:
43 sprintf(appendage, "Erosion");
44 break;
45 }
46
47 if (debugMode) sprintf(appendage, "%s", strcat(appendage, "Debug"));
48
49 // Allocate memory to output name buffer
50 if (!(outputFileName=(char *)malloc(strlen(searchPath)+strlen(prefix)+strlen(appendage)+8))){
51 return ERROR_MEMORY_ALLOCATION;
52 }
53
54 sprintf(outputFileName,"%s%s%s",searchPath,prefix,appendage);
55 }
56
57 if ( (errorNumber=outputDistanceField(dout, outputFileName))!=ERROR_NONE ){
58 Cleanup(inputFileName, outputFileName, din);
59 return errorNumber;
60 }
61 }
62
usage()63 int usage(){
64 fprintf(stderr, "SYNOPSIS:\n");
65 fprintf(stderr, "\tdistanceField -i <input filename> [-o <output filename>][-m <metric>]\n\n");
66
67 fprintf(stderr, "The input file is expected to be an AFNI dataset.\n\n");
68
69 fprintf(stderr, "The \"metric\" is a text string (upper case) describing the algorithm used to\n");
70 fprintf(stderr, "estimate the depth. It may be one of the following.\n");
71 fprintf(stderr, "MARCHING_PARABOLAS - Marching parabolas (default)\n");
72 fprintf(stderr, "EROSION - Erosion algorithm.\n\n");
73
74 fprintf(stderr, "Optional arguments specifically for MARCHING_PARABOLAS:\n");
75 fprintf(stderr, "\ts: Square root the output\n");
76 fprintf(stderr, "\te: Treat edge of field of view as zero (default)\n");
77 fprintf(stderr, "\td: (Debug mode.) Generate test object set internally\n\n");
78
79 fprintf(stderr, "If the output filename is not specified, a default name is assigned.\n");
80 fprintf(stderr, "The default name reflects the input filename, metric and whether \n");
81 fprintf(stderr, "the program was run in debug mode.\n");
82
83 return 0;
84 }
85
getDistanceFieldDataSet(THD_3dim_dataset * din,THD_3dim_dataset ** dout,int metric,bool do_sqrt,bool edges_are_zero_for_nz,bool debugMode)86 ERROR_NUMBER getDistanceFieldDataSet(THD_3dim_dataset *din, THD_3dim_dataset **dout, int metric,
87 bool do_sqrt, bool edges_are_zero_for_nz, bool debugMode){
88
89 ERROR_NUMBER errorNumber;
90 float *outImg;
91
92 if (!(outImg = (float *)calloc(DSET_NVOX(din), sizeof(float)))){
93 return ERROR_MEMORY_ALLOCATION;
94 }
95
96 // Apply metric
97 switch (metric){
98 case MARCHING_PARABOLAS:
99 if ((errorNumber=afni_edt(din, outImg, do_sqrt, edges_are_zero_for_nz, debugMode))!=ERROR_NONE){
100 return errorNumber;
101 }
102 break;
103 case EROSION:
104 if ((errorNumber=erosion(din, outImg))!=ERROR_NONE){
105 return errorNumber;
106 }
107 break;
108 }
109
110 *dout = EDIT_empty_copy(din);
111 EDIT_dset_items( *dout ,
112 ADN_type, MRI_float,
113 ADN_none ) ;
114
115 if (debugMode){
116 THD_ivec3 nxyz={20, 40, 80};
117 THD_fvec3 xyzdel = {2.0, 1.0, 0.5};
118
119 EDIT_dset_items( *dout ,
120 ADN_nxyz, nxyz,
121 ADN_xyzdel, xyzdel,
122 ADN_none ) ;
123 }
124
125 EDIT_substitute_brick(*dout, 0, MRI_float, outImg);
126
127 return ERROR_NONE;
128 }
129
outputDistanceField(THD_3dim_dataset * dout,char * outputFileName)130 int outputDistanceField(THD_3dim_dataset *dout, char *outputFileName){
131
132 // Output Fourier distance image
133 EDIT_dset_items( dout ,
134 ADN_prefix, outputFileName,
135 ADN_none ) ;
136 DSET_write(dout);
137
138 return ERROR_NONE;
139 }
140
outputDistanceFieldDebug(float * outImg,THD_3dim_dataset * din,char * outputFileName)141 int outputDistanceFieldDebug(float *outImg, THD_3dim_dataset *din, char *outputFileName){
142
143 // Set output dimensions
144 THD_ivec3 nxyz={debugNx, debugNy, debugNz};
145 THD_fvec3 xyzdel = {debugScaleFactors[2], debugScaleFactors[1], debugScaleFactors[0]};
146
147 // Output Fourier distance image
148 THD_3dim_dataset *dout = EDIT_empty_copy(din);
149 EDIT_dset_items( dout ,
150 ADN_prefix, outputFileName,
151 ADN_type, MRI_float,
152 ADN_nxyz, nxyz,
153 ADN_xyzdel, xyzdel,
154 ADN_none ) ;
155 EDIT_substitute_brick(dout, 0, MRI_float, outImg);
156 DSET_write(dout);
157
158 return ERROR_NONE;
159 }
160
erosion(THD_3dim_dataset * din,float * outImg)161 int erosion(THD_3dim_dataset * din, float *outImg){
162
163 // Get dimensions in voxels
164 int nz = DSET_NZ(din);
165 int ny = DSET_NY(din);
166 int nx = DSET_NX(din);
167 int nvox = nx*ny*nz;
168 int i;
169 bool objectVoxelsLeft=TRUE;
170 BYTE * buffer;
171
172 if ((nvox < 1) || (nx < 2) || (ny < 2) || (nz < 1)) return ERROR_DIFFERENT_DIMENSIONS;
173
174 int brickType=DSET_BRICK_TYPE(din, 0);
175 if( brickType != MRI_byte ) return ERROR_DATATYPENOTHANDLED;
176 BYTE * img = DSET_ARRAY(din, 0);
177
178 // Add uneroded volume to output
179 for (i=0; i<nvox; ++i) outImg[i]+=(img[i]!=0);
180
181 // Allocate memory to buffer
182 if (!(buffer = (BYTE *)malloc(nvox*sizeof(BYTE)))) return ERROR_MEMORY_ALLOCATION;
183
184 // Erode volume, adding eroede volume to output until no object voxels left
185 do {
186 objectVoxelsLeft = FALSE;
187
188 // Copy inpout image to buffer
189 for (i=0; i<nvox; ++i) buffer[i] = img[i];
190
191 // Erode input
192 for (i=0; i<nvox; ++i){
193 img[i]=(buffer[i]!=0) && sixConnectedAllHi(buffer, i, nx, ny, nz);
194 if (img[i]) objectVoxelsLeft = TRUE;
195 }
196
197 // Add eroded volume to output
198 for (i=0; i<nvox; ++i) outImg[i]+=(img[i]!=0);
199 } while (objectVoxelsLeft);
200
201 // Cleanup
202 free(buffer);
203
204
205 return ERROR_NONE;
206 }
207
sixConnectedAllHi(BYTE * buffer,int index,int nx,int ny,int nz)208 bool sixConnectedAllHi(BYTE *buffer, int index, int nx, int ny, int nz){
209 int planeSize = nx*ny;
210 int z = (int)(index/planeSize);
211 int y = (int)((index-z*planeSize)/nx);
212 int x = index - z*planeSize - y*nx;
213
214 return buffer[getIndex((x>0)? x-1:x,y,z, nx, ny,nz)] && buffer[getIndex((x<nx-1)? x+1:x,y,z, nx, ny,nz)] &&
215 buffer[getIndex(x, (y>0)? y-1:y,z, nx, ny,nz)] && buffer[getIndex(x, (y<ny-1)? y+1:y,z, nx, ny,nz)] &&
216 buffer[getIndex(x,y,(z>0)? z-1:z, nx, ny,nz)] && buffer[getIndex(x,y,(z<nz-1)? z+1:z, nx, ny,nz)] ;
217 }
218
getIndex(int x,int y,int z,int nx,int ny,int nz)219 int getIndex(int x, int y, int z, int nx, int ny, int nz){
220 return z*nx*ny + y*nx + x;
221 }
222
223
afni_edt(THD_3dim_dataset * din,float * outImg,bool do_sqrt,bool edges_are_zero_for_nz,bool debugMode)224 int afni_edt(THD_3dim_dataset * din, float *outImg, bool do_sqrt, bool edges_are_zero_for_nz, bool debugMode){
225
226 // Get dimensions in voxels
227 int nz = DSET_NZ(din);
228 int ny = DSET_NY(din);
229 int nx = DSET_NX(din);
230 int nvox = nx*ny*nz;
231 int *inputImg;
232 int *indices;
233 BYTE * byteImg;
234 short * shortImg;
235 float *floatImg, ad3[3];
236 int *vol;
237 int i;
238
239 if ((nvox < 1) || (nx < 2) || (ny < 2) || (nz < 1)) return ERROR_DIFFERENT_DIMENSIONS;
240
241 // Alliocate memory to integer input buffer
242 if (!(inputImg=(int *)calloc(nvox,sizeof(int)))) return ERROR_MEMORY_ALLOCATION;
243
244 DSET_load(din);
245 int brickType=DSET_BRICK_TYPE(din, 0);
246 fprintf(stderr, "brickType=%d\n", brickType);
247 switch(brickType){
248 case MRI_byte:
249 byteImg = DSET_ARRAY(din, 0);
250 for (i=0; i<nvox; ++i) inputImg[i] = byteImg[i];
251 break;
252 case MRI_short:
253 shortImg = DSET_ARRAY(din, 0);
254 for (i=0; i<nvox; ++i) inputImg[i] = shortImg[i];
255 break;
256 case MRI_int:
257 free(inputImg);
258 inputImg = DSET_ARRAY(din, 0);
259 break;
260 case MRI_float:
261 floatImg = DSET_ARRAY(din, 0);
262 for (i=0; i<nvox; ++i) inputImg[i] = (int)(floatImg[i]+0.5);
263 break;
264 }
265
266 if (debugMode){
267 int x, y, z;
268 // DEBUG: Make test volume
269 // make a test image: a map of various ROIs
270 debugNz = nz = 80;
271 debugNy = ny = 40;
272 debugNx = nx = 20;
273 nvox = nx*ny*nz;
274 vol = (int *)calloc(nx*ny*nz, sizeof(int));
275 //free(outImg);
276 outImg = (float *)calloc(nvox,sizeof(float));
277 debugOutImage = outImg;
278 ad3[0] = 0.5;
279 ad3[1] = 1.0;
280 ad3[2] = 2.0;
281 memcpy(debugScaleFactors, ad3, 3*sizeof(float));
282
283 // LX, LY, LZ = np.shape(vol)
284
285 int planesize=nx*ny;
286
287 for (z=4; z<18; ++z){
288 int zOffset=z*planesize;
289 for (y=2; y<8; ++y){
290 int yOffset = zOffset + y*nx;
291 for (x=4; x<8; ++x){
292 vol[yOffset+x] = 1;
293 }
294 }
295 }
296
297 for (z=2; z<8; ++z){
298 int zOffset=z*planesize;
299 for (y=4; y<18; ++y){
300 int yOffset = zOffset + y*nx;
301 for (x=4; x<8; ++x){
302 vol[yOffset+x] = 4;
303 }
304 }
305 }
306
307 for (z=21; z<30; ++z){
308 int zOffset=z*planesize;
309 for (y=0; y<10; ++y){
310 int yOffset = zOffset + y*nx;
311 for (x=11; x<14; ++x){
312 vol[yOffset+x] = 7;
313 }
314 }
315 }
316
317 for (z=0; z<nz; ++z){
318 int zOffset=z*planesize;
319 for (y=0; y<ny; ++y){
320 int yOffset = zOffset + y*nx;
321 for (x=0; x<nx; ++x){
322 if (pow((15-x),2.0) + pow((25-y),2.0) + pow((10-z),2.0)< 31)
323 vol[yOffset+x] = 1;
324 }
325 }
326 }
327
328 for (z=17; z<19; ++z){
329 int zOffset=z*planesize;
330 for (y=0; y<ny; ++y){
331 int yOffset = zOffset + y*nx;
332 for (x=3; x<6; ++x){
333 vol[yOffset+x] = 1;
334 }
335 }
336 }
337
338 for (z=0; z<nz; ++z){
339 int zOffset=z*planesize;
340 for (y=19; y<21; ++y){
341 int yOffset = zOffset + y*nx;
342 for (x=3; x<6; ++x){
343 vol[yOffset+x] = 10;
344 }
345 }
346 }
347
348 for (z=60; z<70; ++z){
349 int zOffset=z*planesize;
350 for (y=28; y<36; ++y){
351 int yOffset = zOffset + y*nx;
352 for (x=14; x<19; ++x){
353 vol[yOffset+x] = 17;
354 }
355 }
356 }
357
358 for (z=0; z<nz; ++z){
359 int zOffset=z*planesize;
360 for (y=0; y<ny; ++y){
361 int yOffset = zOffset + y*nx;
362 for (x=0; x<nx; ++x){
363 if ((pow((65-x),2.0) + pow((10-y),2.0) + pow((10-z),2.0)< 75) &&
364 !(pow((60-x),2.0) + pow((7-y),2.0) + pow((10-z),2.0)< 31))
365 vol[yOffset+x] = 2;
366 }
367 }
368 }
369
370 for (z=40; z<56; ++z){
371 int zOffset=z*planesize;
372 for (y=25; y<33; ++y){
373 int yOffset = zOffset + y*nx;
374 for (x=4; x<8; ++x){
375 vol[yOffset+x] = 5;
376 }
377 }
378 }
379 }
380
381 #if PROCESS_ROIS_SEPARATELY
382 // Get unique nonzero index values
383 if ((errorNumber=getNonzeroIndices(nvox, inputImg, &numIndices, &indices))!=ERROR_NONE){
384 free(inputImg);
385 return errorNumber;
386 }
387
388 // Process each index
389 for (i=0; i<numIndices; ++i){
390 if ((errorNumber=processIndex(indices[i], inputImg, &addend, din))!=ERROR_NONE){
391 free(inputImg);
392 free(indices);
393 return errorNumber;
394 }
395 for (j=0; j<nvox; ++j) outImg[j]+=addend[j];
396 }
397
398 free(indices);
399 #else
400 if (debugMode){
401 inputImg = vol;
402 } else {
403 // Get real world voxel sizes
404 // float ad3[3]={fabs(DSET_DX(din)), fabs(DSET_DY(din)), fabs(DSET_DZ(din))};
405 ad3[0] = fabs(DSET_DX(din));
406 ad3[1] = fabs(DSET_DY(din));
407 ad3[2] = fabs(DSET_DZ(din));
408 }
409
410 img3d_Euclidean_DT(inputImg, nx, ny, nz,
411 do_sqrt, edges_are_zero_for_nz, ad3, outImg);
412 #endif
413
414 // Cleanup
415 free(inputImg);
416
417 return ERROR_NONE;
418 }
419
img3d_Euclidean_DT(int * im,int nx,int ny,int nz,bool do_sqrt,bool edges_are_zero_for_nz,float * ad3,float * odt)420 ERROR_NUMBER img3d_Euclidean_DT(int *im, int nx, int ny, int nz,
421 bool do_sqrt, bool edges_are_zero_for_nz, float *ad3, float *odt){
422
423 int nvox = nx*ny*nz; // number of voxels
424 int planeSize = nx*ny;
425 int i, z, y, x;
426
427 // initialize the "output" or answer array
428 // for (i=0; i<nvox; ++i) odt[i] = (im[i]>0)? BIG : 0;
429 for (i=0; i<nvox; ++i) odt[i] = BIG;
430
431 // first pass: start with all BIGs (along x-axes)
432 int *inRow = im;
433 float *outRow = odt;
434 for (z = 0; z <nz; ++z){
435 for (y = 0; y < ny; ++y){
436 // Calc with it, and save results
437 // [PT: Jan 5, 2020] fix which index goes here
438 run_EDTD_per_line( inRow, outRow, nx, ad3[0], edges_are_zero_for_nz) ;
439
440 // Increment row
441 inRow += nx;
442 outRow += nx;
443 }
444 }
445
446 if (!(inRow=(int *)malloc(ny*sizeof(int)))) return ERROR_MEMORY_ALLOCATION;
447 if (!(outRow=(float *)calloc(ny,sizeof(float)))) {
448 free(inRow);
449 return ERROR_MEMORY_ALLOCATION;
450 }
451 for (z = 0; z < nz; ++z){
452 int *inPlane = im + (z*planeSize);
453 float *outPlane = odt + (z*planeSize);
454 for (x = 0; x < nx; ++x){
455 // get a line...
456 for (y=0; y<ny; ++y){
457 int offset = (y*nx) + x;
458 inRow[y] = inPlane[offset ];
459 outRow[y] = outPlane[offset ];
460 }
461
462 // ... and then calc with it, and save results
463 run_EDTD_per_line( inRow, outRow, ny, ad3[1], edges_are_zero_for_nz) ;
464
465 // Record new output row
466 for (y=0; y<ny; ++y){
467 int offset = (y*nx) + x;
468 outPlane[offset] = outRow[y];
469 }
470 }
471 }
472 free(outRow);
473 free(inRow);
474
475
476 // 2nd pass: start from previous; any other dimensions would carry
477 // on from here
478 if (!(inRow=(int *)malloc(nz*sizeof(int)))) return ERROR_MEMORY_ALLOCATION;
479 if (!(outRow=(float *)calloc(nz, sizeof(float)))) {
480 free(inRow);
481 return ERROR_MEMORY_ALLOCATION;
482 }
483 for (y = 0; y < ny; ++y){
484 for (x = 0; x < nx; ++x){
485 int planeOffset = (y*nx) + x;
486 // get a line...
487 for (z=0; z<nz; ++z){
488 int offset = planeOffset + (z*planeSize);
489 inRow[z] = im[offset] ;
490 outRow[z] = odt[offset] ;
491 }
492
493 // ... and then calc with it, and save results
494 // [PT: Jan 5, 2020] fix which index goes here
495 run_EDTD_per_line( inRow, outRow, nz, ad3[2], edges_are_zero_for_nz) ;
496
497 // Record new output row
498 for (z=0; z<nz; ++z) {
499 int offset = planeOffset + (z*planeSize);
500 odt[offset] = outRow[z];
501 }
502 }
503 }
504 free(outRow);
505 free(inRow);
506
507 if (do_sqrt)
508 for (i=0; i<nvox; ++i) odt[i] = sqrt(odt[i]);
509
510 return ERROR_NONE;
511 }
512
run_EDTD_per_line(int * roi_line,float * dist2_line,int Na,float delta,bool edges_are_zero_for_nz)513 ERROR_NUMBER run_EDTD_per_line(int *roi_line, float *dist2_line, int Na,
514 float delta, bool edges_are_zero_for_nz) {
515 int idx = 0;
516 int m, n, i;
517 float *line_out;
518
519 size_t rowLengthInBytes = Na*sizeof(float);
520 if (!(line_out=(float *)malloc(rowLengthInBytes))) return ERROR_MEMORY_ALLOCATION;
521
522 int limit = Na-1;
523 while (idx < Na){
524 // get interval of line with current ROI value
525 int roi = roi_line[idx];
526 n = idx;
527 while (n < Na){
528 if (roi_line[n] != roi){
529 break;
530 }
531 n += 1;
532 }
533 n -= 1;
534 // n now has the index of last matching element
535
536 float *paddedLine=(float *)calloc(Na+2,sizeof(float));
537 // actual ROI is in range of indices [start, stop] in
538 // paddedLine. 'inc' will tell us length of distance array
539 // put into Euclidean_DT_delta(), which can include padding at
540 // either end.
541 int start = 0, stop = limit;
542 int inc=0;
543 if (idx != 0 || (edges_are_zero_for_nz && roi != 0)){
544 start = 1;
545 inc = 1;
546 }
547 // put actual values from dist**2 field...
548 for (m=idx; m<=n; ++m){
549 //paddedLine[inc++] = dist2_line[m];
550 paddedLine[inc++] = dist2_line[m];
551 }
552 // inc finishes 1 greater than the actual end: is length so far
553 stop = inc-1; // 'stop' is index of actual end of roi
554 // pad at end?
555 if (n < limit || (edges_are_zero_for_nz && roi != 0)){
556 inc+=1; // [PT] not 'stop', which is index of actual ROI
557 // (not changing)
558 }
559
560 // float *Df = Euclidean_DT_delta(paddedLine, stop-start+1, delta);
561 // [PT] and 'inc' should already have correct value from
562 // above; don't add 1 here
563 float *Df = Euclidean_DT_delta(paddedLine, inc, delta);
564
565 // memcpy(&(line_out[idx]), &(Df[start]), (stop-start+1)*sizeof(float));
566 memcpy(&(line_out[idx]), &(Df[start]), (stop-start+1)*sizeof(float));
567
568 free(paddedLine);
569 free(Df);
570
571 idx = n+1;
572 }
573
574 // DEBUG
575 for (i=0; i<Na; ++i) if (line_out[i]==0){
576 fprintf(stderr, "Zero valued distance\n");
577 }
578
579 memcpy(dist2_line, line_out, rowLengthInBytes);
580 free(line_out);
581
582 return ERROR_NONE;
583 }
584
Euclidean_DT_delta(float * f,int n,float delta)585 float * Euclidean_DT_delta(float *f, int n, float delta){
586 // Classical Euclidean Distance Transform (EDT) of Felzenszwalb and
587 // Huttenlocher (2012), but for given voxel lengths.
588 //
589 // Assumes that: len(f) < sqrt(10**10).
590 //
591 // In this version, all voxels should have equal length, and units
592 // are "edge length" or "number of voxels."
593 //
594 // Parameters
595 // ----------
596 //
597 // f : 1D array or list, distance**2 values (or, to start, binarized
598 // between 0 and BIG).
599 //
600 // delta : voxel edge length size along a particular direction
601
602
603 int *v, k = 0;
604 float *z, *Df;
605 int q, r;
606
607 if (!(v=(int *)calloc(n, sizeof(int))) ||
608 !(z=(float *)calloc(n+1, sizeof(float)))){
609 if (v) free(v);
610 return NULL;
611 }
612 z[0] = -BIG;
613 z[1] = BIG;
614
615 for (q = 1; q<n; ++q) {
616 float s = f[q] + pow(q*delta, 2.0) - (f[v[k]] + pow(v[k]*delta,2.0));
617 s/= 2. * delta * (q - v[k]);
618 while (s <= z[k]){
619 k-= 1;
620 s = f[q] + pow(q*delta,2.0) - (f[v[k]] + pow(v[k]*delta, 2.0));
621 s/= 2. * delta * (q - v[k]);
622 }
623 k+= 1;
624 v[k] = q;
625 z[k] = s;
626 z[k+1] = BIG;
627 }
628
629 k = 0;
630 if (!(Df=(float *)calloc(n, sizeof(float)))){
631 free(v);
632 free(z);
633 return NULL;
634 }
635 for (q=0; q<n; ++q){
636 while (z[k+1] < q * delta) k+= 1;
637 Df[q] = pow(delta*(q - v[k]), 2.0) + f[v[k]];
638 }
639
640 free(v);
641 free(z);
642
643 return Df;
644 }
645
processIndex(int index,int * inputImg,float ** outImg,THD_3dim_dataset * din)646 ERROR_NUMBER processIndex(int index, int *inputImg, float **outImg, THD_3dim_dataset *din){
647 // Get dimensions in voxels
648 int nz = DSET_NZ(din);
649 int ny = DSET_NY(din);
650 int nx = DSET_NX(din);
651 int nvox = nx*ny*nz;
652 int r, v, x, y, z;
653
654 int nVol = 1;
655 int nvox3D = nx * ny * MAX(nz, 1);
656 size_t si;
657
658 nVol = nvox / nvox3D;
659 if ((nvox3D * nVol) != nvox) return ERROR_DIFFERENT_DIMENSIONS;
660
661 // Get real world voxel sizes
662 float xDim = fabs(DSET_DX(din));
663 float yDim = fabs(DSET_DY(din));
664 float zDim = fabs(DSET_DZ(din));
665
666 float yDimSqrd = yDim*yDim;
667 float zDimSqrd = zDim*zDim;
668
669 if (!(*outImg=(float *)calloc(nvox, sizeof(float)))) return ERROR_MEMORY_ALLOCATION;
670
671 for (si = 0; si < nvox; si++ ) {
672 if (inputImg[si] == index)
673 (*outImg)[si] = BIG;
674 }
675 size_t nRow = ny*nz;
676
677 //EDT in left-right direction
678 for (r = 0; r < nRow; r++ ) {
679 flt * imgRow = (*outImg) + (r * nx);
680 edt_local(xDim, imgRow, nx);
681 }
682
683 //EDT in anterior-posterior direction
684 nRow = nx * nz; //transpose XYZ to YXZ and blur Y columns with XZ Rows
685 for (v = 0; v < nVol; v++ ) { //transpose each volume separately
686 flt * img3D = (flt *)calloc(nvox3D*sizeof(flt), 64); //alloc for each volume to allow openmp
687
688 //transpose data
689 size_t vo = v * nvox3D; //volume offset
690 for (z = 0; z < nz; z++ ) {
691 int zo = z * nx * ny;
692 for (y = 0; y < ny; y++ ) {
693 int xo = 0;
694 for (x = 0; x < nx; x++ ) {
695 img3D[zo+xo+y] = (*outImg)[vo]/yDimSqrd;
696 vo += 1;
697 xo += ny;
698 }
699 }
700 }
701 //perform EDT for all rows
702 for (r = 0; r < nRow; r++ ) {
703 flt * imgRow = img3D + (r * ny);
704 edt_local(yDim, imgRow, ny);
705 }
706 //transpose data back
707 vo = v * nvox3D; //volume offset
708 for (z = 0; z < nz; z++ ) {
709 int zo = z * nx * ny;
710 for (y = 0; y < ny; y++ ) {
711 int xo = 0;
712 for (x = 0; x < nx; x++ ) {
713 (*outImg)[vo] = img3D[zo+xo+y];
714 vo += 1;
715 xo += ny;
716 }
717 }
718 }
719 free (img3D);
720 } //for each volume
721
722 //EDT in head-foot direction
723 nRow = nx * ny; //transpose XYZ to ZXY and blur Z columns with XY Rows
724 for (v = 0; v < nVol; v++ ) { //transpose each volume separately
725 flt * img3D = (flt *)calloc(nvox3D*sizeof(flt), 64); //alloc for each volume to allow openmp
726 //transpose data
727 size_t vo = v * nvox3D; //volume offset
728 for (z = 0; z < nz; z++ ) {
729 for (y = 0; y < ny; y++ ) {
730 int yo = y * nz * nx;
731 int xo = 0;
732 for (x = 0; x < nx; x++ ) {
733 img3D[z+xo+yo] = (*outImg)[vo]/zDimSqrd;
734 vo += 1;
735 xo += nz;
736 }
737 }
738 }
739 //perform EDT for all "rows"
740 for (r = 0; r < nRow; r++ ) {
741 flt * imgRow = img3D + (r * nz);
742 edt_local(zDim, imgRow, nz);
743 }
744 //transpose data back
745 vo = v * nvox3D; //volume offset
746 for (z = 0; z < nz; z++ ) {
747 for (y = 0; y < ny; y++ ) {
748 int yo = y * nz * nx;
749 int xo = 0;
750 for (x = 0; x < nx; x++ ) {
751 (*outImg)[vo] = img3D[z+xo+yo];
752 vo += 1;
753 xo += nz;
754 } //x
755 } //y
756 } //z
757 free (img3D);
758 } //for each volume
759
760 return ERROR_NONE;
761 }
762
getNonzeroIndices(int nvox,int * inputImg,int * numIndices,int ** indices)763 ERROR_NUMBER getNonzeroIndices(int nvox, int *inputImg, int *numIndices, int **indices){
764 int *buffer;
765 int i, j, voxelValue;
766 bool old;
767
768 // Initialize
769 *numIndices = 0;
770 if (!(buffer=(int *)calloc(nvox,sizeof(int)))) return ERROR_MEMORY_ALLOCATION;
771
772 // Count unique indices and fill buffer with set of indices
773 for (i=0; i<nvox; ++i) if ((voxelValue=inputImg[i])>0){
774 old = false;
775 // for (j=0; j<*numIndices; ++j) if ((*indices)[j]==voxelValue) old = true; // Core dump
776 for (j=0; j<*numIndices; ++j) if (buffer[j]==voxelValue) old = true;
777 if (!old){
778 buffer[(*numIndices)++]=voxelValue;
779 }
780 }
781
782 // Trim output buffer to number of indices
783 if (!(*indices=(int *)malloc(*numIndices*sizeof(int)))){
784 free(buffer);
785 return ERROR_MEMORY_ALLOCATION;
786 }
787 for (i=0; i<*numIndices; ++i) (*indices)[i] = buffer[i];
788 free(buffer);
789
790 return ERROR_NONE;
791 }
792
vx(flt * f,int p,int q)793 flt vx(flt * f, int p, int q) {
794 if ((f[p] == BIG) || (f[q] == BIG))
795 return BIG;
796 else
797 return ((f[q] + q*q) - (f[p] + p*p)) / (2.0*q - 2.0*p);
798 }
799
edt_local(float scale,flt * f,int n)800 void edt_local(float scale, flt * f, int n) {
801 float scaleSqrd = scale*scale;
802
803 int q, p, k;
804 flt s, dx;
805 flt * d = (flt *)calloc((n)*sizeof(flt), 64);
806 flt * z = (flt *)calloc((n)*sizeof(flt), 64);
807 int * v = (int *)calloc((n)*sizeof(int), 64);
808
809 // # Find the lower envelope of a sequence of parabolas.
810 // # f...source data (returns the Y of the parabola vertex at X)
811 // # d...destination data (final distance values are written here)
812 // # z...temporary used to store X coords of parabola intersections
813 // # v...temporary used to store X coords of parabola vertices
814 // # i...resulting X coords of parabola vertices
815 // # n...number of pixels in "f" to process
816 // # Always add the first pixel to the enveloping set since it is
817 // # obviously lower than all parabolas processed so far.
818
819 k = 0;
820 v[0] = 0;
821 z[0] = -BIG;
822 z[1] = BIG;
823
824 for (q = 1; q < n; q++ ) {
825 // If the new parabola is lower than the right-most parabola in
826 // # the envelope, remove it from the envelope. To make this
827 // # determination, find the X coordinate of the intersection (s)
828 // # between the parabolas with vertices at (q,f[q]) and (p,f[p]).
829 p = v[k]; // DO NOT CHANGE
830 s = vx(f, p,q);
831 while (s <= z[k]) {
832 k = k - 1;
833 p = v[k]; // DO NOT CHANGE
834 s = vx(f, p,q); // DO NOT CHANGE
835 }
836 //# Add the new parabola to the envelope.
837 k = k + 1;
838 v[k] = q; // DO NOT CHANGE
839 z[k] = s;
840 z[k + 1] = BIG;
841 }
842 // # Go back through the parabolas in the envelope and evaluate them
843 // # in order to populate the distance values at each X coordinate.
844 k = 0;
845 for (q = 0; q < n; q++ ) {
846 while (z[k + 1] < q)
847 k = k + 1;
848 dx = (q - v[k])*scale;
849 // d[q] = dx * dx + f[v[k]];
850 d[q] = dx * dx + f[v[k]]*scaleSqrd;
851 }
852 for (q = 0; q < n; q++ )
853 f[q] = d[q];
854 free (d);
855 free (z);
856 free (v);
857 }
858
open_input_dset(THD_3dim_dataset ** din,char * fname)859 int open_input_dset(THD_3dim_dataset ** din, char * fname)
860 {
861 *din = THD_open_dataset(fname);
862 if( ! *din ) {
863 fprintf(stderr,"** failed to read input dataset '%s'\n", fname);
864 return ERROR_READING_FILE;
865 }
866
867 return ERROR_NONE;
868 }
869
Cleanup(char * inputFileName,char * outputFileName,THD_3dim_dataset * din)870 int Cleanup(char *inputFileName, char *outputFileName, THD_3dim_dataset *din){
871
872 if (inputFileName) free(inputFileName);
873 if (outputFileName) free(outputFileName);
874
875 return 0;
876 }
877
sqr(float x)878 float sqr(float x){
879 return x*x;
880 }
881
882