/***************************************************************************** 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 "mrilib.h" /*** NOT 7D SAFE ***/ /**************************************************************************/ /* mode = -1 for forward transform = +1 for inverse (including scaling) taper = fraction of data to taper (0 to 1) */ void mri_fft_complex( int mode , float taper , MRI_IMAGE *im ) { float *rbuf , *ibuf , *xtap , *ytap ; complex *cxim ; int ii , jj , npix , jbase , nx,ny ; if( im->kind != MRI_complex ){ fprintf( stderr , "mri_fft_complex only works on complex images!\n" ) ; MRI_FATAL_ERROR ; } if( ! MRI_IS_2D(im) ){ fprintf(stderr,"mri_fft_complex only works on 2D images!\n") ; MRI_FATAL_ERROR ; } /*** set up buffers ***/ npix = im->nx * im->ny ; /* number of pixels */ rbuf = (float *)malloc( sizeof(float) * npix ) ; /* real and imag buffs */ ibuf = (float *)malloc( sizeof(float) * npix ) ; cxim = mri_data_pointer( im ) ; /* easy access to im */ for( ii=0 ; ii < npix ; ii++ ){ rbuf[ii] = cxim[ii].r ; ibuf[ii] = cxim[ii].i ; } /*** taper buffers, if desired ***/ if( taper > 0.0 && taper <= 1.0 ){ nx = im->nx ; ny = im->ny ; xtap = mri_setup_taper( nx , taper ) ; /*** printf( "taper" ) ; for( ii=0 ; ii < nx ; ii++ ){ if( (ii%5) == 0 ) printf("\n") ; printf( "%12.4e " , xtap[ii] ) ; } printf("\n") ; ***/ if( nx == ny ) ytap = xtap ; else ytap = mri_setup_taper( ny , taper ) ; for( jj=0 ; jj < ny ; jj++ ){ jbase = jj * nx ; for( ii=0 ; ii < nx ; ii++ ){ rbuf[ii] *= xtap[ii] * ytap[jj] ; ibuf[ii] *= xtap[ii] * ytap[jj] ; } } free( xtap ) ; if( ytap != xtap ) free(ytap) ; } /*** FFT buffers and copy them back to original image ***/ cfft2d_cox( mode , im->nx , im->ny , rbuf,ibuf ) ; for( ii=0 ; ii < npix ; ii++ ){ cxim[ii].r = rbuf[ii] ; cxim[ii].i = ibuf[ii] ; } return ; } /***********************************************************************/ float *mri_setup_taper( int nx , float taper ) { register int ii ; int ntap ; float *tap ; float phi ; tap = (float *)malloc( sizeof(float) * nx ) ; /* make array */ for( ii=0 ; ii < nx ; ii++ ) tap[ii] = 1.0 ; /* default 1's */ ntap = (int) (nx * 0.5 * taper ) ; /* # pts on each end */ if( ntap == 0 ){ /* special case of no points: taper is tiny */ tap[0] = tap[nx-1] = 0.5 ; return tap ; } phi = PI / ntap ; for( ii=0 ; ii < ntap ; ii++ ){ tap[ii] = 0.54 - 0.46 * cos( ii*phi ) ; tap[nx-1-ii] = tap[ii] ; } return tap ; } /*----------------------------------------------------------------------------*/ /* macro to alternate signs in workspace array */ #undef ALTERN #define ALTERN(nn) \ do{ register int qq; \ for( qq=1; qq<(nn); qq+=2 ){ cbig[qq].r=-cbig[qq].r; cbig[qq].i=-cbig[qq].i; } \ } while(0) /******* Note that thd_ballcorr.c has OpenMP compatible versions of these 3D FFT functions, which could make your life better (or at least faster). ********/ /*----------------------------------------------------------------------------*/ /* FFT lengths are in Lxx, Lyy, Lzz; however, Lxx = 0 ==> no FFT in that direction (etc.). *//*--------------------------------------------------------------------------*/ MRI_IMAGE * mri_fft_3D( int Sign, MRI_IMAGE *inim, int Lxx,int Lyy,int Lzz, int alt ) { MRI_IMAGE *outim ; int ii,jj,kk , nx,ny,nxy,nz , nbig , fx,fy,fz,fxy , joff,koff ; complex *cbig , *car , *far ; if( inim->kind != MRI_complex ) return NULL ; /* input data and its dimensions */ car = MRI_COMPLEX_PTR(inim) ; nx = inim->nx ; ny = inim->ny ; nz = inim->nz ; nxy = nx*ny ; /* output dimensions and data */ fx = (Lxx == 0) ? nx : (Lxx > nx) ? csfft_nextup_one35(Lxx) : csfft_nextup_one35(nx); fy = (Lyy == 0) ? ny : (Lyy > ny) ? csfft_nextup_one35(Lyy) : csfft_nextup_one35(ny); fz = (Lzz == 0) ? nz : (Lzz > nz) ? csfft_nextup_one35(Lzz) : csfft_nextup_one35(nz); fxy = fx*fy ; outim = mri_new_vol( fx,fy,fz , MRI_complex ) ; /* zero filled */ far = MRI_COMPLEX_PTR(outim) ; /* buffer space */ nbig = MAX(fx,fy) ; nbig = MAX(nbig,fz) ; nbig = 4*nbig + 512 ; cbig = (complex *)malloc(sizeof(complex)*nbig) ; /* copy input data into output image */ for( kk=0 ; kk < nz ; kk++ ) for( jj=0 ; jj < ny ; jj++ ) memcpy( far + jj*fx + kk*fxy, car + jj*nx + kk*nxy, sizeof(complex)*nx ); /* x-direction FFTs */ if( Lxx > 1 ){ for( kk=0 ; kk < fz ; kk++ ){ koff = kk*fxy ; for( jj=0 ; jj < fy ; jj++ ){ joff = koff + jj*fx ; for( ii=0 ; ii < fx ; ii++ ) cbig[ii] = far[ii+joff] ; if( alt > 0 ) ALTERN(fx) ; csfft_cox( Sign , fx , cbig ) ; if( alt < 0 ) ALTERN(fx) ; for( ii=0 ; ii < fx ; ii++ ) far[ii+joff] = cbig[ii] ; } } } /* y-direction FFTs */ if( Lyy > 1 ){ for( kk=0 ; kk < fz ; kk++ ){ koff = kk*fxy ; for( ii=0 ; ii < fx ; ii++ ){ joff = koff + ii ; for( jj=0 ; jj < fy ; jj++ ) cbig[jj] = far[jj*fx+joff] ; /* copy data */ if( alt > 0 ) ALTERN(fy) ; csfft_cox( Sign , fy , cbig ) ; /* FFT in buffer */ if( alt < 0 ) ALTERN(fy) ; for( jj=0 ; jj < fy ; jj++ ) far[jj*fx+joff] = cbig[jj] ; /* copy back */ } } } /* z-direction FFTs */ if( Lzz > 1 ){ for( jj=0 ; jj < fy ; jj++ ){ joff = jj*fx ; for( ii=0 ; ii < fx ; ii++ ){ koff = joff + ii ; for( kk=0 ; kk < fz ; kk++ ) cbig[kk] = far[kk*fxy+koff] ; if( alt > 0 ) ALTERN(fz) ; csfft_cox( Sign , fz , cbig ) ; if( alt < 0 ) ALTERN(fz) ; for( kk=0 ; kk < fz ; kk++ ) far[kk*fxy+koff] = cbig[kk] ; } } } free(cbig) ; MRI_COPY_AUX(outim,inim) ; return outim ; } /*----------------------------------------------------------------------------*/ /* Convolve (via FFT) image aim with bim. [Sep 2020] Note output image will be at least as big as than the sum of the two sizes. *//*--------------------------------------------------------------------------*/ MRI_IMAGE * mri_fft_3Dconvolve( MRI_IMAGE *aim , MRI_IMAGE *bim ) { MRI_IMAGE *outim=NULL ; MRI_IMAGE *paim , *pbim , *faim , *fbim ; int nxa,nya,nza , nxb,nyb,nzb , Lxx,Lyy,Lzz , Lxyz,ii ; complex ac , bc , qc ; complex *acar , *bcar ; float linv ; if( aim == NULL || bim == NULL ) return NULL ; /* input dimensions */ nxa = aim->nx ; nya = aim->ny ; nza = aim->nz ; nxb = bim->nx ; nyb = bim->ny ; nzb = bim->nz ; /* FFT and output dimensions (sum, bumped up for FFT efficiency) */ Lxx = (nxa > 1 && nxb > 1) ? csfft_nextup_one35(nxa+nxb) : 0 ; Lyy = (nya > 1 && nyb > 1) ? csfft_nextup_one35(nya+nyb) : 0 ; Lzz = (nza > 1 && nzb > 1) ? csfft_nextup_one35(nza+nzb) : 0 ; /* at this time, we don't allow for convolving a 3D image with a 1D or 2D image, for example, which is possible but more complicated */ if( Lxx == 0 || Lyy == 0 || Lzz == 0 ) return NULL ; /* 1) convert A image to complex 2) zero pad it to fit the FFT size 3) FFT that Then repeat these steps for the B image */ faim = mri_to_complex( aim ) ; /* 1) */ paim = mri_zeropad_3D( 0,Lxx-nxa , 0,Lyy-nya , 0,Lzz-nza , faim ) ; /* 2) */ mri_free(faim) ; faim = mri_fft_3D( -1 , paim , Lxx,Lyy,Lzz , 0 ) ; /* 3) */ mri_free(paim) ; acar = MRI_COMPLEX_PTR(faim) ; fbim = mri_to_complex( bim ) ; /* 1) */ pbim = mri_zeropad_3D( 0,Lxx-nxb , 0,Lyy-nyb , 0,Lzz-nzb , fbim ) ; /* 2) */ mri_free(fbim) ; fbim = mri_fft_3D( -1 , pbim , Lxx,Lyy,Lzz , 0 ) ; /* 3) */ mri_free(pbim) ; bcar = MRI_COMPLEX_PTR(fbim) ; /* multiply+scale FFTs, store back in faim/acar */ Lxyz = Lxx * Lyy * Lzz ; linv = 10.f / (float)Lxyz ; /* scaling for inverse FFT */ for( ii=0 ; ii < Lxyz ; ii++ ){ ac = acar[ii] ; bc = bcar[ii] ; qc.r = (ac.r * bc.r - ac.i * bc.i) * linv ; /* complex */ qc.i = (ac.r * bc.i + ac.i * bc.r) * linv ; /* multiply */ acar[ii] = qc ; } mri_free(fbim) ; /* inverse FFT back to 'real' space */ fbim = mri_fft_3D( +1 , faim , Lxx,Lyy,Lzz , 0 ) ; mri_free(faim) ; /* convert to float-valued image (from complex FFT) and return */ outim = mri_complex_to_real( fbim ) ; mri_free(fbim) ; return outim ; }