/* * $Id: bsplines.c 4624 2012-01-13 13:27:08Z john $ * John Ashburner */ /* * This code is a modified version of that of Philippe Thevenaz, which I took from: * http://bigwww.epfl.ch/algorithms.html * * It has been substantially modified, so blame me (John Ashburner) if there * are any bugs. Many thanks to Philippe Thevenaz for advice with the code. * * See: * M. Unser, A. Aldroubi and M. Eden. * "B-Spline Signal Processing: Part I-Theory," * IEEE Transactions on Signal Processing 41(2):821-832 (1993). * * M. Unser, A. Aldroubi and M. Eden. * "B-Spline Signal Processing: Part II-Efficient Design and Applications," * IEEE Transactions on Signal Processing 41(2):834-848 (1993). * * M. Unser. * "Splines: A Perfect Fit for Signal and Image Processing," * IEEE Signal Processing Magazine 16(6):22-38 (1999). * */ #include "mex.h" #include #include #ifdef IMAGE_SINGLE #define IMAGE_DTYPE float #else #define IMAGE_DTYPE double #endif /*************************************************************************************** Starting periodic boundary condition based on Eq. 2.6 of Unser's 2nd 1993 paper. c - vector of unfiltered data m - length of c p - pole (root of polynomial) function returns value that c[0] should initially take The expression for the first pass of the recursive convolution is: for (i=1; im) m1=m; pi = p; s = c[0]; for (i=1; i 1) p - pole function returns value for c[m-1] before 2nd filter pass The expression for the second pass of the recursive convolution is: for (i=m-2; i>=0; i--) c[i] = p*(c[i+1]-c[i]); If m==4, then: c3 = p*(c0-c3); c2 = p*(c3-c2); c1 = p*(c2-c1); c0 = p*(c1-c0); c3 = p*(c0-c3); etc... After recursive substitution, c0 becomes: -(p +p^5+p^9 ...)*c3 -(p^2+p^6+p^10 ...)*c0 -(p^3+p^7+p^11 ...)*c1 -(p^4+p^8+p^12 ...)*c2 These series converge to... (p^n)/(p^m-1) So c0 becomes: (c3*p + c0*p^2 + c1*p^3 + c2*p^4)/(p^4-1) */ static double icc_wrap(IMAGE_DTYPE c[], int m, double p) { double s, pi; int i, m1; m1 = ceil(-30/log(fabs(p))); if (m1>m) m1=m; pi = p; s = pi*c[m-1]; for (i=0; i 1) p - pole function returns value for c[m-1] before 2nd filter pass */ static double icc_mirror(IMAGE_DTYPE c[],int m, double p) { return((p/(p*p-1.0))*(p*c[m-2]+c[m-1])); } /*************************************************************************************** Compute gains required for zero-pole representation - see tf2zp.m in Matlab's Signal Processing Toolbox. p - poles np - number of poles function returns the gain of the system */ static double gain(double p[], int np) { int j; double lambda = 1.0; for (j = 0; j < np; j++) lambda = lambda*(1.0-p[j])*(1.0-1.0/p[j]); return(lambda); } /*************************************************************************************** One dimensional recursive filtering - assuming wrapped boundaries See Eq. 2.5 of Unsers 2nd 1993 paper. c - original vector on input, coefficients on output m - length of vector p - poles (polynomial roots) np - number of poles */ void splinc_wrap(IMAGE_DTYPE c[], int m, double p[], int np) { double lambda = 1.0; int i, k; if (m == 1) return; /* compute gain and apply it */ lambda = gain(p,np); for (i = 0; i < m; i++) c[i] *= lambda; /* loop over poles */ for (k = 0; k < np; k++) { double pp = p[k]; c[0] = cc_wrap(c, m, pp); for (i=1; i=0; i--) c[i] = pp*(c[i+1]-c[i]); } } /*************************************************************************************** One dimensional recursive filtering - assuming mirror boundaries See Eq. 2.5 of Unsers 2nd 1993 paper. c - original vector on input, coefficients on output m - length of vector p - poles (polynomial roots) np - number of poles */ void splinc_mirror(IMAGE_DTYPE c[], int m, double p[], int np) { double lambda = 1.0; int i, k; if (m == 1) return; /* compute gain and apply it */ lambda = gain(p,np); for (i = 0; i < m; i++) c[i] *= lambda; /* loop over poles */ for (k = 0; k < np; k++) { double pp = p[k]; c[0] = cc_mirror(c, m, pp); for (i=1; i=0; i--) c[i] = pp*(c[i+1]-c[i]); } } /*************************************************************************************** Return roots of B-spline kernels. d - degree of B-spline np - number of roots of magnitude less than one p - roots. */ int get_poles(int d, int *np, double p[]) { /* Return polynomial roots that are less than one. */ switch (d) { case 0: *np = 0; break; case 1: *np = 0; break; case 2: /* roots([1 6 1]) */ *np = 1; p[0] = sqrt(8.0) - 3.0; break; case 3: /* roots([1 4 1]) */ *np = 1; p[0] = sqrt(3.0) - 2.0; break; case 4: /* roots([1 76 230 76 1]) */ *np = 2; p[0] = sqrt(664.0 - sqrt(438976.0)) + sqrt(304.0) - 19.0; p[1] = sqrt(664.0 + sqrt(438976.0)) - sqrt(304.0) - 19.0; break; case 5: /* roots([1 26 66 26 1]) */ *np = 2; p[0] = sqrt(67.5 - sqrt(4436.25)) + sqrt(26.25) - 6.5; p[1] = sqrt(67.5 + sqrt(4436.25)) - sqrt(26.25) - 6.5; break; case 6: /* roots([1 722 10543 23548 10543 722 1]) */ *np = 3; p[0] = -0.488294589303044755130118038883789062112279161239377608394; p[1] = -0.081679271076237512597937765737059080653379610398148178525368; p[2] = -0.00141415180832581775108724397655859252786416905534669851652709; break; case 7: /* roots([1 120 1191 2416 1191 120 1]) */ *np = 3; p[0] = -0.5352804307964381655424037816816460718339231523426924148812; p[1] = -0.122554615192326690515272264359357343605486549427295558490763; p[2] = -0.0091486948096082769285930216516478534156925639545994482648003; break; default: return(1); } return(0); } /***************************************************************************************/ /*************************************************************************************** Different degrees of B-splines x - position relative to origin returns value of basis function at x */ /*static double wt1(double x) { x = fabs(x); return((x > 1.0) ? (0.0) : (1.0 - x)); }*/ static double wt2(double x) { x = fabs(x); if (x < 0.5) return(0.75 - x*x); if (x < 1.5) { x = 1.5 - x; return(0.5*x*x); } return(0.0); } static double wt3(double x) { x = fabs(x); if (x < 1.0) return(x*x*(x - 2.0)*0.5 + 2.0/3.0); if (x < 2.0) { x = 2.0 - x; return(x*x*x*(1.0/6.0)); } return(0.0); } static double wt4(double x) { x = fabs(x); if (x < 0.5) { x *= x; return(x*(x*0.25 - 0.625) + 115.0/192.0); } if (x < 1.5) return(x*(x*(x*(5.0/6.0 - x*(1.0/6.0)) - 1.25) + 5.0/24.0) + 55.0/96.0); if (x < 2.5) { x -= 2.5; x *= x; return(x*x*(1.0/24.0)); } return(0.0); } static double wt5(double x) { double y; x = fabs(x); if (x < 1.0) { y = x*x; return(y*(y*(0.25 - x*(1.0/12.0)) - 0.5) + 0.55); } if (x < 2.0) return(x*(x*(x*(x*(x*(1.0/24.0) - 0.375) + 1.25) - 1.75) + 0.625) + 0.425); if (x < 3.0) { y = 3.0 - x; x = y*y; return(y*x*x*(1.0/120.0)); } return(0.0); } static double wt6(double x) { x = fabs(x); if (x < 0.5) { x *= x; return(x*(x*(7.0/48.0 - x*(1.0/36.0)) - 77.0/192.0) + 5887.0/11520.0); } if (x < 1.5) return(x*(x*(x*(x*(x*(x*(1.0/48.0) - 7.0/48.0) + 0.328125) - 35.0/288.0) - 91.0/256.0) - 7.0/768.0) + 7861.0/15360.0); if (x < 2.5) return(x*(x*(x*(x*(x*(7.0/60.0 - x*(1.0/120.0)) - 0.65625) + 133.0/72.0) - 2.5703125) + 1267.0/960.0) + 1379.0/7680.0); if (x < 3.5) { x -= 3.5; x *= x*x; return(x*x*(1.0/720.0)); } return(0.0); } static double wt7(double x) { double y; x = fabs(x); if (x < 1.0) { y = x*x; return(y*(y*(y*(x*(1.0/144.0) - 1.0/36.0) + 1.0/9.0) - 1.0/3.0) + 151.0/315.0); } if (x < 2.0) return(x*(x*(x*(x*(x*(x*(0.05 - x*(1.0/240.0)) - 7.0/30.0) + 0.5) - 7.0/18.0) - 0.1) - 7.0/90.0) + 103.0/210.0); if (x < 3.0) return(x*(x*(x*(x*(x*(x*(x*(1.0/720.0) - 1.0/36.0) + 7.0/30.0) - 19.0/18.0) + 49.0/18.0) - 23.0/6.0) + 217.0/90.0) - 139.0/630.0); if (x < 4.0) { y = 4.0 - x; x = y*y*y; return(x*x*y*(1.0/5040.0)); } return(0.0); } /*************************************************************************************** Derivatives of different degrees of B-splines x - position relative to origin returns derivative of basis function at x */ static double dwt2(double x) { int s; s = (x>0 ? 1 : -1); x = fabs(x); if (x < 0.5) return(-2*x*s); if (x < 1.5) return((x - 1.5)*s); return(0.0); } static double dwt3(double x) { int s; s = (x>0 ? 1 : -1); x = fabs(x); if (x < 1.0) return(x*(1.5*x - 2.0)*s); if (x < 2.0) { x = x - 2.0; return(-0.5*x*x*s); } return(0.0); } static double dwt4(double x) { int s; s = (x>0 ? 1 : -1); x = fabs(x); if (x < 0.5) { return((x*(x*x - 5.0/4.0))*s); } if (x < 1.5) return((x*(x*(x*(-2.0/3.0) + 2.5) - 5.0/2.0) + 5.0/24.0)*s); if (x < 2.5) { x = x*2.0 - 5.0; return((1.0/48.0)*x*x*x*s); } return(0.0); } static double dwt5(double x) { int s; s = (x>0 ? 1 : -1); x = fabs(x); if (x < 1.0) return((x*(x*(x*(x*(-5.0/12.0) + 1.0)) - 1.0))*s); if (x < 2.0) return((x*(x*(x*(x*(5.0/24.0) - 1.5) + 3.75) - 3.5) + 0.625)*s); if (x < 3.0) { x -= 3.0; x *= x; return((-1.0/24.0)*x*x*s); } return(0.0); } static double dwt6(double x) { double y; int s; s = (x>0 ? 1 : -1); x = fabs(x); if (x < 0.5) { y = x*x; return(x*((7.0/12.0)*y - (1.0/6.0)*y*y - (77.0/96.0))*s); } if (x < 1.5) return((x*(x*(x*(x*(x*0.125 - 35.0/48.0) + 1.3125) - 35.0/96.0) - 0.7109375) - 7.0/768.0)*s); if (x < 2.5) return((x*(x*(x*(x*(x*(-1.0/20.0) + 7.0/12.0) - 2.625) + 133.0/24.0) - 5.140625) + 1267.0/960.0)*s); if (x < 3.5) { x *= 2.0; x -= 7.0; y = x*x; return((1.0/3840.0)*y*y*x*s); } return(0.0); } static double dwt7(double x) { double y; int s; s = (x>0 ? 1 : -1); x = fabs(x); if (x < 1.0) { y = x*x; return(x*(y*(y*(x*(7.0/144.0) - 1.0/6.0) + 4.0/9.0) - 2.0/3.0)*s); } if (x < 2.0) return((x*(x*(x*(x*(x*(x*(-7.0/240.0) + 3.0/10.0) - 7.0/6.0) + 2.0) - 7.0/6.0) - 1.0/5.0) - 7.0/90.0)*s); if (x < 3.0) return((x*(x*(x*(x*(x*(x*(7.0/720.0) - 1.0/6.0) + 7.0/6.0) -38.0/9.0) + 49.0/6.0) - 23.0/3.0) + 217.0/90.0)*s); if (x < 4.0) { x -= 4; x *= x*x; x *= x; return((-1.0/720.0)*x*s); } return(0.0); } /*************************************************************************************** Generate B-spline basis functions d - degree of spline x - position relative to centre i - pointer to first voxel position in convolution w - vector of spline values Should really combine this function with wt2 to wt7 for most efficiency (as for case 0). Note that 0th degree B-spline returns nearest neighbour basis. */ static void weights(int d, double x, int *i, double w[]) { int k; *i = floor(x-(d-1)*0.5); x -= *i; switch (d){ case 2: for(k=0; k<=2; k++) w[k] = wt2(x-k); break; case 3: for(k=0; k<=3; k++) w[k] = wt3(x-k); break; case 4: for(k=0; k<=4; k++) w[k] = wt4(x-k); break; case 5: for(k=0; k<=5; k++) w[k] = wt5(x-k); break; case 6: for(k=0; k<=6; k++) w[k] = wt6(x-k); break; case 7: for(k=0; k<=7; k++) w[k] = wt7(x-k); break; case 1: w[0] = 1.0-x; w[1] = x; break; case 0: w[0] = 1.0; /* Not correct at discontinuities */ break; default: for(k=0; k<=7; k++) w[k] = wt7(x-k); } } /*************************************************************************************** Generate derivatives of B-spline basis functions d - degree of spline x - position relative to centre i - pointer to first voxel position in convolution w - vector of spline values Should really combine this function with dwt2 to dwt7 for most efficiency (as for case 0 and case 1). Note that 0th and 1st degree B-spline return derivatives of nearest neighbour and linear interpolation bases. */ static void dweights(int d, double x, int *i, double w[]) { int k; *i = floor(x-(d-1)*0.5); x -= *i; switch (d){ case 2: for(k=0; k<=2; k++) w[k] = dwt2(x-k); break; case 3: for(k=0; k<=3; k++) w[k] = dwt3(x-k); break; case 4: for(k=0; k<=4; k++) w[k] = dwt4(x-k); break; case 5: for(k=0; k<=5; k++) w[k] = dwt5(x-k); break; case 6: for(k=0; k<=6; k++) w[k] = dwt6(x-k); break; case 7: for(k=0; k<=7; k++) w[k] = dwt7(x-k); break; case 1: w[0] = -1.0; /* Not correct at discontinuities */ w[1] = 1.0; /* Not correct at discontinuities */ break; case 0: w[0] = 0.0; /* Not correct at discontinuities */ break; default: for(k=0; k<=7; k++) w[k] = dwt7(x-k); } } /*************************************************************************************** Work out what to do with positions outside the FOV i - Co-ordinate (0<=i