changeset 1611:158fc1ec4950

Added some new utility functions
author bert <bert>
date Tue, 06 Jan 2004 20:45:41 +0000
parents 7fd267d9071d
children 1a2decaf408a
files libsrc2/m2util.c
diffstat 1 files changed, 555 insertions(+), 128 deletions(-) [+]
line wrap: on
line diff
--- a/libsrc2/m2util.c
+++ b/libsrc2/m2util.c
@@ -5,6 +5,7 @@
 #include <math.h>
 #include <hdf5.h>
 #include <minc.h>
+#include <limits.h>
 #include "minc2.h"
 #include "minc2_private.h"
 
@@ -154,71 +155,169 @@
     return (nvoxels);
 }
 
-/* Get the volume of a spatial voxel */
-double
-miget_voxel_volume(int mincid)
+/** Set an attribute from a minc file */
+int
+miset_attribute(mihandle_t volume, const char *path, const char *name, 
+                mitype_t data_type, int length, const void *values)
 {
-    int imgid;
-    int dim[MAX_VAR_DIMS];
-    int idim;
-    int ndims;
-    double volume;
-    double step;
-    char dimname[MAX_NC_NAME];
+    hid_t hdf_file;
+    hid_t hdf_loc;
+    hid_t hdf_type;
+    hid_t hdf_space;
+    hid_t hdf_attr;
+    hsize_t hdf_len;
+    hsize_t hdf_max;
+
+    /* Get a handle to the actual HDF file 
+     */
+    hdf_file = volume->hdf_id;
+    if (hdf_file < 0) {
+	return (MI_ERROR);
+    }
 
-    /* Get the dimension ids for the image variable */
-    imgid = ncvarid(mincid, MIimage);
-    (void)ncvarinq(mincid, imgid, NULL, NULL, &ndims, dim, NULL);
+    /* Search through the path, descending into each group encountered.
+     */
+    hdf_loc = midescend_path(hdf_file, path);
+    if (hdf_loc < 0) {
+	return (MI_ERROR);
+    }
+
+    H5E_BEGIN_TRY {
+      hdf_attr = H5Aopen_name(hdf_loc, name);
+    } H5E_END_TRY;
+
+    /* Delete the existing attribute. */
+    if (hdf_attr >= 0) {
+        H5Adelete(hdf_loc, name);
+    }
 
-    /* Loop over them to get the total spatial volume */
-    volume = 1.0;
-    for (idim = 0; idim < ndims; idim++) {
-
-        /* Get the name and check that this is a spatial dimension */
-        (void)ncdiminq(mincid, dim[idim], dimname, NULL);
-        if (!strcmp(dimname, MIxspace) ||
-            !strcmp(dimname, MIyspace) ||
-            !strcmp(dimname, MIzspace)) {
+    switch (data_type) {
+    case MI_TYPE_INT:
+	hdf_type = H5Tcopy(H5T_NATIVE_INT);
+	break;
+    case MI_TYPE_FLOAT:
+	hdf_type = H5Tcopy(H5T_NATIVE_FLOAT);
+	break;
+    case MI_TYPE_DOUBLE:
+	hdf_type = H5Tcopy(H5T_NATIVE_DOUBLE);
+	break;
+    case MI_TYPE_STRING:
+	hdf_type = H5Tcopy(H5T_C_S1);
+	H5Tset_size(hdf_type, length);
+        length = 1;             /* Apparent length is one. */
+	break;
+    default:
+	return (MI_ERROR);
+    }
 
-            /* Get the step attribute of the coordinate variable */
-            step = 1.0;
-            miget_attribute(mincid, dimname, MIstep, 1, &step);
+    hdf_len = (hsize_t) length;
+    hdf_space = H5Screate_simple(1, &hdf_len, NULL);
+    if (hdf_space < 0) {
+	return (MI_ERROR);
+    }
+    
+    hdf_attr = H5Acreate(hdf_loc, name, hdf_type, hdf_space, H5P_DEFAULT);
+    if (hdf_attr < 0) {
+	return (MI_ERROR);
+    }
+
+    H5Awrite(hdf_attr, hdf_type, values);
 
-            /* Make sure that it is positive and calculate the volume */
-            if (step < 0.0)
-                step = -step;
-            volume *= step;
-        }
-   }
-   return (volume);
+    H5Aclose(hdf_attr);
+    H5Tclose(hdf_type);
+    H5Sclose(hdf_space);
+
+    /* The hdf_loc identifier could be a group or a dataset.
+     */
+    if (H5Iget_type(hdf_loc) == H5I_GROUP) {
+        H5Gclose(hdf_loc);
+    }
+    else {
+        H5Dclose(hdf_loc);
+    }
+    return (MI_NOERROR);
 }
 
 /** Get a double attribute from a minc file */
