/* * $Id: shoot_invdef.c 4875 2012-08-30 20:04:30Z john $ * John Ashburner */ /* Invert a deformation field. The field is assumed to consist of a piecewise affine transformations, whereby each cube jointing 8 neighbouring voxels contains 8 tetrahedra. The mapping within each tetrahedron is assumed to be affine. More documentation can be found in the appendix of: J. Ashburner, J. Andersson and K. J. Friston (2000). "Image Registration using a Symmetric Prior - in Three-Dimensions". Human Brain Mapping 9(4):212-225. */ #include #include "mex.h" #define MAXV 16384 #define REAL double static void invertX(REAL X[4][3], REAL IX[4][4]) /* X is a matrix containing the co-ordinates of the four vertices of a tetrahedron. IX = inv([X ; 1 1 1 1]); */ { REAL id; id = X[0][0]*(X[3][1]*(X[1][2]-X[2][2])+X[1][1]*(X[2][2]-X[3][2])+X[2][1]*(X[3][2]-X[1][2]))+ X[1][0]*(X[3][2]*(X[0][1]-X[2][1])+X[0][2]*(X[2][1]-X[3][1])+X[2][2]*(X[3][1]-X[0][1]))+ X[2][0]*(X[0][1]*(X[1][2]-X[3][2])+X[3][1]*(X[0][2]-X[1][2])+X[1][1]*(X[3][2]-X[0][2]))+ X[3][0]*(X[1][2]*(X[2][1]-X[0][1])+X[0][2]*(X[1][1]-X[2][1])+X[2][2]*(X[0][1]-X[1][1])); id = 1/id; IX[0][0] = id*(X[1][1]*(X[2][2]-X[3][2])+X[2][1]*(X[3][2]-X[1][2])+X[3][1]*(X[1][2]-X[2][2])); IX[0][1] = id*(X[0][1]*(X[3][2]-X[2][2])+X[2][1]*(X[0][2]-X[3][2])+X[3][1]*(X[2][2]-X[0][2])); IX[0][2] = id*(X[0][1]*(X[1][2]-X[3][2])+X[1][1]*(X[3][2]-X[0][2])+X[3][1]*(X[0][2]-X[1][2])); IX[0][3] = id*(X[0][1]*(X[2][2]-X[1][2])+X[1][1]*(X[0][2]-X[2][2])+X[2][1]*(X[1][2]-X[0][2])); IX[1][0] = id*(X[1][0]*(X[3][2]-X[2][2])+X[2][0]*(X[1][2]-X[3][2])+X[3][0]*(X[2][2]-X[1][2])); IX[1][1] = id*(X[0][0]*(X[2][2]-X[3][2])+X[2][0]*(X[3][2]-X[0][2])+X[3][0]*(X[0][2]-X[2][2])); IX[1][2] = id*(X[0][0]*(X[3][2]-X[1][2])+X[1][0]*(X[0][2]-X[3][2])+X[3][0]*(X[1][2]-X[0][2])); IX[1][3] = id*(X[0][0]*(X[1][2]-X[2][2])+X[1][0]*(X[2][2]-X[0][2])+X[2][0]*(X[0][2]-X[1][2])); IX[2][0] = id*(X[1][0]*(X[2][1]-X[3][1])+X[2][0]*(X[3][1]-X[1][1])+X[3][0]*(X[1][1]-X[2][1])); IX[2][1] = id*(X[0][0]*(X[3][1]-X[2][1])+X[2][0]*(X[0][1]-X[3][1])+X[3][0]*(X[2][1]-X[0][1])); IX[2][2] = id*(X[0][0]*(X[1][1]-X[3][1])+X[1][0]*(X[3][1]-X[0][1])+X[3][0]*(X[0][1]-X[1][1])); IX[2][3] = id*(X[0][0]*(X[2][1]-X[1][1])+X[1][0]*(X[0][1]-X[2][1])+X[2][0]*(X[1][1]-X[0][1])); IX[3][0] = id*(X[1][0]*(X[2][2]*X[3][1]-X[3][2]*X[2][1])+ X[2][0]*(X[3][2]*X[1][1]-X[1][2]*X[3][1])+ X[3][0]*(X[1][2]*X[2][1]-X[2][2]*X[1][1])); IX[3][1] = id*(X[0][0]*(X[3][2]*X[2][1]-X[2][2]*X[3][1])+ X[2][0]*(X[0][2]*X[3][1]-X[3][2]*X[0][1])+ X[3][0]*(X[2][2]*X[0][1]-X[0][2]*X[2][1])); IX[3][2] = id*(X[0][0]*(X[1][2]*X[3][1]-X[3][2]*X[1][1])+ X[1][0]*(X[3][2]*X[0][1]-X[0][2]*X[3][1])+ X[3][0]*(X[0][2]*X[1][1]-X[1][2]*X[0][1])); IX[3][3] = id*(X[0][0]*(X[2][2]*X[1][1]-X[1][2]*X[2][1])+ X[1][0]*(X[0][2]*X[2][1]-X[2][2]*X[0][1])+ X[2][0]*(X[1][2]*X[0][1]-X[0][2]*X[1][1])); } static void getM(REAL Y[4][3], REAL IX[4][4], REAL M[4][3], int i, int j, int k) /* Determines the affine transform (M) mapping from [X+repmat([i j k]', 1,4) ; 1 1 1 1] to [Y ; 1 1 1 1], where IX = inv([X ; 1 1 1 1]); This is given by: M = Y*inv([X+repmat([i j k]', 1,4) ; 1 1 1 1]); or more efficiently by: M = Y*(IX - IX(:,1:3)*[i j k]'); */ { REAL ix30, ix31, ix32, ix33; ix30 = IX[3][0] - (i*IX[0][0] + j*IX[1][0] + k*IX[2][0]); ix31 = IX[3][1] - (i*IX[0][1] + j*IX[1][1] + k*IX[2][1]); ix32 = IX[3][2] - (i*IX[0][2] + j*IX[1][2] + k*IX[2][2]); ix33 = IX[3][3] - (i*IX[0][3] + j*IX[1][3] + k*IX[2][3]); M[0][0] = Y[0][0]*IX[0][0] + Y[1][0]*IX[0][1] + Y[2][0]*IX[0][2] + Y[3][0]*IX[0][3]; M[0][1] = Y[0][1]*IX[0][0] + Y[1][1]*IX[0][1] + Y[2][1]*IX[0][2] + Y[3][1]*IX[0][3]; M[0][2] = Y[0][2]*IX[0][0] + Y[1][2]*IX[0][1] + Y[2][2]*IX[0][2] + Y[3][2]*IX[0][3]; M[1][0] = Y[0][0]*IX[1][0] + Y[1][0]*IX[1][1] + Y[2][0]*IX[1][2] + Y[3][0]*IX[1][3]; M[1][1] = Y[0][1]*IX[1][0] + Y[1][1]*IX[1][1] + Y[2][1]*IX[1][2] + Y[3][1]*IX[1][3]; M[1][2] = Y[0][2]*IX[1][0] + Y[1][2]*IX[1][1] + Y[2][2]*IX[1][2] + Y[3][2]*IX[1][3]; M[2][0] = Y[0][0]*IX[2][0] + Y[1][0]*IX[2][1] + Y[2][0]*IX[2][2] + Y[3][0]*IX[2][3]; M[2][1] = Y[0][1]*IX[2][0] + Y[1][1]*IX[2][1] + Y[2][1]*IX[2][2] + Y[3][1]*IX[2][3]; M[2][2] = Y[0][2]*IX[2][0] + Y[1][2]*IX[2][1] + Y[2][2]*IX[2][2] + Y[3][2]*IX[2][3]; M[3][0] = Y[0][0]*ix30 + Y[1][0]*ix31 + Y[2][0]*ix32 + Y[3][0]*ix33; M[3][1] = Y[0][1]*ix30 + Y[1][1]*ix31 + Y[2][1]*ix32 + Y[3][1]*ix33; M[3][2] = Y[0][2]*ix30 + Y[1][2]*ix31 + Y[2][2]*ix32 + Y[3][2]*ix33; } static void mulMM(REAL A[4][3], REAL B[4][3], REAL C[4][3]) /* [A ; 0 0 0 1] = [B ; 0 0 0 1]*[C ; 0 0 0 1]; */ { A[0][0] = B[0][0]*C[0][0] + B[1][0]*C[0][1] + B[2][0]*C[0][2]; A[0][1] = B[0][1]*C[0][0] + B[1][1]*C[0][1] + B[2][1]*C[0][2]; A[0][2] = B[0][2]*C[0][0] + B[1][2]*C[0][1] + B[2][2]*C[0][2]; A[1][0] = B[0][0]*C[1][0] + B[1][0]*C[1][1] + B[2][0]*C[1][2]; A[1][1] = B[0][1]*C[1][0] + B[1][1]*C[1][1] + B[2][1]*C[1][2]; A[1][2] = B[0][2]*C[1][0] + B[1][2]*C[1][1] + B[2][2]*C[1][2]; A[2][0] = B[0][0]*C[2][0] + B[1][0]*C[2][1] + B[2][0]*C[2][2]; A[2][1] = B[0][1]*C[2][0] + B[1][1]*C[2][1] + B[2][1]*C[2][2]; A[2][2] = B[0][2]*C[2][0] + B[1][2]*C[2][1] + B[2][2]*C[2][2]; A[3][0] = B[0][0]*C[3][0] + B[1][0]*C[3][1] + B[2][0]*C[3][2] + B[3][0]; A[3][1] = B[0][1]*C[3][0] + B[1][1]*C[3][1] + B[2][1]*C[3][2] + B[3][1]; A[3][2] = B[0][2]*C[3][0] + B[1][2]*C[3][1] + B[2][2]*C[3][2] + B[3][2]; } static void mulMX(REAL A[4][3], REAL B[4][3], REAL C[4][3]) /* [A ; 1 1 1 1] = [B ; 0 0 0 1]*[C ; 1 1 1 1]; */ { A[0][0] = B[0][0]*C[0][0] + B[1][0]*C[0][1] + B[2][0]*C[0][2] + B[3][0]; A[0][1] = B[0][1]*C[0][0] + B[1][1]*C[0][1] + B[2][1]*C[0][2] + B[3][1]; A[0][2] = B[0][2]*C[0][0] + B[1][2]*C[0][1] + B[2][2]*C[0][2] + B[3][2]; A[1][0] = B[0][0]*C[1][0] + B[1][0]*C[1][1] + B[2][0]*C[1][2] + B[3][0]; A[1][1] = B[0][1]*C[1][0] + B[1][1]*C[1][1] + B[2][1]*C[1][2] + B[3][1]; A[1][2] = B[0][2]*C[1][0] + B[1][2]*C[1][1] + B[2][2]*C[1][2] + B[3][2]; A[2][0] = B[0][0]*C[2][0] + B[1][0]*C[2][1] + B[2][0]*C[2][2] + B[3][0]; A[2][1] = B[0][1]*C[2][0] + B[1][1]*C[2][1] + B[2][1]*C[2][2] + B[3][1]; A[2][2] = B[0][2]*C[2][0] + B[1][2]*C[2][1] + B[2][2]*C[2][2] + B[3][2]; A[3][0] = B[0][0]*C[3][0] + B[1][0]*C[3][1] + B[2][0]*C[3][2] + B[3][0]; A[3][1] = B[0][1]*C[3][0] + B[1][1]*C[3][1] + B[2][1]*C[3][2] + B[3][1]; A[3][2] = B[0][2]*C[3][0] + B[1][2]*C[3][1] + B[2][2]*C[3][2] + B[3][2]; } static void invertM(REAL M[4][3], REAL IM[4][3]) /* [IM ; 0 0 0 1] = inv([M ; 0 0 0 1]); */ { REAL id; id = M[0][0]*(M[1][1]*M[2][2]-M[1][2]*M[2][1])+ M[0][1]*(M[1][2]*M[2][0]-M[1][0]*M[2][2])+ M[0][2]*(M[1][0]*M[2][1]-M[1][1]*M[2][0]); id = 1.0/id; IM[0][0] = (M[1][1]*M[2][2]-M[1][2]*M[2][1])*id; IM[0][1] = (M[0][2]*M[2][1]-M[0][1]*M[2][2])*id; IM[0][2] = (M[0][1]*M[1][2]-M[0][2]*M[1][1])*id; IM[1][0] = (M[1][2]*M[2][0]-M[1][0]*M[2][2])*id; IM[1][1] = (M[0][0]*M[2][2]-M[0][2]*M[2][0])*id; IM[1][2] = (M[0][2]*M[1][0]-M[0][0]*M[1][2])*id; IM[2][0] = (M[1][0]*M[2][1]-M[1][1]*M[2][0])*id; IM[2][1] = (M[0][1]*M[2][0]-M[0][0]*M[2][1])*id; IM[2][2] = (M[0][0]*M[1][1]-M[0][1]*M[1][0])*id; IM[3][0] = (M[1][0]*(M[3][1]*M[2][2]-M[2][1]*M[3][2])+ M[1][1]*(M[2][0]*M[3][2]-M[3][0]*M[2][2])+ M[1][2]*(M[3][0]*M[2][1]-M[2][0]*M[3][1]))*id; IM[3][1] = (M[0][0]*(M[2][1]*M[3][2]-M[3][1]*M[2][2])+ M[0][1]*(M[3][0]*M[2][2]-M[2][0]*M[3][2])+ M[0][2]*(M[2][0]*M[3][1]-M[3][0]*M[2][1]))*id; IM[3][2] = (M[0][0]*(M[3][1]*M[1][2]-M[1][1]*M[3][2])+ M[0][1]*(M[1][0]*M[3][2]-M[3][0]*M[1][2])+ M[0][2]*(M[3][0]*M[1][1]-M[1][0]*M[3][1]))*id; } /****************************************************************************************************/ /* These routines are for locating integer co-ordinates that lie inside a tetrahedron. See: J. Ashburner, J. Andersson and K. J. Friston (2000). "Image Registration using a Symmetric Prior - in Three-Dimensions". Human Brain Mapping 9(4):212-225. */ static void scan_line(REAL lin[2], int y, int z, int *n, int vox[][3], int maxv) { REAL p[2], t; int x, xe; /* sort p into ascending order of x */ p[0] = lin[0]; p[1] = lin[1]; if (p[1]=maxv-1) mexErrMsgTxt("Too many voxels inside a tetrahedron"); vox[*n][0] = x; vox[*n][1] = y; vox[*n][2] = z; (*n)++; } } static void scan_triangle(REAL tri[][2], int z, int *n, int vox[][3], int maxv) { REAL *p[3], *t, lin[2]; REAL x1, x2, y1, y2; int y, ye, i; /* sort p into ascending order of y */ p[0] = tri[0]; p[1] = tri[1]; p[2] = tri[2]; if (p[1][1]0) { /* Invert the affine mapping */ invertM(M, IM); /* Convert the mapping from voxels to mm */ mulMM(M, M2, IM); if (pass==0) { /* Insert the new mappings into each voxel within the tetrahedron */ for(j=0; j=1) && (vox[j][0]<=dim_iy[0]) && (vox[j][1]>=1) && (vox[j][1]<=dim_iy[1]) && (vox[j][2]>=1) && (vox[j][2]<=dim_iy[2])) { int o = vox[j][0]+dim_iy[0]*(vox[j][1]+dim_iy[1]*vox[j][2]); iy0[o] = M[0][0]*vox[j][0] + M[1][0]*vox[j][1] + M[2][0]*vox[j][2] + M[3][0]; iy1[o] = M[0][1]*vox[j][0] + M[1][1]*vox[j][1] + M[2][1]*vox[j][2] + M[3][1]; iy2[o] = M[0][2]*vox[j][0] + M[1][2]*vox[j][1] + M[2][2]*vox[j][2] + M[3][2]; } } } else { /* Average the new mappings with those from the 0th pass */ for(j=0; j=1) && (vox[j][0]<=dim_iy[0]) && (vox[j][1]>=1) && (vox[j][1]<=dim_iy[1]) && (vox[j][2]>=1) && (vox[j][2]<=dim_iy[2])) { int o = vox[j][0]+dim_iy[0]*(vox[j][1]+dim_iy[1]*vox[j][2]); iy0[o] = (iy0[o] + M[0][0]*vox[j][0] + M[1][0]*vox[j][1] + M[2][0]*vox[j][2] + M[3][0])/2.0; iy1[o] = (iy1[o] + M[0][1]*vox[j][0] + M[1][1]*vox[j][1] + M[2][1]*vox[j][2] + M[3][1])/2.0; iy2[o] = (iy2[o] + M[0][2]*vox[j][0] + M[1][2]*vox[j][1] + M[2][2]*vox[j][2] + M[3][2])/2.0; } } } } } } } /* Some regions of the inverse deformation may be undefined. */ static void setnan(float *dat, mwSize n) { mwIndex j; float NaN; NaN = mxGetNaN(); for (j=0; j