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