/***************************************************************************** Major portions of this software are copyrighted by the Medical College of Wisconsin, 1994-2000, and are released under the Gnu General Public License, Version 2. See the file README.Copyright for details. ******************************************************************************/ #include "afni.h" #include "maxima.h" char grMessage[ R_MESSAGE_L ]; /*---------------------------------------------------------------------- ** ** Find local maxima, storing the extrema as a mask. ** **---------------------------------------------------------------------- */ int process_data( maxima_s * M ) { ENTRY("process_data"); ( void )find_local_maxima( M ); if ( ! create_point_list( M ) ) RETURN(0); gr_orig_data = M->sdata; /* global needed for sorting */ if ( M->negatives ) qsort( M->P.plist, M->P.used, sizeof( int ), point_comp_neg ); else qsort( M->P.plist, M->P.used, sizeof( int ), point_comp_pos ); if ( M->debug > 1 ) show_point_list_s( "+d point list sorted: ", &M->P, M->debug ); if ( ( M->min_dist > 1.0 ) && ! apply_min_dist( M ) ) RETURN(0); if ( M->debug > 1 ) show_point_list_s( "+d point list cleaned: ", &M->P, M->debug ); if ( M->outfile ) apply_fill_radius( M ); RETURN(1); } /*---------------------------------------------------------------------- ** Display the contents of the point_list_s struct. **---------------------------------------------------------------------- */ void show_point_list_s( char * mesg, point_list_s * p, int debug ) { int c; ENTRY("show_point_list_s"); if ( mesg ) fputs( mesg, stderr ); fprintf(stderr, "point_list_s @ %p, used = %d, M = %d\n", (void *)p, p->used, p->M); if ( debug <= 0 ) EXRETURN; /* we're done */ fprintf(stderr," plist starting @ %p:", (void *)p->plist ); for ( c = 0; c < p->used; c++ ) fprintf(stderr," %d", p->plist[c] ); fprintf(stderr,"\n"); EXRETURN; } /*---------------------------------------------------------------------- ** ** Display the contents of the maxima structure. ** **---------------------------------------------------------------------- */ void show_maxima_s( char * mesg, maxima_s * M ) { ENTRY("show_maxima_s"); if ( mesg ) fputs( mesg, stderr ); fprintf( stderr, "------------------------------\n" "dset * : %p\n" "sdata * : %p\n" "result * : %p\n" "nx : %d\n" "ny : %d\n" "nz : %d\n" "nxy : %d\n" "nvox : %d\n" "P.plist : %p\n" "P.used : %d\n" "P.M : %d\n" "extrema count : %d\n" "data_type : %d\n" "adn_type : %d\n" "func_type : %d\n" "outfile : %s\n" "sval_style : %d\n" "cutoff : %f\n" "min_dist : %f\n" "out_rad : %f\n" "negatives : %d\n" "ngbr_style : %d\n" "overwrite : %d\n" "quiet : %d\n" "coords_only : %d\n" "true_max : %d\n" "dicom_coords : %d\n" "debug : %d\n" "------------------------------\n", (void *)M->dset, (void *)M->sdata, (void *)M->result, M->nx, M->ny, M->nz, M->nxy, M->nvox, (void *)M->P.plist, M->P.used, M->P.M, M->extrema_count, M->data_type, M->adn_type, M->func_type, M->outfile, M->sval_style, M->cutoff, M->min_dist, M->out_rad, M->negatives, M->ngbr_style, M->overwrite, M->quiet, M->coords_only, M->true_max, M->dicom_coords, M->debug ); EXRETURN; } /*---------------------------------------------------------------------- ** ** Remove mask points around lower intensity extrema. ** **---------------------------------------------------------------------- */ static int apply_min_dist( maxima_s * M ) { int * iptr, count; point_list_s newP = { NULL, 0, 0 }; for ( count = 0, iptr = M->P.plist; count < M->P.used; count++, iptr++ ) clear_around_point( *iptr, M, &newP ); free( M->P.plist ); /* replace old point list with new */ M->P = newP; return 1; } /*---------------------------------------------------------------------- ** ** Simply clear to a radius of min_dist around the given point. ** ** Clear all memory in the given radius and reset the point. ** **---------------------------------------------------------------------- */ static int clear_around_point( int p, maxima_s * M, point_list_s * newP ) { int X, Y, Z; int xmin, xmax, ymin, ymax, zmin, zmax; int yc, zc, xrad, yrad, yrad2; int xbase, ybase, zbase; short * optr; float radius = M->min_dist; static point_list_s P = { NULL, 0, 0 }; /* for allocation speed */ P.used = 0; X = p % M->nx; Y = (p % M->nxy) / M->nx; Z = p / M->nxy; if ( ! *(M->result + ( Z*M->ny + Y ) * M->nx + X ) ) return 1; zmin = ( Z < radius ) ? Z : radius; zmax = ( Z + radius >= M->nz ) ? ( M->nz-Z-1 ) : radius; if ( M->debug > 1 ) fprintf(stderr,"+d index %d, val %f\n", p, M->sdata[p]*gr_fac); for ( zc = -zmin; zc <= zmax; zc++ ) { zbase = ( Z + zc ) * M->nx * M->ny; yrad2 = radius * radius - zc * zc; yrad = (int)sqrt( yrad2 ); ymin = ( Y < yrad ) ? Y : yrad; ymax = ( Y + yrad >= M->ny ) ? ( M->ny - Y - 1 ) : yrad; for ( yc = -ymin; yc <= ymax; yc++ ) { ybase = ( Y + yc ) * M->nx; xrad = (int)sqrt( yrad2 - yc * yc ); xmin = ( X < xrad ) ? X : xrad; xmax = ( X + xrad >= M->nx ) ? ( M->nx - X - 1 ) : xrad; optr = M->result + ybase + zbase; for ( xbase = X-xmin; xbase <= X+xmax; xbase++ ) if ( *( optr + xbase ) ) { *(optr + xbase) = 0; /* maybe a switch later */ if ( M->ngbr_style == MAX_WEIGHTED_AVE_STYLE ) { if ( ! add_point_to_list( &P, xbase+ybase+zbase ) ) return 0; if ( M->debug > 2 ) fprintf(stderr," coords %d %d %d [%d], value %f\n", xbase, Y+yc, Z+zc, xbase+ybase+zbase, M->sdata[xbase+ybase+zbase]*gr_fac); } } } } switch( M->ngbr_style ) { case MAX_SORT_N_REMOVE_STYLE : if ( ! add_point_to_list( newP, p ) ) return 0; break; case MAX_WEIGHTED_AVE_STYLE : if ( ( p = weighted_index( &P, M ) ) < 0 ) return 0; if ( ! add_point_to_list( newP, p ) ) return 0; break; default : sprintf( grMessage, "Error: cap_00\nInvalid ngbr_style %d.", M->ngbr_style ); rERROR( grMessage ); return 0; break; } return 1; } /*---------------------------------------------------------------------- ** ** Given a list of points and the data in M, calculate the index ** of the weighted average of those points. Values are from M->sdata. ** **---------------------------------------------------------------------- */ static int weighted_index( point_list_s * P, maxima_s * M ) { double total_x = 0.0, total_y = 0.0, total_z = 0.0; /* weight*position */ double weight = 0.0; double value; int x, y, z; int count, index; int * iptr; if ( ( P->plist == NULL ) || ( P->used <= 0 ) ) { rERROR( "Error wi_00\nEmpty point list." ); return( -1 ); } if ( P->used == 1 ) /* no weighting necessary */ return( P->plist[0] ); for ( count = 0, iptr = P->plist; count < P->used; count++, iptr++ ) { index = *iptr; x = index % M->nx; y = ( index % M->nxy ) / M->nx; z = index / M->nxy; value = M->sdata[ index ]; weight += value; total_x += value * x; total_y += value * y; total_z += value * z; } if ( M->debug > 1 ) fprintf(stderr, "-d nvals, weight, ave = %d, %f, %f\n", P->used, weight*gr_fac, weight*gr_fac/P->used); if ( weight <= 0.0 ) { sprintf( grMessage, "Error: wi_10\nunexpected weight of %f", weight ); rERROR( grMessage ); } x = ( int )( total_x / weight + 0.4 ); /* ~rounded average */ y = ( int )( total_y / weight + 0.4 ); z = ( int )( total_z / weight + 0.4 ); index = ( z * M->ny + y ) * M->nx + x; if ( M->debug > 1 ) fprintf(stderr, "-d weighted i,j,k, ind, val = %d, %d, %d, %d, %f\n", x, y, z, index, M->sdata[index]*gr_fac); return index; } /*---------------------------------------------------------------------- ** ** Create a point list of all set extremas. ** ** Walk through the result data, and for any set point, insert into list. ** **---------------------------------------------------------------------- */ static int create_point_list( maxima_s * M ) { short * mptr; int count; point_list_s * P = &M->P; ENTRY("create_pint_list"); mptr = M->result; for ( count = 0; count < M->nvox; count++ ) if ( *mptr++ ) if ( ! add_point_to_list( P, count ) ) RETURN(0); if ( M->debug > 0 ) show_point_list_s( "+d point list created: ", &M->P, M->debug ); RETURN(1); } /*---------------------------------------------------------------------- ** ** Add a point to the list. ** **---------------------------------------------------------------------- */ static int add_point_to_list( point_list_s * P, int offset ) { ENTRY("add_point_to_list"); if ( ! P->plist ) { P->M = 100; P->used = 0; if ( ( P->plist = (int *)malloc( P->M * sizeof(int) ) ) == NULL ) { rERROR( "Error: aptl_10\n" "Failed to allocate memory for initial point list.\n" ); RETURN(0); } } else if ( P->used == P->M ) { P->M = P->M + 100; if ( ( P->plist = (int *)realloc( P->plist, P->M*sizeof(int))) == NULL ) { sprintf( grMessage, "Error: aptl_20\n" "Failed to reallocate %d ints for point list", P->M ); RETURN(0); } } P->plist[P->used] = offset; P->used++; RETURN(1); } /*---------------------------------------------------------------------- ** ** Fill a sphere around any set point. ** **---------------------------------------------------------------------- */ static int apply_fill_radius( maxima_s * M ) { int count, outval; int x, y, z; int * iptr; ENTRY("apply_fill_radius"); for ( count = 0, iptr = M->P.plist; count < M->P.used; count++, iptr++ ) { outval = (M->sval_style == 0) ? MAX_MASK_FILL_VAL : (M->sval_style == 1) ? (count+1) : (M->P.used - count); if ( M->out_rad < 1.0 ) { M->result[ *iptr ] = outval; continue; } x = *iptr % M->nx; y = ( *iptr % M->nxy ) / M->nx; z = *iptr / M->nxy; radial_fill( x, y, z, M, outval ); } RETURN(1); } /*---------------------------------------------------------------------- ** ** Fill a radius around the current point. ** **---------------------------------------------------------------------- */ static int radial_fill( int X, int Y, int Z, maxima_s * M, int val ) { int xmin, xmax, ymin, ymax, zmin, zmax; int yc, zc, xrad, yrad, yrad2; int xbase, ybase, zbase; short * sptr, * optr; float radius = M->out_rad; ENTRY("radial_fill"); zmin = ( Z < radius ) ? Z : radius; zmax = ( Z + radius >= M->nz ) ? ( M->nz-Z-1 ) : radius; for ( zc = -zmin; zc <= zmax; zc++ ) { zbase = ( Z + zc ) * M->nx * M->ny; yrad2 = radius * radius - zc * zc; yrad = (int)sqrt( yrad2 ); ymin = ( Y < yrad ) ? Y : yrad; ymax = ( Y + yrad >= M->ny ) ? ( M->ny - Y - 1 ) : yrad; for ( yc = -ymin; yc <= ymax; yc++ ) { ybase = ( Y + yc ) * M->nx; xrad = (int)sqrt( yrad2 - yc * yc ); xmin = ( X < xrad ) ? X : xrad; xmax = ( X + xrad >= M->nx ) ? ( M->nx - X - 1 ) : xrad; optr = M->result + ybase + zbase; for ( xbase = X-xmin; xbase <= X+xmax; xbase++ ) { sptr = optr + xbase; if ( ! *sptr ) *sptr = val; } } } RETURN(1); } /*---------------------------------------------------------------------- ** ** Find local maxima in short brick. ** **---------------------------------------------------------------------- */ static int find_local_maxima( maxima_s * M ) { short * sourcep, * destp; int c, cx, cy, cz; int maxx = M->nx - 1; int maxy = M->ny - 1; int nx = M->nx, nxy = M->nx * M->ny; int offset[ 26 ]; /* for speed */ ENTRY("find_local_maxima"); offset[ 0] = +1; offset[ 1] = -1; offset[ 2] = nx; offset[ 3] = nx+1; offset[ 4] = nx-1; offset[ 5] = -nx; offset[ 6] = -nx+1; offset[ 7] = -nx-1; offset[ 8] = nxy; offset[ 9] = nxy+1; offset[10] = nxy-1; offset[11] = nxy+nx; offset[12] = nxy+nx+1; offset[13] = nxy+nx-1; offset[14] = nxy-nx; offset[15] = nxy-nx+1; offset[16] = nxy-nx-1; offset[17] = -nxy; offset[18] = -nxy+1; offset[19] = -nxy-1; offset[20] = -nxy+nx; offset[21] = -nxy+nx+1; offset[22] = -nxy+nx-1; offset[23] = -nxy-nx; offset[24] = -nxy-nx+1; offset[25] = -nxy-nx-1; sourcep = M->sdata + nxy; /* skip first plane */ destp = M->result + nxy; for ( cz = 0; cz < M->nz-2; cz++ ) /* and skip last plane (1->2) [rickr] */ { for ( cy = 0; cy < M->ny; cy++ ) { if ( ( cy == 0 ) || ( cy == maxy ) ) { sourcep += nx; destp += nx; continue; } for ( cx = 0; cx < M->nx; cx++ ) { if ( ( cx == 0 ) || ( cx == maxx ) ) { sourcep++; destp++; continue; } if ( ! M->negatives && ( *sourcep < M->cutoff ) ) { sourcep++; destp++; continue; } if ( M->negatives && ( *sourcep > M->cutoff ) ) { sourcep++; destp++; continue; } *destp = MAX_MASK_FILL_VAL; M->extrema_count++; if ( M->true_max ) { if ( ! M->negatives ) { for ( c = 0; c < 26; c++ ) if ( *sourcep <= sourcep[offset[c]] ) { *destp = 0; M->extrema_count--; break; } } else { for ( c = 0; c < 26; c++ ) if ( *sourcep >= sourcep[offset[c]] ) { *destp = 0; M->extrema_count--; break; } } } else { if ( ! M->negatives ) { for ( c = 0; c < 26; c++ ) if ( *sourcep < sourcep[offset[c]] ) { *destp = 0; M->extrema_count--; break; } } else { for ( c = 0; c < 26; c++ ) if ( *sourcep > sourcep[offset[c]] ) { *destp = 0; M->extrema_count--; break; } } } sourcep++; destp++; } } } if ( M->debug > 3 ) show_maxima_s( "post find local maxima: ", M ); RETURN(1); } /*---------------------------------------------------------------------- ** ** Display extrema coordinates and write output BRIK. ** ** Put any new BRIK into memory. ** **---------------------------------------------------------------------- */ int set_results( r_afni_s * A, maxima_s * M, THD_3dim_dataset * dset ) { ENTRY("write_results"); if ( ! dset ) RETURN(1); EDIT_dset_items( dset, ADN_prefix, M->outfile, ADN_label1, M->outfile, ADN_nvals, 1, ADN_ntt, 0, ADN_type, HEAD_FUNC_TYPE, ADN_func_type, FUNC_FIM_TYPE, ADN_none ); EDIT_substitute_brick( dset, 0, M->data_type, M->result ); EDIT_BRICK_FACTOR ( dset, 0, 0.0 ); RETURN(1); } /*---------------------------------------------------------------------- * print a list of strings **---------------------------------------------------------------------- */ int disp_str_list( char * list[], int len ) { int c; for( c = 0; c < len; c++ ) puts(list[c]); fflush(stdout); return 0; } /*---------------------------------------------------------------------- ** ** For every point in list, display the talairach coordinates. ** **---------------------------------------------------------------------- */ int display_coords( r_afni_s * A, maxima_s * M ) { THD_fvec3 f3; THD_ivec3 i3; float prod, factor = A->factor[0]; short * optr; short * mptr; int * iptr; int X, Y, Z, count; point_list_s * P = &M->P; ENTRY("display_coords"); if( !M->coords_only ) /* 18 Aug 2006 [rickr] */ { printf( "---------------------------------------------\n" ); if ( M->dicom_coords ) printf( "RAI mm coordinates:\n\n" ); else printf( "%c%c%c mm coordinates:\n\n", ORIENT_typestr[M->dset->daxes->xxorient][0], ORIENT_typestr[M->dset->daxes->yyorient][0], ORIENT_typestr[M->dset->daxes->zzorient][0] ); } for ( count = 0, iptr = P->plist; count < P->used; count++, iptr++ ) { X = *iptr % M->nx; Y = (*iptr % M->nxy) / M->nx; Z = *iptr / M->nxy; i3.ijk[0] = X; i3.ijk[1] = Y; i3.ijk[2] = Z; f3 = THD_3dind_to_3dmm_no_wod(M->dset, i3); if ( M->dicom_coords ) f3 = THD_3dmm_to_dicomm(M->dset, f3); optr = M->sdata + *iptr; mptr = M->result + *iptr; if ( M->coords_only ) printf( "%7.2f %7.2f %7.2f\n", f3.xyz[0], f3.xyz[1], f3.xyz[2] ); else { prod = *optr * factor; printf( "(%7.2f %7.2f %7.2f) : val = %f\n", f3.xyz[0], f3.xyz[1], f3.xyz[2], prod ); } } if( !M->coords_only ) { if ( P->used ) printf( "\nnumber of extrema = %d\n", P->used ); else printf( "No extrema found.\n" ); printf( "---------------------------------------------\n" ); } RETURN(1); } /*---------------------------------------------------------------------- ** ** Initialize the afni structure. ** ** These are the fields that need to be set before reading a dataset. ** **---------------------------------------------------------------------- */ int init_afni_s( r_afni_s * A ) { ENTRY("init_afni_s"); memset( A, 0, sizeof( r_afni_s ) ); A->must_be_short = 1; A->want_floats = 1; A->subs_must_equal = 1; A->max_subs = 1; A->sub_brick = 0; RETURN(1); } /*---------------------------------------------------------------------- ** ** Initialize the maxima structure. ** **---------------------------------------------------------------------- */ int init_maxima_s( maxima_s * M, r_afni_s * A, char * outprefix ) { ENTRY("init_maxima_s"); M->dset = A->dset[0]; M->sdata = A->simage[0]; if ( ( M->result = (short *)calloc( A->nvox, sizeof( short ) ) ) == NULL ) { sprintf( grMessage, "Error: ims_05\n" "Failed to allocate M for %d shorts.", A->nvox ); rERROR( grMessage ); RETURN(0); } M->nx = A->nx; M->ny = A->ny; M->nz = A->nz; M->nxy = A->nx * A->ny; M->nvox = A->nvox; M->P.plist = NULL; M->P.used = 0; M->P.M = 0; M->extrema_count = 0; M->data_type = MRI_short; /* output will be short */ M->adn_type = HEAD_FUNC_TYPE; M->func_type = FUNC_FIM_TYPE; if ( outprefix && strlen( outprefix ) > R_FILE_L ) { sprintf( grMessage, "Error: ims_10\n" "Outfile prefix exceeds %d characters.", R_FILE_L ); rERROR( grMessage ); RETURN(0); } if ( outprefix ) strcpy( M->outfile, outprefix ); else *M->outfile = 0; M->cutoff = 0.0; M->min_dist = 0.0; M->out_rad = 0.0; M->negatives = 0; M->ngbr_style = MAX_SORT_N_REMOVE_STYLE; M->overwrite = 0; M->quiet = 0; M->coords_only = 0; M->true_max = 0; M->debug = 0; RETURN(1); } /*---------------------------------------------------------------------- ** ** Comparasin function of positives for qsort. ** **---------------------------------------------------------------------- */ int point_comp_pos( const void * p1, const void * p2 ) { short v1 = *( gr_orig_data + *(int *)p1 ); short v2 = *( gr_orig_data + *(int *)p2 ); if ( v1 < v2 ) /* reverse order to get large numbers first */ return 1; else if ( v1 > v2 ) return -1; return 0; } /*---------------------------------------------------------------------- ** ** Comparasin function of negatives for qsort. ** **---------------------------------------------------------------------- */ int point_comp_neg( const void * p1, const void * p2 ) { short v1 = *( gr_orig_data + *(int *)p1 ); short v2 = *( gr_orig_data + *(int *)p2 ); /* more negative is greater AND reverse the order */ if ( v1 > v2 ) return 1; else if ( v1 < v2 ) return -1; return 0; } /*---------------------------------------------------------------------- ** ** Fill the afni structure from an existing dataset. ** **---------------------------------------------------------------------- */ int r_set_afni_s_from_dset( r_afni_s * A, THD_3dim_dataset * dset, int debug ) { ENTRY("r_set_afni_s_from_dset"); if( debug > 3 ) disp_r_afni_s( "-d initial struct", A); if ( A->num_dsets >= R_MAX_AFNI_DSETS ) { sprintf( grMessage, "Error: rsasfd_00\n" "We only have memory to hold %d datasets. exiting...\n", R_MAX_AFNI_DSETS ); rERROR( grMessage ); RETURN(0); } A->dset[ 0 ] = dset; /* rickr - use sub-brick */ A->simage[ 0 ] = ( short * )DSET_ARRAY( dset, A->sub_brick ); if ( !A->simage[0] ) { sprintf(grMessage, "** data not available, is this in warp-on-demand mode?\n"); rERROR(grMessage); RETURN(0); } if ((A->factor[0] = DSET_BRICK_FACTOR(dset, A->sub_brick)) == 0.0 ) A->factor[0] = 1.0; A->subs [ 0 ] = DSET_NVALS( dset ); A->nx = dset->daxes->nxx; A->ny = dset->daxes->nyy; A->nz = dset->daxes->nzz; A->nvox = A->nx * A->ny * A->nz; if ( A->want_floats ) { int count; short * sptr; float * fptr; float factor = A->factor[ 0 ]; /* just for speed */ if ( ( A->fimage[ 0 ] = ( float * )calloc( A->nvox, sizeof( float ) ) ) == NULL ) { sprintf( grMessage, "Error: rsasfd_10\n" "Failed to allocate memory for %d floats.\n", A->nvox ); rERROR( grMessage ); RETURN(0); } fptr = A->fimage[ 0 ]; sptr = A->simage[ 0 ]; for ( count = 0; count < A->nvox; count++ ) *fptr++ = *sptr++ * factor; } A->max_u_short = r_get_max_u_short( (u_short *)A->simage[0], A->nvox ); /* A->num_dsets++; not using more than one */ if( debug > 1 ) disp_r_afni_s( "-d initial struct", A); RETURN(1); } /*---------------------------------------------------------------------- ** ** Compute the maximum unsigned short in an array. ** **---------------------------------------------------------------------- */ u_short r_get_max_u_short( u_short * S, int size ) { u_short * usptr, max = *S; int c = 0; for ( c = 0, usptr = S; c < size; c++, usptr++ ) { if ( *usptr > max ) max = *usptr; } return max; } /*---------------------------------------------------------------------- ** ** Free all local memory. ** **---------------------------------------------------------------------- */ void free_memory( r_afni_s * A, maxima_s * M ) { ENTRY("free_memory"); if ( A->want_floats && A->fimage[0] ) free( A->fimage[0] ); if ( M->result && !M->outfile[0] ) free( M->result ); if ( M->P.plist ) free( M->P.plist ); EXRETURN; } /*---------------------------------------------------------------------- * display contents of r_afni_s struct *---------------------------------------------------------------------- */ int disp_r_afni_s( char * mesg, r_afni_s * A ) { ENTRY("disp_r_afni_s"); if( mesg ) puts(mesg); printf("-d r_afni_s @ %p\n" " must_be_short, want_floats = %d, %d\n" " subs_must_equal, max_subs = %d, %d\n" " dset, simage = %p, %p\n" " factor = %f\n" " subs, sub_brick = %d, %d\n" " nx, ny, nz, nvox = %d, %d, %d, %d\n" " fimage = %p\n" " max_u_short, num_dsets = %d, %d\n", A, A->must_be_short, A->want_floats, A->subs_must_equal, A->max_subs, (void *)A->dset[0], (void *)A->simage[0], A->factor[0], A->subs[0], A->sub_brick, A->nx, A->ny, A->nz, A->nvox, (void *)A->fimage, A->max_u_short, A->num_dsets); return 0; }