-void 
-miget_attribute(int mincid, char *varname, char *attname,
-                int maxvals, double vals[])
+int
+miget_attribute(mihandle_t volume, const char *path, const char *name, 
+                mitype_t data_type, int length, void *values)
 {
-    int varid;
-    int old_ncopts;
-    int length;
+    hid_t hdf_file;
+    hid_t hdf_loc;
+    hid_t hdf_type;
+    hid_t hdf_space;
+    hid_t hdf_attr;
+
+    /* Get a handle to the actual HDF file 
+     */
+    hdf_file = volume->hdf_id;
+    if (hdf_file < 0) {
+	return (MI_ERROR);
+    }
+
+    /* Search through the path, descending into each group encountered.
+     */
+    hdf_loc = midescend_path(hdf_file, path);
+    if (hdf_loc < 0) {
+	return (MI_ERROR);
+    }
+
+    hdf_attr = H5Aopen_name(hdf_loc, name);
+    if (hdf_attr < 0) {
+	return (MI_ERROR);
+    }
 
-    if (!mivar_exists(mincid, varname))
-        return;
-    varid = ncvarid(mincid, varname);
-    old_ncopts = ncopts;
-    ncopts = 0;
-    (void)miattget(mincid, varid, attname, NC_DOUBLE, maxvals, vals, &length);
-    ncopts = old_ncopts;
-}
+    switch (data_type) {
+    case MI_TYPE_INT:
+	hdf_type = H5Tcopy(H5T_NATIVE_INT);
+	break;
+    case MI_TYPE_FLOAT:
+	hdf_type = H5Tcopy(H5T_NATIVE_FLOAT);
+	break;
+    case MI_TYPE_DOUBLE:
+	hdf_type = H5Tcopy(H5T_NATIVE_DOUBLE);
+	break;
+    case MI_TYPE_STRING:
+	hdf_type = H5Tcopy(H5T_C_S1);
+	H5Tset_size(hdf_type, length);
+	break;
+    default:
+	return (MI_ERROR);
+    }
+
+    hdf_space = H5Aget_space(hdf_attr);
+    if (hdf_space < 0) {
+	return (MI_ERROR);
+    }
 
-/* Get the total number of image dimensions in a minc file */
-int
-miget_dim_count(int mincid)
-{
-    int ndims;
+    /* If we're retrieving a vector, make certain the length passed into this
+     * function is sufficient.
+     */
+    if (H5Sget_simple_extent_ndims(hdf_space) == 1) {
+	hsize_t hdf_dims[1];
+
+	H5Sget_simple_extent_dims(hdf_space, hdf_dims, NULL);
+	if (length < hdf_dims[0]) {
+	    return (MI_ERROR);
+	}
+    }
+    
+    H5Aread(hdf_attr, hdf_type, values);
 
-    ncvarinq(mincid, ncvarid(mincid, MIimage), NULL, NULL, &ndims, NULL, NULL);
-    return (ndims);
+    H5Aclose(hdf_attr);
+    H5Tclose(hdf_type);
+    H5Sclose(hdf_space);
+
+    /* The hdf_loc identifier could be a group or a dataset.
+     */
+    if (H5Iget_type(hdf_loc) == H5I_GROUP) {
+        H5Gclose(hdf_loc);
+    }
+    else {
+        H5Dclose(hdf_loc);
+    }
+    return (MI_NOERROR);
 }
 
 /* Get the mapping from spatial dimension - x, y, z - to file dimensions
@@ -269,16 +368,19 @@
 }
 
 /** Get the voxel to world transform (for column vectors) */
