#include "mrilib.h" #include "netcdf.h" /*******************************************************************/ /******* 29 Oct 2001: Read a 3D MINC file as an AFNI dataset *******/ /*******************************************************************/ static int first_err = 1 ; static char *fname_err ; #define EPR(x,s1,s2) \ do{ if( first_err ){ fprintf(stderr,"\n"); first_err=0; } \ fprintf(stderr,"** NetCDF error [%s,%s]: %s\n",s1,s2,nc_strerror(x)); \ } while(0) /*----------------------------------------------------------------- Read the attributes relevant to a MINC dimension variable -------------------------------------------------------------------*/ typedef struct { int dimid , varid , good , afni_orient ; size_t len ; float start , step , xcos,ycos,zcos ; char spacetype[32] ; } mincdim ; static mincdim read_mincdim( int ncid , char *dname ) { mincdim ddd ; int code ; float ccc[3] ; size_t lll ; static char *ename=NULL ; ddd.good = 0 ; /* flag for bad result */ ddd.step = 1.0 ; /* defaults */ ddd.xcos = ddd.ycos = ddd.zcos = 0.0 ; ddd.spacetype[0] = '\0' ; lll = strlen(fname_err) + strlen(dname) + 4 ; ename = AFREALL(ename, char, lll) ; sprintf(ename,"%s:%s",fname_err,dname) ; /* get ID of this dimension name */ code = nc_inq_dimid( ncid , dname , &ddd.dimid ) ; if( code != NC_NOERR ){ EPR(code,ename,"dimid"); return ddd; } /* get length of this dimension */ code = nc_inq_dimlen( ncid , ddd.dimid , &ddd.len ) ; if( code != NC_NOERR ){ EPR(code,ename,"dimlen"); return ddd; } /* get ID of corresponding variable */ code = nc_inq_varid( ncid , dname , &ddd.varid ) ; if( code != NC_NOERR ){ /* this is bad: try to fake it */ if( first_err ){ fprintf(stderr,"\n"); first_err=0; } fprintf(stderr,"** MINC warning: %s variable missing\n",ename); ddd.start = -0.5*ddd.step*ddd.len ; ddd.good = 1 ; return ddd ; } /* get step attribute of this variable */ code = nc_get_att_float( ncid , ddd.varid , "step" , &ddd.step ) ; if( code != NC_NOERR ){ if( first_err ){ fprintf(stderr,"\n"); first_err=0; } fprintf(stderr,"** MINC warning: %s:step missing\n",ename); } else if( ddd.step == 0.0 ){ if( first_err ){ fprintf(stderr,"\n"); first_err=0; } fprintf(stderr,"** MINC warning: %s:step=0\n",ename); ddd.step = 1.0 ; } /* get start attribute of this variable */ code = nc_get_att_float( ncid , ddd.varid , "start" , &ddd.start ) ; if( code != NC_NOERR ){ if( first_err ){ fprintf(stderr,"\n"); first_err=0; } fprintf(stderr,"** MINC warning: %s:start missing\n",ename) ; ddd.start = -0.5*ddd.step*ddd.len ; } /* get direction_cosines attribute of this variable [not used yet] */ code = nc_get_att_float( ncid,ddd.varid , "direction_cosines" , ccc ) ; if( code == NC_NOERR ){ ddd.xcos = ccc[0] ; ddd.ycos = ccc[1] ; ddd.zcos = ccc[2] ; } /* get spacetype attribute of this variable [Talairach or not?] */ lll = 0 ; code = nc_inq_attlen( ncid , ddd.varid , "spacetype" , &lll ) ; if( code == NC_NOERR && lll > 0 && lll < 32 ){ code = nc_get_att_text( ncid,ddd.varid , "spacetype" , ddd.spacetype ) ; if( code == NC_NOERR ){ ddd.spacetype[lll] = '\0' ; /* make sure ends in NUL */ } else { ddd.spacetype[0] = '\0' ; /* make sure is empty */ } } ddd.good = 1 ; return ddd ; } /*----------------------------------------------------------------- Open a MINC file as an AFNI dataset -------------------------------------------------------------------*/ THD_3dim_dataset * THD_open_minc( char *pathname ) { THD_3dim_dataset *dset=NULL ; int ncid , code ; mincdim xspace , yspace , zspace , *xyz[3] ; int im_varid , im_rank , im_dimid[4] ; nc_type im_type ; char prefix[1024] , *ppp , tname[12] ; THD_ivec3 nxyz , orixyz ; THD_fvec3 dxyz , orgxyz ; int datum , nvals=1 , ibr , iview ; size_t len ; float dtime=1.0 ; ENTRY("THD_open_minc") ; /*-- open input file --*/ if( pathname == NULL || pathname[0] == '\0' ) RETURN(NULL); first_err = 1 ; /* put a newline before 1st error message */ fname_err = pathname ; /* filename for errors in read_mincdim() */ code = nc_open( pathname , NC_NOWRITE , &ncid ) ; if( code != NC_NOERR ){ EPR(code,pathname,"open"); RETURN(NULL); } /*---------- get info about spatial axes ---------*/ /*[[ MINC x and y are reversed relative to AFNI ]]*/ /*[[ MINC x=L-R y=P-A; AFNI x=R-L y=A-P; z=I-S ]]*/ xspace = read_mincdim( ncid , "xspace" ) ; xspace.step = -xspace.step ; xspace.start = -xspace.start ; xspace.afni_orient = (xspace.step < 0.0) ? ORI_L2R_TYPE : ORI_R2L_TYPE ; yspace = read_mincdim( ncid , "yspace" ) ; yspace.step = -yspace.step ; yspace.start = -yspace.start ; yspace.afni_orient = (yspace.step < 0.0) ? ORI_P2A_TYPE : ORI_A2P_TYPE ; zspace = read_mincdim( ncid , "zspace" ) ; zspace.afni_orient = (zspace.step > 0.0) ? ORI_I2S_TYPE : ORI_S2I_TYPE ; /*-- if don't have all 3 named dimensions, quit --*/ if( !xspace.good || !yspace.good || !zspace.good ){ nc_close(ncid) ; RETURN(NULL) ; } /*-- get info about the image data --*/ code = nc_inq_varid( ncid , "image" , &im_varid ) ; if( code != NC_NOERR ){ EPR(code,pathname,"image varid"); nc_close(ncid); RETURN(NULL); } if( !AFNI_yesenv("AFNI_MINC_FLOATIZE") ){ /* determine type of dataset values */ code = nc_inq_vartype( ncid , im_varid , &im_type ) ; if( code != NC_NOERR ){ EPR(code,pathname,"image vartype"); nc_close(ncid); RETURN(NULL); } switch( im_type ){ default: if( first_err ){ fprintf(stderr,"\n"); first_err=0; } fprintf(stderr,"** MINC error: can't handle image type code %d\n",im_type); nc_close(ncid); RETURN(NULL); break ; case NC_BYTE: datum = MRI_byte ; break ; case NC_SHORT: datum = MRI_short ; break ; case NC_FLOAT: case NC_DOUBLE: case NC_INT: datum = MRI_float ; break ; } } else { /* always read in as floats if */ datum = MRI_float ; /* AFNI_MINC_FLOATIZE is Yes */ } /* we allow nD mages only for n=3 or n=4 */ code = nc_inq_varndims( ncid , im_varid , &im_rank ) ; if( code != NC_NOERR ){ EPR(code,pathname,"image varndims"); nc_close(ncid); RETURN(NULL); } if( im_rank < 3 || im_rank > 4 ){ if( first_err ){ fprintf(stderr,"\n"); first_err=0; } fprintf(stderr,"** MINC error: image rank=%d\n",im_rank) ; nc_close(ncid); RETURN(NULL); } code = nc_inq_vardimid( ncid , im_varid , im_dimid ) ; if( code != NC_NOERR ){ EPR(code,pathname,"image vardimid"); nc_close(ncid); RETURN(NULL); } /*-- find axes order --*/ /* fastest variation */ if( im_dimid[im_rank-1] == xspace.dimid ) xyz[0] = &xspace ; else if( im_dimid[im_rank-1] == yspace.dimid ) xyz[0] = &yspace ; else if( im_dimid[im_rank-1] == zspace.dimid ) xyz[0] = &zspace ; else { if( first_err ){ fprintf(stderr,"\n"); first_err=0; } fprintf(stderr,"** MINC error: image dim[2] illegal\n") ; nc_close(ncid) ; RETURN(NULL) ; } /* next fastest variation */ if( im_dimid[im_rank-2] == xspace.dimid ) xyz[1] = &xspace ; else if( im_dimid[im_rank-2] == yspace.dimid ) xyz[1] = &yspace ; else if( im_dimid[im_rank-2] == zspace.dimid ) xyz[1] = &zspace ; else { if( first_err ){ fprintf(stderr,"\n"); first_err=0; } fprintf(stderr,"** MINC error: image dim[1] illegal\n") ; nc_close(ncid) ; RETURN(NULL) ; } /* slowest variation */ if( im_dimid[im_rank-3] == xspace.dimid ) xyz[2] = &xspace ; else if( im_dimid[im_rank-3] == yspace.dimid ) xyz[2] = &yspace ; else if( im_dimid[im_rank-3] == zspace.dimid ) xyz[2] = &zspace ; else { if( first_err ){ fprintf(stderr,"\n"); first_err=0; } fprintf(stderr,"** MINC error: image dim[0] illegal\n") ; nc_close(ncid) ; RETURN(NULL) ; } /*-- determine the length of the 4th dimension, if any --*/ strcpy(tname,"MINC") ; /* default name for 4th dimension */ if( im_rank == 4 ){ char fname[NC_MAX_NAME+4] ; code = nc_inq_dimlen( ncid , im_dimid[0] , &len ) ; if( code != NC_NOERR ){ EPR(code,pathname,"dimid[0] dimlen"); nc_close(ncid); RETURN(NULL); } nvals = len ; /* number of volumes in this file */ /* get the name of this dimension */ code = nc_inq_dimname( ncid , im_dimid[0] , fname ) ; if( code == NC_NOERR ){ MCW_strncpy(tname,fname,10) ; } /* get the stepsize of this dimension */ code = nc_get_att_float( ncid , im_dimid[0] , "step" , &dtime ) ; if( code != NC_NOERR || dtime <= 0.0 ) dtime = 1.0 ; } /*-- make a dataset --*/ dset = EDIT_empty_copy(NULL) ; ppp = THD_trailname(pathname,0) ; /* strip directory */ MCW_strncpy( prefix , ppp , THD_MAX_PREFIX ) ; /* to make prefix */ nxyz.ijk[0] = xyz[0]->len ; dxyz.xyz[0] = xyz[0]->step ; /* setup axes */ nxyz.ijk[1] = xyz[1]->len ; dxyz.xyz[1] = xyz[1]->step ; nxyz.ijk[2] = xyz[2]->len ; dxyz.xyz[2] = xyz[2]->step ; orixyz.ijk[0] = xyz[0]->afni_orient ; orgxyz.xyz[0] = xyz[0]->start ; orixyz.ijk[1] = xyz[1]->afni_orient ; orgxyz.xyz[1] = xyz[1]->start ; orixyz.ijk[2] = xyz[2]->afni_orient ; orgxyz.xyz[2] = xyz[2]->start ; if( strstr(xyz[0]->spacetype,"alairach") != NULL ){ /* coord system */ iview = VIEW_TALAIRACH_TYPE ; } else { iview = VIEW_ORIGINAL_TYPE ; } dset->idcode.str[0] = 'M' ; /* overwrite 1st 3 bytes with something special */ dset->idcode.str[1] = 'N' ; dset->idcode.str[2] = 'C' ; MCW_hash_idcode( pathname , dset ) ; /* 06 May 2005 */ EDIT_dset_items( dset , ADN_prefix , prefix , ADN_datum_all , datum , ADN_nxyz , nxyz , ADN_xyzdel , dxyz , ADN_xyzorg , orgxyz , ADN_xyzorient , orixyz , ADN_malloc_type , DATABLOCK_MEM_MALLOC , ADN_nvals , nvals , ADN_type , HEAD_ANAT_TYPE , ADN_view_type , iview , ADN_func_type , (nvals==1) ? ANAT_MRAN_TYPE : ANAT_BUCK_TYPE , ADN_none ) ; if( nvals > 9 ) /* pretend it is 3D+time */ EDIT_dset_items( dset , ADN_func_type, ANAT_EPI_TYPE , ADN_ntt , nvals , ADN_ttorg , 0.0 , ADN_ttdel , dtime , ADN_ttdur , 0.0 , ADN_tunits , UNITS_SEC_TYPE , ADN_none ) ; /*-- flag to read data from disk using MINC/netCDF functions --*/ dset->dblk->diskptr->storage_mode = STORAGE_BY_MINC ; strcpy( dset->dblk->diskptr->brick_name , pathname ) ; for( ibr=0 ; ibr < nvals ; ibr++ ){ /* make sub-brick labels */ sprintf(prefix,"%s[%d]",tname,ibr) ; EDIT_BRICK_LABEL( dset , ibr , prefix ) ; } /*-- read and save the global:history attribute --*/ #if 0 sprintf(prefix,"THD_open_minc(%s)",pathname) ; tross_Append_History( dset , prefix ) ; #endif code = nc_inq_attlen( ncid , NC_GLOBAL , "history" , &len ) ; if( code == NC_NOERR && len > 0 ){ ppp = AFMALL(char,len+4) ; code = nc_get_att_text( ncid , NC_GLOBAL , "history" , ppp ) ; if( code == NC_NOERR ){ /* should always happen */ ppp[len] = '\0' ; tross_Append_History( dset , ppp ) ; } free(ppp) ; } /*-- close file and return the empty dataset */ nc_close(ncid) ; RETURN(dset) ; } /*----------------------------------------------------------------- Load a MINC dataset's data into memory (called from THD_load_datablock in thd_loaddblk.c) -------------------------------------------------------------------*/ void THD_load_minc( THD_datablock *dblk ) { THD_diskptr *dkptr ; int im_varid , code , ncid ; int nx,ny,nz,nxy,nxyz,nxyzv , nbad,ibr,nv, nslice ; void *ptr ; nc_type im_type=NC_SHORT ; float im_valid_range[2], fac,denom , intop,inbot , outbot,outtop , sfac=1.0; float *im_min=NULL , *im_max=NULL ; int im_min_varid , im_max_varid , do_scale ; ENTRY("THD_load_minc") ; /*-- open input [these errors should never occur] --*/ if( !ISVALID_DATABLOCK(dblk) || dblk->diskptr->storage_mode != STORAGE_BY_MINC || dblk->brick == NULL ) EXRETURN ; dkptr = dblk->diskptr ; code = nc_open( dkptr->brick_name , NC_NOWRITE , &ncid ) ; if( code != NC_NOERR ){ EPR(code,dkptr->brick_name,"open"); EXRETURN; } code = nc_inq_varid( ncid , "image" , &im_varid ) ; if( code != NC_NOERR ){ EPR(code,dkptr->brick_name,"image varid"); nc_close(ncid); EXRETURN; } /*-- allocate space for data --*/ nx = dkptr->dimsizes[0] ; ny = dkptr->dimsizes[1] ; nxy = nx * ny ; nz = dkptr->dimsizes[2] ; nxyz = nxy * nz ; nv = dkptr->nvals ; nxyzv = nxyz * nv ; nslice = nz*nv ; dblk->malloc_type = DATABLOCK_MEM_MALLOC ; /** malloc space for each brick separately **/ for( nbad=ibr=0 ; ibr < nv ; ibr++ ){ if( DBLK_ARRAY(dblk,ibr) == NULL ){ ptr = AFMALL(void, DBLK_BRICK_BYTES(dblk,ibr) ) ; mri_fix_data_pointer( ptr , DBLK_BRICK(dblk,ibr) ) ; if( ptr == NULL ) nbad++ ; } } /** if couldn't get them all, take our ball and go home in a snit **/ if( nbad > 0 ){ fprintf(stderr,"\n** failed to malloc %d MINC bricks out of %d\n\a",nbad,nv) ; for( ibr=0 ; ibr < nv ; ibr++ ){ if( DBLK_ARRAY(dblk,ibr) != NULL ){ free(DBLK_ARRAY(dblk,ibr)) ; mri_fix_data_pointer( NULL , DBLK_BRICK(dblk,ibr) ) ; } } nc_close(ncid) ; EXRETURN ; } /** get scaling attributes/variables **/ (void) nc_inq_vartype( ncid , im_varid , &im_type ) ; /* N.B.: we don't scale if input data is stored as floats */ do_scale = (im_type==NC_BYTE || im_type==NC_SHORT || im_type==NC_INT) ; if( do_scale && AFNI_noenv("AFNI_MINC_SLICESCALE") ) do_scale = 0 ; code = nc_get_att_float( ncid,im_varid , "valid_range" , im_valid_range ) ; /** if can't get valid_range, make something up **/ if( code != NC_NOERR || im_valid_range[0] >= im_valid_range[1] ){ im_valid_range[0] = 0.0 ; switch( im_type ){ case NC_BYTE: im_valid_range[1] = 255.0 ; break ; case NC_SHORT: im_valid_range[1] = 32767.0 ; break ; case NC_INT: im_valid_range[1] = 2147483647.0 ; break ; case NC_FLOAT: case NC_DOUBLE: im_valid_range[1] = 1.0 ; break ; } } inbot = im_valid_range[0] ; intop = im_valid_range[1] ; denom = intop - inbot ; /* always positive */ /** Get range of image (per 2D slice) to which valid_range will be scaled **/ /** Scaling will only be done if we get both image-min and image-max **/ if( do_scale ){ code = nc_inq_varid( ncid , "image-min" , &im_min_varid ) ; if( code == NC_NOERR ){ im_min = (float *) calloc(sizeof(float),nslice) ; code = nc_get_var_float( ncid , im_min_varid , im_min ) ; if( code != NC_NOERR ){ free(im_min); im_min = NULL; } } /* got im_min ==> try for im_max */ if( im_min != NULL ){ code = nc_inq_varid( ncid , "image-max" , &im_max_varid ) ; if( code == NC_NOERR ){ im_max = (float *) calloc(sizeof(float),nslice) ; code = nc_get_var_float( ncid , im_max_varid , im_max ) ; if( code != NC_NOERR ){ free(im_min); im_min = NULL; free(im_max); im_max = NULL; } } } /* 19 Mar 2003: make sure we don't scale out of range! */ if( im_max != NULL && MRI_IS_INT_TYPE(DBLK_BRICK_TYPE(dblk,0)) ){ float stop=0.0 , vbot,vtop ; int ii ; for( ii=0 ; ii < nslice ; ii++ ){ if( im_min[ii] < im_max[ii] ){ vbot = fabs(im_min[ii]) ; vtop = fabs(im_max[ii]) ; vtop = MAX(vtop,vbot) ; stop = MAX(vtop,stop) ; } } if( stop > MRI_TYPE_maxval[DBLK_BRICK_TYPE(dblk,0)] ){ sfac = MRI_TYPE_maxval[DBLK_BRICK_TYPE(dblk,0)] / stop ; fprintf(stderr,"++ Scaling %s by %g to avoid overflow to %g of %s data!\n", dkptr->brick_name, sfac, stop, MRI_TYPE_name[DBLK_BRICK_TYPE(dblk,0)] ) ; } else if( stop == 0.0 ){ free(im_min); im_min = NULL; free(im_max); im_max = NULL; } } } /* end of do_scale */ /** read data! **/ switch( DBLK_BRICK_TYPE(dblk,0) ){ /* output datum */ /* this case should never happen */ default: fprintf(stderr,"\n** MINC illegal datum %d found\n", DBLK_BRICK_TYPE(dblk,0) ) ; for( ibr=0 ; ibr < nv ; ibr++ ){ if( DBLK_ARRAY(dblk,ibr) != NULL ){ free(DBLK_ARRAY(dblk,ibr)) ; mri_fix_data_pointer( NULL , DBLK_BRICK(dblk,ibr) ) ; } } break ; /* data is stored as bytes in AFNI */ case MRI_byte:{ int kk,ii,qq ; byte *br ; if( nv == 1 ){ code = nc_get_var_uchar( ncid , im_varid , DBLK_ARRAY(dblk,0) ) ; } else { size_t start[4] , count[4] ; start[1] = start[2] = start[3] = 0 ; count[0] = 1 ; count[1] = nz ; count[2] = ny ; count[3] = nx ; for( ibr=0 ; ibr < nv ; ibr++ ){ start[0] = ibr ; code = nc_get_vara_uchar( ncid,im_varid , start,count , DBLK_ARRAY(dblk,ibr) ) ; } } if( code != NC_NOERR ) EPR(code,dkptr->brick_name,"image get_var_uchar") ; if( im_max != NULL ){ /* must scale values */ for( ibr=0 ; ibr < nv ; ibr++ ){ /* loop over bricks */ br = DBLK_ARRAY(dblk,ibr) ; for( kk=0 ; kk < nz ; kk++ ){ /* loop over slices */ outbot = im_min[kk+ibr*nz] ; outtop = im_max[kk+ibr*nz] ; if( outbot >= outtop) continue ; /* skip */ fac = (outtop-outbot) / denom ; qq = kk*nxy ; outbot += 0.499 ; for( ii=0 ; ii < nxy ; ii++ ) /* scale slice */ br[ii+qq] = (byte) ((fac*(br[ii+qq]-inbot) + outbot)*sfac) ; } } } } break ; /* data is stored as shorts in AFNI */ case MRI_short:{ int kk,ii,qq ; short *br ; if( nv == 1 ){ code = nc_get_var_short( ncid , im_varid , DBLK_ARRAY(dblk,0) ) ; } else { size_t start[4] , count[4] ; start[1] = start[2] = start[3] = 0 ; count[0] = 1 ; count[1] = nz ; count[2] = ny ; count[3] = nx ; for( ibr=0 ; ibr < nv ; ibr++ ){ start[0] = ibr ; code = nc_get_vara_short( ncid,im_varid , start,count , DBLK_ARRAY(dblk,ibr) ) ; } } if( code != NC_NOERR ) EPR(code,dkptr->brick_name,"image get_var_short") ; if( im_max != NULL ){ /* must scale values */ for( ibr=0 ; ibr < nv ; ibr++ ){ /* loop over bricks */ br = DBLK_ARRAY(dblk,ibr) ; for( kk=0 ; kk < nz ; kk++ ){ /* loop over slices */ outbot = im_min[kk+ibr*nz] ; outtop = im_max[kk+ibr*nz] ; if( outbot >= outtop) continue ; /* skip */ fac = (outtop-outbot) / denom ; qq = kk*nxy ; outbot += 0.499 ; for( ii=0 ; ii < nxy ; ii++ ) /* scale slice */ br[ii+qq] = (short) ((fac*(br[ii+qq]-inbot) + outbot)*sfac) ; } } } } break ; /* data is stored as floats in AFNI */ case MRI_float:{ int kk,ii,qq ; float *br ; if( nv == 1 ){ code = nc_get_var_float( ncid , im_varid , DBLK_ARRAY(dblk,0) ) ; } else { size_t start[4] , count[4] ; start[1] = start[2] = start[3] = 0 ; count[0] = 1 ; count[1] = nz ; count[2] = ny ; count[3] = nx ; for( ibr=0 ; ibr < nv ; ibr++ ){ start[0] = ibr ; code = nc_get_vara_float( ncid,im_varid , start,count , DBLK_ARRAY(dblk,ibr) ) ; } } if( code != NC_NOERR ) EPR(code,dkptr->brick_name,"image get_var_float") ; if( im_type == NC_BYTE ){ /* fix sign of bytes */ for( ibr=0 ; ibr < nv ; ibr++ ){ /* loop over bricks */ br = DBLK_ARRAY(dblk,ibr) ; for( ii=0 ; ii < nxyz ; ii++ ) if( br[ii] < 0.0 ) br[ii] += 256.0 ; } } if( im_max != NULL ){ /* must scale values */ for( ibr=0 ; ibr < nv ; ibr++ ){ /* loop over bricks */ br = DBLK_ARRAY(dblk,ibr) ; for( kk=0 ; kk < nz ; kk++ ){ /* loop over slices */ outbot = im_min[kk+ibr*nz] ; outtop = im_max[kk+ibr*nz] ; if( outbot >= outtop) continue ; /* skip */ fac = (outtop-outbot) / denom ; qq = kk*nxy ; for( ii=0 ; ii < nxy ; ii++ ) /* scale slice */ br[ii+qq] = (fac*(br[ii+qq]-inbot) + outbot) ; } } } } break ; } /* end of switch on output datum */ /*-- throw away the trash and return --*/ if( im_min != NULL ) free(im_min) ; if( im_max != NULL ) free(im_max) ; nc_close(ncid) ; EXRETURN ; }