#include "mrilib.h" /*---------------------------------------------------------------------------*/ /* Functions to deal with the mat44 elements in an AFNI dataset dataxes structure. Note that the mat44 (4x4 matrix) type is defined in nifti1_io.h, and that the last row of the matrix is ALWAYS [0 0 0 1]. It is to be applied to column vectors of the form [ x y z 1 ]', to define an affine transformation of (x,y,z). The library nifti1_io.c includes nifti1_mat44_inverse() and some other utility functions. -- RWCox - 07 Dec 2005 -----------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ /*! Multiply 2 mat44 matrices (a utility missing from nifti1_io.c). -----------------------------------------------------------------------------*/ mat44 THD_mat44_mul( mat44 A , mat44 B ) { mat44 C ; int i,j ; for( i=0 ; i < 3 ; i++ ) for( j=0 ; j < 4 ; j++ ) C.m[i][j] = A.m[i][0] * B.m[0][j] + A.m[i][1] * B.m[1][j] + A.m[i][2] * B.m[2][j] + A.m[i][3] * B.m[3][j] ; C.m[3][0] = C.m[3][1] = C.m[3][2] = 0.0f ; C.m[3][3] = 1.0f ; return C ; } /*---------------------------------------------------------------------------*/ /*! Compute the mat44 elements of a dataset dataxes struct, given the other elements that define the coordinate system. Also see function THD_daxes_from_mat44(), which does the reverse. On input to this function, the XXdel, XXorg, nXX, and to_dicomm elements of dax must have been set already, for XX=xx,yy,zz. Return value is -1 if an error occurs, 0 if all is cool. -----------------------------------------------------------------------------*/ int THD_daxes_to_mat44( THD_dataxes *dax ) { mat44 ijk_to_dxyz , dxyz_to_dicom ; if( dax == NULL ) return -1 ; /* ijk_to_dxyz: transforms (i,j,k) to dataset (x,y,z) coords */ LOAD_MAT44( ijk_to_dxyz , dax->xxdel , 0.0f , 0.0f , dax->xxorg , 0.0f , dax->yydel , 0.0f , dax->yyorg , 0.0f , 0.0f , dax->zzdel , dax->zzorg ) ; /* set to_dicomm (takes dataset order xyz to DICOM order xyz) */ THD_set_daxes_to_dicomm(dax) ; /* dxyz_to_dicom: transforms dataset (x,y,z) coords to DICOM coords */ LOAD_MAT44( dxyz_to_dicom , dax->to_dicomm.mat[0][0] , dax->to_dicomm.mat[0][1] , dax->to_dicomm.mat[0][2] , 0.0f , dax->to_dicomm.mat[1][0] , dax->to_dicomm.mat[1][1] , dax->to_dicomm.mat[1][2] , 0.0f , dax->to_dicomm.mat[2][0] , dax->to_dicomm.mat[2][1] , dax->to_dicomm.mat[2][2] , 0.0f ) ; /* dax->ijk_to_dicom: transforms (i,j,k) to DICOM (x,y,z) */ dax->ijk_to_dicom = THD_mat44_mul( dxyz_to_dicom , ijk_to_dxyz ) ; /* and the inverse transformation: DICOM (x,y,z) to indexes (i,j,k) */ dax->dicom_to_ijk = nifti_mat44_inverse( dax->ijk_to_dicom ) ; THD_set_dicom_box( dax ) ; #if 0 { THD_dataxes new_daxes ; new_daxes.type = DATAXES_TYPE; THD_edit_dataxes(1.0, dax, &new_daxes); printf("edited dataxes:\n" " nxx=%d xxorg=%11.4g xxdel=%11.4g\n" " nyy=%d yyorg=%11.4g yydel=%11.4g\n" " nzz=%d zzorg=%11.4g zzdel=%11.4g\n" , new_daxes.nxx , new_daxes.xxorg , new_daxes.xxdel , new_daxes.nyy , new_daxes.yyorg , new_daxes.yydel , new_daxes.nzz , new_daxes.zzorg , new_daxes.zzdel ) ; DUMP_MAT44("ijk_to_dicom",new_daxes.ijk_to_dicom) ; } #endif return 0 ; } /*------------------------------------------------------------------------*/ /*! Set the min and max DICOM coords that can occur: scan all 8 possible corners of the box the matrix defines. --------------------------------------------------------------------------*/ void THD_set_dicom_box( THD_dataxes *dax ) { float x,y,z , nx1,ny1,nz1 ; float xbot,ybot,zbot , xtop,ytop,ztop ; if( dax == NULL || !ISVALID_MAT44(dax->ijk_to_dicom) ) return ; nx1 = dax->nxx - 1.0f; ny1 = dax->nyy - 1.0f; nz1 = dax->nzz - 1.0f; MAT44_VEC(dax->ijk_to_dicom , 0,0,0 , x,y,z ) ; /* (0,0,0) corner */ xbot = xtop = x ; ybot = ytop = y ; zbot = ztop = z ; /* this macro checks the (a,b,c) corner and updates the bot/top values */ #undef BT #define BT(a,b,c) \ do{ MAT44_VEC(dax->ijk_to_dicom , a,b,c , x,y,z ) ; \ xbot = MIN(xbot,x); xtop = MAX(xtop,x) ; \ ybot = MIN(ybot,y); ytop = MAX(ytop,y) ; \ zbot = MIN(zbot,z); ztop = MAX(ztop,z) ; } while(0) BT(nx1, 0 , 0 ) ; BT( 0 ,ny1, 0 ) ; BT(nx1,ny1, 0 ) ; BT( 0 , 0 ,nz1) ; BT(nx1, 0 ,nz1) ; BT( 0 ,ny1,nz1) ; BT(nx1,ny1,nz1) ; dax->dicom_xxmin = xbot ; dax->dicom_xxmax = xtop ; dax->dicom_yymin = ybot ; dax->dicom_yymax = ytop ; dax->dicom_zzmin = zbot ; dax->dicom_zzmax = ztop ; #undef BT return ; } /*---------------------------------------------------------------------------*/ /*! Given the ijk_to_dicom index to DICOM transformation in the header, AND the nxx,nyy,nzz values, load the legacy dataxes information: - xxorient = Orientation code - yyorient = Orientation code - zzorient = Orientation code - xxorg = Center of (0,0,0) voxel - yyorg = Center of (0,0,0) voxel - zzorg = Center of (0,0,0) voxel - xxdel = Spacings between voxel centers (mm) - may be negative - yydel = Spacings between voxel centers (mm) - may be negative - zzdel = Spacings between voxel centers (mm) - may be negative - xxmin = Min value of x (etc.) - xxmax = Min value of x (etc.) - to_dicomm = Orthogonal matrix transforming from dataset coordinates to DICOM coordinates Return value is -1 if there is an error, 0 if not. -----------------------------------------------------------------------------*/ int THD_daxes_from_mat44( THD_dataxes *dax ) { int icod , jcod , kcod ; mat44 nmat ; float xx,yy,zz , ss , aa,bb,cc ; /* table to convert NIfTI-1 orientation codes (1..6) into AFNI orientation codes (0..5, and in a different order) */ static int orient_nifti2afni[7] = { -1 , ORI_L2R_TYPE, ORI_R2L_TYPE, ORI_P2A_TYPE, ORI_A2P_TYPE, ORI_I2S_TYPE, ORI_S2I_TYPE } ; if( dax == NULL ) return -1 ; if( dax->nxx < 1 || dax->nyy < 1 || dax->nzz < 1 ) return -1 ; /* use the NIfTI-1 library function to determine best orientation; but, must remember that NIfTI-1 x and y are reversed from AFNI's, so we must negate the x and y rows of the matrix before func call */ nmat = dax->ijk_to_dicom ; XYINVERT_MAT44(nmat) ; nifti_mat44_to_orientation( nmat , &icod, &jcod, &kcod ) ; if( icod == 0 || jcod == 0 || kcod == 0 ) return -1 ; dax->xxorient = orient_nifti2afni[icod] ; dax->yyorient = orient_nifti2afni[jcod] ; dax->zzorient = orient_nifti2afni[kcod] ; /* grid offset int i-direction is projection of last column of ijk_to_dicom matrix (the shifts) along the direction of the first column of the matrix (the i-column) */ aa = dax->ijk_to_dicom.m[0][3] ; /* extract last column */ bb = dax->ijk_to_dicom.m[1][3] ; cc = dax->ijk_to_dicom.m[2][3] ; xx = dax->ijk_to_dicom.m[0][0] ; /* extract 1st column */ yy = dax->ijk_to_dicom.m[1][0] ; zz = dax->ijk_to_dicom.m[2][0] ; ss = sqrt(xx*xx+yy*yy+zz*zz) ; if( ss == 0.0f ) ss = 1.0f ; dax->xxorg = (xx*aa+yy*bb+zz*cc) / ss ; if( ORIENT_sign[dax->xxorient] == '-' ) dax->xxorg = -dax->xxorg ; xx = dax->ijk_to_dicom.m[0][1] ; /* extract 2nd column */ yy = dax->ijk_to_dicom.m[1][1] ; zz = dax->ijk_to_dicom.m[2][1] ; ss = sqrt(xx*xx+yy*yy+zz*zz) ; if( ss == 0.0f ) ss = 1.0f ; dax->yyorg = (xx*aa+yy*bb+zz*cc) / ss ; if( ORIENT_sign[dax->yyorient] == '-' ) dax->yyorg = -dax->yyorg ; xx = dax->ijk_to_dicom.m[0][2] ; /* extract 3rd column */ yy = dax->ijk_to_dicom.m[1][2] ; zz = dax->ijk_to_dicom.m[2][2] ; ss = sqrt(xx*xx+yy*yy+zz*zz) ; if( ss == 0.0f ) ss = 1.0f ; dax->zzorg = (xx*aa+yy*bb+zz*cc) / ss ; if( ORIENT_sign[dax->zzorient] == '-' ) dax->zzorg = -dax->zzorg ; /* grid spacing along i-direction is length of 1st column of matrix */ dax->xxdel = sqrt( SQR(dax->ijk_to_dicom.m[0][0]) +SQR(dax->ijk_to_dicom.m[1][0]) +SQR(dax->ijk_to_dicom.m[2][0]) ) ; if( ORIENT_sign[dax->xxorient] == '-' ) dax->xxdel = -dax->xxdel ; /* mutatis mutandis for j- and k-directions */ dax->yydel = sqrt( SQR(dax->ijk_to_dicom.m[0][1]) +SQR(dax->ijk_to_dicom.m[1][1]) +SQR(dax->ijk_to_dicom.m[2][1]) ) ; if( ORIENT_sign[dax->yyorient] == '-' ) dax->yydel = -dax->yydel ; dax->zzdel = sqrt( SQR(dax->ijk_to_dicom.m[0][2]) +SQR(dax->ijk_to_dicom.m[1][2]) +SQR(dax->ijk_to_dicom.m[2][2]) ) ; if( ORIENT_sign[dax->zzorient] == '-' ) dax->zzdel = -dax->zzdel ; /* to_dicomm orthogonal matrix: we make an orthogonal matrix out of the columns of ijk_to_dicom */ nmat = nifti_make_orthog_mat44( dax->ijk_to_dicom.m[0][0], dax->ijk_to_dicom.m[1][0], dax->ijk_to_dicom.m[2][0], dax->ijk_to_dicom.m[0][1], dax->ijk_to_dicom.m[1][1], dax->ijk_to_dicom.m[2][1], dax->ijk_to_dicom.m[0][2], dax->ijk_to_dicom.m[1][2], dax->ijk_to_dicom.m[2][2] ); dax->to_dicomm.mat[0][0] = nmat.m[0][0] ; dax->to_dicomm.mat[0][1] = nmat.m[1][0] ; dax->to_dicomm.mat[0][2] = nmat.m[2][0] ; dax->to_dicomm.mat[1][0] = nmat.m[0][1] ; dax->to_dicomm.mat[1][1] = nmat.m[1][1] ; dax->to_dicomm.mat[1][2] = nmat.m[2][1] ; dax->to_dicomm.mat[2][0] = nmat.m[0][2] ; dax->to_dicomm.mat[2][1] = nmat.m[1][2] ; dax->to_dicomm.mat[2][2] = nmat.m[2][2] ; /** min and max values of (x,y,z) **/ dax->xxmin = dax->xxorg ; dax->xxmax = dax->xxorg + (dax->nxx-1) * dax->xxdel ; if( dax->xxmin > dax->xxmax ){ float temp = dax->xxmin ; dax->xxmin = dax->xxmax ; dax->xxmax = temp ; } dax->yymin = dax->yyorg ; dax->yymax = dax->yyorg + (dax->nyy-1) * dax->yydel ; if( dax->yymin > dax->yymax ){ float temp = dax->yymin ; dax->yymin = dax->yymax ; dax->yymax = temp ; } dax->zzmin = dax->zzorg ; dax->zzmax = dax->zzorg + (dax->nzz-1) * dax->zzdel ; if( dax->zzmin > dax->zzmax ){ float temp = dax->zzmin ; dax->zzmin = dax->zzmax ; dax->zzmax = temp ; } #ifdef EXTEND_BBOX dax->xxmin -= 0.5 * fabsf(dax->xxdel) ; /* pushes edges back by 1/2 */ dax->xxmax += 0.5 * fabsf(dax->xxdel) ; /* voxel dimensions (the box */ dax->yymin -= 0.5 * fabsf(dax->yydel) ; /* defined above is based on */ dax->yymax += 0.5 * fabsf(dax->yydel) ; /* voxel centers, not edges) */ dax->zzmin -= 0.5 * fabsf(dax->zzdel) ; dax->zzmax += 0.5 * fabsf(dax->zzdel) ; #endif return 0 ; } /*-----------------------------------------------------------------------*/ /*! Inputs: - matrix old_mat: transforms (i,j,k) indexes to (x,y,z) coords - ni,nj,nk: number of grid points along each direction - ri,rj,rk: new grid spacing along each direction Outputs: - ninew,njnew,nknew: number of grid points along each new direction - return value: new matrix; the [3][3] element will be 0 on error -------------------------------------------------------------------------*/ mat44 THD_resample_mat44( mat44 old_mat , int ni , int nj , int nk , float ri , float rj , float rk , int *ninew , int *njnew , int *nknew ) { mat44 new_mat ; float di,dj,dk , fi,fj,fk , aa,bb,cc , pp,qq,rr ; ZZME(new_mat) ; new_mat.m[3][3] = 0.0f ; /* mark output as bad; will fix later */ if( !ISVALID_MAT44(old_mat) || ninew == NULL || njnew == NULL || nknew == NULL || ni <= 0 || nj <= 0 || nk <= 0 ) return new_mat ; /* spacing along input (i,j,k) directions */ di = MAT33_CLEN(old_mat,0) ; if( di == 0.0f ) di = 1.0f ; dj = MAT33_CLEN(old_mat,1) ; if( dj == 0.0f ) dj = di ; dk = MAT33_CLEN(old_mat,2) ; if( dk == 0.0f ) dk = di ; if( ri <= 0.0f ) ri = 1.0f ; /* don't allow negative spacing */ if( rj <= 0.0f ) rj = ri ; /* along output (i,j,k) directions */ if( rk <= 0.0f ) rk = ri ; fi = ri/di ; fj = rj/dj ; fk = rk/dk ; /* copy input matrix to output, then scale the upperleft 3x3 to allow for new spacings */ new_mat = old_mat ; new_mat.m[0][0] *= fi ; /* first column */ new_mat.m[1][0] *= fi ; new_mat.m[2][0] *= fi ; new_mat.m[0][1] *= fj ; /* second column */ new_mat.m[1][1] *= fj ; new_mat.m[2][1] *= fj ; new_mat.m[0][2] *= fk ; /* third column */ new_mat.m[1][2] *= fk ; new_mat.m[2][2] *= fk ; /* number of points along each axis in new grid */ *ninew = (int)rint(ni/fi) ; /* so that ninew*ri == ni*di */ *njnew = (int)rint(nj/fj) ; *nknew = (int)rint(nk/fk) ; /* now compute offset (fourth column) in new_mat so that the input and output grids have the same (x,y,z) centers; center of input is at (i,j,k) = 0.5*(ni-1,nj-1,nk-1), et cetera */ di = 0.5*( ni -1) ; dj = 0.5*( nj -1) ; dk = 0.5*( nk -1) ; fi = 0.5*(*ninew-1) ; fj = 0.5*(*njnew-1) ; fk = 0.5*(*nknew-1) ; MAT33_VEC( old_mat , di,dj,dk , aa,bb,cc ) ; MAT33_VEC( new_mat , fi,fj,fk , pp,qq,rr ) ; /* adjust so that MAT44_VEC(new_mat,fi,fj,fk,...) is same result as MAT44_VEC(old_mat,di,dj,dk,...) */ new_mat.m[0][3] += (aa-pp) ; new_mat.m[1][3] += (bb-qq) ; new_mat.m[2][3] += (cc-rr) ; return new_mat ; }