-void 
-miget_voxel_to_world(int mincid,
-                     mi_lin_xfm_t voxel_to_world)
+void
+miget_voxel_to_world(mihandle_t volume, mi_lin_xfm_t voxel_to_world)
 {
     int i;
     int j;
     double dircos[MI2_3D];
     double step;
     double start;
-    static char *dimensions[] = { MIxspace, MIyspace, MIzspace };
+
+    static char *dimensions[] = { 
+        "/minc-2.0/dimensions/" MIxspace, 
+        "/minc-2.0/dimensions/" MIyspace, 
+        "/minc-2.0/dimensions/" MIzspace };
 
     /* Zero the matrix */
     for (i = 0; i < MI2_LIN_XFM_SIZE; i++) {
@@ -297,10 +399,14 @@
         dircos[j] = 1.0;
 
         /* Get the attributes */
-        miget_attribute(mincid, dimensions[j], MIstart, 1, &start);
-        miget_attribute(mincid, dimensions[j], MIstep, 1, &step);
-        miget_attribute(mincid, dimensions[j], MIdirection_cosines,
-                        MI2_3D, dircos);
+        H5E_BEGIN_TRY {
+            miget_attribute(volume, dimensions[j], MIstart, 
+                            MI_TYPE_DOUBLE, 1, &start);
+            miget_attribute(volume, dimensions[j], MIstep, 
+                            MI_TYPE_DOUBLE, 1, &step);
+            miget_attribute(volume, dimensions[j], MIdirection_cosines,
+                            MI_TYPE_DOUBLE, MI2_3D, dircos);
+        } H5E_END_TRY;
         minormalize_vector(dircos);
 
         /* Put them in the matrix */
@@ -334,7 +440,7 @@
 void 
 mitransform_coord(double out_coord[],
                   mi_lin_xfm_t transform,
-                  double in_coord[])
+                  const double in_coord[])
 {
     int i, j;
     double in_homogeneous[MI2_3D + 1];
@@ -352,7 +458,9 @@
         }
     }
 
+#if 0
     printf("W = %f\n", out_homogeneous[3]);
