/***************************************************************************** ****************************************************************************/ #include #include #include #include #include #include #include "mrilib.h" #include "distanceField.h" #include #define PROCESS_ROIS_SEPARATELY 0 // Applies to marching parabolas #define BIG 10000000000.0 typedef float flt; typedef enum METRIC_TYPE { MARCHING_PARABOLAS, EROSION } METRIC_TYPE ; int Cleanup(char *inputFileName, char *outputFileName, THD_3dim_dataset *din); int afni_edt(THD_3dim_dataset * din, float *outImg, bool do_sqrt, bool edges_are_zero_for_nz, bool debugMode); int erosion(THD_3dim_dataset * din, float *outImg); int open_input_dset(THD_3dim_dataset ** din, char * fname); int outputDistanceField(THD_3dim_dataset *dout, char *outputFileName); int outputDistanceFieldDebug(float *outImg, THD_3dim_dataset *din, char *outputFileName); int doesFileExist(char * searchPath, char * prefix, char *appendage , char * outputFileName); void edt1_local(THD_3dim_dataset * din, flt * df, int n); void edt_local(float scale, flt * f, int n); float sqr(float x); static flt vx(flt * f, int p, int q); bool sixConnectedAllHi(BYTE *buffer, int index, int nx, int ny, int nz); int getIndex(int x, int y, int z, int nx, int ny, int nz); int transposeYZ(float *volume, int nx, int ny, int nz); int testTransposeFunction(THD_3dim_dataset * din); int outputTransposedDataSet(float *buffer, THD_3dim_dataset *din, int nx, int nz, int ny); int shortToByte(THD_3dim_dataset ** din); ERROR_NUMBER getNonzeroIndices(int nvox, int *inputImg, int *numIndices, int **indices); ERROR_NUMBER processIndex(int index, int *inputImg, float **outImg, THD_3dim_dataset *din); int usage(); ERROR_NUMBER img3d_Euclidean_DT(int *im, int nx, int ny, int nz, bool do_sqrt, bool edges_are_zero_for_nz, float *ad3, float *odt); ERROR_NUMBER run_EDTD_per_line(int *roi_line, float *dist2_line, int Na, float delta, bool edges_are_zero_for_nz); float * Euclidean_DT_delta(float *f, int n, float delta); ERROR_NUMBER getDistanceFieldDataSet(THD_3dim_dataset *din, THD_3dim_dataset **dout, int metric, bool do_sqrt, bool edges_are_zero_for_nz, bool debugMode); // Debugging variables int debugNx, debugNy, debugNz; float debugScaleFactors[3], *debugOutImage; float sqr(float x){ return x*x; } int main( int argc, char *argv[] ) { char *inputFileName=NULL, *outputFileName=NULL; int i, metric=MARCHING_PARABOLAS; THD_3dim_dataset *din = NULL, *dout = NULL; ERROR_NUMBER errorNumber; float *outImg; bool do_sqrt=TRUE, edges_are_zero_for_nz=TRUE, debugMode = FALSE; for (i=0; i [-o ][-m ]\n" "\n" "The input file is expected to be a volumetric dataset with integer-valued\n" "voxels.\n" "\n" "The 'metric' is a text string (upper case) describing the algorithm used to\n" "estimate the depth. It may be one of the following.\n" "MARCHING_PARABOLAS - Marching parabolas (default)\n" "EROSION - Erosion algorithm.\n" "\n" "Optional arguments specifically for MARCHING_PARABOLAS:\n" " s: Square root the output\n" " e: Treat edge of field of view as zero (default)\n" " d: (Debug mode.) Generate test object set internally\n" "\n" "If the output filename is not specified, a default name is assigned.\n" "The default name reflects the input filename, metric and whether \n" "the program was run in debug mode.\n" ); return 0; } ERROR_NUMBER getDistanceFieldDataSet(THD_3dim_dataset *din, THD_3dim_dataset **dout, int metric, bool do_sqrt, bool edges_are_zero_for_nz, bool debugMode){ ERROR_NUMBER errorNumber; float *outImg; if (!(outImg = (float *)calloc(DSET_NVOX(din), sizeof(float)))){ return ERROR_MEMORY_ALLOCATION; } // Apply metric switch (metric){ case MARCHING_PARABOLAS: if ((errorNumber=afni_edt(din, outImg, do_sqrt, edges_are_zero_for_nz, debugMode))!=ERROR_NONE){ return errorNumber; } break; case EROSION: if ((errorNumber=erosion(din, outImg))!=ERROR_NONE){ return errorNumber; } break; } *dout = EDIT_empty_copy(din); EDIT_dset_items( *dout , ADN_type, MRI_float, ADN_none ) ; if (debugMode){ THD_ivec3 nxyz={20, 40, 80}; THD_fvec3 xyzdel = {2.0, 1.0, 0.5}; EDIT_dset_items( *dout , ADN_nxyz, nxyz, ADN_xyzdel, xyzdel, ADN_none ) ; } EDIT_substitute_brick(*dout, 0, MRI_float, outImg); return ERROR_NONE; } int outputDistanceField(THD_3dim_dataset *dout, char *outputFileName){ // Output Fourier distance image EDIT_dset_items( dout , ADN_prefix, outputFileName, ADN_none ) ; DSET_write(dout); return ERROR_NONE; } int outputDistanceFieldDebug(float *outImg, THD_3dim_dataset *din, char *outputFileName){ // Set output dimensions THD_ivec3 nxyz={debugNx, debugNy, debugNz}; THD_fvec3 xyzdel = {debugScaleFactors[2], debugScaleFactors[1], debugScaleFactors[0]}; // Output Fourier distance image THD_3dim_dataset *dout = EDIT_empty_copy(din); EDIT_dset_items( dout , ADN_prefix, outputFileName, ADN_type, MRI_float, ADN_nxyz, nxyz, ADN_xyzdel, xyzdel, ADN_none ) ; EDIT_substitute_brick(dout, 0, MRI_float, outImg); DSET_write(dout); return ERROR_NONE; } int erosion(THD_3dim_dataset * din, float *outImg){ // Get dimensions in voxels int nz = DSET_NZ(din); int ny = DSET_NY(din); int nx = DSET_NX(din); int nvox = nx*ny*nz; int i; bool objectVoxelsLeft=TRUE; BYTE * buffer; if ((nvox < 1) || (nx < 2) || (ny < 2) || (nz < 1)) return ERROR_DIFFERENT_DIMENSIONS; int brickType=DSET_BRICK_TYPE(din, 0); if( brickType != MRI_byte ) return ERROR_DATATYPENOTHANDLED; BYTE * img = DSET_ARRAY(din, 0); // Add uneroded volume to output for (i=0; i0)? x-1:x,y,z, nx, ny,nz)] && \ buffer[getIndex((x0)? y-1:y,z, nx, ny,nz)] && \ buffer[getIndex(x, (y0)? z-1:z, nx, ny,nz)] && \ buffer[getIndex(x,y,(z0)? BIG : 0; for ( i=0; i0){ old = false; // for (j=0; j<*numIndices; ++j) // if ((*indices)[j]==voxelValue) // old = true; // Core dump for (j=0; j<*numIndices; ++j) if (buffer[j]==voxelValue) old = true; if (!old){ buffer[(*numIndices)++]=voxelValue; } } // Trim output buffer to number of indices if (!(*indices=(int *)malloc(*numIndices*sizeof(int)))){ free(buffer); return ERROR_MEMORY_ALLOCATION; } for (i=0; i<*numIndices; ++i) (*indices)[i] = buffer[i]; free(buffer); return ERROR_NONE; } int transposeYZ(float *volume, int nx, int ny, int nz){ float * buffer; int nvox=nx*ny*nz; if (!(buffer = (float *)malloc(nvox*sizeof(float)))!=ERROR_NONE) return ERROR_MEMORY_ALLOCATION; int z0 = 0; for (int z=0; z= 0; q-- ) { v = sqr((q-prevX)/xDim)+(prevY/yDim); if (df[q] < v) { prevX = q; // DO NOT CHANGE prevY = df[q]; // DO NOT CHANGE } else df[q] = v; } } flt vx(flt * f, int p, int q) { if ((f[p] == BIG) || (f[q] == BIG)) return BIG; else return ((f[q] + q*q) - (f[p] + p*p)) / (2.0*q - 2.0*p); } void edt_local(float scale, flt * f, int n) { float scaleSqrd = scale*scale; int q, p, k; flt s, dx; flt * d = (flt *)calloc((n)*sizeof(flt), 64); flt * z = (flt *)calloc((n)*sizeof(flt), 64); int * v = (int *)calloc((n)*sizeof(int), 64); // # Find the lower envelope of a sequence of parabolas. // # f...source data (returns the Y of the parabola vertex at X) // # d...destination data (final distance values are written here) // # z...temporary used to store X coords of parabola intersections // # v...temporary used to store X coords of parabola vertices // # i...resulting X coords of parabola vertices // # n...number of pixels in "f" to process // # Always add the first pixel to the enveloping set since it is // # obviously lower than all parabolas processed so far. k = 0; v[0] = 0; z[0] = -BIG; z[1] = BIG; for (q = 1; q < n; q++ ) { // If the new parabola is lower than the right-most parabola in // # the envelope, remove it from the envelope. To make this // # determination, find the X coordinate of the intersection (s) // # between the parabolas with vertices at (q,f[q]) and (p,f[p]). p = v[k]; // DO NOT CHANGE s = vx(f, p,q); while (s <= z[k]) { k = k - 1; p = v[k]; // DO NOT CHANGE s = vx(f, p,q); // DO NOT CHANGE } //# Add the new parabola to the envelope. k = k + 1; v[k] = q; // DO NOT CHANGE z[k] = s; z[k + 1] = BIG; } // # Go back through the parabolas in the envelope and evaluate them // # in order to populate the distance values at each X coordinate. k = 0; for (q = 0; q < n; q++ ) { while (z[k + 1] < q) k = k + 1; dx = (q - v[k])*scale; // d[q] = dx * dx + f[v[k]]; d[q] = dx * dx + f[v[k]]*scaleSqrd; } for (q = 0; q < n; q++ ) f[q] = d[q]; free (d); free (z); free (v); } int doesFileExist(char * searchPath, char * prefix,char *appendage , char * outputFileName){ int outputFileExists=0; struct dirent *dir; DIR *d; d = opendir(searchPath); sprintf(outputFileName,"%s%s",prefix,appendage); if (d) { while ((dir = readdir(d)) != NULL) { if (strstr(dir->d_name,outputFileName)){ outputFileExists=1; break; } } closedir(d); } return outputFileExists; } int open_input_dset(THD_3dim_dataset ** din, char * fname) { *din = THD_open_dataset(fname); if( ! *din ) { fprintf(stderr,"** failed to read input dataset '%s'\n", fname); return ERROR_READING_FILE; } return ERROR_NONE; } int shortToByte(THD_3dim_dataset ** din){ BYTE * outImg; int i; // Get dimensions in voxels int nvox = DSET_NVOX(*din); if (!(outImg = (BYTE *)calloc(nvox, sizeof(BYTE)))){ return ERROR_MEMORY_ALLOCATION; } // Get input data int brickType=DSET_BRICK_TYPE(*din, 0); if( brickType != MRI_short ) return ERROR_DATATYPENOTHANDLED; DSET_load(*din); short * img = (short *)DSET_ARRAY(*din, 0); // Map short input into BYTE output for (i=0; i