int main( int argc , char *argv[] ) { int nfl , ii , ct ; char *sspec ; NI_element *nel ; NI_stream ns ; float *flar ; if( argc < 3 || strcmp(argv[1],"-help") == 0 ){ printf("Usage: nimel N streamspec\n") ; exit(0) ; } nfl = strtol( argv[1] , NULL , 10) ; if( nfl < 1 ) nfl = 100000 ; sspec = argv[2] ; ns = NI_stream_open( sspec , "w" ) ; if( ns == NULL ){ fprintf(stderr,"NI_stream_open fails\n"); exit(1); } nel = NI_new_data_element( "Tester" , nfl ) ; flar = (float *)malloc(sizeof(float)*nfl) ; for( ii=0 ; ii < nfl ; ii++ ) flar[ii] = (float)ii ; NI_add_column( nel , NI_FLOAT , flar ) ; free((void *)flar) ; while(1){ ii = NI_stream_writecheck( ns , 666 ) ; if( ii == 1 ){ fprintf(stderr,"!\n") ; break ; } if( ii < 0 ){ fprintf(stderr,"BAD writecheck\n"); exit(1); } fprintf(stderr,".") ; } ct = NI_clock_time() ; NI_write_element( ns , nel , NI_BINARY_MODE ) ; NI_stream_closenow( ns ) ; ct = NI_clock_time() - ct ; fprintf(stderr,"Wrote %d floats in %d ms\n",nfl,ct) ; exit(0) ; }
MRI_IMAGE * mri_warp3D_align_one( MRI_warp3D_align_basis *bas, MRI_IMAGE *im ) { float *fit , *dfit , *qfit , *tol ; int iter , good,ngood , ii, pp , skip_first ; MRI_IMAGE *tim , *fim ; float *pmat=MRI_FLOAT_PTR(bas->imps) , /* pseudo inverse: n X m matrix */ *tar , tv , sfit ; int n=bas->imps->nx , /* = nfree+1 */ m=bas->imps->ny , /* = imap->nx = length of ima */ npar=bas->nparam , /* = number of warp parameters */ nfree=bas->nfree , /* = number of free warp parameters */ *ima=MRI_INT_PTR(bas->imap) , /* = indexes in fim of voxels to use */ *pma ; /* = map of free to total params */ int ctstart ; int do_twopass=(bas->imps_blur != NULL && bas->twoblur > 0.0f) , passnum=1 ; int blur_pass ; char *save_prefix ; static int save_index=0 ; #define AITMAX 3.33 #define NMEM 5 float *fitmem[NMEM] ; int mm , last_aitken , num_aitken=0 ; float sdif , fitdif[NMEM] ; /* 28 Sep 2005 */ int num_bad_diff ; float best_dif , best_par[999] ; /* 09 Jan 2006 */ int best_ite=0 ; ENTRY("mri_warp3D_align_one") ; ctstart = NI_clock_time() ; save_prefix = getenv("AFNI_WARPDRIVE_SAVER") ; /** pma[k] = external parameter index for the k-th free parameter **/ pma = (int *)malloc(sizeof(int) * nfree) ; for( pp=ii=0 ; ii < npar ; ii++ ) if( !bas->param[ii].fixed ) pma[pp++] = ii ; #if 0 fprintf(stderr,"pma=") ; for(pp=0;pp<nfree;pp++)fprintf(stderr," %d",pma[pp]); fprintf(stderr,"\n") ; #endif fit = (float *)malloc(sizeof(float) * npar ) ; dfit = (float *)malloc(sizeof(float) * npar ) ; qfit = (float *)malloc(sizeof(float) * nfree) ; tol = (float *)malloc(sizeof(float) * npar ) ; for( mm=0 ; mm < NMEM ; mm++ ) fitmem[mm] = NULL ; for( mm=0 ; mm < NMEM ; mm++ ) fitdif[mm] = 3.e+33 ; /*--- loop back point for two pass alignment ---*/ bas->num_iter = 0 ; mri_warp3D_set_womask( bas->imsk ) ; ReStart: best_dif = -666.0f ; /* 09 Jan 2006 */ blur_pass = (do_twopass && passnum==1) ; mri_warp3D_method( blur_pass ? MRI_LINEAR : bas->regmode ) ; /* load initial fit parameters; if they are all the identity transform value, then skip the first transformation of the fim volume */ if( passnum == 1 ){ skip_first = 1 ; for( pp=0 ; pp < npar ; pp++ ){ if( bas->param[pp].fixed ){ fit[pp] = bas->param[pp].val_fixed ; } else { fit[pp] = bas->param[pp].val_init ; } skip_first = skip_first && (fit[pp] == bas->param[pp].ident) ; } } else { skip_first = 0 ; /* and fit[] is unchanged */ } fitmem[0] = (float *)malloc(sizeof(float)*npar) ; memcpy( fitmem[0] , fit , sizeof(float)*npar ) ; for( pp=0 ; pp < npar ; pp++ ) tol[pp] = bas->param[pp].toler ; if( blur_pass ){ float fac = (1.0f+bas->twoblur) ; if( fac < 3.0f ) fac = 3.0f ; for( pp=0 ; pp < npar ; pp++ ) tol[pp] *= fac ; } if( bas->verb ) fprintf(stderr,"++ mri_warp3D_align_one: START PASS #%d\n",passnum) ; /* setup base image for registration into fim, and pseudo-inverse of base+derivative images into pmat */ if( blur_pass ){ /* first pass ==> registering blurred images */ float *far , blur=bas->twoblur ; int nx=im->nx , ny=im->ny , nz=im->nz ; fim = mri_to_float( im ) ; far = MRI_FLOAT_PTR(fim) ; EDIT_blur_volume_3d( nx,ny,nz , 1.0f,1.0f,1.0f , MRI_float , far, blur,blur,blur ) ; pmat = MRI_FLOAT_PTR(bas->imps_blur) ; } else { /* registering original image */ if( im->kind == MRI_float ) fim = im ; else fim = mri_to_float( im ) ; pmat = MRI_FLOAT_PTR(bas->imps) ; } /******** iterate fit ********/ iter = 0 ; good = 1 ; last_aitken = 3 ; num_bad_diff = 0 ; while( good ){ if( skip_first ){ tim = fim ; skip_first = 0 ; } else { bas->vwset( npar , fit ) ; /**************************/ tim = mri_warp3D( fim , 0,0,0 , bas->vwfor ) ; /* warp on current params */ } /**************************/ tar = MRI_FLOAT_PTR(tim) ; sdif = mri_scaled_diff( bas->imbase , tim , bas->imsk ) ; if( bas->verb ) fprintf(stderr,"++++++++++ Start iter=%d RMS_diff=%g\n",iter+1,sdif) ; if( best_dif < 0.0f || sdif < best_dif ){ /* 09 Jan 2006 */ best_dif = sdif ; best_ite = iter ; memcpy( best_par , fit , sizeof(float)*npar ) ; } /* find least squares fit of base + derivatives to warped image */ sfit = 0.0f ; for( pp=0 ; pp < npar ; pp++ ) dfit[pp] = 0.0f ; for( pp=0 ; pp < nfree ; pp++ ) qfit[pp] = 0.0f ; for( ii=0 ; ii < m ; ii++ ){ tv = tar[ima[ii]] ; sfit += P(nfree,ii) * tv ; for( pp=0 ; pp < nfree ; pp++ ) qfit[pp] += P(pp,ii) * tv ; } if( tim != fim ) mri_free( tim ) ; #if 0 fprintf(stderr,"qfit="); for(pp=0;pp<nfree;pp++)fprintf(stderr," %g",qfit[pp]); fprintf(stderr,"\n") ; #endif for( pp=0 ; pp < nfree ; pp++ ) dfit[pma[pp]] = qfit[pp] ; for( pp=0 ; pp < npar ; pp++ ){ fit[pp] += dfit[pp] ; if( fit[pp] > bas->param[pp].max ) fit[pp] = bas->param[pp].max ; else if( fit[pp] < bas->param[pp].min ) fit[pp] = bas->param[pp].min ; } if( bas->verb ){ fprintf(stderr,"+ Delta:") ; for( pp=0 ; pp < npar ; pp++ ) fprintf(stderr," %13.6g",dfit[pp]) ; fprintf(stderr,"\n") ; fprintf(stderr,"+ Total: scale factor=%g\n" "+ #%5d:",sfit,iter+1) ; for( pp=0 ; pp < npar ; pp++ ) fprintf(stderr," %13.6g", fit[pp]) ; fprintf(stderr,"\n") ; } /* save fit results for a while into the past, and then maybe do Aitken */ if( fitmem[NMEM-1] != NULL ) free((void *)fitmem[NMEM-1]) ; for( mm=NMEM-1 ; mm > 0 ; mm-- ) fitmem[mm] = fitmem[mm-1] ; fitmem[0] = (float *)malloc(sizeof(float)*npar) ; memcpy( fitmem[0] , fit , sizeof(float)*npar ) ; /* 28 Sep 2005: if RMS went up, back off the changes! */ for( mm=NMEM-1 ; mm > 0 ; mm-- ) fitdif[mm] = fitdif[mm-1] ; fitdif[0] = sdif ; if( iter > 1 && num_bad_diff < 2 && fitmem[1] != NULL && fitdif[0] > 1.0666f * fitdif[1] ){ for( pp=0 ; pp < npar ; pp++ ) fit[pp] = 0.5 * ( fitmem[0][pp] + fitmem[1][pp] ) ; memcpy( fitmem[0] , fit , sizeof(float)*npar ) ; last_aitken = iter+1 ; num_bad_diff++ ; if( bas->verb ) fprintf(stderr,"+++ RMS_diff changes too much! Shrinking!\n") ; } else { num_bad_diff = 0 ; } iter++ ; if( iter > last_aitken+NMEM && !AFNI_noenv("AFNI_WARPDRIVE_AITKEN") ){ double s0,s1,s2 , dd , de,df ; num_aitken = 0 ; for( pp=0 ; pp < npar ; pp++ ){ dd = fabs(fitmem[1][pp]-fit[pp]) ; if( dd <= tol[pp] ) continue ; /* done here */ de = dd ; for( mm=2 ; mm < NMEM ; mm++ ){ df = fabs(fitmem[mm][pp]-fitmem[mm-1][pp]) ; if( df <= de ) break ; de = df ; } if( mm == NMEM ){ /* do Aitken */ s2 = fit[pp] ; s1 = fitmem[1][pp] ; s0 = fitmem[2][pp] ; de = ( (s2-s1) - (s1-s0) ) ; if( de != 0.0 ){ de = -(s2-s1)*(s2-s1) / de ; dd *= AITMAX ; if( fabs(de) > dd ){ de = (de > 0.0) ? dd : -dd ; } fit[pp] += de ; if( fit[pp] > bas->param[pp].max ) fit[pp] = bas->param[pp].max ; else if( fit[pp] < bas->param[pp].min ) fit[pp] = bas->param[pp].min ; num_aitken++ ; } } } if( num_aitken > 0 ) last_aitken = iter ; if( bas->verb && num_aitken > 0 ){ fprintf(stderr,"+ Aitken on %d params:\n" "+________:",num_aitken) ; for( pp=0 ; pp < npar ; pp++ ){ if( fit[pp] != fitmem[0][pp] ){ fprintf(stderr," %13.6g", fit[pp]) ; fitmem[0][pp] = fit[pp] ; } else { fprintf(stderr," _____________") ; } } fprintf(stderr,"\n") ; } } /* save intermediate image? */ if( save_prefix != NULL ){ char sname[THD_MAX_NAME] ; FILE *fp ; mri_warp3D_set_womask( NULL ) ; /* must warp the whole thing */ bas->vwset( npar , fit ) ; tim = mri_warp3D( fim , 0,0,0 , bas->vwfor ) ; tar = MRI_FLOAT_PTR(tim) ; mri_warp3D_set_womask( bas->imsk ) ; sprintf(sname,"%s_%04d.mmm",save_prefix,save_index++) ; fprintf(stderr,"+ Saving intermediate image to binary file %s\n",sname) ; fp = fopen( sname , "w" ) ; if( fp != NULL ){ fwrite( tar, tim->pixel_size, tim->nvox, fp ) ; fclose(fp) ; } if( bas->vwdet != NULL ){ int i,j,k , nx=tim->nx,ny=tim->ny,nz=tim->nz ; for( k=0 ; k < nz ; k++ ){ for( j=0 ; j < ny ; j++ ){ for( i=0 ; i < nx ; i++ ){ tar[i+(j+k*ny)*nx] = bas->vwdet( (float)i,(float)j,(float)k ) ; }}} sprintf(sname,"%s_%04d.ddd",save_prefix,save_index-1) ; fprintf(stderr,"+ Saving determinant image to binary file %s\n",sname) ; fp = fopen( sname , "w" ) ; if( fp != NULL ){ fwrite( tar, tim->pixel_size, tim->nvox, fp ) ; fclose(fp) ; } } mri_free( tim ) ; } /* loop back for more iterations? */ if( last_aitken == iter ) continue ; /* don't test, just loop */ if( fitmem[2] == NULL ) continue ; ngood = 0 ; for( pp=0 ; pp < npar ; pp++ ) if( !bas->param[pp].fixed ) ngood += ( ( fabs(fitmem[1][pp]-fitmem[0][pp]) <= tol[pp] ) && ( fabs(fitmem[2][pp]-fitmem[1][pp]) <= tol[pp] ) ) ; good = (ngood < nfree) && (iter < bas->max_iter) ; } /******** end while loop for iteration of fitting procedure ********/ for( mm=0 ; mm < NMEM ; mm++ ) if( fitmem[mm] != NULL ){ free((void *)fitmem[mm]); fitmem[mm] = NULL; } /*--- 09 Jan 2006: if prior best fit was better than current, replace ---*/ if( best_dif > 0.0f && 1.0666f*best_dif < sdif ){ memcpy( fit , best_par , sizeof(float)*npar ) ; if( bas->verb ) fprintf(stderr,"+++++ Replacing final fit with iter #%d's fit +++++\n", best_ite+1) ; } /*--- do the second pass? ---*/ if( blur_pass ){ if( bas->verb ) fprintf(stderr,"+++++++++++++ Loop back for next pass +++++++++++++\n"); mri_free(fim) ; fim = NULL ; passnum++ ; goto ReStart ; } else { if( bas->verb ) fprintf(stderr,"+++++++++++++ Convergence test passed +++++++++++++\n"); } /*--- done! ---*/ bas->num_iter = iter ; for( pp=0 ; pp < npar ; pp++ ) bas->param[pp].val_out = fit[pp] ; /*-- do the actual realignment to get the output image --*/ if( bas->regfinal > 0 ) mri_warp3D_method( bas->regfinal ) ; mri_warp3D_set_womask( NULL ) ; bas->vwset( npar , fit ) ; tim = mri_warp3D( fim , 0,0,0 , bas->vwfor ) ; if( fim != im ) mri_free(fim) ; /* if it was a copy, junk it */ free((void *)dfit) ; free((void *)fit) ; free((void *)qfit) ; free((void *)pma) ; free((void *)tol) ; if( bas->verb ){ double st = (NI_clock_time()-ctstart) * 0.001 ; fprintf(stderr,"++ mri_warp3D_align_one EXIT: %.2f seconds elapsed\n",st) ; } RETURN( tim ) ; }
int mri_warp3D_align_setup( MRI_warp3D_align_basis *bas ) { MRI_IMAGE *cim , *fitim ; int nx, ny, nz, nxy, nxyz , ii,jj,kk , nmap, *im ; float *wf , *wtar , clip , clip2 ; int *ima , pp , wtproc , npar , nfree ; byte *msk ; int ctstart ; ENTRY("mri_warp3D_align_setup") ; ctstart = NI_clock_time() ; /*- check for good inputs -*/ if( bas == NULL || bas->imbase == NULL ) RETURN(1) ; if( bas->nparam < 1 || bas->param == NULL ) RETURN(1) ; if( bas->vwfor == NULL || bas->vwinv == NULL || bas->vwset == NULL ) RETURN(1) ; /*- set defaults in bas, if values weren't set by user -*/ if( bas->scale_init <= 0.0f ) bas->scale_init = 1.0f ; if( bas->delfac <= 0.0f ) bas->delfac = 1.0f ; if( bas->regmode <= 0 ) bas->regmode = MRI_LINEAR ; if( bas->max_iter <= 0 ) bas->max_iter = 9 ; /* process the weight image? */ wtproc = (bas->imwt == NULL) ? 1 : bas->wtproc ; npar = bas->nparam ; nfree = npar ; for( pp=0 ; pp < npar ; pp++ ) if( bas->param[pp].fixed ) nfree-- ; if( nfree <= 0 ) RETURN(1) ; bas->nfree = nfree ; /*- clean out anything from last call -*/ mri_warp3D_align_cleanup( bas ) ; /*-- need local copy of input base image --*/ cim = mri_to_float( bas->imbase ) ; nx=cim->nx ; ny=cim->ny ; nz=cim->nz ; nxy = nx*ny ; nxyz=nxy*nz ; /*-- make weight image up from the base image if it isn't supplied --*/ if( bas->verb ) fprintf(stderr,"++ mri_warp3D_align_setup ENTRY\n") ; if( bas->imwt == NULL || bas->imwt->nx != nx || bas->imwt->ny != ny || bas->imwt->nz != nz ) bas->imww = mri_copy( cim ) ; else bas->imww = mri_to_float( bas->imwt ) ; if( bas->twoblur > 0.0f ){ float bmax = cbrt((double)nxyz) * 0.03 ; if( bmax < bas->twoblur ){ if( bas->verb ) fprintf(stderr,"+ shrink bas->twoblur from %.3f to %.3f\n", bas->twoblur , bmax ) ; bas->twoblur = bmax ; } } if( bas->verb ) fprintf(stderr,"+ processing weight:") ; /* make sure weight is non-negative */ wf = MRI_FLOAT_PTR(bas->imww) ; for( ii=0 ; ii < nxyz ; ii++ ) wf[ii] = fabs(wf[ii]) ; /* trim off edges of weight */ if( wtproc ){ int ff ; int xfade=bas->xedge , yfade=bas->yedge , zfade=bas->zedge ; if( xfade < 0 || yfade < 0 || zfade < 0 ) mri_warp3D_align_edging_default(nx,ny,nz,&xfade,&yfade,&zfade) ; if( bas->twoblur > 0.0f ){ xfade += (int)rint(1.5*bas->twoblur) ; yfade += (int)rint(1.5*bas->twoblur) ; zfade += (int)rint(1.5*bas->twoblur) ; } if( 3*zfade >= nz ) zfade = (nz-1)/3 ; if( 3*xfade >= nx ) xfade = (nx-1)/3 ; if( 3*yfade >= ny ) yfade = (ny-1)/3 ; if( bas->verb ) fprintf(stderr," [edge(%d,%d,%d)]",xfade,yfade,zfade) ; for( jj=0 ; jj < ny ; jj++ ) for( ii=0 ; ii < nx ; ii++ ) for( ff=0 ; ff < zfade ; ff++ ) WW(ii,jj,ff) = WW(ii,jj,nz-1-ff) = 0.0f ; for( kk=0 ; kk < nz ; kk++ ) for( jj=0 ; jj < ny ; jj++ ) for( ff=0 ; ff < xfade ; ff++ ) WW(ff,jj,kk) = WW(nx-1-ff,jj,kk) = 0.0f ; for( kk=0 ; kk < nz ; kk++ ) for( ii=0 ; ii < nx ; ii++ ) for( ff=0 ; ff < yfade ; ff++ ) WW(ii,ff,kk) = WW(ii,ny-1-ff,kk) = 0.0f ; } /* spatially blur weight a little */ if( wtproc ){ float blur ; blur = 1.0f + MAX(1.5f,bas->twoblur) ; if( bas->verb ) fprintf(stderr," [blur(%.1f)]",blur) ; EDIT_blur_volume_3d( nx,ny,nz , 1.0f,1.0f,1.0f , MRI_float , wf , blur,blur,blur ) ; } /* get rid of low-weight voxels */ clip = 0.035 * mri_max(bas->imww) ; clip2 = 0.5*THD_cliplevel(bas->imww,0.4) ; if( clip2 > clip ) clip = clip2 ; if( bas->verb ) fprintf(stderr," [clip(%.1f)]",clip) ; for( ii=0 ; ii < nxyz ; ii++ ) if( wf[ii] < clip ) wf[ii] = 0.0f ; /* keep only the largest cluster of nonzero voxels */ { byte *mmm = (byte *)malloc( sizeof(byte)*nxyz ) ; for( ii=0 ; ii < nxyz ; ii++ ) mmm[ii] = (wf[ii] > 0.0f) ; THD_mask_clust( nx,ny,nz, mmm ) ; THD_mask_erode( nx,ny,nz, mmm, 1 ) ; /* cf. thd_automask.c */ THD_mask_clust( nx,ny,nz, mmm ) ; for( ii=0 ; ii < nxyz ; ii++ ) if( !mmm[ii] ) wf[ii] = 0.0f ; free((void *)mmm) ; } if( bas->verb ) fprintf(stderr,"\n") ; /*-- make integer index map of weight > 0 voxels --*/ nmap = 0 ; for( ii=0 ; ii < nxyz ; ii++ ) if( wf[ii] > 0.0f ) nmap++ ; if( bas->verb ) fprintf(stderr,"+ using %d [%.3f%%] voxels\n",nmap,(100.0*nmap)/nxyz); if( nmap < 7*nfree+13 ){ fprintf(stderr,"** mri_warp3D_align error: weight image too zero-ish!\n") ; mri_warp3D_align_cleanup( bas ) ; mri_free(cim) ; RETURN(1) ; } bas->imap = mri_new( nmap , 1 , MRI_int ) ; ima = MRI_INT_PTR(bas->imap) ; bas->imsk = mri_new_conforming( bas->imww , MRI_byte ) ; msk = MRI_BYTE_PTR(bas->imsk) ; for( ii=jj=0 ; ii < nxyz ; ii++ ){ if( wf[ii] > 0.0f ){ ima[jj++] = ii; msk[ii] = 1; } } /* make copy of sqrt(weight), but only at mapped indexes */ wtar = (float *)malloc(sizeof(float)*nmap) ; for( ii=0 ; ii < nmap ; ii++ ) wtar[ii] = sqrt(wf[ima[ii]]) ; /*-- for parameters that don't come with a step size, find one --*/ clip = bas->tolfac ; if( clip <= 0.0f ) clip = 0.03f ; for( ii=0 ; ii < npar ; ii++ ){ if( bas->param[ii].fixed ) continue ; /* don't need this */ if( bas->param[ii].delta <= 0.0f ) mri_warp3D_get_delta( bas , ii ) ; /* find step size */ if( bas->param[ii].toler <= 0.0f ){ /* and set default tolerance */ bas->param[ii].toler = clip * bas->param[ii].delta ; if( bas->verb ) fprintf(stderr,"+ set toler param#%d [%s] = %f\n", ii+1,bas->param[ii].name,bas->param[ii].toler) ; } } /* don't need the computed weight image anymore */ mri_free(bas->imww) ; bas->imww = NULL ; wf = NULL ; /*-- create image containing basis columns, then pseudo-invert it --*/ if( bas->verb ) fprintf(stderr,"+ Compute Derivatives of Base\n") ; fitim = mri_warp3D_align_fitim( bas , cim , bas->regmode , bas->delfac ) ; if( bas->verb ) fprintf(stderr,"+ calculate pseudo-inverse\n") ; bas->imps = mri_psinv( fitim , wtar ) ; mri_free(fitim) ; if( bas->imps == NULL ){ /* bad bad bad */ fprintf(stderr,"** mri_warp3D_align error: can't invert Base matrix!\n") ; free((void *)wtar) ; mri_warp3D_align_cleanup( bas ) ; mri_free(cim) ; RETURN(1) ; } /*--- twoblur? ---*/ if( bas->twoblur > 0.0f ){ float *car=MRI_FLOAT_PTR(cim) ; float blur = bas->twoblur ; float bfac = blur ; if( bfac < 1.1234f ) bfac = 1.1234f ; else if( bfac > 1.3456f ) bfac = 1.3456f ; if( bas->verb ) fprintf(stderr,"+ Compute Derivatives of Blurred Base\n") ; EDIT_blur_volume_3d( nx,ny,nz , 1.0f,1.0f,1.0f , MRI_float , car, blur,blur,blur ) ; fitim = mri_warp3D_align_fitim( bas , cim , MRI_LINEAR , bfac*bas->delfac ) ; if( bas->verb ) fprintf(stderr,"+ calculate pseudo-inverse\n") ; bas->imps_blur = mri_psinv( fitim , wtar ) ; mri_free(fitim) ; if( bas->imps_blur == NULL ){ /* bad */ fprintf(stderr,"** mri_warp3D_align error: can't invert Blur matrix!\n") ; } } /*--- done ---*/ mri_free(cim) ; free((void *)wtar) ; if( bas->verb ){ double st = (NI_clock_time()-ctstart) * 0.001 ; fprintf(stderr,"++ mri_warp3D_align_setup EXIT: %.2f seconds elapsed\n",st); } RETURN(0); }
/* NIML workprocess. - Read and process any new data from open connection. The void * pointer thereiselvis is expected to point to a properly initialized communication structure. (If the return is True, that means don't call this workproc again. If the return is False, that means call this workproc again.......) -------------------------------------------------------------------------*/ int Hallo_niml_workproc( void *thereiselvis ) { static char FuncName[]={"Hallo_niml_workproc"}; int nn, id; void *nini ; static int nwarn=0; char tmpcom[100], *nel_track; NI_element *nel ; int LocalHead = 0; COMM_STRUCT *cs=NULL; cs = (COMM_STRUCT *)thereiselvis; /* check if stream has gone bad */ nn = NI_stream_goodcheck( cs->NimlStream , 1 ) ; if( nn < 0 ){ /* first check in case we are dealing with the intermittent AFNI disruption */ if (1) { NI_stream_close( cs->NimlStream ) ; cs->NimlStream = NULL ; /* try again, a way to get around the weird disruption in SHM connection*/ fprintf(stderr,"Attempting recovery...\n"); cs->Connected = 0; /* Retry calling function */ if (!SendToSuma(cs, NULL, 0)) { /* buzz SUMA */ fprintf (stderr,"Failed to re-initiate call suma\n"); } nn = NI_stream_goodcheck( cs->NimlStream , 1 ) ; } else { fprintf(stderr, "SUMA connection stream gone bad.\n" ); } } if( nn < 0 ){ /* is bad */ if (cs->NimlStream) NI_stream_close( cs->NimlStream ) ; cs->NimlStream = NULL ; /* this will get checked next time */ fprintf(stderr,"Stream gone bad. Stream closed. \n"); } else if (nn == 0) { /* waiting, come back later */ fprintf(stderr,"Waiting on stream\n"); } else { /* if here, stream is good; see if there is any data to be read */ if (cs->NimlStream_flag & SUMA_FLAG_WAITING) { cs->NimlStream_flag = SUMA_FLAG_CONNECTED; fprintf(stderr, "++ NIML connection opened from %s on port %d \n", NI_stream_name(cs->NimlStream), cs->TCP_port) ; } nn = NI_stream_hasinput( cs->NimlStream , 1 ) ; if( nn > 0 ){ /* has data */ int ct = NI_clock_time() ; if (LocalHead) fprintf(stderr,"%s: reading data stream", FuncName) ; nini = NI_read_element( cs->NimlStream , 1 ) ; /* read it */ if (LocalHead) fprintf( stderr, " time=%d ms\n", NI_clock_time()-ct) ; ct = NI_clock_time() ; if( nini != NULL ) { nel = (NI_element *)nini ; if (LocalHead) { fprintf( stderr, "%s: name=%s vec_len=%d vec_filled=%d, vec_num=%d\n", FuncName, nel->name, nel->vec_len, nel->vec_filled, nel->vec_num ); } if (!Hallo_process_NIML_data( nini )) { fprintf(stderr, "Error %s: Failed in SUMA_process_NIML_data.\n", FuncName); } } NI_free_element( nini ) ; if (LocalHead) fprintf(stderr,"processing time=%d ms\n",NI_clock_time()-ct) ; } } /* Return flag for function calling the workprocess */ if (!cs->NimlStream) { return(1); /* Don't call workprocess back */ } else { return (0); /* Call workprocess back */ } }
int main( int argc , char *argv[] ) { int iarg , ct , do_GM=0 ; int do_T2=0 ; float T2_uperc=98.5f ; byte *T2_mask=NULL ; char *prefix = "Unifized" ; THD_3dim_dataset *inset=NULL , *outset=NULL ; MRI_IMAGE *imin , *imout ; float clfrac=0.2f ; AFNI_SETUP_OMP(0) ; /* 24 Jun 2013 */ if( argc < 2 || strcmp(argv[1],"-help") == 0 ){ printf("\n" "Usage: 3dUnifize [options] inputdataset\n\n" "* The input dataset is supposed to be a T1-weighted volume,\n" " possibly already skull-stripped (e.g., via 3dSkullStrip).\n" " ++ However, this program can be a useful step to take BEFORE\n" " 3dSkullStrip, since the latter program can fail if the input\n" " volume is strongly shaded -- 3dUnifize will (mostly) remove\n" " such shading artifacts.\n" "* The output dataset has the white matter (WM) intensity approximately\n" " uniformized across space, and scaled to peak at about 1000.\n" "* The output dataset is always stored in float format!\n" "* If the input dataset has more than 1 sub-brick, only sub-brick\n" " #0 will be processed!\n" "* Method: Obi-Wan's personal variant of Ziad's sneaky trick.\n" " (If you want to know what his trick is, you'll have to ask him, or\n" " read Obi-Wan's source code, which is a world of ecstasy and exaltation,\n" " or just read all the way to the end of this help output.)\n" "* The principal motive for this program is for use in an image\n" " registration script, and it may or may not be useful otherwise.\n" "\n" "--------\n" "Options:\n" "--------\n" " -prefix pp = Use 'pp' for prefix of output dataset.\n" " -input dd = Alternative way to specify input dataset.\n" " -T2 = Treat the input as if it were T2-weighted, rather than\n" " T1-weighted. This processing is done simply by inverting\n" " the image contrast, processing it as if that result were\n" " T1-weighted, and then re-inverting the results.\n" " ++ This option is NOT guaranteed to be useful for anything!\n" " ++ Of course, nothing in AFNI comes with a guarantee :-)\n" " ++ If you want to be REALLY sneaky, giving this option twice\n" " will skip the second inversion step, so the result will\n" " look like a T1-weighted volume (except at the edges and\n" " near blood vessels).\n" " ++ Might be useful for skull-stripping T2-weighted datasets.\n" " ++ Don't try the '-T2 -T2' trick on FLAIR-T2-weighted datasets.\n" " The results aren't pretty!\n" " -GM = Also scale to unifize 'gray matter' = lower intensity voxels\n" " (to aid in registering images from different scanners).\n" " ++ This option is recommended for use with 3dQwarp when\n" " aligning 2 T1-weighted volumes, in order to make the\n" " WM-GM contrast about the same for the datasets, even\n" " if they don't come from the same scanner/pulse-sequence.\n" " ++ Note that standardizing the contrasts with 3dUnifize will help\n" " 3dQwarp match the source dataset to the base dataset. If you\n" " later want the original source dataset to be warped, you can\n" " do so using the 3dNwarpApply program.\n" " -Urad rr = Sets the radius (in voxels) of the ball used for the sneaky trick.\n" " ++ Default value is %.1f, and should be changed proportionally\n" " if the dataset voxel size differs significantly from 1 mm.\n" " -ssave ss = Save the scale factor used at each voxel into a dataset 'ss'.\n" " ++ This is the white matter scale factor, and does not include\n" " the factor from the '-GM' option (if that was included).\n" " ++ The input dataset is multiplied by the '-ssave' image\n" " (voxel-wise) to get the WM-unifized image.\n" " ++ Another volume (with the same grid dimensions) could be\n" " scaled the same way using 3dcalc, if that is needed.\n" " -quiet = Don't print the fun fun fun progress messages (but whyyyy?).\n" " ++ For the curious, the codes used are:\n" " A = Automask\n" " D = Duplo down (process a half-size volume)\n" " V = Voxel-wise histograms to get local scale factors\n" " U = duplo Up (convert local scale factors to full-size volume)\n" " W = multiply by White matter factors\n" " G = multiply by Gray matter factors [cf the -GM option]\n" " I = contrast inversion [cf the -T2 option]\n" " ++ 'Duplo down' means to scale the input volume to be half the\n" " grid size in each direction for speed when computing the\n" " voxel-wise histograms. The sub-sampling is done using the\n" " median of the central voxel value and its 6 nearest neighbors.\n" "\n" "------------------------------------------\n" "Special options for Jedi AFNI Masters ONLY:\n" "------------------------------------------\n" " -rbt R b t = Specify the 3 parameters for the algorithm, as 3 numbers\n" " following the '-rbt':\n" " R = radius; same as given by option '-Urad' [default=%.1f]\n" " b = bottom percentile of normalizing data range [default=%.1f]\n" " r = top percentile of normalizing data range [default=%.1f]\n" "\n" " -T2up uu = Set the upper percentile point used for T2-T1 inversion.\n" " The default value is 98.5 (for no good reason), and 'uu' is\n" " allowed to be anything between 90 and 100 (inclusive).\n" " ++ The histogram of the data is built, and the uu-th percentile\n" " point value is called 'U'. The contrast inversion is simply\n" " given by output_value = max( 0 , U - input_value ).\n" "\n" " -clfrac cc = Set the automask 'clip level fraction' to 'cc', which\n" " must be a number between 0.1 and 0.9.\n" " A small 'cc' means to make the initial threshold\n" " for clipping (a la 3dClipLevel) smaller, which\n" " will tend to make the mask larger. [default=0.1]\n" " ++ [22 May 2013] The previous version of this program used a\n" " clip level fraction of 0.5, which proved to be too large\n" " for some users, who had images with very strong shading issues.\n" " Thus, the default value for this parameter was lowered to 0.1.\n" " ++ [24 May 2016] The default value for this parameter was\n" " raised to 0.2, since the lower value often left a lot of\n" " noise outside the head on non-3dSkullStrip-ed datasets.\n" " You can still manually set -clfrac to 0.1 if you need to\n" " correct for very large shading artifacts.\n" " ++ If the results of 3dUnifize have a lot of noise outside the head,\n" " then using '-clfrac 0.5' value will probably help.\n" #ifndef USE_ALL_VALS "\n" " -useall = The 'old' way of operating was to use all dataset values\n" " in the local WM histogram. The 'new' way [May 2016] is to\n" " only use positive values. If you want to use the 'old' way,\n" " then this option is what you want.\n" #endif "\n" "-- Feb 2013 - by Obi-Wan Unifobi\n" #ifdef USE_OMP "-- This code uses OpenMP to speed up the slowest part (voxel-wise histograms).\n" #endif , Uprad , Uprad , Upbot , Uptop ) ; printf("\n" "----------------------------------------------------------------------------\n" "HOW IT WORKS (Ziad's sneaky trick is revealed at last! And more.)\n" "----------------------------------------------------------------------------\n" "The basic idea is that white matter in T1-weighted images is reasonably\n" "uniform in intensity, at least when averaged over 'large-ish' regions.\n" "\n" "The first step is to create a local white matter intensity volume.\n" "Around each voxel (inside the volume 'automask'), the ball of values\n" "within a fixed radius (default=18.3 voxels) is extracted and these\n" "numbers are sorted. The values in the high-intensity range of the\n" "histogram (default=70%% to 80%%) are averaged. The result from this\n" "step is a smooth 3D map of the 'white matter intensity' (WMI).\n" "\n" " [The parameters of the above process can be altered with the '-rbt' option.]\n" " [For speed, the WMI map is produced on an image that is half-size in all ]\n" " [directions ('Duplo down'), and then is expanded back to the full-size ]\n" " [volume ('Duplo up'). The automask procedure can be somewhat controlled ]\n" " [via the '-clfrac' option. The default setting is designed to deal with ]\n" " [heavily shaded images, where the WMI varies by a factor of 5 or more over ]\n" " [the image volume. ]\n" "\n" "The second step is to scale the value at every voxel location x in the input\n" "volume by the factor 1000/WMI(x), so that the 'white matter intensity' is\n" "now uniform-ized to be 1000 everywhere. (This is Ziad's 'trick'; it is easy,\n" "works well, and doesn't require fitting some spatial model to the data: the\n" "data provides its own model.)\n" "\n" "If the '-GM' option is used, then this scaled volume is further processed\n" "to make the lower intensity values (presumably gray matter) have a contrast\n" "similar to that from a collection of 3 Tesla MP-RAGE images that were\n" "acquired at the NIH. (This procedure is not Ziad's fault, and should be\n" "blamed on the reclusive Obi-Wan Unifobi.)\n" "\n" "From the WM-uniform-ized volume, the median of all values larger than 1000\n" "is computed; call this value P. P-1000 represents the upward dispersion\n" "of the high-intensity (white matter) voxels in the volume. This value is\n" "'reflected' below 1000 to Q = 1000 - 2*(P-1000), and Q is taken to be the\n" "upper bound for gray matter voxel intensities. A lower bound for gray\n" "matter voxel values is estimated via the 'clip fraction' algorithm as\n" "implemented in program 3dClipLevel; call this lower bound R. The median\n" "of all values between R and Q is computed; call this value G, which is taken\n" "to be a 'typical' gray matter voxel instensity. Then the values z in the\n" "entire volume are linearly scaled by the formula\n" " z_out = (1000-666)/(1000-G) * (z_in-1000) + 1000\n" "so that the WM uniform-ized intensity of 1000 remains at 1000, and the gray\n" "matter median intensity of G is mapped to 666. (Values z_out that end up\n" "negative are set to 0; as a result, some of CSF might end up as 0.)\n" "The value 666 was chosen because it gave results visually comparable to\n" "various NIH-generated 3 Tesla T1-weighted datasets. (Any suggestions that\n" "this value was chosen for other reasons will be treated as 'beastly'.)\n" "\n" "To recap: the WM uniform-ization process provides a linear scaling factor\n" "that varies for each voxel ('local'), while the GM normalization process\n" "uses a global linear scaling. The GM process is optional, and is simply\n" "designed to make the various T1-weighted images look similar.\n" "\n" "-----** CAVEAT **-----\n" "This procedure was primarily developed to aid in 3D registration, especially\n" "when using 3dQwarp, so that the registration algorithms are trying to match\n" "images that are alike. It is *NOT* intended to be used for quantification\n" "purposes, such as Voxel Based Morphometry! That would better be done via\n" "the 3dSeg program, which is far more complicated.\n" "----------------------------------------------------------------------------\n" ) ; PRINT_COMPILE_DATE ; exit(0) ; } mainENTRY("3dUnifize main"); machdep(); AFNI_logger("3dUnifize",argc,argv); PRINT_VERSION("3dUnifize") ; ct = NI_clock_time() ; /*-- scan command line --*/ THD_automask_set_clipfrac(0.1f) ; /* 22 May 2013 */ THD_automask_extclip(1) ; /* 19 Dec 2014 */ iarg = 1 ; while( iarg < argc && argv[iarg][0] == '-' ){ if( strcmp(argv[iarg],"-clfrac") == 0 || strcmp(argv[iarg],"-mfrac") == 0 ){ /* 22 May 2013 */ if( ++iarg >= argc ) ERROR_exit("Need argument after '%s'",argv[iarg-1]) ; clfrac = (float)strtod( argv[iarg] , NULL ) ; if( clfrac < 0.1f || clfrac > 0.9f ) ERROR_exit("-clfrac value %f is illegal!",clfrac) ; THD_automask_set_clipfrac(clfrac) ; iarg++ ; continue ; } if( strcmp(argv[iarg],"-prefix") == 0 ){ if( ++iarg >= argc ) ERROR_exit("Need argument after '%s'",argv[iarg-1]) ; prefix = argv[iarg] ; if( !THD_filename_ok(prefix) ) ERROR_exit("Illegal value after -prefix!") ; iarg++ ; continue ; } if( strcmp(argv[iarg],"-ssave") == 0 ){ if( ++iarg >= argc ) ERROR_exit("Need argument after '%s'",argv[iarg-1]) ; sspref = strdup(argv[iarg]) ; if( !THD_filename_ok(sspref) ) ERROR_exit("Illegal value after -ssave!") ; iarg++ ; continue ; } if( strcmp(argv[iarg],"-input") == 0 || strcmp(argv[iarg],"-inset") == 0 ){ if( ++iarg >= argc ) ERROR_exit("Need argument after '%s'",argv[iarg-1]) ; if( inset != NULL ) ERROR_exit("Can't use '%s' twice" ,argv[iarg-1]) ; inset = THD_open_dataset( argv[iarg] ) ; CHECK_OPEN_ERROR(inset,argv[iarg]) ; iarg++ ; continue ; } if( strcmp(argv[iarg],"-Urad") == 0 ){ if( ++iarg >= argc ) ERROR_exit("Need argument after '%s'",argv[iarg-1]) ; Uprad = (float)strtod(argv[iarg],NULL) ; if( Uprad < 5.0f || Uprad > 40.0f ) ERROR_exit("Illegal value %f after option -Urad",Uprad) ; iarg++ ; continue ; } #ifndef USE_ALL_VALS if( strcmp(argv[iarg],"-useall") == 0 ){ /* 17 May 2016 */ USE_ALL_VALS = 1 ; iarg++ ; continue ; } #else if( strcmp(argv[iarg],"-useall") == 0 ){ WARNING_message("-useall option is disabled in this version") ; iarg++ ; continue ; } #endif if( strcmp(argv[iarg],"-param") == 0 || /*--- HIDDEN OPTION ---*/ strcmp(argv[iarg],"-rbt" ) == 0 ){ if( ++iarg >= argc-2 ) ERROR_exit("Need 3 arguments (R pb pt) after '%s'",argv[iarg-1]) ; Uprad = (float)strtod(argv[iarg++],NULL) ; Upbot = (float)strtod(argv[iarg++],NULL) ; Uptop = (float)strtod(argv[iarg++],NULL) ; if( Uprad < 5.0f || Uprad > 40.0f || Upbot < 30.0f || Upbot > 80.0f || Uptop <= Upbot || Uptop > 90.0f ) ERROR_exit("Illegal values (R pb pt) after '%s'",argv[iarg-4]) ; continue ; } if( strcasecmp(argv[iarg],"-GM") == 0 ){ do_GM++ ; iarg++ ; continue ; } if( strcasecmp(argv[iarg],"-T2") == 0 ){ /* 18 Dec 2014 */ do_T2++ ; iarg++ ; continue ; } if( strcasecmp(argv[iarg],"-T2up") == 0 ){ /* 18 Dec 2014 */ T2_uperc = (float)strtod( argv[++iarg] , NULL ) ; if( T2_uperc < 90.0f || T2_uperc > 100.0f ) ERROR_exit("-T2up value is out of range 90..100 :-(") ; iarg++ ; continue ; } if( strcasecmp(argv[iarg],"-quiet") == 0 ){ verb = 0 ; iarg++ ; continue ; } if( strcasecmp(argv[iarg],"-verb") == 0 ){ verb++ ; iarg++ ; continue ; } ERROR_exit("Unknown option: %s\n",argv[iarg]); } /* read input dataset, if not already there */ if( inset == NULL ){ if( iarg >= argc ) ERROR_exit("No dataset name on command line?\n") ; inset = THD_open_dataset( argv[iarg] ) ; CHECK_OPEN_ERROR(inset,argv[iarg]) ; } if( verb ) fprintf(stderr," + Pre-processing: ") ; /* load input from disk */ DSET_load( inset ) ; CHECK_LOAD_ERROR(inset) ; if( DSET_NVALS(inset) > 1 ) WARNING_message("Only processing sub-brick #0 (out of %d)",DSET_NVALS(inset)) ; /* make a float copy for processing */ imin = mri_to_float( DSET_BRICK(inset,0) ) ; DSET_unload(inset) ; if( imin == NULL ) ERROR_exit("Can't copy input dataset brick?!") ; #if 0 THD_cliplevel_search(imin) ; exit(0) ; /* experimentation only */ #endif THD_automask_set_clipfrac(clfrac) ; /* invert T2? */ if( do_T2 ){ if( verb ) fprintf(stderr,"I") ; T2_mask = mri_automask_image(imin) ; mri_invertcontrast_inplace( imin , T2_uperc , T2_mask ) ; } /* do the actual work */ imout = mri_WMunifize(imin) ; /* local WM scaling */ free(imin) ; if( sspref != NULL && sclim != NULL ){ /* 25 Jun 2013 */ STATUS("output -ssave") ; outset = EDIT_empty_copy( inset ) ; EDIT_dset_items( outset , ADN_prefix , sspref , ADN_nvals , 1 , ADN_ntt , 0 , ADN_none ) ; EDIT_substitute_brick( outset , 0 , MRI_float , MRI_FLOAT_PTR(sclim) ) ; tross_Copy_History( inset , outset ) ; tross_Make_History( "3dUnifize" , argc,argv , outset ) ; DSET_write(outset) ; outset = NULL ; } if( sclim != NULL ){ mri_free(sclim) ; sclim = NULL ; } if( imout == NULL ){ /* this is bad-ositiness */ if( verb ) fprintf(stderr,"\n") ; ERROR_exit("Can't compute Unifize-d dataset for some reason :-(") ; } if( do_GM ) mri_GMunifize(imout) ; /* global GM scaling */ if( do_T2 == 1 ){ /* re-invert T2? */ if( verb ) fprintf(stderr,"I") ; mri_invertcontrast_inplace( imout , T2_uperc , T2_mask ) ; } else if( do_T2 == 2 ){ /* don't re-invert, but clip off bright edges */ mri_clipedges_inplace( imout , PKVAL*1.111f , PKVAL*1.055f ) ; } if( verb ) fprintf(stderr,"\n") ; /* create output dataset, and write it into the historical record */ outset = EDIT_empty_copy( inset ) ; EDIT_dset_items( outset , ADN_prefix , prefix , ADN_nvals , 1 , ADN_ntt , 0 , ADN_none ) ; EDIT_substitute_brick( outset , 0 , MRI_float , MRI_FLOAT_PTR(imout) ) ; tross_Copy_History( inset , outset ) ; tross_Make_History( "3dUnifize" , argc,argv , outset ) ; DSET_write(outset) ; WROTE_DSET(outset) ; DSET_delete(outset) ; DSET_delete(inset) ; /* vamoose the ranch */ if( verb ){ double cput = COX_cpu_time() ; if( cput > 0.05 ) INFO_message("===== CPU time = %.1f sec Elapsed = %.1f\n", COX_cpu_time() , 0.001*(NI_clock_time()-ct) ) ; else INFO_message("===== Elapsed = %.1f sec\n", 0.001*(NI_clock_time()-ct) ) ; } exit(0) ; }
int main( int argc , char *argv[] ) { THD_3dim_dataset *dset , *oset=NULL , *tset=NULL ; int nvals , iv , nxyz , ii,jj,kk , iarg , kz,kzold ; float cut1=2.5,cut2=4.0 , sq2p,sfac , fq ; MRI_IMAGE *flim ; char *prefix="despike" , *tprefix=NULL ; int corder=-1 , nref , ignore=0 , polort=2 , nuse , nomask=0 ; int nspike, nbig, nproc ; float **ref ; float c21,ic21 , pspike,pbig ; short *sar , *qar ; byte *tar , *mask=NULL ; float *zar , *yar ; int datum ; int localedit=0 ; /* 04 Apr 2007 */ int verb=1 ; int do_NEW = 0 ; /* 29 Nov 2013 */ MRI_IMAGE *NEW_psinv=NULL ; int dilate = 4 ; /* 04 Dec 2013 */ int ctim = 0 ; /*----- Read command line -----*/ AFNI_SETUP_OMP(0) ; /* 24 Jun 2013 */ if( argc < 2 || strcmp(argv[1],"-help") == 0 ){ printf("Usage: 3dDespike [options] dataset\n" "Removes 'spikes' from the 3D+time input dataset and writes\n" "a new dataset with the spike values replaced by something\n" "more pleasing to the eye.\n" "\n" "Method:\n" " * L1 fit a smooth-ish curve to each voxel time series\n" " [see -corder option for description of the curve]\n" " [see -NEW option for a different & faster fitting method]\n" " * Compute the MAD of the difference between the curve and\n" " the data time series (the residuals).\n" " * Estimate the standard deviation 'sigma' of the residuals\n" " as sqrt(PI/2)*MAD.\n" " * For each voxel value, define s = (value-curve)/sigma.\n" " * Values with s > c1 are replaced with a value that yields\n" " a modified s' = c1+(c2-c1)*tanh((s-c1)/(c2-c1)).\n" " * c1 is the threshold value of s for a 'spike' [default c1=2.5].\n" " * c2 is the upper range of the allowed deviation from the curve:\n" " s=[c1..infinity) is mapped to s'=[c1..c2) [default c2=4].\n" "\n" "Options:\n" " -ignore I = Ignore the first I points in the time series:\n" " these values will just be copied to the\n" " output dataset [default I=0].\n" " -corder L = Set the curve fit order to L:\n" " the curve that is fit to voxel data v(t) is\n" "\n" " k=L [ (2*PI*k*t) (2*PI*k*t) ]\n" " f(t) = a+b*t+c*t*t + SUM [ d * sin(--------) + e * cos(--------) ]\n" " k=1 [ k ( T ) k ( T ) ]\n" "\n" " where T = duration of time series;\n" " the a,b,c,d,e parameters are chosen to minimize\n" " the sum over t of |v(t)-f(t)| (L1 regression);\n" " this type of fitting is is insensitive to large\n" " spikes in the data. The default value of L is\n" " NT/30, where NT = number of time points.\n" "\n" " -cut c1 c2 = Alter default values for the spike cut values\n" " [default c1=2.5, c2=4.0].\n" " -prefix pp = Save de-spiked dataset with prefix 'pp'\n" " [default pp='despike']\n" " -ssave ttt = Save 'spikiness' measure s for each voxel into a\n" " 3D+time dataset with prefix 'ttt' [default=no save]\n" " -nomask = Process all voxels\n" " [default=use a mask of high-intensity voxels, ]\n" " [as created via '3dAutomask -dilate 4 dataset'].\n" " -dilate nd = Dilate 'nd' times (as in 3dAutomask). The default\n" " value of 'nd' is 4.\n" " -q[uiet] = Don't print '++' informational messages.\n" "\n" " -localedit = Change the editing process to the following:\n" " If a voxel |s| value is >= c2, then replace\n" " the voxel value with the average of the two\n" " nearest non-spike (|s| < c2) values; the first\n" " one previous and the first one after.\n" " Note that the c1 cut value is not used here.\n" "\n" " -NEW = Use the 'new' method for computing the fit, which\n" " should be faster than the L1 method for long time\n" " series (200+ time points); however, the results\n" " are similar but NOT identical. [29 Nov 2013]\n" " * You can also make the program use the 'new'\n" " method by setting the environment variable\n" " AFNI_3dDespike_NEW\n" " to the value YES; as in\n" " setenv AFNI_3dDespike_NEW YES (csh)\n" " export AFNI_3dDespike_NEW=YES (bash)\n" " * If this variable is set to YES, you can turn off\n" " the '-NEW' processing by using the '-OLD' option.\n" " -->>* For time series more than 500 points long, the\n" " '-OLD' algorithm is tremendously slow. You should\n" " use the '-NEW' algorith in such cases.\n" " ** At some indeterminate point in the future, the '-NEW'\n" " method will become the default!\n" " -->>* As of 29 Sep 2016, '-NEW' is the default if there\n" " is more than 500 points in the time series dataset.\n" "\n" " -NEW25 = A slightly more aggressive despiking approach than\n" " the '-NEW' method.\n" "\n" "Caveats:\n" "* Despiking may interfere with image registration, since head\n" " movement may produce 'spikes' at the edge of the brain, and\n" " this information would be used in the registration process.\n" " This possibility has not been explored or calibrated.\n" "* [LATER] Actually, it seems like the registration problem\n" " does NOT happen, and in fact, despiking seems to help!\n" "* Check your data visually before and after despiking and\n" " registration!\n" " [Hint: open 2 AFNI controllers, and turn Time Lock on.]\n" ) ; PRINT_AFNI_OMP_USAGE("3dDespike",NULL) ; PRINT_COMPILE_DATE ; exit(0) ; } /** AFNI package setup and logging **/ mainENTRY("3dDespike main"); machdep(); AFNI_logger("3dDespike",argc,argv); PRINT_VERSION("3dDespike") ; AUTHOR("RW Cox") ; /** parse options **/ if( AFNI_yesenv("AFNI_3dDespike_NEW") ) do_NEW = 1 ; /* 29 Nov 2013 */ iarg = 1 ; while( iarg < argc && argv[iarg][0] == '-' ){ if( strncmp(argv[iarg],"-q",2) == 0 ){ /* 04 Apr 2007 */ verb = 0 ; iarg++ ; continue ; } if( strncmp(argv[iarg],"-v",2) == 0 ){ verb++ ; iarg++ ; continue ; } if( strcmp(argv[iarg],"-NEW") == 0 ){ /* 29 Nov 2013 */ do_NEW = 1 ; iarg++ ; continue ; } if( strcmp(argv[iarg],"-NEW25") == 0 ){ /* 29 Sep 2016 */ do_NEW = 1 ; use_des25 = 1 ; cut1 = 2.5f ; cut2 = 3.2f ; iarg++ ; continue ; } if( strcmp(argv[iarg],"-OLD") == 0 ){ do_NEW = 0 ; iarg++ ; continue ; } /** -localedit **/ if( strcmp(argv[iarg],"-localedit") == 0 ){ /* 04 Apr 2007 */ localedit = 1 ; iarg++ ; continue ; } /** don't use masking **/ if( strcmp(argv[iarg],"-nomask") == 0 ){ nomask = 1 ; iarg++ ; continue ; } /** dilation count [04 Dec 2013] **/ if( strcmp(argv[iarg],"-dilate") == 0 ){ dilate = (int)strtod(argv[++iarg],NULL) ; if( dilate <= 0 ) dilate = 1 ; else if( dilate > 99 ) dilate = 99 ; iarg++ ; continue ; } /** output dataset prefix **/ if( strcmp(argv[iarg],"-prefix") == 0 ){ prefix = argv[++iarg] ; if( !THD_filename_ok(prefix) ) ERROR_exit("-prefix is not good"); iarg++ ; continue ; } /** ratio dataset prefix **/ if( strcmp(argv[iarg],"-ssave") == 0 ){ tprefix = argv[++iarg] ; if( !THD_filename_ok(tprefix) ) ERROR_exit("-ssave prefix is not good"); iarg++ ; continue ; } /** trigonometric polynomial order **/ if( strcmp(argv[iarg],"-corder") == 0 ){ corder = strtol( argv[++iarg] , NULL , 10 ) ; if( corder < 0 ) ERROR_exit("Illegal value of -corder"); iarg++ ; continue ; } /** how much to ignore at start **/ if( strcmp(argv[iarg],"-ignore") == 0 ){ ignore = strtol( argv[++iarg] , NULL , 10 ) ; if( ignore < 0 ) ERROR_exit("Illegal value of -ignore"); iarg++ ; continue ; } /** thresholds for s ratio **/ if( strcmp(argv[iarg],"-cut") == 0 ){ cut1 = strtod( argv[++iarg] , NULL ) ; cut2 = strtod( argv[++iarg] , NULL ) ; if( cut1 < 1.0 || cut2 < cut1+0.5 ) ERROR_exit("Illegal values after -cut"); iarg++ ; continue ; } ERROR_exit("Unknown option: %s",argv[iarg]) ; } c21 = cut2-cut1 ; ic21 = 1.0/c21 ; /*----- read input dataset -----*/ if( iarg >= argc ) ERROR_exit("No input dataset!!??"); dset = THD_open_dataset( argv[iarg] ) ; CHECK_OPEN_ERROR(dset,argv[iarg]) ; datum = DSET_BRICK_TYPE(dset,0) ; if( (datum != MRI_short && datum != MRI_float) || !DSET_datum_constant(dset) ) ERROR_exit("Can't process non-short, non-float dataset!") ; if( verb ) INFO_message("Input data type = %s\n",MRI_TYPE_name[datum]) ; nvals = DSET_NUM_TIMES(dset) ; nuse = nvals - ignore ; if( nuse < 15 ) ERROR_exit("Can't use dataset with < 15 time points per voxel!") ; if( nuse > 500 && !do_NEW ){ INFO_message("Switching to '-NEW' method since number of time points = %d > 500",nuse) ; do_NEW = 1 ; } if( use_des25 && nuse < 99 ) use_des25 = 0 ; if( verb ) INFO_message("ignoring first %d time points, using last %d",ignore,nuse); if( corder > 0 && 4*corder+2 > nuse ){ ERROR_exit("-corder %d is too big for NT=%d",corder,nvals) ; } else if( corder < 0 ){ corder = rint(nuse/30.0) ; if( corder > 50 && !do_NEW ) corder = 50 ; if( verb ) INFO_message("using %d time points => -corder %d",nuse,corder) ; } else { if( verb ) INFO_message("-corder %d set from command line",corder) ; } nxyz = DSET_NVOX(dset) ; if( verb ) INFO_message("Loading dataset %s",argv[iarg]) ; DSET_load(dset) ; CHECK_LOAD_ERROR(dset) ; /*-- create automask --*/ if( !nomask ){ mask = THD_automask( dset ) ; if( verb ){ ii = THD_countmask( DSET_NVOX(dset) , mask ) ; INFO_message("%d voxels in the automask [out of %d in dataset]",ii,DSET_NVOX(dset)) ; } for( ii=0 ; ii < dilate ; ii++ ) THD_mask_dilate( DSET_NX(dset), DSET_NY(dset), DSET_NZ(dset), mask, 3 ) ; if( verb ){ ii = THD_countmask( DSET_NVOX(dset) , mask ) ; INFO_message("%d voxels in the dilated automask [out of %d in dataset]",ii,DSET_NVOX(dset)) ; } } else { if( verb ) INFO_message("processing all %d voxels in dataset",DSET_NVOX(dset)) ; } /*-- create empty despiked dataset --*/ oset = EDIT_empty_copy( dset ) ; EDIT_dset_items( oset , ADN_prefix , prefix , ADN_brick_fac , NULL , ADN_datum_all , datum , ADN_none ) ; if( THD_deathcon() && THD_is_file(DSET_HEADNAME(oset)) ) ERROR_exit("output dataset already exists: %s",DSET_HEADNAME(oset)); tross_Copy_History( oset , dset ) ; tross_Make_History( "3dDespike" , argc , argv , oset ) ; /* create bricks (will be filled with zeros) */ for( iv=0 ; iv < nvals ; iv++ ) EDIT_substitute_brick( oset , iv , datum , NULL ) ; /* copy the ignored bricks */ switch( datum ){ case MRI_short: for( iv=0 ; iv < ignore ; iv++ ){ sar = DSET_ARRAY(oset,iv) ; qar = DSET_ARRAY(dset,iv) ; memcpy( sar , qar , DSET_BRICK_BYTES(dset,iv) ) ; DSET_unload_one(dset,iv) ; } break ; case MRI_float: for( iv=0 ; iv < ignore ; iv++ ){ zar = DSET_ARRAY(oset,iv) ; yar = DSET_ARRAY(dset,iv) ; memcpy( zar , yar , DSET_BRICK_BYTES(dset,iv) ) ; DSET_unload_one(dset,iv) ; } break ; } /*-- setup to save a threshold statistic dataset, if desired --*/ if( tprefix != NULL ){ float *fac ; tset = EDIT_empty_copy( dset ) ; fac = (float *) malloc( sizeof(float) * nvals ) ; for( ii=0 ; ii < nvals ; ii++ ) fac[ii] = TFAC ; EDIT_dset_items( tset , ADN_prefix , tprefix , ADN_brick_fac , fac , ADN_datum_all , MRI_byte , ADN_func_type , FUNC_FIM_TYPE , ADN_none ) ; free(fac) ; tross_Copy_History( tset , dset ) ; tross_Make_History( "3dDespike" , argc , argv , tset ) ; #if 0 if( THD_is_file(DSET_HEADNAME(tset)) ) ERROR_exit("-ssave dataset already exists"); #endif tross_Copy_History( tset , dset ) ; tross_Make_History( "3dDespike" , argc , argv , tset ) ; for( iv=0 ; iv < nvals ; iv++ ) EDIT_substitute_brick( tset , iv , MRI_byte , NULL ) ; } /*-- setup to find spikes --*/ sq2p = sqrt(0.5*PI) ; sfac = sq2p / 1.4826f ; /* make ref functions */ nref = 2*corder+3 ; ref = (float **) malloc( sizeof(float *) * nref ) ; for( jj=0 ; jj < nref ; jj++ ) ref[jj] = (float *) malloc( sizeof(float) * nuse ) ; /* r(t) = 1 */ for( iv=0 ; iv < nuse ; iv++ ) ref[0][iv] = 1.0 ; jj = 1 ; /* r(t) = t - tmid */ { float tm = 0.5 * (nuse-1.0) ; float fac = 2.0 / nuse ; for( iv=0 ; iv < nuse ; iv++ ) ref[1][iv] = (iv-tm)*fac ; jj = 2 ; /* r(t) = (t-tmid)**jj */ for( ; jj <= polort ; jj++ ) for( iv=0 ; iv < nuse ; iv++ ) ref[jj][iv] = pow( (iv-tm)*fac , (double)jj ) ; } for( kk=1 ; kk <= corder ; kk++ ){ fq = (2.0*PI*kk)/nuse ; /* r(t) = sin(2*PI*k*t/N) */ for( iv=0 ; iv < nuse ; iv++ ) ref[jj][iv] = sin(fq*iv) ; jj++ ; /* r(t) = cos(2*PI*k*t/N) */ for( iv=0 ; iv < nuse ; iv++ ) ref[jj][iv] = cos(fq*iv) ; jj++ ; } /****** setup for the NEW solution method [29 Nov 2013] ******/ if( do_NEW ){ NEW_psinv = DES_get_psinv(nuse,nref,ref) ; INFO_message("Procesing time series with NEW model fit algorithm") ; } else { INFO_message("Procesing time series with OLD model fit algorithm") ; } /*--- loop over voxels and do work ---*/ #define Laplace_t2p(val) ( 1.0 - nifti_stat2cdf( (val), 15, 0.0, 1.4427 , 0.0 ) ) if( verb ){ if( !localedit ){ INFO_message("smash edit thresholds: %.1f .. %.1f MADs",cut1*sq2p,cut2*sq2p) ; ININFO_message(" [ %.3f%% .. %.3f%% of normal distribution]", 200.0*qg(cut1*sfac) , 200.0*qg(cut2*sfac) ) ; ININFO_message(" [ %.3f%% .. %.3f%% of Laplace distribution]" , 100.0*Laplace_t2p(cut1) , 100.0*Laplace_t2p(cut2) ) ; } else { INFO_message("local edit threshold: %.1f MADS",cut2*sq2p) ; ININFO_message(" [ %.3f%% of normal distribution]", 200.0*qg(cut2*sfac) ) ; ININFO_message(" [ %.3f%% of Laplace distribution]", 100.0*Laplace_t2p(cut1) ) ; } INFO_message("%d slices to process",DSET_NZ(dset)) ; } kzold = -1 ; nspike = 0 ; nbig = 0 ; nproc = 0 ; ctim = NI_clock_time() ; AFNI_OMP_START ; #pragma omp parallel if( nxyz > 6666 ) { int ii , iv , iu , id , jj ; float *far , *dar , *var , *fitar , *ssp , *fit , *zar ; short *sar , *qar ; byte *tar ; float fsig , fq , cls , snew , val ; float *NEW_wks=NULL ; #pragma omp critical (DESPIKE_malloc) { far = (float *) malloc( sizeof(float) * nvals ) ; dar = (float *) malloc( sizeof(float) * nvals ) ; var = (float *) malloc( sizeof(float) * nvals ) ; fitar = (float *) malloc( sizeof(float) * nvals ) ; ssp = (float *) malloc( sizeof(float) * nvals ) ; fit = (float *) malloc( sizeof(float) * nref ) ; if( do_NEW ) NEW_wks = (float *)malloc(sizeof(float)*DES_workspace_size(nuse,nref)) ; } #ifdef USE_OMP INFO_message("start OpenMP thread #%d",omp_get_thread_num()) ; #endif #pragma omp for for( ii=0 ; ii < nxyz ; ii++ ){ /* ii = voxel index */ if( mask != NULL && mask[ii] == 0 ) continue ; /* skip this voxel */ #ifndef USE_OMP kz = DSET_index_to_kz(dset,ii) ; /* starting a new slice */ if( kz != kzold ){ if( verb ){ fprintf(stderr, "++ start slice %2d",kz ) ; if( nproc > 0 ){ pspike = (100.0*nspike)/nproc ; pbig = (100.0*nbig )/nproc ; fprintf(stderr, "; so far %d data points, %d edits [%.3f%%], %d big edits [%.3f%%]", nproc,nspike,pspike,nbig,pbig ) ; } fprintf(stderr,"\n") ; } kzold = kz ; } #else if( verb && ii % 2345 == 1234 ) fprintf(stderr,".") ; #endif /*** extract ii-th time series into far[] ***/ switch( datum ){ case MRI_short: for( iv=0 ; iv < nuse ; iv++ ){ qar = DSET_ARRAY(dset,iv+ignore) ; /* skip ignored data */ far[iv] = (float)qar[ii] ; } break ; case MRI_float: for( iv=0 ; iv < nuse ; iv++ ){ zar = DSET_ARRAY(dset,iv+ignore) ; far[iv] = zar[ii] ; } break ; } AAmemcpy(dar,far,sizeof(float)*nuse) ; /* copy time series into dar[] */ /*** solve for L1 fit ***/ if( do_NEW ) cls = DES_solve( NEW_psinv , far , fit , NEW_wks ) ; /* 29 Nov 2013 */ else cls = cl1_solve( nuse , nref , far , ref , fit,0 ) ; /* the slow part */ if( cls < 0.0f ){ /* fit failed! */ #if 0 fprintf(stderr,"curve fit fails at voxel %d %d %d\n", DSET_index_to_ix(dset,ii) , DSET_index_to_jy(dset,ii) , DSET_index_to_kz(dset,ii) ) ; #endif continue ; /* skip this voxel */ } for( iv=0 ; iv < nuse ; iv++ ){ /* detrend */ val = fit[0] + fit[1]*ref[1][iv] /* quadratic part of curve fit */ + fit[2]*ref[2][iv] ; for( jj=3 ; jj < nref ; jj++ ) /* rest of curve fit */ val += fit[jj] * ref[jj][iv] ; fitar[iv] = val ; /* save curve fit value */ var[iv] = dar[iv]-val ; /* remove fitted value = resid */ far[iv] = fabsf(var[iv]) ; /* abs value of resid */ } /*** compute estimate standard deviation of detrended data ***/ fsig = sq2p * qmed_float(nuse,far) ; /* also mangles far array */ /*** process time series for spikes, editing data in dar[] ***/ if( fsig > 0.0f ){ /* data wasn't fit perfectly */ /* find spikiness for each point in time */ fq = 1.0f / fsig ; for( iv=0 ; iv < nuse ; iv++ ){ ssp[iv] = fq * var[iv] ; /* spikiness s = how many sigma out */ } /* save spikiness in -ssave datset */ if( tset != NULL ){ for( iv=0 ; iv < nuse ; iv++ ){ tar = DSET_ARRAY(tset,iv+ignore) ; snew = ITFAC*fabsf(ssp[iv]) ; /* scale for byte storage */ tar[ii] = BYTEIZE(snew) ; /* cf. mrilib.h */ } } /* process values of |s| > cut1, editing dar[] */ for( iv=0 ; iv < nuse ; iv++ ){ /* loop over time points */ if( !localedit ){ /** classic 'smash' edit **/ if( ssp[iv] > cut1 ){ snew = cut1 + c21*mytanh((ssp[iv]-cut1)*ic21) ; /* edit s down */ dar[iv] = fitar[iv] + snew*fsig ; #pragma omp critical (DESPIKE_counter) { nspike++ ; if( ssp[iv] > cut2 ) nbig++ ; } } else if( ssp[iv] < -cut1 ){ snew = -cut1 + c21*mytanh((ssp[iv]+cut1)*ic21) ; /* edit s up */ dar[iv] = fitar[iv] + snew*fsig ; #pragma omp critical (DESPIKE_counter) { nspike++ ; if( ssp[iv] < -cut2 ) nbig++ ; } } } else { /** local edit: 04 Apr 2007 **/ if( ssp[iv] >= cut2 || ssp[iv] <= -cut2 ){ for( iu=iv+1 ; iu < nuse ; iu++ ) /* find non-spike above */ if( ssp[iu] < cut2 && ssp[iu] > -cut2 ) break ; for( id=iv-1 ; id >= 0 ; id-- ) /* find non-spike below */ if( ssp[id] < cut2 && ssp[id] > -cut2 ) break ; switch( (id>=0) + 2*(iu<nuse) ){ /* compute replacement val */ case 3: val = 0.5*(dar[iu]+dar[id]); break; /* iu and id OK */ case 2: val = dar[iu] ; break; /* only iu OK */ case 1: val = dar[id] ; break; /* only id OK */ default: val = fitar[iv] ; break; /* shouldn't be */ } dar[iv] = val ; #pragma omp critical (DESPIKE_counter) { nspike++ ; nbig++ ; } } } } /* end of loop over time points */ #pragma omp atomic nproc += nuse ; /* number data points processed */ } /* end of processing time series when fsig is positive */ /* put dar[] time series (possibly edited above) into output bricks */ switch( datum ){ case MRI_short: for( iv=0 ; iv < nuse ; iv++ ){ sar = DSET_ARRAY(oset,iv+ignore) ; /* output brick */ sar[ii] = (short)dar[iv] ; /* original or mutated data */ } break ; case MRI_float: for( iv=0 ; iv < nuse ; iv++ ){ zar = DSET_ARRAY(oset,iv+ignore) ; /* output brick */ zar[ii] = dar[iv] ; /* original or mutated data */ } break ; } } /* end of loop over voxels #ii */ #pragma omp critical (DESPIKE_malloc) { free(fit); free(ssp); free(fitar); free(var); free(dar); free(far); if( do_NEW ) free(NEW_wks) ; } } /* end OpenMP */ AFNI_OMP_END ; #ifdef USE_OMP if( verb ) fprintf(stderr,"\n") ; #endif ctim = NI_clock_time() - ctim ; INFO_message( "Elapsed despike time = %s" , nice_time_string(ctim) ) ; if( ctim > 345678 && !do_NEW ) ININFO_message("That was SLOW -- try the '-NEW' option for a speedup") ; #ifdef USE_OMP if( verb ) fprintf(stderr,"\n") ; #endif /*--- finish up ---*/ if( do_NEW ) mri_free(NEW_psinv) ; DSET_delete(dset) ; /* delete input dataset */ if( verb ){ if( nproc > 0 ){ pspike = (100.0*nspike)/nproc ; pbig = (100.0*nbig )/nproc ; INFO_message("FINAL: %d data points, %d edits [%.3f%%], %d big edits [%.3f%%]", nproc,nspike,pspike,nbig,pbig ) ; } else { INFO_message("FINAL: no good voxels found to process!!??") ; } } /* write results */ DSET_write(oset) ; if( verb ) WROTE_DSET(oset) ; DSET_delete(oset) ; if( tset != NULL ){ DSET_write(tset) ; if( verb ) WROTE_DSET(tset) ; DSET_delete(tset) ; } exit( THD_get_write_error_count() ) ; }
int main( int argc , char *argv[] ) { NI_stream ns ; char lbuf[NBUF] , *reop=NULL ; int nn , nl=0 , jj , ntot=0 , ct , ech , iarg=1 ; if( argc < 2 || strcmp(argv[1],"-help") == 0 ){ printf("Usage: nicat [-reopen rr] [-rR] streamspec\n" "Copies stdin to the NIML stream, which will be opened\n" "for writing.\n" "\n" "-reopen rr == reopen the stream after connection\n" " to the stream specified by 'rr'\n" "-r == Copy the stream to stdout instead; the\n" " 'streamspec' will be opened for reading.\n" "-R == Read the stream but don't copy to stdout.\n" "\n" "Intended for testing other programs that use NIML for\n" "various services. Example:\n" " aiv -p 4444 &\n" " im2niml zork.jpg | nicat tcp:localhost:4444\n" "Starts aiv listening on TCP/IP port 444, then sends image\n" "file zork.jpg to that port for display.\n" ) ; exit(0) ; } if( strcmp(argv[iarg],"-reopen") == 0 ){ reop = argv[++iarg] ; iarg++ ; } /** write stdin to the stream **/ if( toupper(argv[iarg][1]) != 'R' ){ ns = NI_stream_open( argv[iarg] , "w" ) ; if( ns == NULL ){ fprintf(stderr,"** NI_stream_open fails\n") ; exit(1) ; } while(1){ nn = NI_stream_writecheck( ns , 400 ) ; if( nn == 1 ){ fprintf(stderr,"!\n") ; break ; } if( nn < 0 ){ fprintf(stderr,"BAD\n"); exit(1) ; } fprintf(stderr,"-") ; } if( reop ){ /* 23 Aug 2002 */ fprintf(stderr,"++ reopening") ; nn = NI_stream_reopen( ns , reop ) ; if( nn == 0 ) fprintf(stderr,".BAD") ; else fprintf(stderr,".GOOD") ; fprintf(stderr,"\n") ; } ct = NI_clock_time() ; while(1){ jj = fread(lbuf,1,NBUF,stdin) ; if( jj <= 0 ) break ; nn = NI_stream_write( ns , lbuf , jj ) ; if( nn < 0 ){ fprintf(stderr,"** NI_stream_write fails\n"); break ; } else if( nn < jj ){ fprintf(stderr,"++ nl=%d: wrote %d/%d bytes\n",nl,nn,jj) ; } nl++ ; ntot += jj ; } NI_sleep(1) ; NI_stream_close(ns) ; ct = NI_clock_time()-ct ; fprintf(stderr,"++ Wrote %d bytes in %d ms: %.3f Mbytes/s\n", ntot,ct,(9.5367e-7*ntot)/(1.e-3*ct) ) ; sleep(1) ; exit(0) ; } /** write the stream to stdout */ if( argc < iarg+2 ){ fprintf(stderr,"** %s needs argv[%d]\n",argv[1],iarg+1); exit(1); } ech = (argv[iarg][1] == 'r') ; ns = NI_stream_open( argv[iarg+1], (argv[iarg][2]=='\0') ? "r" : "w" ) ; if( ns == NULL ){ fprintf(stderr,"** NI_stream_open fails\n") ; exit(1) ; } while(1){ nn = NI_stream_readcheck( ns , 400 ) ; if( nn == 1 ){ fprintf(stderr,"!\n") ; break ; } if( nn < 0 ){ fprintf(stderr,"BAD\n"); exit(1) ; } fprintf(stderr,"-") ; } if( reop ){ /* 23 Aug 2002 */ fprintf(stderr,"++ reopening") ; nn = NI_stream_reopen( ns , reop ) ; if( nn == 0 ) fprintf(stderr,".BAD") ; else fprintf(stderr,".GOOD") ; fprintf(stderr,"\n") ; } ct = NI_clock_time() ; while(1){ nn = NI_stream_read( ns , lbuf , NBUF ) ; if( nn < 0 ){ fprintf(stderr,"\nNI_stream_read fails\n"); break; } else { ntot += nn ; } if( ech && nn > 0 ){ printf("%.*s",nn,lbuf) ; nl++ ; } } ct = NI_clock_time()-ct ; fprintf(stderr,"Read %d bytes in %d ms: %.3f Mbytes/s\n", ntot,ct,(9.5367e-7*ntot)/(1.e-3*ct) ) ; sleep(1) ; exit(0) ; }
int main( int argc , char *argv[] ) { char *drive_afni[128] ; int ndrive=0 , iarg=1 ; char host[1024]="localhost", nsname[2048], *geomstr, *cpt, temp[32] ; int dt=1000 , ctold,ctnew , ctzero ; int verbose=0 , kk,nn , nvox,nval , do_accum=0 ; THD_3dim_dataset *dset ; NI_element *nel ; MRI_IMAGE *fim ; float *far ; char *targname="niml_feedme" ; /*-- help the ignorant user --*/ if( argc < 2 || strcmp(argv[1],"-help") == 0 ){ printf( "Usage: niml_feedme [options] dataset\n" "\n" "* Sends volumes from the dataset to AFNI via the NIML socket interface.\n" "* You must run AFNI with the command 'afni -niml' so that the program\n" " will be listening for the socket connection.\n" "* Inside AFNI, the transmitted dataset will be named 'niml_feedme'.\n" "* For another way to send image data to AFNI, see progam rtfeedme.\n" "* At present, there is no way to attach statistical parameters to\n" " a transmitted volume.\n" "* This program sends all volumes in float format, simply because\n" " that's easy for me. But you can also send byte, short, and\n" " complex valued volumes.\n" "* This program is really just a demo; it has little practical use.\n" "\n" "OPTIONS:\n" " -host sname = Send data, via TCP/IP, to AFNI running on the\n" " computer system 'sname'. By default, uses the\n" " current system (localhost), if you don't use this\n" " option.\n" "\n" " -dt ms = Tries to maintain an inter-transmit interval of 'ms'\n" " milliseconds. The default is 1000 msec per volume.\n" "\n" " -verb = Be (very) talkative about actions.\n" "\n" " -accum = Send sub-bricks so that they accumulate in AFNI.\n" " The default is to create only a 1 volume dataset\n" " inside AFNI, and each sub-brick just replaces\n" " that one volume when it is received.\n" "\n" " -target nam = Change the dataset name transmitted to AFNI from\n" " 'niml_feedme' to 'nam'.\n" "\n" " -drive cmd = Send 'cmd' as a DRIVE_AFNI command.\n" " * If cmd contains blanks, it must be in 'quotes'.\n" " * Multiple -drive options may be used.\n" " * These commands will be sent to AFNI just after\n" " the first volume is transmitted.\n" " * See file README.driver for a list of commands.\n" "\n" "EXAMPLE: Send volumes from a 3D+time dataset to AFNI:\n" "\n" " niml_feedme -dt 1000 -verb -accum -target Elvis \\\n" " -drive 'OPEN_WINDOW axialimage' \\\n" " -drive 'OPEN_WINDOW axialgraph' \\\n" " -drive 'SWITCH_UNDERLAY Elvis' \\\n" " timeseries+orig\n" "\n" "Author: RW Cox -- July 2009\n" ) ; PRINT_COMPILE_DATE ; exit(0) ; } mainENTRY("niml_feedme") ; /*-- scan arguments --*/ while( iarg < argc && argv[iarg][0] == '-' ){ if( strncmp(argv[iarg],"-target",5) == 0 ){ targname = strdup(argv[++iarg]) ; iarg++ ; continue ; } if( strcmp(argv[iarg],"-drive") == 0 ){ drive_afni[ndrive++] = argv[++iarg] ; iarg++ ; continue ; } if( strcmp(argv[iarg],"-host") == 0 ){ strcpy( host , argv[++iarg] ) ; iarg++ ; continue ; } if( strcmp(argv[iarg],"-dt") == 0 ){ dt = (int)strtod(argv[++iarg],NULL) ; if( dt < 9 ) dt = 9 ; iarg++ ; continue ; } if( strncmp(argv[iarg],"-verbose",4) == 0 ){ verbose = 1 ; iarg++ ; continue ; } if( strncmp(argv[iarg],"-accum",4) == 0 ){ do_accum = 1 ; iarg++ ; continue ; } ERROR_exit("Unrecognized option: %s",argv[iarg]) ; } if( iarg >= argc ) ERROR_exit("No dataset on command line?!") ; /*-- read in the dataset --*/ dset = THD_open_dataset( argv[iarg] ) ; if( dset == NULL ) ERROR_exit("Can't open dataset '%s'",argv[iarg]) ; DSET_load(dset) ; if( !DSET_LOADED(dset) ) ERROR_exit("Can't load dataset '%s'",argv[iarg]) ; cpt = EDIT_get_geometry_string(dset) ; geomstr = strdup(cpt) ; /* describes geometry of dataset grid */ if( verbose ) INFO_message("geometry string = '%s'",geomstr) ; nvox = DSET_NVOX(dset); /* number of voxels in dataset */ nval = DSET_NVALS(dset); /* number of sub-bricks in dataset */ /*-- this stuff is one-time-only setup of the I/O to AFNI --*/ atexit(NF_exit) ; /* call this when program ends */ signal(SIGINT ,NF_sigfunc) ; /* setup signal handler */ signal(SIGBUS ,NF_sigfunc) ; /* for fatal errors */ signal(SIGSEGV,NF_sigfunc) ; signal(SIGTERM,NF_sigfunc) ; /* name of NIML stream (socket) to open */ sprintf( nsname , "tcp:%s:%d" , host , get_port_named("AFNI_DEFAULT_LISTEN_NIML")); /* open the socket (i.e., dial the telephone call) */ fprintf(stderr,"opening NIML stream '%s' ",nsname) ; NF_stream = NI_stream_open( nsname , "w" ) ; /* loop until AFNI connects (answers the call), printing a '.' every 1/2 second to keep the user happy */ while(1){ kk = NI_stream_writecheck( NF_stream , 500 ) ; if( kk == 1 ){ fprintf(stderr," connected!\n") ; break ; } if( kk < 0 ){ fprintf(stderr," ** connection fails **\n") ; exit(1) ; } fprintf(stderr,".") ; } /*-- Create VOLUME_DATA NIML element to hold the brick data --*/ nel = NI_new_data_element( "VOLUME_DATA" , nvox ) ; /* add attributes to the element to help AFNI construct the dataset */ /* define the grid of the dataset */ NI_set_attribute( nel , "geometry_string" , geomstr ) ; /* define the name of the dataset */ NI_set_attribute( nel , "target_name" , targname ) ; /* all sub-bricks in the input dataset will be sent to be sub-brick #0 in the dataset inside AFNI -- if you don't want this behavior, and want the dataset inside AFNI to keep growing, then don't set this attribute! */ if( !do_accum ) NI_set_attribute( nel , "index" , "0" ) ; /* +tlrc view? [default in AFNI is +orig view] */ if( dset->view_type == VIEW_TALAIRACH_TYPE ) NI_set_attribute( nel , "view" , "tlrc" ) ; /**-- loop over sub-bricks and send them to AFNI --*/ ctzero = NI_clock_time() ; /* for later reference */ if( verbose ) INFO_message("Starting sub-brick loop") ; for( kk=0 ; kk < nval ; kk++ ){ ctold = NI_clock_time() ; /* clock time at start of work (ms) */ /* get a float copy of the kk-th sub-brick */ fim = THD_extract_float_brick( kk , dset ) ; DSET_unload_one(dset,kk) ; /* unload this sub-brick now */ if( fim == NULL ){ /* should never happen */ ERROR_message("Can't get sub-brick #%d?? -- skipping",kk) ; NI_sleep(dt) ; continue ; } /* copy the float data into the NIML element for transmission */ far = MRI_FLOAT_PTR(fim) ; if( kk == 0 ) /* first time: create data column in element */ NI_add_column( nel , NI_FLOAT , far ) ; else /* later times: overwrite nel data column */ memcpy( nel->vec[0] , far , sizeof(float)*nvox ) ; mri_free(fim) ; /* done with this now [data all copied to nel] */ /* set sub-brick index in AFNI if doing accumulation */ if( do_accum ){ sprintf(temp,"%d",kk) ; NI_set_attribute( nel , "index" , temp ) ; } /*** send the data element to AFNI ***/ nn = NI_write_element( NF_stream , nel , NI_BINARY_MODE ) ; /* if something bad happened in the transmission, report it */ if( nn <= 0 ){ ERROR_message("Can't write sub-brick #%d to AFNI!",kk) ; break ; } /*** first time through ==> do the '-drive' commands now by sending processing instructions ***/ if( kk == 0 && ndrive > 0 ){ int ii ; NI_procins *npi ; if( verbose ) ININFO_message("Sending %d 'drive_afni' elements now",ndrive) ; npi = NI_new_processing_instruction( "DRIVE_AFNI" ) ; NI_sleep(1) ; /* give AFNI a msec to digest the data */ for( ii=0 ; ii < ndrive ; ii++ ){ NI_set_attribute( npi , "cmd" , drive_afni[ii] ) ; (void)NI_write_element( NF_stream , npi , NI_TEXT_MODE ) ; } NI_free_element(npi) ; /* delete this struct from the world! */ } ctnew = NI_clock_time() ; /* clock time now */ if( verbose ) ININFO_message("Sent %d bytes for sub-brick #%d in %d ms", nn , kk , ctnew-ctold ) ; NI_sleep( dt - (ctnew-ctold) ) ; /* sleep so that time delay is right */ } /* end of loop over sub-bricks */ /** summarize, do some cleanup, and exit stage left **/ if( verbose && kk > 0 ){ float dtav = (NI_clock_time()-ctzero) / (float)kk ; INFO_message("Transmission finished: %.1f ms = average time per volume",dtav) ; } NI_free_element(nel) ; /* destroy the data element */ DSET_delete(dset) ; /* destroy the dataset */ exit(0) ; }