+#endif /* 0 */
 
     for (i = 0; i < MI2_3D; i++) {
         out_coord[i] = out_homogeneous[i];
@@ -372,58 +480,106 @@
                void *bkg_ptr,
                hid_t dset_xfer_plist)
 {
-    double *dptr;
-    unsigned char *sptr;
-    int i;
-    int nb;
-    H5T_sign_t sg;
+    unsigned char *dst_ptr;
+    unsigned char *src_ptr;
+    int src_nb;
+    int dst_nb;
+    H5T_sign_t src_sg;
     double t;
+    int dst_cnt;
+    int src_cnt;
 
     switch (cdata->command) {
     case H5T_CONV_INIT:
-	nb = H5Tget_size(src_id);
-	if (nb != 1 && nb != 2 && nb != 4) {
+        cdata->need_bkg = H5T_BKG_NO;
+	src_nb = H5Tget_size(src_id);
+	if (src_nb != 1 && src_nb != 2 && src_nb != 4) {
 	    return (-1);
 	}
+        dst_nb = H5Tget_size(dst_id);
+        if (dst_nb != 8) {
+            return (-1);
+        }
 	break;
 
     case H5T_CONV_CONV:
-	nb = H5Tget_size(src_id);
-	sg = H5Tget_sign(src_id);
+	src_nb = H5Tget_size(src_id);
+	src_sg = H5Tget_sign(src_id);
+        dst_nb = H5Tget_size(dst_id);
+        if (buf_stride == 0) {
+            dst_cnt = dst_nb;
+            src_cnt = src_nb;
+        }
+        else {
+            dst_cnt = buf_stride;
+            src_cnt = buf_stride;
+        }
 
         /* Convert starting from "far side" of buffer... (Hope this works!)
          */
-        dptr = ((double *) buf_ptr) + nelements;
-	sptr = ((unsigned char *) buf_ptr) + (nelements * nb);
+        dst_ptr = ((unsigned char *) buf_ptr) + ((nelements - 1) * dst_nb);
+	src_ptr = ((unsigned char *) buf_ptr) + ((nelements - 1) * src_nb);
 
-	if (sg == H5T_SGN_2) {
-	    for (i = 0; i < nelements; i++) {
-		sptr -= nb;
-		if (nb == 4) {
-		    t = *((int *)sptr);
+	if (src_sg == H5T_SGN_2) {
+            switch (src_nb) {
+            case 4:
+                while (nelements-- > 0) {
+		    t = *(int *)src_ptr;
+                    *(double *)dst_ptr = t;
+                    src_ptr -= src_cnt;
+                    dst_ptr -= dst_cnt;
 		}
-		else if (nb == 2) {
-		    t = *((short *)sptr);
+                break;
+            case 2:
+                while (nelements-- > 0) {
+		    t = *(short *)src_ptr;
+                    *(double *)dst_ptr = t;
+                    src_ptr -= src_cnt;
+                    dst_ptr -= dst_cnt;
 		}
-		else if (nb == 1) {
-		    t = *((char *)sptr);
-		}
-                *--dptr = t;
+                break;
+            case 1:
+                while (nelements-- > 0) {
+		    t = *(char *)src_ptr;
+                    *(double *)dst_ptr = t;
+                    src_ptr -= src_cnt;
+                    dst_ptr -= dst_cnt;
+                }
+                break;
+            default:
+                /* Should not happen! */
+                break;
 	    }
 	}
 	else {
-	    for (i = 0; i < nelements; i++) {
-		sptr -= nb;
-		if (nb == 4) {
-		    t = *((unsigned int *)sptr);
+            switch (src_nb) {
+            case 4:
+                while (nelements-- > 0) {
+		    t = *(unsigned int *)src_ptr;
+                    *(double *)dst_ptr = t;
+                    src_ptr -= src_cnt;
+                    dst_ptr -= dst_cnt;
 		}
-		else if (nb == 2) {
-		    t = *((unsigned short *)sptr);
+                break;
+            case 2:
+                while (nelements-- > 0) {
+		    t = *(unsigned short *)src_ptr;
+                    *(double *)dst_ptr = t;
+                    src_ptr -= src_cnt;
+                    dst_ptr -= dst_cnt;
 		}
-		else if (nb == 1) {
-		    t = *((unsigned char *)sptr);
-		}
-                *--dptr = t;
+                break;
+            case 1:
+                while (nelements-- > 0) {
+		    t = *(unsigned char *)src_ptr;
+                    *(double *)dst_ptr = t;
+                    src_ptr -= src_cnt;
+                    dst_ptr -= dst_cnt;
+                }
+                break;
+            default:
+                /* Should not happen! */
+                break;
 	    }
 	}
 	break;
@@ -451,56 +607,147 @@
                void *bkg_ptr,
                hid_t dset_xfer_plist)
 {
-    unsigned char *uptr;
-    double *dptr;
-    int i;
-    int nb;
-    H5T_sign_t sg;
-    double t;                   /* Signed data */
+    unsigned char *dst_ptr;
+    unsigned char *src_ptr;
+    int src_nb;
+    int dst_nb;
+    H5T_sign_t dst_sg;
+    double t;
+    int dst_cnt;
+    int src_cnt;
 
     switch (cdata->command) {
     case H5T_CONV_INIT:
-	nb = H5Tget_size(src_id);
-        fprintf(stderr, "d2i(INIT): %d\n", nb);
-	if (nb != 8) {
+        cdata->need_bkg = H5T_BKG_NO;
+        /* Verify that we can handle this conversion.
+         */
+	src_nb = H5Tget_size(src_id);
+	if (src_nb != 8) {
 	    return (-1);
 	}
+        dst_nb = H5Tget_size(dst_id);
+        if (dst_nb != 4 && dst_nb != 2 && dst_nb != 1) {
+            return (-1);
+        }
 	break;
 
     case H5T_CONV_CONV:
-	nb = H5Tget_size(dst_id);
-	sg = H5Tget_sign(dst_id);
-	uptr = (unsigned char *) buf_ptr;
-	dptr = (double *) buf_ptr;
+	dst_nb = H5Tget_size(dst_id);
+	dst_sg = H5Tget_sign(dst_id);
+        src_nb = H5Tget_size(src_id);
+	dst_ptr = (unsigned char *) buf_ptr;
+	src_ptr = (unsigned char *) buf_ptr;
+        /* The logic of HDF5 seems to be that if a stride is specified,
+         * both the source and destination pointers should advance by that
+         * amount.  This seems wrong to me, but I've examined the HDF5 sources
+         * and that's what their own type converters do.
+         */
+        if (buf_stride == 0) {
+            dst_cnt = dst_nb;
+            src_cnt = src_nb;
+        }
+        else {
+            dst_cnt = buf_stride;
+            src_cnt = buf_stride;
+        }
 
-	if (sg == H5T_SGN_2) {
-	    for (i = 0; i < nelements; i++) {
-                t = rint(*dptr++);
-		if (nb == 4) {
-		    *((int *)uptr) = (int) t;
+	if (dst_sg == H5T_SGN_2) {
+            switch (dst_nb) {
+            case 4:
+                while (nelements-- > 0) {
+                    t = rint(*(double *) src_ptr);
+                    if (t > INT_MAX) {
+                        t = INT_MAX; 
+                    }
+                    else if (t < INT_MIN) {
+                        t = INT_MIN;
+                    }
+                    *((int *)dst_ptr) = (int) t;
+                    dst_ptr += dst_cnt;
+                    src_ptr += src_cnt;
+                }
+                break;
+            case 2:
+                while (nelements-- > 0) {
+                    t = rint(*(double *) src_ptr);
+                    if (t > SHRT_MAX) {
+                        t = SHRT_MAX;
+                    }
+                    else if (t < SHRT_MIN) {
+                        t = SHRT_MIN;
+                    }
+		    *((short *)dst_ptr) = (short) t;
+                    dst_ptr += dst_cnt;
+                    src_ptr += src_cnt;
 		}
-		else if (nb == 2) {
-		    *((short *)uptr) = (short) t;
+                break;
+            case 1:
+                while (nelements-- > 0) {
+                    t = rint(*(double *) src_ptr);
+                    if (t > CHAR_MAX) {
+                        t = CHAR_MAX;
+                    }
+                    else if (t < CHAR_MIN) {
+                        t = CHAR_MIN;
+                    }
+                    *((char *)src_ptr) = (char) t;
+                    dst_ptr += dst_cnt;
+                    src_ptr += src_cnt;
 		}
-		else if (nb == 1) {
-                    *((char *)uptr) = (char) t;
-		}
-		uptr += nb;
+                break;
+            default:
+                /* Can't handle this! */
+                break;
 	    }
 	}
 	else {
-	    for (i = 0; i < nelements; i++) {
-                t = rint(*dptr++);
-		if (nb == 4) {
-		    *((unsigned int *)uptr) = (unsigned int) t;
+            switch (dst_nb) {
+            case 4:
+                while (nelements-- > 0) {
+                    t = rint(*(double *)src_ptr);
+                    if (t > UINT_MAX) {
+                        t = UINT_MAX;
+                    }
+                    else if (t < 0) {
+                        t = 0;
+                    }
+		    *((unsigned int *)dst_ptr) = (unsigned int) t;
+                    dst_ptr += dst_cnt;
+                    src_ptr += src_cnt;
+                }
+                break;
+
+            case 2:
+                while (nelements-- > 0) {
+                    t = rint(*(double *)src_ptr);
+                    if (t > USHRT_MAX) {
+                        t = USHRT_MAX;
+                    }
+                    else if (t < 0) {
+                        t = 0;
+                    }
+		    *((unsigned short *)dst_ptr) = (unsigned short) t;
+                    dst_ptr += dst_cnt;
+                    src_ptr += src_cnt;
 		}
-		else if (nb == 2) {
-		    *((unsigned short *)uptr) = (unsigned short) t;
+                break;
+            case 1:
+                while (nelements-- > 0) {
+                    t = rint(*(double *)src_ptr);
+                    if (t > UCHAR_MAX) {
+                        t = UCHAR_MAX;
+                    }
+                    else if (t < 0) {
+                        t = 0;
+                    }
+		    *((unsigned char *)dst_ptr) = (unsigned char) t;
+                    dst_ptr += dst_cnt;
+                    src_ptr += src_cnt;
 		}
-		else if (nb == 1) {
-		    *((unsigned char *)uptr) = (unsigned char) t;
-		}
-		uptr += nb;
+                break;
+            default:
+                /* Can't handle any other values */
+                break;
 	    }
 	}
 	break;
@@ -763,3 +1010,183 @@
 
 }
 
+/** Performs scaled maximal pivoting gaussian elimination as a
+    numerically robust method to solve systems of linear equations.
+*/
+
+static int
+scaled_maximal_pivoting_gaussian_elimination(int   row[4],
+                                             double  a[4][4],
+                                             double solution[4][4] )
+{
+    int i, j, k, p, v, tmp;
+    double s[4], val, best_val, m, scale_factor;
+    int result;
+
+    for ( i = 0; i < 4; i++) {
+        row[i] = i;
+    }
+
+    for ( i = 0; i < 4; i++) {
+        s[i] = fabs( a[i][0] );
+        for ( j = 1; j < 4; j++ ) {
+            if ( fabs(a[i][j]) > s[i] ) {
+                s[i] = fabs(a[i][j]);
+            }
+        }
+
+        if ( s[i] == 0.0 ) {
+            return ( MI_ERROR );
+        }
+    }
+
+    result = MI_NOERROR;
+
+    for ( i = 0; i < 4 - 1; i++ ) {
+        p = i;
+        best_val = a[row[i]][i] / s[row[i]];
+        best_val = fabs( best_val );
+        for ( j = i + 1; j < 4; j++ ) {
+            val = a[row[j]][i] / s[row[j]];
+            val = fabs( val );
+            if( val > best_val ) {
+                best_val = val;
+                p = j;
+            }
+        }
+
+        if ( a[row[p]][i] == 0.0 ) {
+            result = MI_ERROR;
+            break;
+        }
+
+        if ( i != p ) {
+            tmp = row[i];
+            row[i] = row[p];
+            row[p] = tmp;
+        }
+
+        for ( j = i + 1; j < 4; j++ ) {
+            if ( a[row[i]][i] == 0.0 ) {
+                result = MI_ERROR;
+                break;
+            }
+
+            m = a[row[j]][i] / a[row[i]][i];
+            for ( k = i + 1; k < 4; k++ )
+                a[row[j]][k] -= m * a[row[i]][k];
+            for( v = 0; v < 4; v++ )
+                solution[row[j]][v] -= m * solution[row[i]][v];
+        }
+
+        if (result != MI_NOERROR)
+            break;
+    }
+
+    if ( result == MI_NOERROR && a[row[4-1]][4-1] == 0.0 )
+        result = MI_ERROR;
+
+    if ( result == MI_NOERROR ) {
+        for ( i = 4-1;  i >= 0;  --i ) {
+            for ( j = i+1; j < 4; j++) {
+                scale_factor = a[row[i]][j];
+                for ( v = 0; v < 4; v++ )
+                    solution[row[i]][v] -= scale_factor * solution[row[j]][v];
+            }
+
+            for( v = 0; v < 4; v++ )
+                solution[row[i]][v] /= a[row[i]][i];
+        }
+    }
+
+    return (result);
+}
+
+static int
+scaled_maximal_pivoting_gaussian_elimination_real(double coefs[4][4],
+                                                  double values[4][4])
+{
+    int i, j, v, row[4];
+    double a[4][4], solution[4][4];
+    int result;
+
+    for ( i = 0; i < 4; i++) {
+        for (j = 0; j < 4; j++)
+            a[i][j] = coefs[i][j];
+        for ( v = 0; v < 4; v++ )
+            solution[i][v] = values[v][i];
+    }
+
+    result = scaled_maximal_pivoting_gaussian_elimination(row, a, solution);
+
+    if ( result == MI_NOERROR ) {
+        for ( i = 0; i < 4; i++ ) {
+            for ( v = 0; v < 4; v++ )
+                values[v][i] = solution[row[i]][v];
+        }
+    }
+
+    return ( result );
+}
+
+/** Computes the inverse of a square matrix.
+ */
+static int
+invert_4x4_matrix(double matrix[4][4], /**< Input matrix */
+                  double inverse[4][4]) /**< Output (inverted) matrix */
+{
+    double tmp;
+    int result;
+    int i, j;
+
+    /* Start off with the identity matrix. */
+    for ( i = 0; i < 4; i++ ) {
+        for ( j = 0; j < 4; j++ ) {
+            inverse[i][j] = 0.0;
+        }
+        inverse[i][i] = 1.0;
+    }
+
+    result = scaled_maximal_pivoting_gaussian_elimination_real( matrix,
+                                                                inverse );
+
+    if ( result == MI_NOERROR )  {
+        for ( i = 0; i < 4 - 1; i++) {
+            for ( j = i + 1; j < 4; j++ ) {
+                tmp = inverse[i][j];
+                inverse[i][j] = inverse[j][i];
+                inverse[j][i] = tmp;
+            }
+        }
+    }
+    return (result);
+}
+
+/** Fills in the transform with the identity matrix.
+ */
+static void
+mimake_identity_transform(mi_lin_xfm_t transform)
+{
+    int i, j;
+
+    for (i = 0; i < MI2_LIN_XFM_SIZE; i++) {
+        for (j = 0; j < MI2_LIN_XFM_SIZE; j++) {
+            transform[i][j] = 0.0;
+        }
+        transform[i][i] = 1.0;
+    }
+}
+
+/** Function for inverting a MINC linear transform.
+ */
+int 
+miinvert_transform(mi_lin_xfm_t transform, mi_lin_xfm_t inverse )
+{
+    int result;
+
+    result = invert_4x4_matrix( transform, inverse );
+    if (result != MI_NOERROR) {
+        mimake_identity_transform(inverse);
+    }
+    return ( result );
+}