/*
 *       FILE NAME:   mri.c
 *
 *       DESCRIPTION: utilities for MRI  data structure
 *
 *       AUTHOR:      Bruce Fischl
 *       DATE:        1/8/97
 *
 */
// Warning: Do not edit the following four lines.  CVS maintains them.
// Revision Author: $Author: nicks $
// Revision Date  : $Date: 2006/01/17 22:16:34 $
// Revision       : $Revision: 1.325 $
char *MRI_C_VERSION = "$Revision: 1.325 $";

/*-----------------------------------------------------
  INCLUDE FILES
  -------------------------------------------------------*/
#define USE_ELECTRIC_FENCE  1

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <memory.h>
#include <errno.h>
#include <ctype.h>
#include "error.h"
#include "proto.h"
#include "mri.h"
#include "macros.h"
#include "diag.h"
#include "volume_io.h"
#include "filter.h"
#include "box.h"
#include "region.h"
#include "mri_transform.h"
#include "utils.h"
#include "matrix.h"
#include "pdf.h"
#include "cma.h"
#include "talairachex.h"
#include "voxlist.h"

extern int errno;

/*-----------------------------------------------------
  MACROS AND CONSTANTS
  -------------------------------------------------------*/

#define DEBUG_POINT(x,y,z)  (((x==8&&y==9) || (x==9&&y==8)) &&((z)==15))

/*-----------------------------------------------------
  STATIC DATA
  -------------------------------------------------------*/

static long mris_alloced = 0 ;

/*-----------------------------------------------------
  STATIC PROTOTYPES
  -------------------------------------------------------*/

/*-----------------------------------------------------
  GLOBAL FUNCTIONS
  -------------------------------------------------------*/

/*----------------------------------------------------------
  MRIxfmCRS2XYZ() - computes the matrix needed to compute the
  XYZ of the center of a voxel at a given Col, Row, and Slice
  from the native geometry of the volume

  x         col
  y  = T *  row
  z        slice
  1          1

  T = [Mdc*D Pxyz0]
  [0 0 0   1  ]

  Mdc = [Vcol Vrow Vslice]
  V<dim> = the direction cosine pointing from the center of one voxel
  to the center of an adjacent voxel in the next dim, where
  dim is either colum, row, or slice. Vcol = [x_r x_a x_s],
  Vrow = [y_r y_a y_s], Vslice = [z_r z_a z_s]. Vcol can also
  be described as the vector normal to the plane formed by
  the rows and slices of a given column (ie, the column normal).

  D = diag([colres rowres sliceres])
  dimres = the distance between adjacent dim, where colres = mri->xsize,
  rowres = mri->ysize, and sliceres = mri->zsize.

  Pxyz0 = the XYZ location at CRS=0. This number is not part of the
  mri structure, so it is computed here according to the formula:
  Pxyz0 = PxyzCenter - Mdc*D*PcrsCenter

  PcrsCenter = the col, row, and slice at the center of the volume,
  = [ ncols/2 nrows/2 nslices/2 ]

  PxyzCenter = the X, Y, and Z at the center of the volume and does
  exist in the header as mri->c_r, mri->c_a, and mri->c_s,
  respectively.

  Note: to compute the matrix with respect to the first voxel being
  at CRS 1,1,1 instead of 0,0,0, then set base = 1. This is
  necessary with SPM matrices.

  See also: MRIxfmCRS2XYZtkreg, MRItkReg2Native
  ------------------------------------------------------*/
MATRIX *MRIxfmCRS2XYZ(MRI *mri, int base)
{
  MATRIX *m;
  MATRIX *Pcrs, *PxyzOffset;

  m = MatrixAlloc(4, 4, MATRIX_REAL);

  /* direction cosine between columns scaled by
     distance between colums */
  *MATRIX_RELT(m, 1, 1) = mri->x_r * mri->xsize;
  *MATRIX_RELT(m, 2, 1) = mri->x_a * mri->xsize;
  *MATRIX_RELT(m, 3, 1) = mri->x_s * mri->xsize;

  /* direction cosine between rows scaled by
     distance between rows */
  *MATRIX_RELT(m, 1, 2) = mri->y_r * mri->ysize;
  *MATRIX_RELT(m, 2, 2) = mri->y_a * mri->ysize;
  *MATRIX_RELT(m, 3, 2) = mri->y_s * mri->ysize;

  /* direction cosine between slices scaled by
     distance between slices */
  *MATRIX_RELT(m, 1, 3) = mri->z_r * mri->zsize;
  *MATRIX_RELT(m, 2, 3) = mri->z_a * mri->zsize;
  *MATRIX_RELT(m, 3, 3) = mri->z_s * mri->zsize;

  /* Preset the offsets to 0 */
  *MATRIX_RELT(m, 1, 4) = 0.0;
  *MATRIX_RELT(m, 2, 4) = 0.0;
  *MATRIX_RELT(m, 3, 4) = 0.0;

  /* Last row of matrix */
  *MATRIX_RELT(m, 4, 1) = 0.0;
  *MATRIX_RELT(m, 4, 2) = 0.0;
  *MATRIX_RELT(m, 4, 3) = 0.0;
  *MATRIX_RELT(m, 4, 4) = 1.0;

  /* At this point, m = Mdc * D */

  /* Col, Row, Slice at the Center of the Volume */
  Pcrs = MatrixAlloc(4, 1, MATRIX_REAL);
  *MATRIX_RELT(Pcrs, 1, 1) = mri->width/2.0  + base;
  *MATRIX_RELT(Pcrs, 2, 1) = mri->height/2.0 + base;
  *MATRIX_RELT(Pcrs, 3, 1) = mri->depth/2.0  + base;
  *MATRIX_RELT(Pcrs, 4, 1) = 1.0;

  /* XYZ offset the first Col, Row, and Slice from Center */
  /* PxyzOffset = Mdc*D*PcrsCenter */
  PxyzOffset = MatrixMultiply(m,Pcrs,NULL);

  /* XYZ at the Center of the Volume is mri->c_r, c_a, c_s  */

  /* The location of the center of the voxel at CRS = (0,0,0)*/
  *MATRIX_RELT(m, 1, 4) = mri->c_r - PxyzOffset->rptr[1][1];
  *MATRIX_RELT(m, 2, 4) = mri->c_a - PxyzOffset->rptr[2][1];
  *MATRIX_RELT(m, 3, 4) = mri->c_s - PxyzOffset->rptr[3][1];

  MatrixFree(&Pcrs);
  MatrixFree(&PxyzOffset);

  return(m);
}
/*-------------------------------------------------------------
  MRIxfmCRS2XYZtkreg() - computes the linear transform between the
  column, row, and slice of a voxel and the x, y, z of that voxel as
  expected by tkregister (or for when using a tkregister compatible
  matrix). For tkregister, the column DC points in the "x" direction,
  the row DC points in the "z" direction, and the slice DC points in
  the "y" direction. The center of the coordinates is set to the
  center of the FOV. These definitions are arbitrary (and more than a
  little confusing). Since they are arbitrary, they must be applied
  consistently.
  -------------------------------------------------------------*/
MATRIX *MRIxfmCRS2XYZtkreg(MRI *mri)
{
  MRI *tmp;
  MATRIX *K;

  tmp = MRIallocHeader(mri->width, mri->height, mri->depth, mri->type);

  /* Set tkregister defaults */
  /* column         row           slice          center      */
  tmp->x_r = -1; tmp->y_r =  0; tmp->z_r =  0; tmp->c_r = 0.0;
  tmp->x_a =  0; tmp->y_a =  0; tmp->z_a =  1; tmp->c_a = 0.0;
  tmp->x_s =  0; tmp->y_s = -1; tmp->z_s =  0; tmp->c_s = 0.0;

  /* Copy the voxel resolutions */
  tmp->xsize = mri->xsize;
  tmp->ysize = mri->ysize;
  tmp->zsize = mri->zsize;

  K = MRIxfmCRS2XYZ(tmp,0);

  MRIfree(&tmp);

  return(K);
}
/*-------------------------------------------------------------------
  MRItkReg2Native() - converts a tkregister-compatible registration matrix
  R to one that works with the geometry native to the two volumes. The
  matrix R maps tkXYZ of the ref volume to tkXYZ of the mov volume. In
  a typical application, ref is the anatomical volume and mov is the
  functional volume. The purpose of this function is to be able to use
  registration matrices computed by (or compatible with) tkregister
  without losing the geometries native to the volumes. If R is null,
  it is assumed to be the identity. R: MovXYZ = R*RefXYZ. Typically,
  Ref is the Anatomical Reference, and Mov is the functional.

  See also: MRItkRegMtx, MRIxfmCRS2XYZtkreg, MRIxfmCRS2XYZ
  -------------------------------------------------------------------*/
MATRIX *MRItkReg2Native(MRI *ref, MRI *mov, MATRIX *R)
{
  MATRIX *Kref, *Kmov;
  MATRIX *Tref, *Tmov, *D;
  MATRIX *invKmov, *invTref;

  Tref = MRIxfmCRS2XYZ(ref,0);
  Tmov = MRIxfmCRS2XYZ(mov,0);

  Kref = MRIxfmCRS2XYZtkreg(ref);
  Kmov = MRIxfmCRS2XYZtkreg(mov);

  /* D = Tmov * inv(Kmov) * R * Kref *inv(Tref) */
  invKmov = MatrixInverse(Kmov,NULL);
  invTref = MatrixInverse(Tref,NULL);

  D = MatrixMultiply(Tmov,invKmov,NULL);
  if(R!=NULL) MatrixMultiply(D,R,D);
  MatrixMultiply(D,Kref,D);
  MatrixMultiply(D,invTref,D);

  if(0) {
    printf("MRITkReg2Native -----------------------------\n");
    printf("Tref ----------------\n");
    MatrixPrint(stdout,Tref);
    printf("Tmov ----------------\n");
    MatrixPrint(stdout,Tmov);
    printf("Kref ----------------\n");
    MatrixPrint(stdout,Kref);
    printf("Kmov ----------------\n");
    MatrixPrint(stdout,Kmov);
    printf("------------------------------------------\n");
  }

  MatrixFree(&Kref);
  MatrixFree(&Tref);
  MatrixFree(&Kmov);
  MatrixFree(&Tmov);
  MatrixFree(&invKmov);
  MatrixFree(&invTref);

  return(D);
}
/*----------------------------------------------------------------
  MRItkRegMtx() - creates a tkregsiter-compatible matrix from the
  matrix D that aligns the two volumes assuming the native geometry.
  This is the counterpart to MRITkReg2Native(). If D is null, it is
  assumed to be the identity. R: MovXYZ = R*RefXYZ. Typically,
  Ref is the Anatomical Reference, and Mov is the functional.
  Use this function with D=NULL when the two volumes have been
  aligned with SPM.
  ---------------------------------------------------------------*/
MATRIX *MRItkRegMtx(MRI *ref, MRI *mov, MATRIX *D)
{
  MATRIX *Kref, *Kmov;
  MATRIX *Tref, *Tmov, *R;
  MATRIX *invTmov, *invKref;

  /* Native Goemetry */
  Tref = MRIxfmCRS2XYZ(ref,0);
  Tmov = MRIxfmCRS2XYZ(mov,0);

  /* TkReg Goemetry */
  Kref = MRIxfmCRS2XYZtkreg(ref);
  Kmov = MRIxfmCRS2XYZtkreg(mov);

  invTmov = MatrixInverse(Tmov,NULL);
  invKref = MatrixInverse(Kref,NULL);

  /* R = Kmov * inv(Tmov) * D * Tref *inv(Kref) */
  R = MatrixMultiply(Kmov,invTmov,NULL);
  if(D != NULL) MatrixMultiply(R,D,R);
  MatrixMultiply(R,Tref,R);
  MatrixMultiply(R,invKref,R);

  MatrixFree(&Kref);
  MatrixFree(&Tref);
  MatrixFree(&Kmov);
  MatrixFree(&Tmov);
  MatrixFree(&invTmov);
  MatrixFree(&invKref);

  return(R);
}
/*-------------------------------------------------------------
  MRIfixTkReg() - this routine will adjust a matrix created by the
  "old" tkregister program. The old program had a problem in the way
  it chose the CRS of a voxel in the functional volume based on a
  point in the anatomical volume. The functional CRS of a point in
  anatomical space rarely (if ever) falls directly on a functional
  voxel, so it's necessary to choose a functional voxel given that the
  point falls between functional voxels (or it can be interpolated).
  The old tkregister program did not interpolate, rather it would
  choose the CRS in the following way: iC = floor(fC), iR = ceil(fR),
  and iS = floor(fS), where iC is the integer column number and fC is
  the floating point column, etc. Unfortunately, this is not nearest
  neighbor and it's not invertible. The right way to do it is to do
  nearest neighbor (ie, round to the closest integer). Unfortunately,
  there are a lot of data sets out there that have been regsitered
  with the old program, and we don't want to force poeple to
  reregister with the "new" program. This routine attempts to adjust
  the matrix created with the old program so that it will work with
  code that assumes that pure nearest neighbor was used.

  It does this by randomly sampling the anatomical volume in xyz
  and computing the tkreg'ed CRS for each point.

  Pcrs = inv(Tmov)*R*Pxyz
  PcrsTkReg = fcf(Pcrs) -- fcf is floor ceiling floor

  We seek a new R (Rfix) define with

  PcrsFix = inv(Tmov)*Rfix*Pxyz

  such that that the difference between PcrsFix and PcrsTkReg are
  minimized. To do this, we set

  PcrsFix = PcrsTkReg = inv(Tmov)*Rfix*Pxyz

  and solve for Rfix (this is an LMS solution):

  Rfix = Tmov*(PcrsTkReg*Pxyz')*inv(Pxyz*Pxyz');

  Applications that read in the registration matrix should detect the
  truncation method used (see below), fix the matrix if necessary, and
  proceed as if nearest neighbor/rounding was used.  The type of
  truncation can be determined from the last line of the registration
  file (after the matrix itself). If there is nothing there or the
  string "tkregister" is there, then the matrix should be
  converted. Otherwise, the string "round" should be there. The
  function regio_read_register (from registerio.c) will return the
  type of matrix in the float2int variable. It will be either
  FLT2INT_TKREG or FLT2INT_ROUND (constants defined in resample.h).
  ---------------------------------------------------------------*/
MATRIX *MRIfixTkReg(MRI *mov, MATRIX *R)
{
  int n, ntest = 1000;
  MATRIX *Pxyz, *Pcrs, *PcrsTkReg;
  MATRIX *PxyzT, *PxyzPxyzT, *invPxyzPxyzT;
  MATRIX *Tmov,*invTmov,*Rfix;
  MATRIX *tmp;
  float xrange, yrange, zrange;

  /* Assume a COR reference image */
  xrange = 256.0;
  yrange = 256.0;
  zrange = 256.0;

  Tmov = MRIxfmCRS2XYZtkreg(mov);
  invTmov = MatrixInverse(Tmov,NULL);

  Pxyz      = MatrixAlloc(4,ntest,MATRIX_REAL);
  PcrsTkReg = MatrixAlloc(4,ntest,MATRIX_REAL);

  /* Fill xyz with rand within the reference volume range */
  for(n=0;n<ntest;n++) {
    Pxyz->rptr[1][n+1] = xrange * (drand48()-0.5);
    Pxyz->rptr[2][n+1] = yrange * (drand48()-0.5);
    Pxyz->rptr[3][n+1] = zrange * (drand48()-0.5);
    Pxyz->rptr[4][n+1] = 1;
  }

  /* Compute floating mov CRS from targ XYZ */
  /* Pcrs = inv(Tmov)*R*Pxyz */
  tmp = MatrixMultiply(R,Pxyz,NULL);
  Pcrs = MatrixMultiply(invTmov,tmp,NULL);
  MatrixFree(&tmp);

  /* Truncate floating mov CRS using tkregister method*/
  for(n=0;n<ntest;n++) {
    PcrsTkReg->rptr[1][n+1] = floor(Pcrs->rptr[1][n+1]);
    PcrsTkReg->rptr[2][n+1] =  ceil(Pcrs->rptr[2][n+1]);
    PcrsTkReg->rptr[3][n+1] = floor(Pcrs->rptr[3][n+1]);
    PcrsTkReg->rptr[4][n+1] = 1;
  }
  MatrixFree(&Pcrs);

  //Rfix = Tmov*(PcrsTkreg*Pxyz')*inv(Pxyz*Pxyz');
  PxyzT = MatrixTranspose(Pxyz,NULL);
  PxyzPxyzT = MatrixMultiply(Pxyz,PxyzT,NULL);
  invPxyzPxyzT = MatrixInverse(PxyzPxyzT,NULL);
  tmp = MatrixMultiply(PcrsTkReg,PxyzT,NULL);
  MatrixMultiply(Tmov,tmp,tmp);
  Rfix = MatrixMultiply(tmp,invPxyzPxyzT,NULL);


  MatrixFree(&Pxyz);
  MatrixFree(&PcrsTkReg);
  MatrixFree(&PxyzT);
  MatrixFree(&PxyzPxyzT);
  MatrixFree(&invPxyzPxyzT);
  MatrixFree(&Tmov);
  MatrixFree(&invTmov);
  MatrixFree(&tmp);

  return(Rfix);
}

/*-------------------------------------------------------------------
  MRIfsl2TkReg() - converts an FSL registration matrix to one
  compatible with tkregister.  Note: the FSL matrix is assumed to map
  from the mov to the ref whereas the tkreg matrix maps from the ref
  to the mov.

  mov voxel --(Tmov)--> tkRegXYZ(mov)
  ^                          ^
  | inv(Dmov)                |
  |                          |
  mov' physvox                |
  ^                          |
  | inv(Mfsl)                | R
  |                          |
  ref' physvox                |
  ^                          |
  | Dref                     |
  |                          |
  ref voxel <--inv(Tref)--tkRegXYZ(ref)
  -------------------------------------------------------------------*/
MATRIX *MRIfsl2TkReg(MRI *ref, MRI *mov, MATRIX *FSLRegMat)
{
  MATRIX *RegMat=NULL, *invDmov, *Tmov, *Dref;
  MATRIX *invFSLRegMat, *invTref, *Tref;

  /* R = Tmov * inv(Dmov) * inv(Mfsl) * Dref * inv(Tref) */
  invDmov = MatrixAlloc(4,4,MATRIX_REAL);
  invDmov->rptr[1][1] = 1.0/mov->xsize;
  invDmov->rptr[2][2] = 1.0/mov->ysize;
  invDmov->rptr[3][3] = 1.0/mov->zsize;
  invDmov->rptr[4][4] = 1.0;

  Dref = MatrixAlloc(4,4,MATRIX_REAL);
  Dref->rptr[1][1] = ref->xsize;
  Dref->rptr[2][2] = ref->ysize;
  Dref->rptr[3][3] = ref->zsize;
  Dref->rptr[4][4] = 1.0;

  Tmov = MRIxfmCRS2XYZtkreg(mov);
  Tref = MRIxfmCRS2XYZtkreg(ref);
  invTref = MatrixInverse(Tref,NULL);

  invFSLRegMat = MatrixInverse(FSLRegMat,NULL);

  RegMat = MatrixMultiply(Tmov,invDmov,RegMat);
  RegMat = MatrixMultiply(RegMat,invFSLRegMat,RegMat);
  RegMat = MatrixMultiply(RegMat,Dref,RegMat);
  RegMat = MatrixMultiply(RegMat,invTref,RegMat);

  MatrixFree(&invDmov);
  MatrixFree(&FSLRegMat);
  MatrixFree(&invFSLRegMat);
  MatrixFree(&Tmov);
  MatrixFree(&Tref);
  MatrixFree(&invTref);
  MatrixFree(&Dref);

  return(RegMat);
}
/*-------------------------------------------------------------------
  MRItkreg2FSL() - converts tkregister registration matrix to one
  compatible with FSL. Note: the FSL matrix is assumed to map from the
  mov to the ref whereas the tkreg matrix maps from the ref to the
  mov.

  mov voxel<--inv(Tmov)-- tkRegXYZ(mov)
  |                           ^
  |  Dmov                     |
  V                           |
  mov' physvox                 |
  |                           |
  |  Mfsl                     | R
  V                           |
  ref' physvox                 |
  |                           |
  |  inv(Dref)                |
  V                           |
  ref voxel -- Tref  -->  tkRegXYZ(ref)

  -------------------------------------------------------------------*/
MATRIX *MRItkreg2FSL(MRI *ref, MRI *mov, MATRIX *tkRegMat)
{
  MATRIX *FSLRegMat=NULL, *Dmov, *Tmov, *invTmov, *Tref, *Dref, *invDref;

  /* R = Tmov * inv(Dmov) * inv(Mfsl) * Dref * inv(Tref) */
  /* Mfsl =  inv( Dmov * inv(Tmov) * R * Tref * inv(Dref)) */
  Dmov = MatrixAlloc(4,4,MATRIX_REAL);
  Dmov->rptr[1][1] = mov->xsize;
  Dmov->rptr[2][2] = mov->ysize;
  Dmov->rptr[3][3] = mov->zsize;
  Dmov->rptr[4][4] = 1.0;

  Dref = MatrixAlloc(4,4,MATRIX_REAL);
  Dref->rptr[1][1] = ref->xsize;
  Dref->rptr[2][2] = ref->ysize;
  Dref->rptr[3][3] = ref->zsize;
  Dref->rptr[4][4] = 1.0;
  invDref = MatrixInverse(Dref,NULL);

  Tmov = MRIxfmCRS2XYZtkreg(mov);
  invTmov = MatrixInverse(Tmov,NULL);
  Tref = MRIxfmCRS2XYZtkreg(ref);

  FSLRegMat = MatrixMultiply(Dmov,invTmov,FSLRegMat);
  FSLRegMat = MatrixMultiply(FSLRegMat,tkRegMat,FSLRegMat);
  FSLRegMat = MatrixMultiply(FSLRegMat,Tref,FSLRegMat);
  FSLRegMat = MatrixMultiply(FSLRegMat,invDref,FSLRegMat);
  FSLRegMat = MatrixInverse(FSLRegMat,FSLRegMat);

  if(0){
    printf("--- Dmov ---------------------\n");
    MatrixPrint(stdout,Dmov);
    printf("--- Tmov ---------------------\n");
    MatrixPrint(stdout,Tmov);
    printf("--- R ---------------------\n");
    MatrixPrint(stdout,tkRegMat);
    printf("--- Tref ---------------------\n");
    MatrixPrint(stdout,Tref);
    printf("--- Dref ---------------------\n");
    MatrixPrint(stdout,Dref);
    printf("--- Rfsl ---------------------\n");
    MatrixPrint(stdout,FSLRegMat);
    printf("--- R (from Rfsl) ------------\n");
    tkRegMat = MRIfsl2TkReg(ref,mov,FSLRegMat);
    MatrixPrint(stdout,tkRegMat);
  }

  MatrixFree(&Dmov);
  MatrixFree(&Tmov);
  MatrixFree(&invTmov);
  MatrixFree(&Tref);
  MatrixFree(&Dref);
  MatrixFree(&invDref);

  return(FSLRegMat);
}

/*--------------------------------------------------------------------------
  MtxCRS1toCRS0() - generates a matrix that will convert 1-based CRS (as
  found in SPM matrices) to 0-based CRS, ie, CRS0 = Q*CRS1 (assuming that
  CRS1 has been packed with a 1 in the 4th component.
  --------------------------------------------------------------------------*/
MATRIX *MtxCRS1toCRS0(MATRIX *Q)
{
  int r,c;

  if(Q == NULL) Q = MatrixAlloc(4,4,MATRIX_REAL);
  else{
    if(Q->rows != 4 || Q->cols != 4){
      printf("ERROR: MtxCRS1toCRS0(): input matrix is not 4x4\n");
      return(NULL);
    }
  }

  for(r=1;r<=4;r++){
    for(c=1;c<=4;c++){
      if(r==c || c==4) Q->rptr[r][c] = 1.0;
      else             Q->rptr[r][c] = 0.0;
    }
  }

  return(Q);
}


/*-------------------------------------------------------------------
  MRIgetVoxVal() - returns voxel value as a float regardless of
  the underlying data type.
  -------------------------------------------------------------------*/
float MRIgetVoxVal(MRI *mri, int c, int r, int s, int f)
{
  switch(mri->type){
  case MRI_UCHAR: return( (float) MRIseq_vox(mri,c,r,s,f)); break;
  case MRI_SHORT: return( (float) MRISseq_vox(mri,c,r,s,f)); break;
  case MRI_INT:   return( (float) MRIIseq_vox(mri,c,r,s,f)); break;
  case MRI_LONG:  return( (float) MRILseq_vox(mri,c,r,s,f)); break;
  case MRI_FLOAT: return( (float) MRIFseq_vox(mri,c,r,s,f)); break;
  }
  return(-10000000000.9);
}
/*-------------------------------------------------------------------
  MRIsetVoxVal() - sets voxel value to that passed as the float
  voxval, regardless of the underlying data type. If the underlying
  type is integer-based, then it is rounded to the nearest integer. No
  attempt is made to prevent overflows.
  -------------------------------------------------------------------*/
int MRIsetVoxVal(MRI *mri, int c, int r, int s, int f, float voxval)
{
  switch(mri->type){
  case MRI_UCHAR: MRIseq_vox(mri,c,r,s,f)  = nint(voxval); break;
  case MRI_SHORT: MRISseq_vox(mri,c,r,s,f) = nint(voxval); break;
  case MRI_INT:   MRIIseq_vox(mri,c,r,s,f) = nint(voxval); break;
  case MRI_LONG:  MRILseq_vox(mri,c,r,s,f) = nint(voxval); break;
  case MRI_FLOAT: MRIFseq_vox(mri,c,r,s,f) = voxval;       break;
  default: return(1); break;
  }
  return(0);
}

/*------------------------------------------------------------------
  MRIinterpCode() - returns the numeric interpolation code given the
  name of the interpolation method.
  -----------------------------------------------------------------*/
int MRIinterpCode(char *InterpString)
{
  if(!strncasecmp(InterpString,"nearest",3))
    return(SAMPLE_NEAREST);
  if(!strncasecmp(InterpString,"trilinear",3))
    return(SAMPLE_TRILINEAR);
  if(!strncasecmp(InterpString,"sinc",3))
    return(SAMPLE_SINC);
  if(!strncasecmp(InterpString,"cubic",3))
    return(SAMPLE_CUBIC);

  return(-1);
}

/*------------------------------------------------------------------
  MRIinterpString() - returns the the name of the interpolation method
  given numeric interpolation code
  -----------------------------------------------------------------*/
char * MRIinterpString(int InterpCode)
{
  switch (InterpCode)
    {
    case SAMPLE_NEAREST:   return("nearest"); break ;
    case SAMPLE_TRILINEAR: return("trilinear"); break ;
    case SAMPLE_SINC:      return("sinc"); break ;
    case SAMPLE_CUBIC:     return("cubic"); break ;
    }
  return(NULL);
}
/*------------------------------------------------------------------
  MRIprecisionCode() - returns the numeric code given the
  name of the precision. This corresponds to the value of the type
  field in the MRI structure.
  -----------------------------------------------------------------*/
int MRIprecisionCode(char *PrecisionString)
{
  if(!strcasecmp(PrecisionString,"uchar"))
    return(MRI_UCHAR);
  if(!strcasecmp(PrecisionString,"short"))
    return(MRI_SHORT);
  if(!strcasecmp(PrecisionString,"int"))
    return(MRI_INT);
  if(!strcasecmp(PrecisionString,"long"))
    return(MRI_LONG);
  if(!strcasecmp(PrecisionString,"float"))
    return(MRI_FLOAT);

  return(-1);
}

/*------------------------------------------------------------------
  MRIprecisionString() - returns the the name of the precision given
  numeric precision code. The code corresponds to the value of the
  type field in the MRI structure.
  -----------------------------------------------------------------*/
char * MRIprecisionString(int PrecisionCode)
{
  switch (PrecisionCode)
    {
    case MRI_UCHAR: return("uchar"); break ;
    case MRI_SHORT: return("short"); break ;
    case MRI_INT:   return("int"); break ;
    case MRI_LONG:  return("long"); break ;
    case MRI_FLOAT: return("float"); break ;
    }
  return(NULL);
}

/*-----------------------------------------------------
  ------------------------------------------------------*/
int
MRImatch(MRI *mri1, MRI *mri2)
{
  return(
         (mri1->width == mri2->width) &&
         (mri1->height == mri2->height) &&
         (mri1->depth == mri2->depth) &&
         (mri1->type == mri2->type)
         ) ;
}
/*-----------------------------------------------------
  ------------------------------------------------------*/
MRI *
MRIscalarMul(MRI *mri_src, MRI *mri_dst, float scalar)
{
  int     width, height, depth, x, y, z, frame ;
  BUFTYPE *psrc, *pdst ;
  float   *pfsrc, *pfdst, dval ;
  short   *pssrc, *psdst ;

  width = mri_src->width ;
  height = mri_src->height ;
  depth = mri_src->depth ;
  if (!mri_dst)
    mri_dst = MRIclone(mri_src, NULL) ;

  for (frame = 0 ; frame < mri_src->nframes ; frame++)
    {
      for (z = 0 ; z < depth ; z++)
        {
          for (y = 0 ; y < height ; y++)
            {
              switch (mri_src->type)
                {
                case MRI_UCHAR:
                  psrc = &MRIseq_vox(mri_src, 0, y, z, frame) ;
                  pdst = &MRIseq_vox(mri_dst, 0, y, z, frame) ;
                  for (x = 0 ; x < width ; x++)
                    {
                      dval = *psrc++ * scalar ;
                      if (dval < 0)
                        dval = 0 ;
                      if (dval > 255)
                        dval = 255 ;
                      *pdst++ = dval ;
                    }
                  break ;
                case MRI_FLOAT:
                  pfsrc = &MRIFseq_vox(mri_src, 0, y, z, frame) ;
                  pfdst = &MRIFseq_vox(mri_dst, 0, y, z, frame) ;
                  for (x = 0 ; x < width ; x++)
                    *pfdst++ = *pfsrc++ * scalar ;
                  break ;
                case MRI_SHORT:
                  pssrc = &MRISseq_vox(mri_src, 0, y, z, frame) ;
                  psdst = &MRISseq_vox(mri_dst, 0, y, z, frame) ;
                  for (x = 0 ; x < width ; x++)
                    *psdst++ = (short)nint((float)*pssrc++ * scalar) ;
                  break ;
                default:
                  ErrorReturn
                    (NULL,
                     (ERROR_UNSUPPORTED,
                      "MRIscalarMul: unsupported type %d", mri_src->type)) ;
                }
            }
        }
    }
  return(mri_dst) ;
}
/*-----------------------------------------------------
  ------------------------------------------------------*/
MRI *
MRIscalarMulFrame(MRI *mri_src, MRI *mri_dst, float scalar, int frame)
{
  int     width, height, depth, x, y, z ;
  BUFTYPE *psrc, *pdst ;
  float   *pfsrc, *pfdst, dval ;
  short   *pssrc, *psdst ;

  width = mri_src->width ;
  height = mri_src->height ;
  depth = mri_src->depth ;
  if (!mri_dst)
    mri_dst = MRIclone(mri_src, NULL) ;

  for (z = 0 ; z < depth ; z++)
    {
      for (y = 0 ; y < height ; y++)
        {
          switch (mri_src->type)
            {
            case MRI_UCHAR:
              psrc = &MRIseq_vox(mri_src, 0, y, z, frame) ;
              pdst = &MRIseq_vox(mri_dst, 0, y, z, frame) ;
              for (x = 0 ; x < width ; x++)
                {
                  dval = *psrc++ * scalar ;
                  if (dval < 0)
                    dval = 0 ;
                  if (dval > 255)
                    dval = 255 ;
                  *pdst++ = dval ;
                }
              break ;
            case MRI_FLOAT:
              pfsrc = &MRIFseq_vox(mri_src, 0, y, z, frame) ;
              pfdst = &MRIFseq_vox(mri_dst, 0, y, z, frame) ;
              for (x = 0 ; x < width ; x++)
                *pfdst++ = *pfsrc++ * scalar ;
              break ;
            case MRI_SHORT:
              pssrc = &MRISseq_vox(mri_src, 0, y, z, frame) ;
              psdst = &MRISseq_vox(mri_dst, 0, y, z, frame) ;
              for (x = 0 ; x < width ; x++)
                *psdst++ = (short)nint((float)*pssrc++ * scalar) ;
              break ;
            default:
              ErrorReturn
                (NULL,
                 (ERROR_UNSUPPORTED,
                  "MRIscalarMulFrame: unsupported type %d", mri_src->type)) ;
            }
        }
    }
  return(mri_dst) ;
}
/*-----------------------------------------------------
  ------------------------------------------------------*/
int
MRIvalRange(MRI *mri, float *pmin, float *pmax)
{
  int      width, height, depth, x, y, z, frame ;
  float    fmin, fmax, *pf, val ;
  BUFTYPE  *pb ;

  width = mri->width ;
  height = mri->height ;
  depth = mri->depth ;

  fmin = 10000.0f ;
  fmax = -10000.0f ;
  switch (mri->type)
    {
    case MRI_FLOAT:
      for (frame = 0 ; frame < mri->nframes ; frame++)
        {
          for (z = 0 ; z < depth ; z++)
            {
              for (y = 0 ; y < height ; y++)
                {
                  pf = &MRIFseq_vox(mri, 0, y, z, frame) ;
                  for (x = 0 ; x < width ; x++)
                    {
                      val = *pf++ ;
                      if (val < fmin)
                        fmin = val ;
                      if (val > fmax)
                        fmax = val ;
                    }
                }
            }
        }
      break ;
    case MRI_INT:
      for (frame = 0 ; frame < mri->nframes ; frame++)
        {
          for (z = 0 ; z < depth ; z++)
            {
              for (y = 0 ; y < height ; y++)
                {
                  for (x = 0 ; x < width ; x++)
                    {
                      val = (float)MRIIseq_vox(mri, x, y, z, frame) ;
                      if (val < fmin)
                        fmin = val ;
                      if (val > fmax)
                        fmax = val ;
                    }
                }
            }
        }
      break ;
    case MRI_SHORT:
      for (frame = 0 ; frame < mri->nframes ; frame++)
        {
          for (z = 0 ; z < depth ; z++)
            {
              for (y = 0 ; y < height ; y++)
                {
                  for (x = 0 ; x < width ; x++)
                    {
                      val = (float)MRISseq_vox(mri, x, y, z, frame) ;
                      if (val < fmin)
                        fmin = val ;
                      if (val > fmax)
                        fmax = val ;
                    }
                }
            }
        }
      break ;
    case MRI_UCHAR:
      for (frame = 0 ; frame < mri->nframes ; frame++)
        {
          for (z = 0 ; z < depth ; z++)
            {
              for (y = 0 ; y < height ; y++)
                {
                  pb = &MRIseq_vox(mri, 0, y, z, frame) ;
                  for (x = 0 ; x < width ; x++)
                    {
                      val = (float)*pb++ ;
                      if (val < fmin)
                        fmin = val ;
                      if (val > fmax)
                        fmax = val ;
                    }
                }
            }
        }
      break ;
    default:
      for (frame = 0 ; frame < mri->nframes ; frame++)
        {
          for (z = 0 ; z < depth ; z++)
            {
              for (y = 0 ; y < height ; y++)
                {
                  for (x = 0 ; x < width ; x++)
                    {
                      val = (float)MRIgetVoxVal(mri, x, y, z, frame) ;
                      if (val < fmin)
                        fmin = val ;
                      if (val > fmax)
                        fmax = val ;
                    }
                }
            }
        }
      break ;
    }

  *pmin = fmin ;
  *pmax = fmax ;
  return(NO_ERROR) ;
}
int
MRInonzeroValRange(MRI *mri, float *pmin, float *pmax)
{
  int      width, height, depth, x, y, z, frame ;
  float    fmin, fmax, val ;

  width = mri->width ;
  height = mri->height ;
  depth = mri->depth ;

  fmin = 10000.0f ;
  fmax = -10000.0f ;
  for (frame = 0 ; frame < mri->nframes ; frame++)
    {
      for (z = 0 ; z < depth ; z++)
        {
          for (y = 0 ; y < height ; y++)
            {
              for (x = 0 ; x < width ; x++)
                {
                  val = MRIgetVoxVal(mri, x, y, z, 0) ;
                  if (FZERO(val))
                    continue ;
                  if (val < fmin)
                    fmin = val ;
                  if (val > fmax)
                    fmax = val ;
                }
            }
        }
    }

  *pmin = fmin ;
  *pmax = fmax ;
  return(NO_ERROR) ;
}
/*-----------------------------------------------------
  ------------------------------------------------------*/
int
MRIvalRangeFrame(MRI *mri, float *pmin, float *pmax, int frame)
{
  int      width, height, depth, x, y, z ;
  float    fmin, fmax, *pf, val ;
  BUFTYPE  *pb ;

  width = mri->width ;
  height = mri->height ;
  depth = mri->depth ;

  fmin = 10000.0f ;
  fmax = -10000.0f ;
  switch (mri->type)
    {
    case MRI_FLOAT:
      for (z = 0 ; z < depth ; z++)
        {
          for (y = 0 ; y < height ; y++)
            {
              pf = &MRIFseq_vox(mri, 0, y, z, frame) ;
              for (x = 0 ; x < width ; x++)
                {
                  val = *pf++ ;
                  if (val < fmin)
                    fmin = val ;
                  if (val > fmax)
                    fmax = val ;
                }
            }
        }
      break ;
    case MRI_INT:
      for (z = 0 ; z < depth ; z++)
        {
          for (y = 0 ; y < height ; y++)
            {
              for (x = 0 ; x < width ; x++)
                {
                  val = (float)MRIIseq_vox(mri, x, y, z, frame) ;
                  if (val < fmin)
                    fmin = val ;
                  if (val > fmax)
                    fmax = val ;
                }
            }
        }
      break ;
    case MRI_SHORT:
      for (z = 0 ; z < depth ; z++)
        {
          for (y = 0 ; y < height ; y++)
            {
              for (x = 0 ; x < width ; x++)
                {
                  val = (float)MRISseq_vox(mri, x, y, z, frame) ;
                  if (val < fmin)
                    fmin = val ;
                  if (val > fmax)
                    fmax = val ;
                }
            }
        }
      break ;
    case MRI_UCHAR:
      for (z = 0 ; z < depth ; z++)
        {
          for (y = 0 ; y < height ; y++)
            {
              pb = &MRIseq_vox(mri, 0, y, z, frame) ;
              for (x = 0 ; x < width ; x++)
                {
                  val = (float)*pb++ ;
                  if (val < fmin)
                    fmin = val ;
                  if (val > fmax)
                    fmax = val ;
                }
            }
        }
      break ;
    default:
      ErrorReturn(ERROR_UNSUPPORTED,
                  (ERROR_UNSUPPORTED, "MRIvalRange: unsupported type %d",
                   mri->type)) ;
    }

  *pmin = fmin ;
  *pmax = fmax ;
  return(NO_ERROR) ;
}
/*-----------------------------------------------------
  ------------------------------------------------------*/
int
MRIvalRangeRegion(MRI *mri, float *pmin, float *pmax, MRI_REGION *region)
{
  int      width, height, depth, x, y, z, x0, y0, z0 ;
  float    fmin, fmax, *pf, val ;
  BUFTYPE  *pb ;

  width = region->x + region->dx ;
  if (width > mri->width)
    width = mri->width ;
  height = region->y + region->dy ;
  if (height > mri->height)
    height = mri->height ;
  depth = region->z + region->dz ;
  if (depth > mri->depth)
    depth = mri->depth ;
  x0 = region->x ;
  if (x0 < 0)
    x0 = 0 ;
  y0 = region->y ;
  if (y0 < 0)
    y0 = 0 ;
  z0 = region->z ;
  if (z0 < 0)
    z0 = 0 ;

  fmin = 10000.0f ;
  fmax = -10000.0f ;
  switch (mri->type)
    {
    default:
      for (z = z0 ; z < depth ; z++)
        {
          for (y = y0 ; y < height ; y++)
            {
              pf = &MRIFvox(mri, x0, y, z) ;
              for (x = x0 ; x < width ; x++)
                {
                  val = MRIgetVoxVal(mri, x, y, z, 0) ;
                  if (val < fmin)
                    fmin = val ;
                  if (val > fmax)
                    fmax = val ;
                }
            }
        }
      break ;

    case MRI_FLOAT:
      for (z = z0 ; z < depth ; z++)
        {
          for (y = y0 ; y < height ; y++)
            {
              pf = &MRIFvox(mri, x0, y, z) ;
              for (x = x0 ; x < width ; x++)
                {
                  val = *pf++ ;
                  if (val < fmin)
                    fmin = val ;
                  if (val > fmax)
                    fmax = val ;
                }
            }
        }
      break ;
    case MRI_UCHAR:
      for (z = z0 ; z < depth ; z++)
        {
          for (y = y0 ; y < height ; y++)
            {
              pb = &MRIvox(mri, x0, y, z) ;
              for (x = x0 ; x < width ; x++)
                {
                  val = (float)*pb++ ;
                  if (val < fmin)
                    fmin = val ;
                  if (val > fmax)
                    fmax = val ;
                }
            }
        }
      break ;
    }

  *pmin = fmin ;
  *pmax = fmax ;
  return(NO_ERROR) ;
}
/*-----------------------------------------------------
  ------------------------------------------------------*/
MRI_REGION *
MRIclipRegion(MRI *mri, MRI_REGION *reg_src, MRI_REGION *reg_clip)
{
  int  x2, y2, z2 ;

  x2 = MIN(mri->width-1, reg_src->x + reg_src->dx - 1) ;
  y2 = MIN(mri->height-1, reg_src->y + reg_src->dy - 1) ;
  z2 = MIN(mri->depth-1, reg_src->z + reg_src->dz - 1) ;
  reg_clip->x = MAX(0, reg_src->x) ;
  reg_clip->y = MAX(0, reg_src->y) ;
  reg_clip->z = MAX(0, reg_src->z) ;
  reg_clip->dx = x2 - reg_clip->x + 1 ;
  reg_clip->dy = y2 - reg_clip->y + 1 ;
  reg_clip->dz = z2 - reg_clip->z + 1 ;
  return(reg_clip) ;
}
/*-----------------------------------------------------
  ------------------------------------------------------*/
MRI *
MRIvalScale(MRI *mri_src, MRI *mri_dst, float flo, float fhi)
{
  int      width, height, depth, x, y, z ;
  float    fmin, fmax, *pf_src, *pf_dst, val, scale ;
  short    *ps_src, *ps_dst ;
  BUFTYPE  *pb_src, *pb_dst ;

  if (!mri_dst)
    mri_dst = MRIclone(mri_src, NULL) ;

  MRIvalRange(mri_src, &fmin, &fmax) ;
  scale = (fhi - flo) / (fmax - fmin) ;

  width = mri_src->width ;
  height = mri_src->height ;
  depth = mri_src->depth ;

  switch (mri_src->type)
    {
    case MRI_FLOAT:
      for (z = 0 ; z < depth ; z++)
        {
          for (y = 0 ; y < height ; y++)
            {
              pf_src = &MRIFvox(mri_src, 0, y, z) ;
              pf_dst = &MRIFvox(mri_dst, 0, y, z) ;
              for (x = 0 ; x < width ; x++)
                {
                  val = *pf_src++ ;
                  *pf_dst++ = (val - fmin) * scale + flo ;
                }
            }
        }
      break ;
    case MRI_SHORT:
      for (z = 0 ; z < depth ; z++)
        {
          for (y = 0 ; y < height ; y++)
            {
              ps_src = &MRISvox(mri_src, 0, y, z) ;
              ps_dst = &MRISvox(mri_dst, 0, y, z) ;
              for (x = 0 ; x < width ; x++)
                {
                  val = (float)(*ps_src++) ;
                  *ps_dst++ = (short)nint((val - fmin) * scale + flo) ;
                }
            }
        }
      break ;
    case MRI_UCHAR:
      for (z = 0 ; z < depth ; z++)
        {
          for (y = 0 ; y < height ; y++)
            {
              pb_src = &MRIvox(mri_src, 0, y, z) ;
              pb_dst = &MRIvox(mri_dst, 0, y, z) ;
              for (x = 0 ; x < width ; x++)
                {
                  val = (float)*pb_src++ ;
                  *pb_dst++ = (BUFTYPE)nint((val - fmin) * scale + flo) ;
                }
            }
        }
      break ;
    default:
      ErrorReturn(mri_dst,
                  (ERROR_UNSUPPORTED, "MRIvalScale: unsupported type %d",
                   mri_src->type)) ;
    }

  return(mri_dst) ;
}
/*-----------------------------------------------------
  ------------------------------------------------------*/
MRI *
MRIconfThresh(MRI *mri_src, MRI *mri_probs, MRI *mri_classes, MRI *mri_dst,
              float thresh, int min_target, int max_target)
{
  int      x, y, z, width, height, depth, class ;
  float    *pprobs, prob ;
  BUFTYPE  *pclasses, *pdst, *psrc, src ;

  if (!mri_dst)
    mri_dst = MRIclone(mri_classes, NULL) ;

  width = mri_classes->width ;
  height = mri_classes->height ;
  depth = mri_classes->depth ;

  for (z = 0 ; z < depth ; z++)
    {
      for (y = 0 ; y < height ; y++)
        {
          pprobs = &MRIFvox(mri_probs, 0, y, z) ;
          pclasses = &MRIvox(mri_classes, 0, y, z) ;
          pdst = &MRIvox(mri_dst, 0, y, z) ;
          psrc = &MRIvox(mri_src, 0, y, z) ;
          for (x = 0 ; x < width ; x++)
            {
              src = *psrc++ ;
              prob = *pprobs++ ;
              class = (int)*pclasses++ ;
              if (prob >= thresh &&
                  ((class >= min_target) && (class <= max_target)))
                *pdst++ = src ;
              else if ((class >= min_target) && (class <= max_target))
                *pdst++ = 25 ;
              else
                *pdst++ = 0 ;
            }
        }
    }
  return(mri_dst) ;
}
/*-----------------------------------------------------
  ------------------------------------------------------*/
int
MRIboundingBoxNbhd(MRI *mri, int thresh, int wsize,MRI_REGION *box)
{
  int      width, height, depth, x, y, z, x1, y1, z1, xi, yi, zi, xk, yk, zk,
    whalf, in_brain ;
  BUFTYPE  *psrc ;
  float    *pfsrc ;
  short    *pssrc ;

  whalf = (wsize-1)/2 ;
  box->dx = width = mri->width ;
  box->dy = height = mri->height ;
  box->dz = depth = mri->depth ;

  x1 = y1 = z1 = 0 ;
  box->x = width-1 ;
  box->y = height-1 ;
  box->z = depth-1 ;
  switch (mri->type)
    {
    case MRI_UCHAR:
      for (z = 0 ; z < depth ; z++)
        {
          for (y = 0 ; y < height ; y++)
            {
              psrc = &MRIvox(mri, 0, y, z) ;
              for (x = 0 ; x < width ; x++)
                {
                  if (*psrc++ > thresh)
                    {
                      in_brain = 1 ;
                      for (zk = -whalf ; in_brain && zk <= whalf ; zk++)
                        {
                          zi = mri->zi[z+zk] ;
                          for (yk = -whalf ; in_brain && yk <= whalf ; yk++)
                            {
                              yi = mri->yi[y+yk] ;
                              for (xk = -whalf ;
                                   in_brain && xk <= whalf ;
                                   xk++)
                                {
                                  xi = mri->xi[x+xk] ;
                                  if (MRIvox(mri, xi, yi, zi) < thresh)
                                    in_brain = 0 ;
                                }
                            }
                        }
                      if (in_brain)
                        {
                          if (x < box->x)
                            box->x = x ;
                          if (y < box->y)
                            box->y = y ;
                          if (z < box->z)
                            box->z = z ;
                          if (x > x1)
                            x1 = x ;
                          if (y > y1)
                            y1 = y ;
                          if (z > z1)
                            z1 = z ;
                        }
                    }
                }
            }
        }
      break ;
    case MRI_SHORT:
      for (z = 0 ; z < depth ; z++)
        {
          for (y = 0 ; y < height ; y++)
            {
              pssrc = &MRISvox(mri, 0, y, z) ;
              for (x = 0 ; x < width ; x++)
                {
                  if (*pssrc++ > thresh)
                    {
                      in_brain = 1 ;
                      for (zk = -whalf ; in_brain && zk <= whalf ; zk++)
                        {
                          zi = mri->zi[z+zk] ;
                          for (yk = -whalf ; in_brain && yk <= whalf ; yk++)
                            {
                              yi = mri->yi[y+yk] ;
                              for (xk = -whalf ;
                                   in_brain && xk <= whalf ;
                                   xk++)
                                {
                                  xi = mri->xi[x+xk] ;
                                  if (MRISvox(mri, xi, yi, zi) < thresh)
                                    in_brain = 0 ;
                                }
                            }
                        }
                      if (in_brain)
                        {
                          if (x < box->x)
                            box->x = x ;
                          if (y < box->y)
                            box->y = y ;
                          if (z < box->z)
                            box->z = z ;
                          if (x > x1)
                            x1 = x ;
                          if (y > y1)
                            y1 = y ;
                          if (z > z1)
                            z1 = z ;
                        }
                    }
                }
            }
        }
      break ;
    case MRI_FLOAT:
      for (z = 0 ; z < depth ; z++)
        {
          for (y = 0 ; y < height ; y++)
            {
              pfsrc = &MRIFvox(mri, 0, y, z) ;
              for (x = 0 ; x < width ; x++)
                {
                  if (*pfsrc++ > thresh)
                    {
                      in_brain = 1 ;
                      for (zk = -whalf ; in_brain && zk <= whalf ; zk++)
                        {
                          zi = mri->zi[z+zk] ;
                          for (yk = -whalf ; in_brain && yk <= whalf ; yk++)
                            {
                              yi = mri->yi[y+yk] ;
                              for (xk = -whalf ;
                                   in_brain && xk <= whalf ;
                                   xk++)
                                {
                                  xi = mri->xi[x+xk] ;
                                  if (MRIFvox(mri, xi, yi, zi) < thresh)
                                    in_brain = 0 ;
                                }
                            }
                        }
                      if (in_brain)
                        {
                          if (x < box->x)
                            box->x = x ;
                          if (y < box->y)
                            box->y = y ;
                          if (z < box->z)
                            box->z = z ;
                          if (x > x1)
                            x1 = x ;
                          if (y > y1)
                            y1 = y ;
                          if (z > z1)
                            z1 = z ;
                        }
                    }
                }
            }
        }
      break ;
    default:
      ErrorReturn(ERROR_UNSUPPORTED,
                  (ERROR_UNSUPPORTED, "MRIboundingBoxNbd: unsupported type %d",
                   mri->type)) ;
      break ;
    }
  box->x -= whalf+1 ; box->y -= whalf+1 ; box->z -= whalf+1 ;
  x1 += whalf+1 ; y1 += whalf+1 ; z1 += whalf+1 ;
  box->x = MAX(0,box->x) ; box->y = MAX(0,box->y) ; box->z = MAX(0,box->z) ;
  x1 = MIN(width-1,x1) ; y1 = MIN(height-1, y1) ; z1 = MIN(depth-1, z1) ;
  box->dx = x1 - box->x + 1 ;
  box->dy = y1 - box->y + 1 ;
  box->dz = z1 - box->z + 1 ;
  return(NO_ERROR) ;
}
/*-----------------------------------------------------
  ------------------------------------------------------*/
#define MIN_DARK 10

int
MRIfindApproximateSkullBoundingBox(MRI *mri, int thresh,MRI_REGION *box)
{
  int      width, height, depth, x, y, z, x1, y1, z1;
  int      ndark, max_dark, start, nlight, max_light ;
  double   means[3] ;
  Real     val ;

  width = mri->width ; height = mri->height ; depth = mri->depth ;

  MRIcenterOfMass(mri, means, thresh) ;


#define MAX_LIGHT 30   /* don't let there by 3 cm of
                          bright stuff 'outside' of brain */

  /* search for left edge */
  nlight = ndark = max_dark = 0 ;
  y = nint(means[1]) ; z = nint(means[2]) ;
  for (start = x1 = x = nint(means[0]) ; x >= 0 ; x--)
    {
      MRIsampleVolumeType(mri, x,  y, z, &val, SAMPLE_NEAREST) ;

      if (val < thresh)
        {
          if (!ndark)
            start = x ;
          ndark++ ;
          nlight = 0  ;
        }
      else
        {
          if (++nlight > MAX_LIGHT)
            max_dark = 0 ;
          if (ndark > max_dark)
            {
              max_dark = ndark ; x1 = start ;
            }
          ndark = 0 ;
        }
    }
  if (ndark > max_dark)
    {
      max_dark = ndark ;
      x1 = start ;
    }
  if (max_dark < MIN_DARK)
    x1 = 0 ;
  box->x = x1 ;

  /* search for right edge */
  nlight = ndark = max_dark = 0 ;
  y = nint(means[1]) ; z = nint(means[2]) ;
  for (start = x1 = x = nint(means[0]) ; x < width ; x++)
    {
      MRIsampleVolumeType(mri, x,  y, z, &val, SAMPLE_NEAREST) ;
      if (val < thresh)
        {
          if (!ndark)
            start = x ;
          ndark++ ;
          nlight = 0 ;
        }
      else
        {
          if (++nlight > MAX_LIGHT)
            max_dark = 0 ;
          if (ndark >= max_dark)
            {
              max_dark = ndark ; x1 = start ;
            }
          ndark = 0 ;
        }
    }
  if (ndark > max_dark)
    {
      max_dark = ndark ;
      x1 = start ;
    }
  if (max_dark < MIN_DARK)
    x1 = mri->width-1 ;
  box->dx = x1 - box->x + 1 ;

  /* search for superior edge */
  nlight = ndark = max_dark = max_light = 0 ;
  x = MAX(0,nint(means[0])-20) ; // avoid inter-hemispheric fissure
  z = nint(means[2]) ;
  for (start = y1 = y = nint(means[1]) ; y >= 0 ; y--)
    {
      MRIsampleVolumeType(mri, x,  y, z, &val, SAMPLE_NEAREST) ;
      if (val < thresh)
        {
          if (nlight > max_light)
            max_light = nlight ;
          if (!ndark)
            start = y ;
          ndark++ ;
          nlight = 0 ;
        }
      else
        {
          if (++nlight > MAX_LIGHT)
            max_dark = 0 ;
          if (ndark >= max_dark)
            {
              max_dark = ndark ; y1 = start ;
              max_light = 0 ;  // max_light is max in a row light above dark
            }
          ndark = 0 ;
        }
    }

  /* if we ended on a string of dark voxels, check two things:
     1. the string was longer than the previous longest
     2. the strong was longer than 1/2 the previous longest, and there
     was an intervening string of light voxels indicated it was still in
     brain.
  */
  if ((ndark > max_dark) || (y < 0 &&
                             (ndark > max_dark/2) && max_light > MAX_LIGHT/2))
    {
      max_dark = ndark ;
      y1 = start ;
    }
  if (max_dark < MIN_DARK)
    y1 = 0 ;
  box->y = y1 ;

  /* search for inferior edge */
  nlight = ndark = max_dark = 0 ;
  x = nint(means[0]) ; z = nint(means[2]) ;
  for (start = y = y1 = nint(means[1]) ; y < height ; y++)
    {
      MRIsampleVolumeType(mri, x,  y, z, &val, SAMPLE_NEAREST) ;
      if (val < thresh)
        {
          if (!ndark)
            start = y ;
          ndark++ ;
          nlight = 0 ;
        }
      else
        {
          if (++nlight > MAX_LIGHT)
            max_dark = 0 ;
          if (ndark >= max_dark)
            {
              max_dark = ndark ; y1 = start ;
            }
          ndark = 0 ;
        }
    }
  if (ndark > max_dark)
    {
      max_dark = ndark ;
      y1 = start ;
    }
  if (max_dark < MIN_DARK)
    y1 = mri->height-1 ;
  box->dy = y1 - box->y + 1 ;

  /* search for posterior edge */
  nlight = ndark = max_dark = 0 ;
  x = nint(means[0]) ; y = nint(means[1]) ;
  for (z1 = start = z = nint(means[2]) ; z >= 0 ; z--)
    {
      MRIsampleVolumeType(mri, x,  y, z, &val, SAMPLE_NEAREST) ;
      if (val < thresh)
        {
          if (!ndark)
            start = z ;
          ndark++ ;
          nlight = 0 ;
        }
      else
        {
          if (++nlight > MAX_LIGHT)
            max_dark = 0 ;
          if (ndark >= max_dark)
            {
              max_dark = ndark ; z1 = start ;
            }
          ndark = 0 ;
        }
    }
  if (ndark > max_dark)
    {
      max_dark = ndark ;
      z1 = start ;
    }
  if (max_dark < MIN_DARK)
    z1 = 0 ;
  box->z = z1 ;

  /* search for anterior edge */
  nlight = ndark = max_dark = 0 ;
  x = nint(means[0]) ; y = nint(means[1]) ;
  for (start = z = nint(means[2]) ; z < depth ; z++)
    {
      MRIsampleVolumeType(mri, x,  y, z, &val, SAMPLE_NEAREST) ;
      if (val < thresh)
        {
          if (!ndark)
            start = z ;
          ndark++ ;
          nlight = 0 ;
        }
      else
        {
          if (++nlight > MAX_LIGHT)
            max_dark = 0 ;
          if (ndark >= max_dark)
            {
              max_dark = ndark ; z1 = start ;
            }
          ndark = 0 ;
        }
    }
  if (ndark > max_dark)
    {
      max_dark = ndark ;
      z1 = start ;
    }
  if (max_dark < MIN_DARK)
    z1 = mri->depth-1 ;
  box->dz = z1 - box->z + 1 ;

  return(NO_ERROR) ;
}
/*-----------------------------------------------------
  ------------------------------------------------------*/
int
MRIboundingBox(MRI *mri, int thresh, MRI_REGION *box)
{
  int      width, height, depth, x, y, z, x1, y1, z1 ;
  BUFTYPE  *psrc ;
  float    *pfsrc ;
  short    *pssrc ;

  box->dx = width = mri->width ;
  box->dy = height = mri->height ;
  box->dz = depth = mri->depth ;

  x1 = y1 = z1 = 0 ;
  box->x = width-1 ;
  box->y = height-1 ;
  box->z = depth-1 ;
  switch (mri->type)
    {
    case MRI_UCHAR:
      for (z = 0 ; z < depth ; z++)
        {
          for (y = 0 ; y < height ; y++)
            {
              psrc = &MRIvox(mri, 0, y, z) ;
              for (x = 0 ; x < width ; x++)
                {
                  if (*psrc++ > thresh)
                    {
                      if (x < box->x)
                        box->x = x ;
                      if (y < box->y)
                        box->y = y ;
                      if (z < box->z)
                        box->z = z ;
                      if (x > x1)
                        x1 = x ;
                      if (y > y1)
                        y1 = y ;
                      if (z > z1)
                        z1 = z ;
                    }
                }
            }
        }
      break ;
    case MRI_FLOAT:
      for (z = 0 ; z < depth ; z++)
        {
          for (y = 0 ; y < height ; y++)
            {
              pfsrc = &MRIFvox(mri, 0, y, z) ;
              for (x = 0 ; x < width ; x++)
                {
                  if (*pfsrc++ > thresh)
                    {
                      if (x < box->x)
                        box->x = x ;
                      if (y < box->y)
                        box->y = y ;
                      if (z < box->z)
                        box->z = z ;
                      if (x > x1)
                        x1 = x ;
                      if (y > y1)
                        y1 = y ;
                      if (z > z1)
                        z1 = z ;
                    }
                }
            }
        }
      break ;
    case MRI_SHORT:
      for (z = 0 ; z < depth ; z++)
        {
          for (y = 0 ; y < height ; y++)
            {
              pssrc = &MRISvox(mri, 0, y, z) ;
              for (x = 0 ; x < width ; x++)
                {
                  if (*pssrc++ > thresh)
                    {
                      if (x < box->x)
                        box->x = x ;
                      if (y < box->y)
                        box->y = y ;
                      if (z < box->z)
                        box->z = z ;
                      if (x > x1)
                        x1 = x ;
                      if (y > y1)
                        y1 = y ;
                      if (z > z1)
                        z1 = z ;
                    }
                }
            }
        }
      break ;
    default:
      ErrorReturn(ERROR_UNSUPPORTED,
                  (ERROR_UNSUPPORTED, "MRIboundingBox: unsupported type %d",
                   mri->type)) ;
      break ;
    }
  box->dx = x1 - box->x + 1 ;
  box->dy = y1 - box->y + 1 ;
  box->dz = z1 - box->z + 1 ;
  return(NO_ERROR) ;
}
/*-----------------------------------------------------
  ------------------------------------------------------*/
int
MRIcheckSize(MRI *mri_src, MRI *mri_check, int width, int height, int depth)
{
  if (!mri_check)
    return(0) ;

  if (!width)
    width = mri_src->width ;
  if (!height)
    height = mri_src->height ;
  if (!depth)
    depth = mri_src->depth ;

  if (width != mri_check->width ||
      height != mri_check->height ||
      depth != mri_check->depth)
    return(0) ;

  return(1) ;
}
/*-----------------------------------------------------
  ------------------------------------------------------*/
int
MRItransformRegion(MRI *mri_src, MRI *mri_dst, MRI_REGION *src_region,
                   MRI_REGION *dst_region)
{
  Real  xw, yw, zw, xt, yt, zt, xv, yv, zv ;

  if (getSliceDirection(mri_src) != getSliceDirection(mri_dst))
    ErrorReturn(ERROR_UNSUPPORTED,
                (ERROR_UNSUPPORTED,
                 "MRItransformRegion(%s): slice directions must match",
                 mri_src->fname)) ;

  if (!mri_src->linear_transform)
    ErrorReturn(ERROR_UNSUPPORTED,
                (ERROR_UNSUPPORTED,
                 "MRItransformRegion(%s): no transform loaded",
                 mri_src->fname)) ;
  if (!mri_dst->linear_transform)
    ErrorReturn(ERROR_UNSUPPORTED,
                (ERROR_UNSUPPORTED,
                 "MRItransformRegion(%s): no transform loaded",
                 mri_dst->fname)) ;
  /*
    The convention  is  that  positive xspace coordinates run
    from the patient's  left  side  to  right  side,  positive
    yspace  coordinates run from patient posterior to anterior
    and positive zspace coordinates run from inferior to superior.
  */
  switch (getSliceDirection(mri_src))
    {
    case MRI_CORONAL:
      break ;
    default:
      ErrorReturn
        (ERROR_UNSUPPORTED,
         (ERROR_UNSUPPORTED,
          "MRIregionToTalairachRegion: unsupported slice direction %d",
          getSliceDirection(mri_src))) ;
    }

  xv = (Real)src_region->x ;
  yv = (Real)src_region->y ;
  zv = (Real)src_region->z ;
  MRIvoxelToWorld(mri_src, xv, yv, zv, &xw, &yw, &zw) ;
  transform_point(mri_src->linear_transform, xw, yw, zw, &xt, &yt, &zt) ;
  transform_point(mri_dst->inverse_linear_transform, xt, yt, zt, &xw,&yw,&zw);
  MRIworldToVoxel(mri_dst, xw, yw, zw, &xv, &yv, &zv) ;
  dst_region->x = nint(xv) ;
  dst_region->y = nint(yv) ;
  dst_region->z = nint(zv) ;

  xv = (Real)(src_region->x + src_region->dx - 1) ;
  yv = (Real)(src_region->y + src_region->dy - 1) ;
  zv = (Real)(src_region->z + src_region->dz - 1) ;
  MRIvoxelToWorld(mri_src, xv, yv, zv, &xw, &yw, &zw) ;
  transform_point(mri_src->linear_transform, xw, yw, zw, &xt, &yt, &zt) ;
  transform_point(mri_dst->inverse_linear_transform, xt, yt, zt, &xw,&yw,&zw);
  MRIworldToVoxel(mri_dst, xw, yw, zw, &xv, &yv, &zv) ;
  dst_region->dx = nint(xv - (Real)dst_region->x) + 1 ;
  dst_region->dy = nint(yv - (Real)dst_region->y) + 1 ;
  dst_region->dz = nint(zv - (Real)dst_region->z) + 1 ;

  return(NO_ERROR) ;
}
/*-----------------------------------------------------
  ------------------------------------------------------*/
int
MRIvoxelToVoxel(MRI *mri_src, MRI *mri_dst, Real xv, Real yv, Real zv,
                Real *pxt, Real *pyt, Real *pzt)
{
  Real  xw, yw, zw, xt, yt, zt ;

#if 0
  if (!mri_src->linear_transform)
    ErrorReturn(ERROR_UNSUPPORTED,
                (ERROR_UNSUPPORTED,
                 "MRIvoxelToVoxel(%s): no transform loaded", mri_src->fname));
#endif

  /*
    The convention  is  that  positive xspace coordinates run
    from the patient's  left  side  to  right  side,  positive
    yspace  coordinates run from patient posterior to anterior
    and positive zspace coordinates run from inferior to superior.
  */
  switch (getSliceDirection(mri_src))
    {
    case MRI_CORONAL:
      break ;
    default:
      ErrorReturn(ERROR_UNSUPPORTED,
                  (ERROR_UNSUPPORTED,
                   "MRIvoxelToVoxel: unsupported slice direction %d",
                   getSliceDirection(mri_src))) ;
    }

  if (!mri_src->linear_transform || !mri_dst->inverse_linear_transform)
    {
      /*
         if either doesn't have a transform defined, assume they are in
         the same coordinate system.
      */
      *pxt = xv ; *pyt = yv ; *pzt = zv ;
    }
  else
    {
      MRIvoxelToWorld(mri_src, xv, yv, zv, &xw, &yw, &zw) ;
      if (mri_src->linear_transform)
        transform_point(mri_src->linear_transform, xw, yw, zw, &xt, &yt, &zt) ;
      else
        {
          xt = xw ; yt = yw ; zt = zw ;
        }
      if (mri_dst->inverse_linear_transform)
        transform_point
          (mri_dst->inverse_linear_transform, xt,yt,zt,&xw,&yw,&zw);
      else
        {
          xw = xt ; yw = yt ; zw = zt ;
        }
      MRIworldToVoxel(mri_dst, xw, yw, zw, pxt, pyt, pzt) ;
    }

  return(NO_ERROR) ;
}
/*-----------------------------------------------------
  ------------------------------------------------------*/
int
MRIvoxelToTalairachVoxel(MRI *mri, Real xv, Real yv, Real zv,
                         Real *pxt, Real *pyt, Real *pzt)
{
  Real  xw, yw, zw, xt, yt, zt ;

#if 0
  if (!mri->linear_transform)
    ErrorReturn(ERROR_UNSUPPORTED,
                (ERROR_UNSUPPORTED,
                 "MRIvoxelToTalairachVoxel(%s): no transform loaded",
                 mri->fname)) ;
#endif
  /*
    The convention  is  that  positive xspace coordinates run
    from the patient's  left  side  to  right  side,  positive
    yspace  coordinates run from patient posterior to anterior
    and positive zspace coordinates run from inferior to superior.
  */
  switch (getSliceDirection(mri))
    {
    case MRI_CORONAL:
      break ;
    default:
      ErrorReturn(ERROR_UNSUPPORTED,
                  (ERROR_UNSUPPORTED,
                   "MRIvoxelToTalairachVoxel: unsupported slice direction %d",
                   getSliceDirection(mri))) ;
    }

  MRIvoxelToWorld(mri, xv, yv, zv, &xw, &yw, &zw) ;
  if (mri->linear_transform)
    transform_point(mri->linear_transform, xw, yw, zw, &xt, &yt, &zt) ;
  else
    { xt = xw ; yt = yw ; zt = zw ; }
  MRIworldToVoxel(mri, xt, yt, zt, pxt, pyt, pzt) ;

  return(NO_ERROR) ;
}
/*-----------------------------------------------------
  ------------------------------------------------------*/
int
MRIvoxelToTalairach(MRI *mri, Real xv, Real yv, Real zv,
                    Real *pxt, Real *pyt, Real *pzt)
{
  Real  xw, yw, zw ;

#if 0
  if (!mri->linear_transform)
    ErrorReturn(ERROR_UNSUPPORTED,
                (ERROR_UNSUPPORTED,
                 "MRIvoxelToTalairachVoxel(%s): no transform loaded",
                 mri->fname)) ;
#endif

  /*
    The convention  is  that  positive xspace coordinates run
    from the patient's  left  side  to  right  side,  positive
    yspace  coordinates run from patient posterior to anterior
    and positive zspace coordinates run from inferior to superior.
  */
  switch (getSliceDirection(mri))
    {
    case MRI_CORONAL:
      break ;
    default:
      ErrorReturn(ERROR_UNSUPPORTED,
                  (ERROR_UNSUPPORTED,
                   "MRIvoxelToTalairachVoxel: unsupported slice direction %d",
                   getSliceDirection(mri))) ;
    }

  MRIvoxelToWorld(mri, xv, yv, zv, &xw, &yw, &zw) ;
  if (mri->linear_transform)
    transform_point(mri->linear_transform, xw, yw, zw, pxt, pyt, pzt) ;
  else
    { *pxt = xw ; *pyt = yw ; *pzt = zw ; }

  return(NO_ERROR) ;
}
/*-----------------------------------------------------
  ------------------------------------------------------*/
int
MRItalairachToVoxel(MRI *mri, Real xt, Real yt, Real zt,
                    Real *pxv, Real *pyv, Real *pzv)
{
  Real  xw, yw, zw ;

#if 0
  if (!mri->inverse_linear_transform)
    ErrorReturn(ERROR_UNSUPPORTED,
                (ERROR_UNSUPPORTED,
                 "MRItalairachToVoxel(%s): no transform loaded",
                 mri->fname)) ;
#endif

  /*
    The convention  is  that  positive xspace coordinates run
    from the patient's  left  side  to  right  side,  positive
    yspace  coordinates run from patient posterior to anterior
    and positive zspace coordinates run from inferior to superior.
  */
  switch (getSliceDirection(mri))
    {
    case MRI_CORONAL:
      break ;
    default:
      ErrorReturn(ERROR_UNSUPPORTED,
                  (ERROR_UNSUPPORTED,
                   "MRIvoxelToTalairachVoxel: unsupported slice direction %d",
                   getSliceDirection(mri))) ;
    }

  if (mri->inverse_linear_transform)
    transform_point(mri->inverse_linear_transform, xt, yt, zt, &xw, &yw,&zw);
  else
    { xw = xt ; yw = yt ; zw = zt ; }
  MRIworldToVoxel(mri, xw, yw, zw, pxv, pyv, pzv) ;

  return(NO_ERROR) ;
}
/*-----------------------------------------------------
  ------------------------------------------------------*/
int
MRItalairachVoxelToVoxel(MRI *mri, Real xtv, Real ytv, Real ztv,
                         Real *pxv, Real *pyv, Real *pzv)
{
  Real  xw, yw, zw, xt, yt, zt ;

#if 0
  if (!mri->inverse_linear_transform)
    ErrorReturn(ERROR_UNSUPPORTED,
                (ERROR_UNSUPPORTED,
                 "MRItalairachVoxelToVoxel(%s): no transform loaded",
                 mri->fname)) ;
#endif

  /*
    The convention  is  that  positive xspace coordinates run
    from the patient's  left  side  to  right  side,  positive
    yspace  coordinates run from patient posterior to anterior
    and positive zspace coordinates run from inferior to superior.
  */
  switch (getSliceDirection(mri))
    {
    case MRI_CORONAL:
      break ;
    default:
      ErrorReturn(ERROR_UNSUPPORTED,
                  (ERROR_UNSUPPORTED,
                   "MRIvoxelToTalairachVoxel: unsupported slice direction %d",
                   getSliceDirection(mri))) ;
    }

  MRIvoxelToWorld(mri, xtv, ytv, ztv, &xt, &yt, &zt) ;
  if (mri->inverse_linear_transform)
    transform_point(mri->inverse_linear_transform, xt, yt, zt, &xw, &yw,&zw);
  else
    { xw = xt ; yw = yt ; zw = zt ; }
  MRIworldToVoxel(mri, xw, yw, zw, pxv, pyv, pzv) ;

  return(NO_ERROR) ;
}
/*-----------------------------------------------------
  ------------------------------------------------------*/
int
MRItalairachVoxelToWorld(MRI *mri, Real xtv, Real ytv, Real ztv,
                         Real *pxw, Real *pyw, Real *pzw)
{
  Real  xw, yw, zw, xt, yt, zt ;

#if 0
  if (!mri->inverse_linear_transform)
    ErrorReturn(ERROR_UNSUPPORTED,
                (ERROR_UNSUPPORTED,
                 "MRItalairachVoxelToVoxel(%s): no transform loaded",
                 mri->fname)) ;
#endif

  /*
    The convention  is  that  positive xspace coordinates run
    from the patient's  left  side  to  right  side,  positive
    yspace  coordinates run from patient posterior to anterior
    and positive zspace coordinates run from inferior to superior.
  */
  switch (getSliceDirection(mri))
    {
    case MRI_CORONAL:
      break ;
    default:
      ErrorReturn(ERROR_UNSUPPORTED,
                  (ERROR_UNSUPPORTED,
                   "MRIvoxelToTalairachVoxel: unsupported slice direction %d",
                   getSliceDirection(mri))) ;
    }

  MRIvoxelToWorld(mri, xtv, ytv, ztv, &xt, &yt, &zt) ;
  if (mri->inverse_linear_transform)
    transform_point(mri->inverse_linear_transform, xt, yt, zt, &xw, &yw,&zw);
  else
    { xw = xt ; yw = yt ; zw = zt ; }
  *pxw = xw ; *pyw = yw ; *pzw = zw ;

  return(NO_ERROR) ;
}
/*-----------------------------------------------------
  ------------------------------------------------------*/
#define V4_LOAD(v, x, y, z, r)  (VECTOR_ELT(v,1)=x, VECTOR_ELT(v,2)=y, \
                                  VECTOR_ELT(v,3)=z, VECTOR_ELT(v,4)=r) ;

int
MRIvoxelToWorld(MRI *mri, Real xv, Real yv, Real zv,
                Real *pxw, Real *pyw, Real *pzw)
{
  VECTOR *vw, *vv;
  MATRIX *RfromI;

  // if the transform is not cached yet, then
  if (!mri->i_to_r__)
    mri->i_to_r__ = extract_i_to_r(mri);
  if (!mri->r_to_i__)
    mri->r_to_i__ = extract_r_to_i(mri);

  RfromI = mri->i_to_r__; // extract_i_to_r(mri);

  vv = VectorAlloc(4, MATRIX_REAL) ;
  V4_LOAD(vv, xv, yv, zv, 1.) ;
  vw = MatrixMultiply(RfromI, vv, NULL) ;
  *pxw = V3_X(vw);
  *pyw = V3_Y(vw);
  *pzw = V3_Z(vw);

  // MatrixFree(&RfromI);
  VectorFree(&vv);
  VectorFree(&vw);
  return(NO_ERROR) ;
}
/*-----------------------------------------------------
  ------------------------------------------------------*/
int
MRIworldToTalairachVoxel(MRI *mri, Real xw, Real yw, Real zw,
                         Real *pxv, Real *pyv, Real *pzv)
{
  Real  xt, yt, zt ;

  transform_point(mri->linear_transform, xw, yw, zw, &xt, &yt, &zt) ;
  MRIworldToVoxel(mri, xt, yt, zt, pxv, pyv, pzv) ;
  return(NO_ERROR) ;
}
/*-----------------------------------------------------
  ------------------------------------------------------*/
int MRIworldToVoxelIndex(MRI *mri, Real xw, Real yw, Real zw,
                         int *pxv, int *pyv, int *pzv)
{
  Real xv, yv, zv;
  MRIworldToVoxel(mri, xw, yw, zw, &xv, &yv, &zv);
  *pxv = (int) xv;
  *pyv = (int) yv;
  *pzv = (int) zv;

  /*
    switch (getSliceDirection(mri))
    {
    case MRI_CORONAL:
    #if 0
    *pxv = ((Real)mri->xend - xw) / mri->xsize ;
    *pzv = (yw - (Real)mri->zstart) / mri->zsize ;
    *pyv = (-zw - (Real)mri->ystart) / mri->ysize ;
    #else
    trans_SetBounds ( mri->xstart, mri->xend, mri->ystart, mri->yend,
    mri->zstart, mri->zend );
    trans_SetResolution ( mri->xsize, mri->ysize, mri->zsize );
    trans_RASToVoxelIndex(xw, yw, zw, pxv, pyv, pzv) ;
    #endif
    break ;
    default:
    ErrorReturn(ERROR_UNSUPPORTED,
    (ERROR_UNSUPPORTED,
    "MRIworldToVoxel: unsupported slice direction %d",
    getSliceDirection(mri))) ;
    break ;
    }
  */
  return(NO_ERROR) ;
}

/*
   int MRIvoxelToSurfaceRAS and int MRIsurfaceRASToVoxel

   get the surfaceRAS values from original voxel
   Note that this is different from MRIvoxelToWorld().

   Note that currently MATRIX uses float** to store data.
   Going around the circle of transform causes error accumulation quickly.
   I noticed that 7 x 10^(-6) is very common.

*/

MATRIX *surfaceRASFromVoxel_(MRI *mri)
{
  // Derivation (avoid expensive matrix calculation)
  //
  //     orig -----(rasFromVoxel)------> RAS (scanner RAS or physical RAS)
  //       |                              |
  //(conformedVoxelFromVoxel)            (1)
  //       |                              |
  //       V                              V
  //conformed--(RASfromConformed)----->RAS (scanner RAS or physical RAS)
  //       |                              |
  //      (1)                      (surfaceRASFromRAS)
  //       |                              |
  //       V                              V
  //conformed-(surfaceRASFromConformed)->surfaceRAS
  //
  //
  // where  RASFromConformed = [ -1  0  0   s1 ] where s1 = c_r + 128
  //                           [  0  0  1   s2 ]       s2 = c_a - 128
  //                           [  0 -1  0   s3 ]       s3 = c_s + 128
  //                           [  0  0  0    1 ]
  //
  //  surfaceRASFromConformed= [ -1  0  0  128 ]
  //                           [  0  0  1 -128 ]
  //                           [  0 -1  0  128 ]
  //                           [  0  0  0   1  ]
  // Therefore
  //        surfaceRASFromRAS= [  1  0  0  -c_r]  just a translation matrix
  //                           [  0  1  0  -c_a]
  //                           [  0  0  1  -c_s]
  //                           [  0  0  0    1 ]
  //
  //  surfaceRASFromVoxel = surfaceRASFromRAS (x) rasFromVoxel
  //
  //  i.e.       applying translation on RASFromVoxel
  //
  // This means tha
  //
  //  if RASFromVoxel = (  X  | T ), then
  //                    (  0  | 1 )
  //
  //    surfaceRASFromVoxel =  ( 1 | -C) * ( X | T ) = ( X | T - C )
  //                           ( 0 | 1 )   ( 0 | 1 )   ( 0 |   1   )
  //
  MATRIX *rasFromVoxel;
  MATRIX *sRASFromVoxel;
  double m14, m24, m34;

  // if the transform is not cached yet, then
  if (!mri->i_to_r__)
    mri->i_to_r__ = extract_i_to_r(mri);
  if (!mri->r_to_i__)
    mri->r_to_i__ = extract_r_to_i(mri);

  rasFromVoxel = mri->i_to_r__; // extract_i_to_r(mri);

  sRASFromVoxel = MatrixCopy(rasFromVoxel, NULL);
  // MatrixFree(&rasFromVoxel);
  // modify
  m14 = *MATRIX_RELT(sRASFromVoxel, 1,4);
  *MATRIX_RELT(sRASFromVoxel, 1,4) = m14 - mri->c_r;
  m24 = *MATRIX_RELT(sRASFromVoxel, 2,4);
  *MATRIX_RELT(sRASFromVoxel, 2,4) = m24 - mri->c_a;
  m34 = *MATRIX_RELT(sRASFromVoxel, 3,4);
  *MATRIX_RELT(sRASFromVoxel, 3,4) = m34 - mri->c_s;

  return sRASFromVoxel;
}

MATRIX *surfaceRASFromRAS_(MRI *mri)
{
  MATRIX *sRASFromRAS;
  sRASFromRAS = MatrixAlloc(4, 4, MATRIX_REAL);
  MatrixIdentity(4, sRASFromRAS);
  *MATRIX_RELT(sRASFromRAS, 1,4) = - mri->c_r;
  *MATRIX_RELT(sRASFromRAS, 2,4) = - mri->c_a;
  *MATRIX_RELT(sRASFromRAS, 3,4) = - mri->c_s;
  return sRASFromRAS;
}

MATRIX *RASFromSurfaceRAS_(MRI *mri)
{
  MATRIX *RASFromSRAS;
  RASFromSRAS = MatrixAlloc(4, 4, MATRIX_REAL);
  MatrixIdentity(4, RASFromSRAS);
  *MATRIX_RELT(RASFromSRAS, 1,4) = mri->c_r;
  *MATRIX_RELT(RASFromSRAS, 2,4) = mri->c_a;
  *MATRIX_RELT(RASFromSRAS, 3,4) = mri->c_s;
  return RASFromSRAS;
}

int MRIRASToSurfaceRAS(MRI *mri, Real xr, Real yr, Real zr,
                       Real *xsr, Real *ysr, Real *zsr)
{
  MATRIX *surfaceRASFromRAS=0;
  VECTOR *v, *sr;
  v = VectorAlloc(4, MATRIX_REAL);
  V4_LOAD(v, xr, yr, zr, 1.);
  surfaceRASFromRAS = surfaceRASFromRAS_(mri);
  sr = MatrixMultiply(surfaceRASFromRAS, v, NULL);
  *xsr = V3_X(sr);
  *ysr = V3_Y(sr);
  *zsr = V3_Z(sr);
  MatrixFree(&surfaceRASFromRAS);
  VectorFree(&v);
  VectorFree(&sr);
  return (NO_ERROR);
}

int MRIsurfaceRASToRAS(MRI *mri, Real xsr, Real ysr, Real zsr,
                       Real *xr, Real *yr, Real *zr)
{
  MATRIX *RASFromSurfaceRAS=0;
  VECTOR *v, *r;
  v = VectorAlloc(4, MATRIX_REAL);
  V4_LOAD(v, xsr, ysr, zsr, 1.);
  RASFromSurfaceRAS = RASFromSurfaceRAS_(mri);
  r = MatrixMultiply(RASFromSurfaceRAS, v, NULL);
  *xr = V3_X(r);
  *yr = V3_Y(r);
  *zr = V3_Z(r);
  MatrixFree(&RASFromSurfaceRAS);
  VectorFree(&v);
  VectorFree(&r);
  return (NO_ERROR);
}


int MRIvoxelToSurfaceRAS(MRI *mri, Real xv, Real yv, Real zv,
                         Real *xs, Real *ys, Real *zs)
{
  MATRIX *sRASFromVoxel;
  VECTOR *vv, *sr;

  sRASFromVoxel = surfaceRASFromVoxel_(mri);
  // calculate the surface ras value
  vv = VectorAlloc(4, MATRIX_REAL);
  V4_LOAD(vv, xv, yv, zv, 1.);
  sr = MatrixMultiply(sRASFromVoxel, vv, NULL);
  *xs = V3_X(sr);
  *ys = V3_Y(sr);
  *zs = V3_Z(sr);

  MatrixFree(&sRASFromVoxel);
  VectorFree(&vv);
  VectorFree(&sr);

  return (NO_ERROR);
}

MATRIX *voxelFromSurfaceRAS_(MRI *mri)
{
  MATRIX *voxelFromSRAS =0;
  //////////////////////////////////////////////////////////////////
  // it turned out that this can be done easily without taking inverse
  // Note the surfaceRASFromVoxel_ is given by
  //
  //       ( X | T - C)
  //       ( 0 |  1   )
  // Note, however, that we define C by
  //
  //      (  X | T ) (S/2) = ( C )  where S = (w/2, h/2, d/2)^t
  //      (  0 | 1 ) ( 1 )   ( 1 )
  // Thus
  //        X*S/2 + T = C
  // or
  //        T - C = - X*S/2
  // or
  //     surfaceRASFromVoxel = ( X | - X*S/2 )
  //                           ( 0 |    1    )
  // whose inverse is given by
  //
  //     voxelFromSurfaceRAS = ( X ^(-1)| S/2 )
  //                           (  0     |  1  )
  //
  // since
  //           ( X^(-1)  S/2) ( X  -X*S/2) = ( 1   S/2 - S/2) = 1
  //           (   0      1 ) ( 0     1  )   ( 0      1     )
  //
  // thus get r_to_i__ and set translation part to S/2 is the quickest way
  /////////////////////////////////////////////////////////////////////
  // if the transform is not cached yet, then
  if (!mri->i_to_r__)
    mri->i_to_r__ = extract_i_to_r(mri);
  if (!mri->r_to_i__)
    mri->r_to_i__ = extract_r_to_i(mri);

  voxelFromSRAS = MatrixCopy(mri->r_to_i__, NULL);
  // modify translation part
  *MATRIX_RELT(voxelFromSRAS, 1,4) = mri->width/2;
  *MATRIX_RELT(voxelFromSRAS, 2,4) = mri->height/2;
  *MATRIX_RELT(voxelFromSRAS, 3,4) = mri->depth/2;

#if 0
  // no more expensive inverse
  MATRIX *sRASFromVoxel = surfaceRASFromVoxel_(mri);
  MATRIX *voxelFromSRAS = MatrixInverse(sRASFromVoxel, NULL);
  MatrixFree(&sRASFromVoxel) ;
#endif

  return voxelFromSRAS;
}

/* extract the RASToVoxel Matrix */
MATRIX *GetSurfaceRASToVoxelMatrix(MRI *mri){
  return voxelFromSurfaceRAS_(mri);
}

int MRIsurfaceRASToVoxel(MRI *mri, Real xr, Real yr, Real zr,
                         Real *xv, Real *yv, Real *zv)
{
  MATRIX *voxelFromSRAS;
  static VECTOR *sr = NULL, *vv = NULL;

  voxelFromSRAS=voxelFromSurfaceRAS_(mri);
  if (sr == NULL)
    sr = VectorAlloc(4, MATRIX_REAL);
  V4_LOAD(sr, xr, yr, zr, 1.);
  vv = MatrixMultiply(voxelFromSRAS, sr, vv);
  *xv = V3_X(vv);
  *yv = V3_Y(vv);
  *zv = V3_Z(vv);

  MatrixFree(&voxelFromSRAS);
  //  VectorFree(&sr);
  //  VectorFree(&vv);

  return (NO_ERROR);
}

/*------------------------------------------------------*/
int
MRIworldToVoxel(MRI *mri, Real xw, Real yw, Real zw,
                Real *pxv, Real *pyv, Real *pzv)
{
  VECTOR *vv, *vw;
  MATRIX *IfromR;

  // if transform is not cached yet, then
  if (!mri->r_to_i__)
    mri->r_to_i__ = extract_r_to_i(mri);
  if (!mri->i_to_r__)
    mri->i_to_r__ = extract_i_to_r(mri);

  IfromR = mri->r_to_i__;
  vw = VectorAlloc(4, MATRIX_REAL) ;
  V4_LOAD(vw, xw, yw, zw, 1.) ;
  vv = MatrixMultiply(IfromR, vw, NULL) ;
  *pxv = V3_X(vv);
  *pyv = V3_Y(vv);
  *pzv = V3_Z(vv);

  VectorFree(&vv);
  VectorFree(&vw);

  return(NO_ERROR) ;
}
/*-----------------------------------------------------
  ------------------------------------------------------*/
int
MRIinitHeader(MRI *mri)
{
  mri->ptype = 2 ;

  /* most of these are in mm */
  mri->imnr0 = 1 ;
  mri->imnr1 = mri->depth ;
  mri->fov = mri->width ;
  mri->thick = 1.0 ;
  mri->xsize = 1.0 ;
  mri->ysize = 1.0 ;
  mri->zsize = 1.0 ;
  mri->ps = 1 ;
  mri->xstart = -mri->width/2.0 ;
  mri->xend = mri->width/2.0 ;
  mri->ystart = -mri->height/2.0 ;
  mri->yend = mri->height/2.0 ;
  mri->zstart = -mri->depth/2.0 ;
  mri->zend = mri->depth/2 ;
  // coronal
  mri->x_r = -1;
  mri->x_a = 0.;
  mri->x_s = 0.;
  //
  mri->y_r = 0.;
  mri->y_a = 0.;
  mri->y_s = -1;
  //
  mri->z_r = 0.;
  mri->z_a = 1;
  mri->z_s = 0.;
  //
  mri->c_r = mri->c_a = mri->c_s = 0.0;

  mri->ras_good_flag = 1;
  mri->gdf_image_stem[0] = '\0';
  mri->tag_data = NULL;
  mri->tag_data_size = 0;

  if (!mri->i_to_r__)
    mri->i_to_r__ = extract_i_to_r(mri);
  if (!mri->r_to_i__)
    mri->r_to_i__ = extract_r_to_i(mri);

  return(NO_ERROR) ;
}

/**
 * MRIreInitCache
 *
 * @param mri MRI* whose header information was modified
 *
 * @return NO_ERROR
 */
int MRIreInitCache(MRI *mri)
{
  if (mri->i_to_r__)
    {
      MatrixFree(&mri->i_to_r__);
      mri->i_to_r__ = 0;
    }
  if (mri->r_to_i__)
    {
      MatrixFree(&mri->r_to_i__);
      mri->r_to_i__ = 0;
    }
  mri->i_to_r__ = extract_i_to_r(mri);
  mri->r_to_i__ = extract_r_to_i(mri);

  return (NO_ERROR);
}

/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  change the direction of slices
  ------------------------------------------------------*/
MRI *
MRIextract(MRI *mri_src, MRI *mri_dst, int x0, int y0, int z0,
           int dx, int dy, int dz)
{
  return(MRIextractInto(mri_src, mri_dst, x0, y0, z0, dx, dy, dz, 0, 0, 0)) ;
}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  Extract a cubic region of an MR image and return it to the caller
  ------------------------------------------------------*/
MRI *
MRIextractRegion(MRI *mri_src, MRI *mri_dst, MRI_REGION *region)
{
  return(MRIextractInto(mri_src, mri_dst, region->x, region->y, region->z,
                        region->dx, region->dy, region->dz, 0, 0, 0)) ;
}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  Extract a cubic region of an MR image and return it to the caller
  ------------------------------------------------------*/
MRI *
MRIextractIntoRegion(MRI *mri_src, MRI *mri_dst, int x0, int y0, int z0,
                     MRI_REGION *region)
{
  return(MRIextractInto(mri_src, mri_dst, x0, y0, z0, region->dx, region->dy,
                        region->dz, region->x, region->y, region->z)) ;
}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  Extract a cubic region of an MR image and return it to the caller
  ------------------------------------------------------*/
MRI *
MRIextractInto(MRI *mri_src, MRI *mri_dst, int x0, int y0, int z0,
               int dx, int dy, int dz, int x1, int y1, int z1)
{
  int  width, height, depth, ys, zs, yd, zd, bytes, frame, xsize,ysize,zsize,
    dst_alloced = 0 ;
  Real c_r, c_a, c_s;

  width = mri_src->width ;
  depth = mri_src->depth ;
  height = mri_src->height ;

  if (z0 >= depth || y0 >= height || x0 >= width)
    ErrorReturn(NULL,
                (ERROR_BADPARM,
                 "MRIextractInto: bad src location (%d, %d, %d)", x0,y0,z0));
  // validation
  if (x0 < 0)
    x0 = 0 ;
  if (y0 < 0)
    y0 = 0 ;
  if (z0 < 0)
    z0 = 0 ;
  if (x0+dx > width)
    dx = (width - x0) ;
  if (y0+dy > height)
    dy = (height - y0) ;
  if (z0+dz > depth)
    dz = (depth - z0) ;
  if (x1 < 0)
    x1 = 0 ;
  if (y1 < 0)
    y1 = 0 ;
  if (z1 < 0)
    z1 = 0 ;

  if (!mri_dst)
    {
      mri_dst = MRIallocSequence(dx, dy, dz, mri_src->type, mri_src->nframes) ;
      MRIcopyHeader(mri_src, mri_dst) ;
      mri_dst->imnr0 = z0 + mri_src->imnr0 - z1 ;
      mri_dst->imnr1 = mri_dst->imnr0 + dz - 1 ;
      dst_alloced = 1 ;
    }

  if (mri_src->type != mri_dst->type)
    {
      MRIfree(&mri_dst) ;
      ErrorReturn(NULL,
                  (ERROR_BADPARM,
                   "MRIextractInto: src and dst types must match"));
    }

  if (x1+dx > mri_dst->width)
    dx = (mri_dst->width - x1) ;
  if (y1+dy > mri_dst->height)
    dy = (mri_dst->height - y1) ;
  if (z1+dz > mri_dst->depth)
    dz = (mri_dst->depth - z1) ;

  xsize = mri_src->xsize ;
  ysize = mri_src->ysize ;
  zsize = mri_src->zsize ;

  if (dst_alloced)
    {
      mri_dst->xstart += x0*xsize ;
      mri_dst->xend = mri_dst->xstart + dx*xsize ;
      mri_dst->ystart += y0*ysize ;
      mri_dst->yend = mri_dst->ystart + dy*ysize ;
      mri_dst->zstart += z0*zsize ;
      mri_dst->zend = mri_dst->zstart + dz*zsize  ;
    }

  bytes = dx ;
  switch (mri_src->type)
    {
    case MRI_FLOAT:
      bytes *= sizeof(float) ;
      break ;
    case MRI_LONG:
      bytes *= sizeof(long) ;
      break ;
    case MRI_INT:
      bytes *= sizeof(int) ;
      break ;
    case MRI_SHORT:
      bytes *= sizeof(short) ;
      break ;
    default:
      break ;
    }

  for (frame = 0 ; frame < mri_src->nframes ; frame++)
    {
      for (zd = z1, zs = z0 ; zs < z0+dz ; zs++, zd++)
        {
          for (yd = y1, ys = y0 ; ys < y0+dy ; ys++, yd++)
            {
              switch (mri_src->type)
                {
                case MRI_UCHAR:
                  memcpy(&MRIseq_vox(mri_dst, x1, yd, zd,frame),
                         &MRIseq_vox(mri_src,x0,ys,zs,frame), bytes);
                  break ;
                case MRI_FLOAT:
                  memcpy(&MRIFseq_vox(mri_dst, x1, yd, zd,frame),
                         &MRIFseq_vox(mri_src,x0,ys,zs,frame), bytes);
                  break ;
                case MRI_SHORT:
                  memcpy(&MRISseq_vox(mri_dst, x1, yd, zd,frame),
                         &MRISseq_vox(mri_src,x0,ys,zs,frame), bytes);
                  break ;
                case MRI_LONG:
                  memcpy(&MRILseq_vox(mri_dst, x1, yd, zd,frame),
                         &MRILseq_vox(mri_src,x0,ys,zs,frame), bytes);
                  break ;
                }
            }
        }
    }
  // calculate c_ras
  MRIcalcCRASforExtractedVolume
    (mri_src, mri_dst, x0, y0, z0, x1, y1, z1, &c_r, &c_a, &c_s);
  mri_dst->c_r = c_r;
  mri_dst->c_a = c_a;
  mri_dst->c_s = c_s;
  // initialize cached transform
  MRIreInitCache(mri_dst);

  return(mri_dst) ;
}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  change the direction of slices
  ------------------------------------------------------*/
MRI *
MRIreslice(MRI *mri_src, MRI *mri_dst, int slice_direction)
{
  int     width, height, depth, x1, x2, x3 ;
  BUFTYPE *psrc, val, *pdst ;

  int src_slice_direction = getSliceDirection(mri_src);
  if (slice_direction == src_slice_direction)
    {
      mri_dst = MRIcopy(mri_src, NULL) ;
      return(mri_dst) ;
    }

  width = mri_src->width ;
  height = mri_src->height ;
  depth = mri_src->depth ;


  if ((src_slice_direction == MRI_SAGITTAL &&
       slice_direction == MRI_CORONAL) ||
      (src_slice_direction == MRI_CORONAL &&
       slice_direction == MRI_SAGITTAL))
    {
      /*
        coronal images are back to front of the head, thus the depth axis
        points from the nose to the back of the head, with x from neck to
        crown, and y from ear to ear.
      */
      /* x1 --> x3
         x2 --> x2
         x3 --> x1
      */
      if (!mri_dst)
        {
          mri_dst = MRIalloc(depth, height, width, mri_src->type) ;
          MRIcopyHeader(mri_src, mri_dst) ;
        }
      else if (depth != mri_dst->width || height != mri_dst->height ||
               width != mri_dst->depth)
        {
          ErrorReturn(NULL,
                      (ERROR_BADPARM,
                       "MRIreslice: invalid destination size (%d, %d, %d)",
                       mri_dst->width, mri_dst->height, mri_dst->depth)) ;
        }

      for (x3 = 0 ; x3 < depth ; x3++)
        {
          for (x2 = 0 ; x2 < height ; x2++)
            {
              psrc = &MRIvox(mri_src, 0, x2, x3) ;
              for (x1 = 0 ; x1 < width ; x1++)
                {
                  /* swap so in place transformations are possible */
                  mri_dst->slices[x1][x2][x3] = *psrc++ ;
                }
            }
        }
    }
  else
    if ((src_slice_direction == MRI_HORIZONTAL &&
         slice_direction == MRI_CORONAL) ||
        (src_slice_direction == MRI_CORONAL &&
         slice_direction == MRI_HORIZONTAL))
      {
        /*
          horizontal images are top to bottom of the head, thus the depth axis
          points from the top of the head to the neck, with x from ear to ear
          and y from nose to back of head.
        */
        /* x3 --> x2
           x2 --> x3
           x1 --> x1
        */
        if (!mri_dst)
          {
            mri_dst = MRIalloc(width, depth, height, mri_src->type) ;
            MRIcopyHeader(mri_src, mri_dst) ;
          }
        else if (depth != mri_dst->height || height != mri_dst->depth ||
                 width != mri_dst->width)
          ErrorReturn(NULL,
                      (ERROR_BADPARM,
                       "MRIreslice: invalid destination size (%d, %d, %d)",
                       mri_dst->width, mri_dst->height, mri_dst->depth)) ;

        for (x3 = 0 ; x3 < depth ; x3++)
          {
            for (x2 = 0 ; x2 < height ; x2++)
              {
                psrc = &MRIvox(mri_src, 0, x2, x3) ;
                pdst = &MRIvox(mri_dst, 0, x3, x2) ;
                for (x1 = 0 ; x1 < width ; x1++)
                  {
                    /* swap so in place transformations are possible */
                    *pdst++ = *psrc++ ;
                  }
              }
          }
      }
    else
      if ((src_slice_direction == MRI_SAGITTAL &&
           slice_direction == MRI_HORIZONTAL))
        {
          /*
            horizontal images are top to bottom of the head,
            thus the depth axis
            points from the top of the head to the neck, with x from
            ear to ear
            and y from nose to back of head.
          */
          /* x3 --> x2
             x1 --> x3
             x2 --> x1
          */
          if (!mri_dst)
            {
              mri_dst = MRIalloc(width, depth, height, mri_src->type) ;
              MRIcopyHeader(mri_src, mri_dst) ;
            }
          else if (depth != mri_dst->height || height != mri_dst->depth ||
                   width != mri_dst->width)
            ErrorReturn(NULL,
                        (ERROR_BADPARM,
                         "MRIreslice: invalid destination size (%d, %d, %d)",
                         mri_dst->width, mri_dst->height, mri_dst->depth)) ;

          for (x3 = 0 ; x3 < depth ; x3++)
            {
              for (x2 = 0 ; x2 < height ; x2++)
                {
                  psrc = &MRIvox(mri_src, 0, x2, x3) ;
                  for (x1 = 0 ; x1 < width ; x1++)
                    {
                      /* swap so in place transformations are possible */
                      mri_dst->slices[x2][x1][x3] = *psrc++ ;
                    }
                }
            }
        }
      else
        if (src_slice_direction == MRI_HORIZONTAL &&
            slice_direction == MRI_SAGITTAL)
          {
            /*
              horizontal images are top to bottom of the head,
              thus the depth axis
              points from the top of the head to the neck,
              with x from ear to ear
              and y from nose to back of head.
            */
            /* x2 --> x3
               x3 --> x1
               x1 --> x2
            */
            if (!mri_dst)
              {
                mri_dst = MRIalloc(width, depth, height, mri_src->type) ;
                MRIcopyHeader(mri_src, mri_dst) ;
              }
            else if (depth != mri_dst->height || height != mri_dst->depth ||
                     width != mri_dst->width)
              ErrorReturn(NULL,
                          (ERROR_BADPARM,
                           "MRIreslice: invalid destination size (%d, %d, %d)",
                           mri_dst->width, mri_dst->height, mri_dst->depth)) ;

            for (x3 = 0 ; x3 < depth ; x3++)
              {
                for (x2 = 0 ; x2 < height ; x2++)
                  {
                    psrc = &MRIvox(mri_src, 0, x2, x3) ;
                    for (x1 = 0 ; x1 < width ; x1++)
                      {
                        /* swap so in place transformations are possible */
                        mri_dst->slices[x1][x3][x2] = *psrc++ ;
                      }
                  }
              }
          }
        else
          switch (src_slice_direction)
            {
            default:
              MRIfree(&mri_dst) ;
              ErrorReturn(NULL,
                          (ERROR_BADPARM,
                           "MRIreslice: mri_src unknown slice direction %d",
                           src_slice_direction)) ;
              break ;
            case MRI_CORONAL:
              /*
                coronal images are back to front of the head,
                thus the depth axis
                points from the nose to the back of the head,
                with x from neck to
                crown, and y from ear to ear.
              */
              switch (slice_direction)
                {
                case MRI_SAGITTAL:
                  /* x1 --> x3
                     x2 --> x2
                     x3 --> x1
                  */
                  if (!mri_dst)
                    {
                      mri_dst = MRIalloc(depth, height, width, mri_src->type) ;
                      MRIcopyHeader(mri_src, mri_dst) ;
                    }
                  else if (depth != mri_dst->width ||
                           height != mri_dst->height ||
                           width != mri_dst->depth)
                    ErrorReturn
                      (NULL,
                       (ERROR_BADPARM,
                        "MRIreslice: invalid destination size (%d, %d, %d)",
                        mri_dst->width, mri_dst->height, mri_dst->depth)) ;

                  for (x3 = 0 ; x3 < depth ; x3++)
                    {
                      for (x2 = 0 ; x2 < height ; x2++)
                        {
                          psrc = &MRIvox(mri_src, 0, x2, x3) ;
                          for (x1 = 0 ; x1 < width ; x1++)
                            {
                              /* swap so in place transformations
                                 are possible */
                              val = *psrc++ ;
#if 0
                              mri_dst->slices[x3][x2][x1] =
                                mri_src->slices[x1][x2][x3] ;
#endif
                              mri_dst->slices[x1][x2][x3] = val ;
                            }
                        }
                    }
                  break ;
                case MRI_HORIZONTAL:
                  break ;
                }
              break ;
            case MRI_SAGITTAL:
              /*
                sagittal images are slices in
                the plane of the nose, with depth going
                from ear to ear.
              */
              break ;
            }
  setDirectionCosine(mri_dst, slice_direction);
  mri_dst->ras_good_flag = 0;
  return(mri_dst) ;
}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  Set an MRI intensity values to 0
  ------------------------------------------------------*/
int
MRIclear(MRI *mri)
{
  int   width, depth, height, bytes, y, z, frame, nframes ;

  width = mri->width ;
  height = mri->height ;
  depth = mri->depth ;
  nframes = mri->nframes ;
  bytes = width ;

  switch (mri->type)
    {
    case MRI_UCHAR:
      bytes *= sizeof(unsigned char) ;
      break ;
    case MRI_BITMAP:
      bytes /= 8 ;
      break ;
    case MRI_FLOAT:
      bytes *= sizeof(float) ;
      break ;
    case MRI_LONG:
      bytes *= sizeof(long) ;
      break ;
    case MRI_INT:
      bytes *= sizeof(int) ;
      break ;
    case MRI_SHORT:
      bytes *= sizeof(short) ;
      break ;
    default:
      ErrorReturn(ERROR_UNSUPPORTED,
                  (ERROR_UNSUPPORTED,
                   "MRIclear: unsupported input type %d", mri->type)) ;
      break ;
    }

  for (frame = 0 ; frame < nframes ; frame++)
    {
      for (z = 0 ; z < depth ; z++)
        {
          for (y = 0 ; y < height ; y++)
            memset(mri->slices[z+frame*depth][y], 0, bytes) ;
        }
    }

  return(NO_ERROR) ;
}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  find the principle components of a (binary) MRI. The
  eigenvectors are the columns of the matrix mEvectors, the
  eigenvalues are returned in the array evalues and the means
  in means (these last two must be three elements long.)
  ------------------------------------------------------*/
int
MRIcenterOfMass(MRI *mri,double *means, BUFTYPE threshold)
{
  int     width, height, depth, x, y, z ;
  long    npoints ;
  double  mx, my, mz, weight ;
  Real    val ;

  width = mri->width ;
  height = mri->height ;
  depth = mri->depth ;

  mx = my = mz = weight = 0.0f ; npoints = 0L ;

  for (z = 0 ; z < depth ; z++)
    {
      for (y = 0 ; y < height ; y++)
        {
          for (x = 0 ; x < width ; x++)
            {
              MRIsampleVolumeType(mri, x,  y, z, &val, SAMPLE_NEAREST) ;
              if (val > threshold)
                {
                  weight += val ;
                  mx += (float)x*val ;
                  my += (float)y*val ;
                  mz += (float)z*val ;
                  npoints++ ;
                }
            }
        }
    }

  if (weight > 0.0)
    {
      mx /= weight ;
      my /= weight  ;
      mz /= weight ;
      means[0] = mx ;
      means[1] = my ;
      means[2] = mz ;
    }
  else
    means[0] = means[1] = means[2] = 0.0f ;


  return(NO_ERROR) ;
}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  find the principle components of a (binary) MRI. The
  eigenvectors are the columns of the matrix mEvectors, the
  eigenvalues are returned in the array evalues and the means
  in means (these last two must be three elements long.)
  ------------------------------------------------------*/
int
MRIprincipleComponents(MRI *mri, MATRIX *mEvectors, float *evalues,
                       double *means, BUFTYPE threshold)
{
  int     width, height, depth, x, y, z ;
  BUFTYPE *psrc, val ;
  long    npoints ;
  MATRIX  *mCov, *mX, *mXT, *mTmp ;
  double  mx, my, mz, weight ;

  if (mri->type != MRI_UCHAR)
    ErrorReturn(ERROR_UNSUPPORTED,
                (ERROR_UNSUPPORTED,
                 "MRIprincipleComponents: unsupported input type %d",
                 mri->type)) ;

  width = mri->width ;
  height = mri->height ;
  depth = mri->depth ;

  mx = my = mz = weight = 0.0f ; npoints = 0L ;

  for (z = 0 ; z < depth ; z++)
    {
      for (y = 0 ; y < height ; y++)
        {
          psrc = &MRIvox(mri, 0, y, z) ;
          for (x = 0 ; x < width ; x++)
            {
              val = *psrc++ ;
              if (val > threshold)
                {
                  weight += val ;
                  mx += (float)x*val ;
                  my += (float)y*val ;
                  mz += (float)z*val ;
                  npoints++ ;
                }
            }
        }
    }

  if (weight > 0.0)
    {
      mx /= weight ;
      my /= weight  ;
      mz /= weight ;
      means[0] = mx ;
      means[1] = my ;
      means[2] = mz ;
    }
  else
    means[0] = means[1] = means[2] = 0.0f ;

  mX = MatrixAlloc(3, 1, MATRIX_REAL) ;     /* zero-mean coordinate vector */
  mXT = NULL ;                              /* transpose of above */
  mTmp = MatrixAlloc(3, 3, MATRIX_REAL) ;   /* tmp matrix for covariance */
  mCov = MatrixAlloc(3, 3, MATRIX_REAL) ;   /* covariance matrix */

  for (z = 0 ; z < depth ; z++)
    {
      for (y = 0 ; y < height ; y++)
        {
          psrc = &MRIvox(mri, 0, y, z) ;
          for (x = 0 ; x < width ; x++)
            {
              val = *psrc++ ;
              if (val > threshold)
                {
                  mX->rptr[1][1] = (x - (int)mx)*val ;
                  mX->rptr[2][1] = (y - (int)my)*val ;
                  mX->rptr[3][1] = (z - (int)mz)*val ;
                  mXT = MatrixTranspose(mX, mXT) ;
                  mTmp = MatrixMultiply(mX, mXT, mTmp) ;
                  mCov = MatrixAdd(mTmp, mCov, mCov) ;
                }
            }
        }
    }

  if (weight > 0)
    MatrixScalarMul(mCov, 1.0f/weight, mCov) ;

  MatrixEigenSystem(mCov, evalues, mEvectors) ;

  return(NO_ERROR) ;
}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  find the principle components of a (binary) MRI. The
  eigenvectors are the columns of the matrix mEvectors, the
  eigenvalues are returned in the array evalues and the means
  in means (these last two must be three elements long.)
  ------------------------------------------------------*/
int
MRIbinaryPrincipleComponents(MRI *mri, MATRIX *mEvectors, float *evalues,
                             double *means, BUFTYPE threshold)
{
  int     width, height, depth, x, y, z ;
  BUFTYPE *psrc, val ;
  long    npoints ;
  MATRIX  *mCov, *mX, *mXT, *mTmp ;
  double  mx, my, mz, weight ;

  if (mri->type != MRI_UCHAR)
    ErrorReturn(ERROR_UNSUPPORTED,
                (ERROR_UNSUPPORTED,
                 "MRIprincipleComponents: unsupported input type %d",
                 mri->type)) ;

  width = mri->width ;
  height = mri->height ;
  depth = mri->depth ;

  mx = my = mz = weight = 0.0f ; npoints = 0L ;

  for (z = 0 ; z < depth ; z++)
    {
      for (y = 0 ; y < height ; y++)
        {
          psrc = &MRIvox(mri, 0, y, z) ;
          for (x = 0 ; x < width ; x++)
            {
              val = *psrc++ ;
              if (val > threshold)
                {
                  weight++ ;
                  mx += (float)x ;
                  my += (float)y ;
                  mz += (float)z ;
                  npoints++ ;
                }
            }
        }
    }

  if (weight > 0.0)
    {
      mx /= weight ;
      my /= weight  ;
      mz /= weight ;
      means[0] = mx ;
      means[1] = my ;
      means[2] = mz ;
    }
  else
    means[0] = means[1] = means[2] = 0.0f ;

  mX = MatrixAlloc(3, 1, MATRIX_REAL) ;     /* zero-mean coordinate vector */
  mXT = NULL ;                              /* transpose of above */
  mTmp = MatrixAlloc(3, 3, MATRIX_REAL) ;   /* tmp matrix for covariance */
  mCov = MatrixAlloc(3, 3, MATRIX_REAL) ;   /* covariance matrix */

  for (z = 0 ; z < depth ; z++)
    {
      for (y = 0 ; y < height ; y++)
        {
          psrc = &MRIvox(mri, 0, y, z) ;
          for (x = 0 ; x < width ; x++)
            {
              val = *psrc++ ;
              if (val > threshold)
                {
                  mX->rptr[1][1] = (x - (int)mx) ;
                  mX->rptr[2][1] = (y - (int)my) ;
                  mX->rptr[3][1] = (z - (int)mz) ;
                  mXT = MatrixTranspose(mX, mXT) ;
                  mTmp = MatrixMultiply(mX, mXT, mTmp) ;
                  mCov = MatrixAdd(mTmp, mCov, mCov) ;
                }
            }
        }
    }

  if (weight > 0)
    MatrixScalarMul(mCov, 1.0f/weight, mCov) ;

  MatrixEigenSystem(mCov, evalues, mEvectors) ;

  return(NO_ERROR) ;
}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  threshold an MRI.
  ------------------------------------------------------*/
MRI *
MRIthresholdRangeInto(MRI *mri_src,MRI *mri_dst,BUFTYPE low_val,BUFTYPE hi_val)
{
  int     width, height, depth, x, y, z ;
  BUFTYPE *psrc, *pdst, val ;
  float   *pfsrc, *pfdst, fval ;

  if (!mri_dst)
    mri_dst = MRIclone(mri_src, NULL) ;

  width = mri_src->width ;
  height = mri_src->height ;
  depth = mri_src->depth ;

  switch (mri_src->type)
    {
    case MRI_UCHAR:
      for (z = 0 ; z < depth ; z++)
        {
          for (y = 0 ; y < height ; y++)
            {
              psrc = &MRIvox(mri_src, 0, y, z) ;
              pdst = &MRIvox(mri_dst, 0, y, z) ;
              for (x = 0 ; x < width ; x++, pdst++)
                {
                  val = *psrc++ ;
                  if (val >=  low_val && val <= hi_val)
                    *pdst = val ;
                  else
                    *pdst = 0 ;
                }
            }
        }
      break ;
    case MRI_FLOAT:
      for (z = 0 ; z < depth ; z++)
        {
          for (y = 0 ; y < height ; y++)
            {
              pfsrc = &MRIFvox(mri_src, 0, y, z) ;
              pfdst = &MRIFvox(mri_dst, 0, y, z) ;
              for (x = 0 ; x < width ; x++, pdst++)
                {
                  fval = *pfsrc++ ;
                  if (fval >=  low_val && fval <= hi_val)
                    *pfdst = fval ;
                  else
                    *pfdst = 0 ;
                }
            }
        }
      break ;
    default:
      ErrorReturn(mri_dst,
                  (ERROR_UNSUPPORTED,
                   "MRIthresholdRangeInto: unsupported type %d",
                   mri_src->type)) ;
      break ;
    }
  return(mri_dst) ;
}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  threshold an MRI.
  ------------------------------------------------------*/
MRI *
MRIthreshold(MRI *mri_src, MRI *mri_dst, float threshold)
{
  int     width, height, depth, x, y, z ;
  float   val ;

  if (!mri_dst)
    mri_dst = MRIclone(mri_src, NULL) ;

  width = mri_src->width ;
  height = mri_src->height ;
  depth = mri_src->depth ;

  for (z = 0 ; z < depth ; z++)
    {
      for (y = 0 ; y < height ; y++)
        {
          for (x = 0 ; x < width ; x++)
            {
              val = MRIgetVoxVal(mri_src, x, y, z, 0) ;
              if (val < threshold)
                val = 0 ;
              MRIsetVoxVal(mri_dst, x, y, z, 0, val) ;
            }
        }
    }

  return(mri_dst) ;
}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  threshold an MRI.
  ------------------------------------------------------*/
MRI  *
MRIinvertContrast(MRI *mri_src, MRI *mri_dst, float threshold)
{
  int     width, height, depth, x, y, z ;
  BUFTYPE *psrc, *pdst, val ;

  if (!mri_dst)
    mri_dst = MRIclone(mri_src, NULL) ;

  width = mri_src->width ;
  height = mri_src->height ;
  depth = mri_src->depth ;

  for (z = 0 ; z < depth ; z++)
    {
      for (y = 0 ; y < height ; y++)
        {
          psrc = &MRIvox(mri_src, 0, y, z) ;
          pdst = &MRIvox(mri_dst, 0, y, z) ;
          for (x = 0 ; x < width ; x++)
            {
              val = *psrc++ ;
              if (val > threshold)
                val = 255 - val ;
              *pdst++ = val ;
            }
        }
    }

  return(mri_dst) ;
}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  threshold an MRI.
  ------------------------------------------------------*/
MRI *
MRIbinarize(MRI *mri_src, MRI *mri_dst, BUFTYPE threshold, BUFTYPE low_val,
            BUFTYPE hi_val)
{
  int     width, height, depth, x, y, z, f ;
  Real    val ;

  if (!mri_dst)
    mri_dst = MRIclone(mri_src, NULL) ;

  width = mri_src->width ;
  height = mri_src->height ;
  depth = mri_src->depth ;

  for (f = 0 ; f < mri_src->nframes ; f++)
    {
      for (z = 0 ; z < depth ; z++)
        {
          for (y = 0 ; y < height ; y++)
            {
              for (x = 0 ; x < width ; x++)
                {
                  MRIsampleVolumeFrameType
                    (mri_src, x, y, z, f, SAMPLE_NEAREST, &val) ;
                  if (val < threshold)
                    val = low_val ;
                  else
                    val = hi_val ;
                  MRIsetVoxVal(mri_dst, x, y, z, f, val) ;
                }
            }
        }
    }

  return(mri_dst) ;
}

/*-----------------------------------------------------*/
MRI *MRIsubtract(MRI *mri1, MRI *mri2, MRI *mri_dst)
{
  int     nframes, width, height, depth, x, y, z, f, s ;
  float v1, v2, v=0.0;
  BUFTYPE *p1=NULL, *p2=NULL, *pdst=NULL ;
  short *ps1=NULL, *ps2=NULL, *psdst=NULL ;
  int   *pi1=NULL, *pi2=NULL, *pidst=NULL ;
  long  *pl1=NULL, *pl2=NULL, *pldst=NULL ;
  float *pf1=NULL, *pf2=NULL, *pfdst=NULL ;

  width = mri1->width ;
  height = mri1->height ;
  depth = mri1->depth ;
  nframes = mri1->nframes ;
  if(nframes == 0) nframes = 1;

  if (!mri_dst){
    mri_dst = MRIallocSequence(width, height, depth, mri1->type,nframes) ;
    MRIcopyHeader(mri1, mri_dst) ;
  }

  if(mri1->type != mri2->type){
    /* Generic but slow */
    for (f = 0 ; f < nframes ; f++){
      for (z = 0 ; z < depth ; z++){
        for (y = 0 ; y < height ; y++){
          for (x = 0 ; x < width ; x++){
            v1 = MRIgetVoxVal(mri1,x,y,z,f);
            v2 = MRIgetVoxVal(mri2,x,y,z,f);
            v = v1-v2;
            MRIsetVoxVal(mri_dst,x,y,z,f,v);
          }
        }
      }
    }
    return(mri_dst) ;
  }


  s = 0;
  for (f = 0 ; f < nframes ; f++){
    for (z = 0 ; z < depth ; z++){
      for (y = 0 ; y < height ; y++){

        switch(mri_dst->type){
        case MRI_UCHAR: pdst  =           mri_dst->slices[s][y] ; break;
        case MRI_SHORT: psdst = (short *) mri_dst->slices[s][y] ; break;
        case MRI_INT:   pidst = (int *)   mri_dst->slices[s][y] ; break;
        case MRI_LONG:  pldst = (long *)  mri_dst->slices[s][y] ; break;
        case MRI_FLOAT: pfdst = (float *) mri_dst->slices[s][y] ; break;
        }
        switch(mri1->type){
        case MRI_UCHAR:
          p1 = mri1->slices[s][y] ;
          p2 = mri2->slices[s][y] ;
          break;
        case MRI_SHORT:
          ps1 = (short *) mri1->slices[s][y] ;
          ps2 = (short *) mri2->slices[s][y] ;
          break;
        case MRI_INT:
          pi1 = (int *) mri1->slices[s][y] ;
          pi2 = (int *) mri2->slices[s][y] ;
          break;
        case MRI_LONG:
          pl1 = (long *) mri1->slices[s][y] ;
          pl2 = (long *) mri2->slices[s][y] ;
          break;
        case MRI_FLOAT:
          pf1 = (float *) mri1->slices[s][y] ;
          pf2 = (float *) mri2->slices[s][y] ;
          break;
        }

        for (x = 0 ; x < width ; x++){

          switch(mri1->type){
          case MRI_UCHAR: v = (float)(*p1++)  - (float)(*p2++);  break;
          case MRI_SHORT: v = (float)(*ps1++) - (float)(*ps2++); break;
          case MRI_INT:   v = (float)(*pi1++) - (float)(*pi2++); break;
          case MRI_LONG:  v = (float)(*pl1++) - (float)(*pl2++); break;
          case MRI_FLOAT: v = (float)(*pf1++) - (float)(*pf2++); break;
          }

          switch(mri_dst->type){
          case MRI_UCHAR: (*pdst++)  = (BUFTYPE) v; break;
          case MRI_SHORT: (*psdst++) = (short)   v; break;
          case MRI_INT:   (*pidst++) = (int)     v; break;
          case MRI_LONG:  (*pldst++) = (long)    v; break;
          case MRI_FLOAT: (*pfdst++) = (float)   v; break;
          }

        }
      }
      s++;
    }
  }

  return(mri_dst) ;
}
#if 0
/*------------------------------------------------------
  MRIsubtract(mri1,mri2,mridiff) - computes mri1-mri2.
  ------------------------------------------------------*/
MRI *MRIsubtract(MRI *mri1, MRI *mri2, MRI *mri_dst)
{
  int     nframes, width, height, depth, x, y, z, f ;
  float v1, v2, vdiff;

  width = mri1->width ;
  height = mri1->height ;
  depth = mri1->depth ;
  nframes = mri1->nframes ;
  if(nframes == 0) nframes = 1;

  if (!mri_dst){
    mri_dst = MRIallocSequence(width, height, depth, mri1->type,nframes) ;
    MRIcopyHeader(mri1, mri_dst) ;
  }

  for (z = 0 ; z < depth ; z++){
    for (y = 0 ; y < height ; y++){
      for (x = 0 ; x < width ; x++){
        for (f = 0 ; f < nframes ; f++){
          v1 = MRIgetVoxVal(mri1,x,y,z,f);
          v2 = MRIgetVoxVal(mri2,x,y,z,f);
          vdiff = v1-v2;
          MRIsetVoxVal(mri_dst,x,y,z,f,vdiff);
        }
      }
    }
  }
  return(mri_dst) ;
}
#endif
/*-----------------------------------------------------
  ------------------------------------------------------*/
MRI *
MRIabsdiff(MRI *mri1, MRI *mri2, MRI *mri_dst)
{
  int     width, height, depth, x, y, z ;
  BUFTYPE *p1, *p2, *pdst, v1, v2 ;
  float   f1, f2 ;

  width = mri1->width ;
  height = mri1->height ;
  depth = mri1->depth ;

  if (!mri_dst)
    {
      mri_dst = MRIalloc(width, height, depth, mri1->type) ;
      MRIcopyHeader(mri1, mri_dst) ;
    }

  if (mri1->type == MRI_UCHAR && mri2->type == MRI_UCHAR)
    {
      for (z = 0 ; z < depth ; z++)
        {
          for (y = 0 ; y < height ; y++)
            {
              p1 = mri1->slices[z][y] ;
              p2 = mri2->slices[z][y] ;
              pdst = mri_dst->slices[z][y] ;
              for (x = 0 ; x < width ; x++)
                {
                  v1 = *p1++ ;
                  v2 = *p2++ ;
                  if (v1 > v2)
                    *pdst++ = v1 - v2 ;
                  else
                    *pdst++ = v2 - v1 ;
                }
            }
        }
    }
  else
    {
      for (z = 0 ; z < depth ; z++)
        {
          for (y = 0 ; y < height ; y++)
            {
              for (x = 0 ; x < width ; x++)
                {
                  f1 = MRIgetVoxVal(mri1, x, y, z, 0)  ;
                  f2 = MRIgetVoxVal(mri2, x, y, z, 0)  ;
                  MRIsetVoxVal(mri_dst, x, y, z, 0, fabs(f1 - f2)) ;
                }
            }
        }
    }
  return(mri_dst) ;
}
/*-----------------------------------------------------
  ------------------------------------------------------*/
MRI *MRIabs(MRI *mri_src, MRI *mri_dst)
{
  int   width, height, depth, nframes, x, y, z,f ;
  float val;

  width = mri_src->width ;
  height = mri_src->height ;
  depth = mri_src->depth ;
  nframes = mri_src->nframes;

  if (!mri_dst){
    mri_dst = MRIallocSequence(width, height, depth, mri_src->type, nframes) ;
    MRIcopyHeader(mri_src, mri_dst) ;
  }

  for (z = 0 ; z < depth ; z++){
    for (y = 0 ; y < height ; y++){
      for (x = 0 ; x < width ; x++){
        for (f = 0 ; f < nframes ; f++){
          val = fabs(MRIgetVoxVal(mri_src,x,y,z,f));
          MRIsetVoxVal(mri_src,x,y,z,f,val);
        }
      }
    }
  }
  return(mri_dst) ;
}
/*-----------------------------------------------------*/
MRI * MRIadd(MRI *mri1, MRI *mri2, MRI *mri_dst)
{
  int     nframes, width, height, depth, x, y, z, f, s ;
  float v1, v2, v=0.0;
  BUFTYPE *p1=NULL, *p2=NULL, *pdst=NULL ;
  short *ps1=NULL, *ps2=NULL, *psdst=NULL ;
  int   *pi1=NULL, *pi2=NULL, *pidst=NULL ;
  long  *pl1=NULL, *pl2=NULL, *pldst=NULL ;
  float *pf1=NULL, *pf2=NULL, *pfdst=NULL ;

  width = mri1->width ;
  height = mri1->height ;
  depth = mri1->depth ;
  nframes = mri1->nframes ;
  if(nframes == 0) nframes = 1;

  if (!mri_dst){
    mri_dst = MRIallocSequence(width, height, depth, mri1->type,nframes) ;
    MRIcopyHeader(mri1, mri_dst) ;
  }

  if(mri1->type == MRI_UCHAR || (mri1->type != mri2->type)){
    /* Generic but slow */
    for (f = 0 ; f < nframes ; f++){
      for (z = 0 ; z < depth ; z++){
        for (y = 0 ; y < height ; y++){
          for (x = 0 ; x < width ; x++){
            v1 = MRIgetVoxVal(mri1,x,y,z,f);
            v2 = MRIgetVoxVal(mri2,x,y,z,f);
            v = v1+v2;
            if (mri_dst->type == MRI_UCHAR && v > 255)
              v = 255 ;
            MRIsetVoxVal(mri_dst,x,y,z,f,v);
          }
        }
      }
    }
    return(mri_dst) ;
  }


  s = 0;
  for (f = 0 ; f < nframes ; f++){
    for (z = 0 ; z < depth ; z++){
      for (y = 0 ; y < height ; y++){

        switch(mri_dst->type){
        case MRI_UCHAR: pdst  = mri_dst->slices[s][y] ; break;
        case MRI_SHORT: psdst = (short *) mri_dst->slices[s][y] ; break;
        case MRI_INT:   pidst = (int *)   mri_dst->slices[s][y] ; break;
        case MRI_LONG:  pldst = (long *)  mri_dst->slices[s][y] ; break;
        case MRI_FLOAT: pfdst = (float *) mri_dst->slices[s][y] ; break;
        }

        switch(mri1->type){
        case MRI_UCHAR:
          p1 = mri1->slices[s][y] ;
          p2 = mri2->slices[s][y] ;
          break;
        case MRI_SHORT:
          ps1 = (short *) mri1->slices[s][y] ;
          ps2 = (short *) mri2->slices[s][y] ;
          break;
        case MRI_INT:
          pi1 = (int *) mri1->slices[s][y] ;
          pi2 = (int *) mri2->slices[s][y] ;
          break;
        case MRI_LONG:
          pl1 = (long *) mri1->slices[s][y] ;
          pl2 = (long *) mri2->slices[s][y] ;
          break;
        case MRI_FLOAT:
          pf1 = (float *) mri1->slices[s][y] ;
          pf2 = (float *) mri2->slices[s][y] ;
          break;
        }

        for (x = 0 ; x < width ; x++){

          switch(mri_dst->type){
          case MRI_UCHAR:
            switch(mri1->type){
            case MRI_UCHAR: (*pdst++) = (BUFTYPE) ((*p1++)+(*p2++));   break;
            case MRI_SHORT: (*pdst++) = (BUFTYPE) ((*ps1++)+(*ps2++)); break;
            case MRI_INT:   (*pdst++) = (BUFTYPE) ((*pi1++)+(*pi2++)); break;
            case MRI_LONG:  (*pdst++) = (BUFTYPE) ((*pl1++)+(*pl2++)); break;
            case MRI_FLOAT: (*pdst++) = (BUFTYPE) ((*pf1++)+(*pf2++)); break;
            }
            break;
          case MRI_SHORT:
            switch(mri1->type){
            case MRI_UCHAR: (*psdst++) = ((short)(*p1++)+(*p2++));   break;
            case MRI_SHORT: (*psdst++) = (short) ((*ps1++)+(*ps2++)); break;
            case MRI_INT:   (*psdst++) = (short) ((*pi1++)+(*pi2++)); break;
            case MRI_LONG:  (*psdst++) = (short) ((*pl1++)+(*pl2++)); break;
            case MRI_FLOAT: (*psdst++) = (short) ((*pf1++)+(*pf2++)); break;
            }
            break;
          case MRI_INT:
            switch(mri1->type){
            case MRI_UCHAR: (*pidst++) = ((int)(*p1++)+(*p2++));   break;
            case MRI_SHORT: (*pidst++) = ((int)(*ps1++)+(*ps2++)); break;
            case MRI_INT:   (*pidst++) = (int) ((*pi1++)+(*pi2++)); break;
            case MRI_LONG:  (*pidst++) = (int) ((*pl1++)+(*pl2++)); break;
            case MRI_FLOAT: (*pidst++) = (int) ((*pf1++)+(*pf2++)); break;
            }
            break;
          case MRI_LONG:
            switch(mri1->type){
            case MRI_UCHAR: (*pldst++) = ((long)(*p1++)+(*p2++));   break;
            case MRI_SHORT: (*pldst++) = ((long)(*ps1++)+(*ps2++)); break;
            case MRI_INT:   (*pldst++) = ((long)(*pi1++)+(*pi2++)); break;
            case MRI_LONG:  (*pldst++) = (long) ((*pl1++)+(*pl2++)); break;
            case MRI_FLOAT: (*pldst++) = (long) ((*pf1++)+(*pf2++)); break;
            }
            break;
          case MRI_FLOAT:
            switch(mri1->type){
            case MRI_UCHAR: (*pfdst++) = ((float)(*p1++)+(*p2++)); break;
            case MRI_SHORT: (*pfdst++) = ((float)(*ps1++)+(*ps2++)); break;
            case MRI_INT:   (*pfdst++) = ((float)(*pi1++)+(*pi2++)); break;
            case MRI_LONG:  (*pfdst++) = ((float)(*pl1++)+(*pl2++)); break;
            case MRI_FLOAT: (*pfdst++) =         (*pf1++)+(*pf2++);  break;
            }
            break;
          }
        }
      }
      s++;
    }
  }

  return(mri_dst) ;
}
/*-----------------------------------------------------------
  MRIaverage() - computes average of source and destination.
  ------------------------------------------------------*/
MRI *
MRIaverage(MRI *mri_src, int dof, MRI *mri_dst)
{
  int     width, height, depth, x, y, z, f ;
  Real    src, dst ;

  width = mri_src->width ;
  height = mri_src->height ;
  depth = mri_src->depth ;

  if (!mri_dst)
    {
      mri_dst = MRIalloc(width, height, depth, mri_src->type) ;
      MRIcopyHeader(mri_src, mri_dst) ;
    }

  if (!MRIcheckSize(mri_src, mri_dst,0,0,0))
    ErrorReturn(NULL,
                (ERROR_BADPARM,"MRIaverage: incompatible volume dimensions"));
#if 0
  if ((mri_src->type != MRI_UCHAR) || (mri_dst->type != MRI_UCHAR))
    ErrorReturn(NULL,
                (ERROR_UNSUPPORTED,
                 "MRISaverage: unsupported voxel format %d",mri_src->type));
#endif

  for (f = 0 ; f < mri_src->nframes ; f++)
    {
      for (z = 0 ; z < depth ; z++)
        {
          for (y = 0 ; y < height ; y++)
            {
              for (x = 0 ; x < width ; x++)
                {
                  MRIsampleVolumeFrameType
                    (mri_src, x,  y, z, f, SAMPLE_NEAREST, &src) ;
                  MRIsampleVolumeFrameType
                    (mri_dst, x,  y, z, f, SAMPLE_NEAREST, &dst) ;
                  MRIsetVoxVal
                    (mri_dst, x, y, z, f, (dst*dof+src)/(Real)(dof+1))  ;
                }
            }
        }
    }
  return(mri_dst) ;
}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description

  ------------------------------------------------------*/
MRI *
MRImultiply(MRI *mri1, MRI *mri2, MRI *mri_dst)
{
  int     width, height, depth, x, y, z ;
  float   f1, f2 ;

  width = mri1->width ;
  height = mri1->height ;
  depth = mri1->depth ;

  if (!mri_dst)
    {
      mri_dst = MRIalloc(width, height, depth, mri1->type) ;
      MRIcopyHeader(mri1, mri_dst) ;
    }

  for (z = 0 ; z < depth ; z++)
    {
      for (y = 0 ; y < height ; y++)
        {
          for (x = 0 ; x < width ; x++)
            {
              f1 = MRIgetVoxVal(mri1, x, y, z, 0) ;
              f2 = MRIgetVoxVal(mri2, x, y, z, 0) ;
              MRIsetVoxVal(mri_dst, x, y, z, 0, f1*f2) ;
            }
        }
    }
  return(mri_dst) ;
}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description

  ------------------------------------------------------*/
MRI *
MRIscaleAndMultiply(MRI *mri1, float scale, MRI *mri2, MRI *mri_dst)
{
  int     width, height, depth, x, y, z ;
  BUFTYPE *p1, *p2, *pdst ;
  float   out_val ;

  width = mri1->width ;
  height = mri1->height ;
  depth = mri1->depth ;

  if (!mri_dst)
    {
      mri_dst = MRIalloc(width, height, depth, mri1->type) ;
      MRIcopyHeader(mri1, mri_dst) ;
    }

  for (z = 0 ; z < depth ; z++)
    {
      for (y = 0 ; y < height ; y++)
        {
          p1 = mri1->slices[z][y] ;
          p2 = mri2->slices[z][y] ;
          pdst = mri_dst->slices[z][y] ;
          for (x = 0 ; x < width ; x++)
            {
              out_val = *p1++ * (*p2++/scale) ;
              if (out_val > 255)
                out_val = 255 ;
              else if (out_val < 0)
                out_val = 0 ;
              *pdst++ = (BUFTYPE)nint(out_val) ;
            }
        }
    }
  return(mri_dst) ;
}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description

  ------------------------------------------------------*/
MRI *
MRIdivide(MRI *mri1, MRI *mri2, MRI *mri_dst)
{
  int     width, height, depth, x, y, z ;
  BUFTYPE *p1, *p2, *pdst ;

  width = mri1->width ;
  height = mri1->height ;
  depth = mri1->depth ;

  if (!mri_dst)
    {
      mri_dst = MRIalloc(width, height, depth, mri1->type) ;
      MRIcopyHeader(mri1, mri_dst) ;
    }

  if (mri1->type != MRI_UCHAR || mri2->type != MRI_UCHAR)
    {
      Real val1, val2, dst ;

      for (z = 0 ; z < depth ; z++)
        {
          for (y = 0 ; y < height ; y++)
            {
              pdst = mri_dst->slices[z][y] ;
              for (x = 0 ; x < width ; x++)
                {
                  if (x == Gx && y == Gy && z==Gz)
                    DiagBreak() ;
                  val1 = MRIgetVoxVal(mri1, x, y, z, 0) ;
                  val2 = MRIgetVoxVal(mri2, x, y, z, 0) ;
                  if  (FZERO(val2))
                    {
                      dst = FZERO(val1) ? 0 : 255 ;
                    }
                  else
                    dst = val1 / val2 ;
                  if (abs(dst) > 1000)
                    DiagBreak() ;
                  MRIsetVoxVal(mri_dst, x, y, z, 0, dst) ;
                }
            }
        }
    }
  else   /* both UCHAR volumes */
    {
      for (z = 0 ; z < depth ; z++)
        {
          for (y = 0 ; y < height ; y++)
            {
              p1 = mri1->slices[z][y] ;
              p2 = mri2->slices[z][y] ;
              pdst = mri_dst->slices[z][y] ;
              for (x = 0 ; x < width ; x++)
                {
                  if  (!*p2)
                    {
                      *pdst = FZERO(*p1) ? 0 : 255 ;
                      p2++ ;
                    }
                  else
                    *pdst++ = *p1++ / *p2++ ;
                }
            }
        }
    }
  return(mri_dst) ;
}
/*-----------------------------------------------------
  MRIclone() - create a copy of an mri struct. Copies
  header info and allocs the pixel space (but does not
  copy pixel data).
  ------------------------------------------------------*/
MRI *MRIclone(MRI *mri_src, MRI *mri_dst)
{
  if (!mri_dst)
    mri_dst =
      MRIallocSequence(mri_src->width, mri_src->height,mri_src->depth,
                       mri_src->type, mri_src->nframes);

  MRIcopyHeader(mri_src, mri_dst) ;
  return(mri_dst) ;
}
/*---------------------------------------------------------------------
  MRIcloneSpace() - create a copy of an mri struct but allows user to
  set nframes (ie, all the spatial stuff is copied). Copies header
  info and allocs the pixel space (but does not copy pixel data).
  -------------------------------------------------------------------*/
MRI *MRIcloneBySpace(MRI *mri_src, int nframes)
{
  MRI *mri_dst;
  mri_dst = MRIallocSequence(mri_src->width, mri_src->height,mri_src->depth,
                             mri_src->type, nframes);
  MRIcopyHeader(mri_src, mri_dst) ;
  mri_dst->nframes = nframes;
  return(mri_dst) ;
}
/*-----------------------------------------------------
  Description
  Copy one MRI into another (including header info)
  ------------------------------------------------------*/
MRI *
MRIcloneRoi(MRI *mri_src, MRI *mri_dst)
{
  int  w, h, d ;

  w = mri_src->width - mri_src->roi.x ;
  h = mri_src->height - mri_src->roi.y ;
  d = mri_src->depth - mri_src->roi.z ;
  mri_dst = MRIallocSequence(w, h, d, MRI_FLOAT, mri_src->nframes) ;
  MRIcopyHeader(mri_src, mri_dst) ;
  mri_dst->xstart = mri_src->xstart + mri_src->roi.x * mri_src->xsize ;
  mri_dst->ystart = mri_src->ystart + mri_src->roi.y * mri_src->ysize ;
  mri_dst->zstart = mri_src->zstart + mri_src->roi.z * mri_src->zsize ;
  mri_dst->xend = mri_src->xstart + w * mri_src->xsize ;
  mri_dst->yend = mri_src->ystart + h * mri_src->ysize ;
  mri_dst->zend = mri_src->zstart + d * mri_src->zsize ;
  return(mri_dst) ;
}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  Copy one MRI into another (including header info and data)
  ------------------------------------------------------*/
MRI *
MRIcopy(MRI *mri_src, MRI *mri_dst)
{
  int     width, height, depth, bytes, x, y, z, frame, val ;
  float   *fdst, *fsrc ;
  BUFTYPE *csrc, *cdst ;
  int     dest_ptype, *isrc;
  short   *ssrc, *sdst ;

  if (mri_src == mri_dst)
    return(mri_dst) ;
  width = mri_src->width ;
  height = mri_src->height ;
  depth = mri_src->depth ;

  if (!mri_dst)
    {
      if(mri_src->slices)
        mri_dst = MRIallocSequence(width, height, depth, mri_src->type,
                                   mri_src->nframes) ;
      else
        mri_dst = MRIallocHeader(width, height, depth, mri_src->type);
    }
  dest_ptype = mri_dst->ptype;
  MRIcopyHeader(mri_src, mri_dst) ;
  mri_dst->ptype = dest_ptype;

  if(!mri_src->slices)
    return(mri_dst);

  if (mri_src->type == mri_dst->type)
    {
      bytes = width ;
      switch (mri_src->type)
        {
        case MRI_UCHAR:
          bytes *= sizeof(BUFTYPE) ;
          break ;
        case MRI_SHORT:
          bytes *= sizeof(short);
          break;
        case MRI_FLOAT:
          bytes *= sizeof(float) ;
          break ;
        case MRI_INT:
          bytes *= sizeof(int) ;
          break ;
        case MRI_LONG:
          bytes *= sizeof(long) ;
          break ;
        }

      for (frame = 0 ; frame < mri_src->nframes ; frame++)
        {
          for (z = 0 ; z < depth ; z++)
            {
              for (y = 0 ; y < height ; y++)
                {
                  memcpy(mri_dst->slices[z+frame*depth][y],
                         mri_src->slices[z+frame*depth][y], bytes) ;
                }
            }
        }
    }
  else
    {
      switch (mri_src->type)
        {
        case MRI_FLOAT:
          switch (mri_dst->type)
            {
            case MRI_SHORT: /* float --> short */
              for (frame = 0 ; frame < mri_src->nframes ; frame++)
                {
                  for (z = 0 ; z < depth ; z++)
                    {
                      for (y = 0 ; y < height ; y++)
                        {
                          sdst = &MRISseq_vox(mri_dst, 0, y, z, frame) ;
                          fsrc = &MRIFseq_vox(mri_src, 0, y, z, frame) ;
                          for (x = 0 ; x < width ; x++)
                            {
                              val = nint(*fsrc++) ;
                              *sdst++ = (short)val ;
                            }
                        }
                    }
                }
              break ;
            case MRI_UCHAR:  /* float --> unsigned char */
              for (frame = 0 ; frame < mri_src->nframes ; frame++)
                {
                  for (z = 0 ; z < depth ; z++)
                    {
                      for (y = 0 ; y < height ; y++)
                        {
                          cdst = &MRIseq_vox(mri_dst, 0, y, z, frame) ;
                          fsrc = &MRIFseq_vox(mri_src, 0, y, z, frame) ;
                          for (x = 0 ; x < width ; x++)
                            {
                              val = nint(*fsrc++) ;
                              if (val > 255)
                                val = 255 ;
                              *cdst++ = (BUFTYPE)val ;
                            }
                        }
                    }
                }
              break ;
            default:
              ErrorReturn(NULL,
                          (ERROR_BADPARM,
                           "MRIcopy: src type %d & dst type %d unsupported",
                           mri_src->type, mri_dst->type)) ;
              break ;
            }
          break ;
        case MRI_UCHAR:
          switch (mri_dst->type)
            {
            case MRI_FLOAT:   /* unsigned char --> float */
              for (frame = 0 ; frame < mri_src->nframes ; frame++)
                {
                  for (z = 0 ; z < depth ; z++)
                    {
                      for (y = 0 ; y < height ; y++)
                        {
                          fdst = &MRIFseq_vox(mri_dst, 0, y, z, frame) ;
                          csrc = &MRIseq_vox(mri_src, 0, y, z, frame) ;
                          for (x = 0 ; x < width ; x++)
                            *fdst++ = (float)*csrc++ ;
                        }
                    }
                }
              break ;
            default:
              ErrorReturn(NULL,
                          (ERROR_BADPARM,
                           "MRIcopy: src type %d & dst type %d unsupported",
                           mri_src->type, mri_dst->type)) ;
              break ;
            }
          break ;
        case MRI_SHORT:
          switch (mri_dst->type)
            {
            case MRI_FLOAT:   /* short --> float */
              for (frame = 0 ; frame < mri_src->nframes ; frame++)
                {
                  for (z = 0 ; z < depth ; z++)
                    {
                      for (y = 0 ; y < height ; y++)
                        {
                          fdst = &MRIFseq_vox(mri_dst, 0, y, z, frame) ;
                          ssrc = &MRISseq_vox(mri_src, 0, y, z, frame) ;
                          for (x = 0 ; x < width ; x++)
                            {
                              if (z == 113 && y == 143 && x == 161)
                                DiagBreak() ;
                              *fdst++ = (float)*ssrc++ ;
                            }
                        }
                    }
                }
              break ;
            case MRI_UCHAR:
              for (frame = 0 ; frame < mri_src->nframes ; frame++)
                {
                  for (z = 0 ; z < depth ; z++)
                    {
                      for (y = 0 ; y < height ; y++)
                        {
                          cdst = &MRIseq_vox(mri_dst, 0, y, z, frame) ;
                          ssrc = &MRISseq_vox(mri_src, 0, y, z, frame) ;
                          for (x = 0 ; x < width ; x++)
                            {
                              *cdst++ = (float)*ssrc++ ;
                            }
                        }
                    }
                }
              break ;
            default:
              ErrorReturn(NULL,
                          (ERROR_BADPARM,
                           "MRIcopy: src type %d & dst type %d unsupported",
                           mri_src->type, mri_dst->type)) ;
              break ;
            }
          break ;
        case MRI_INT:
          switch (mri_dst->type)
            {
            case MRI_FLOAT:   /* unsigned char --> float */
              for (frame = 0 ; frame < mri_src->nframes ; frame++)
                {
                  for (z = 0 ; z < depth ; z++)
                    {
                      for (y = 0 ; y < height ; y++)
                        {
                          fdst = &MRIFseq_vox(mri_dst, 0, y, z, frame) ;
                          isrc = &MRIIseq_vox(mri_src, 0, y, z, frame) ;
                          for (x = 0 ; x < width ; x++)
                            *fdst++ = (float)*isrc++ ;
                        }
                    }
                }
              break ;
            default:
              ErrorReturn(NULL,
                          (ERROR_BADPARM,
                           "MRIcopy: src type %d & dst type %d unsupported",
                           mri_src->type, mri_dst->type)) ;
              break ;
            }
          break ;
        default:
          ErrorReturn(NULL,
                      (ERROR_BADPARM,
                       "MRIcopy: src type %d & dst type %d unsupported",
                       mri_src->type, mri_dst->type)) ;
          break ;  /* in case someone removes the errorreturn */
        }
    }
  return(mri_dst) ;
}
/*
   make MAX_INDEX way larger than it has to be. This will give
   some headroom for bad (e.g. poorly registered) images without
   sacrificing too much space.
*/
#define MAX_INDEX    500
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  allocate a lookup table that allows indices which
  are outside the image region.
  ------------------------------------------------------*/
int
MRIallocIndices(MRI *mri)
{
  int width, height, depth, i ;

  width = mri->width ;
  height = mri->height ;
  depth = mri->depth ;
  mri->xi = (int *)calloc(width+2*MAX_INDEX, sizeof(int)) ;
  if (!mri->xi)
    ErrorExit(ERROR_NO_MEMORY,
              "MRIallocIndices: could not allocate %d elt index array",
              width+2*MAX_INDEX) ;
  mri->yi = (int *)calloc(height+2*MAX_INDEX, sizeof(int)) ;
  if (!mri->yi)
    ErrorExit(ERROR_NO_MEMORY,
              "MRIallocIndices: could not allocate %d elt index array",
              height+2*MAX_INDEX) ;
  mri->zi = (int *)calloc(depth+2*MAX_INDEX, sizeof(int)) ;
  if (!mri->zi)
    ErrorExit(ERROR_NO_MEMORY,
              "MRIallocIndices: could not allocate %d elt index array",
              depth+2*MAX_INDEX) ;

  /*
     indexing into these arrays returns valid pixel indices from
     -MAX_INDEX to width+MAX_INDEX
  */
  mri->xi += MAX_INDEX ;
  mri->yi += MAX_INDEX ;
  mri->zi += MAX_INDEX ;
  for (i = -MAX_INDEX ; i < width+MAX_INDEX ; i++)
    {
      if (i <= 0)
        mri->xi[i] = 0 ;
      else if (i >= width)
        mri->xi[i] = width-1 ;
      else
        mri->xi[i] = i ;
    }
  for (i = -MAX_INDEX ; i < height+MAX_INDEX ; i++)
    {
      if (i <= 0)
        mri->yi[i] = 0 ;
      else if (i >= height)
        mri->yi[i] = height-1 ;
      else
        mri->yi[i] = i ;
    }
  for (i = -MAX_INDEX ; i < depth+MAX_INDEX ; i++)
    {
      if (i <= 0)
        mri->zi[i] = 0 ;
      else if (i >= depth)
        mri->zi[i] = depth-1 ;
      else
        mri->zi[i] = i ;
    }

  return(NO_ERROR) ;
}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  allocate an MRI data structure as well as space for
  the image data
  ------------------------------------------------------*/
MRI *
MRIallocSequence(int width, int height, int depth, int type, int nframes)
{
  MRI     *mri ;
  int     slice, row, bpp ;
  BUFTYPE *buf ;

  mris_alloced++ ;

  if ((width <= 0) || (height <= 0) || (depth <= 0))
    ErrorReturn(NULL,
                (ERROR_BADPARM, "MRIalloc(%d, %d, %d): bad parm",
                 width, height, depth)) ;
#if 1
  mri = MRIallocHeader(width, height, depth, type) ;
  MRIinitHeader(mri) ;
#else
  mri = (MRI *)calloc(1, sizeof(MRI)) ;
  if (!mri)
    ErrorExit(ERROR_NO_MEMORY, "MRIalloc: could not allocate MRI\n") ;

  mri->scale = 1 ;
  mri->height = height ;
  mri->width = width ;
  mri->yinvert = 1 ;
  mri->depth = depth ;
  mri->type = type ;
#endif
  mri->nframes = nframes ;
  MRIallocIndices(mri) ;
  mri->slices = (BUFTYPE ***)calloc(depth*nframes, sizeof(BUFTYPE **)) ;
  if (!mri->slices)
    ErrorExit(ERROR_NO_MEMORY,
              "MRIalloc: could not allocate %d slices\n", mri->depth) ;

  for (slice = 0 ; slice < depth*nframes ; slice++)
    {
      /* allocate pointer to array of rows */
      mri->slices[slice] = (BUFTYPE **)calloc(mri->height, sizeof(BUFTYPE *)) ;
      if (!mri->slices[slice])
        ErrorExit
          (ERROR_NO_MEMORY,
           "MRIalloc(%d, %d, %d): could not allocate "
           "%d bytes for %dth slice\n",
           height, width, depth, mri->height*sizeof(BUFTYPE *), slice) ;

#if USE_ELECTRIC_FENCE
      switch (mri->type)
        {
        case MRI_BITMAP:
          bpp = 1 ;
          break ;
        case MRI_UCHAR:
          bpp = 8 ;
          break ;
        case MRI_TENSOR:
        case MRI_FLOAT:
          bpp = sizeof(float) * 8 ;
          break ;
        case MRI_INT:
          bpp = sizeof(int) * 8 ;
          break ;
        case MRI_SHORT:
          bpp = sizeof(short) * 8 ;
          break ;
        case MRI_LONG:
          bpp = sizeof(long) * 8 ;
          break ;
        default:
          ErrorReturn(NULL,
                      (ERROR_BADPARM,
                       "MRIalloc(%d, %d, %d, %d): unknown type",
                       width, height, depth, mri->type)) ;
          break ;
        }
      bpp /= 8 ;
      buf = (BUFTYPE *)calloc((mri->width*mri->height*bpp), 1) ;
      if (buf == NULL)
        ErrorExit
          (ERROR_NO_MEMORY,
           "MRIalloc(%d, %d, %d): could not allocate "
           "%d bytes for %dth slice\n",
           height, width, depth, (mri->width*mri->height*bpp), slice) ;
      for (row = 0 ; row < mri->height ; row++)
        {
          mri->slices[slice][row] = buf+(row*mri->width*bpp) ;
        }
#else
      /* allocate each row */
      for (row = 0 ; row < mri->height ; row++)
        {
          switch (mri->type)
            {
            case MRI_BITMAP:
              mri->slices[slice][row] =
                (BUFTYPE*)calloc(mri->width/8,sizeof(BUFTYPE));
              break ;
            case MRI_UCHAR:
              mri->slices[slice][row] =
                (BUFTYPE*)calloc(mri->width,sizeof(BUFTYPE));
              break ;
            case MRI_TENSOR:
            case MRI_FLOAT:
              mri->slices[slice][row] =
                (BUFTYPE *)calloc(mri->width, sizeof(float));
              break ;
            case MRI_INT:
              mri->slices[slice][row] =
                (BUFTYPE *)calloc(mri->width, sizeof(int)) ;
              break ;
            case MRI_SHORT:
              mri->slices[slice][row] =
                (BUFTYPE *)calloc(mri->width, sizeof(short));
              break ;
            case MRI_LONG:
              mri->slices[slice][row] =
                (BUFTYPE *)calloc(mri->width, sizeof(long)) ;
              break ;
            default:
              ErrorReturn(NULL,
                          (ERROR_BADPARM,
                           "MRIalloc(%d, %d, %d, %d): unknown type",
                           width, height, depth, mri->type)) ;
              break ;
            }

          if (!mri->slices[slice][row])
            ErrorExit
              (ERROR_NO_MEMORY,
               "MRIalloc(%d,%d,%d): could not allocate "
               "%dth row in %dth slice\n",
               width,height,depth, slice, row) ;
        }
#endif
    }

  return(mri) ;
}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  allocate an MRI data structure but not space for
  the image data
  ------------------------------------------------------*/
MRI *
MRIallocHeader(int width, int height, int depth, int type)
{
  MRI  *mri ;

  mri = (MRI *)calloc(1, sizeof(MRI)) ;
  if (!mri)
    ErrorExit(ERROR_NO_MEMORY, "MRIalloc: could not allocate MRI\n") ;

  mri->imnr0 = 1 ;
  mri->imnr1 = depth;
  mri->fov = width ;
  mri->thick = 1.0 ;
  mri->scale = 1 ;
  mri->roi.dx = mri->width = width ;
  mri->roi.dy = mri->height = height ;
  mri->roi.dz = mri->depth = depth ;
  mri->yinvert = 1 ;
  mri->xsize = mri->ysize = mri->zsize = 1 ;
  mri->type = type ;
  mri->nframes = 1 ;
  mri->xi = mri->yi = mri->zi = NULL ;
  mri->slices = NULL ;
  mri->ps = 1 ;
  mri->xstart = -mri->width/2.0 ;
  mri->xend = mri->width/2.0 ;
  mri->ystart = -mri->height/2.0 ;
  mri->yend = mri->height/2.0 ;
  mri->zstart = -mri->depth/2.0 ;
  mri->zend = mri->depth/2 ;
  //
  mri->x_r = -1;
  mri->x_a = 0.;
  mri->x_s = 0.;
  //
  mri->y_r = 0.;
  mri->y_a = 0.;
  mri->y_s = -1;
  //
  mri->z_r = 0.;
  mri->z_a = 1.;
  mri->z_s = 0.;
  //
  mri->c_r = mri->c_a = mri->c_s = 0.0;
  mri->ras_good_flag = 0;
  mri->brightness = 1;
  mri->register_mat = NULL;
  mri->subject_name[0] = '\0';
  mri->path_to_t1[0] = '\0';
  mri->fname_format[0] = '\0';
  mri->gdf_image_stem[0] = '\0';
  mri->tag_data = NULL;
  mri->tag_data_size = 0;

  mri->i_to_r__ = extract_i_to_r(mri);
  mri->r_to_i__ = extract_r_to_i(mri);

  return(mri) ;
}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  allocate an MRI data structure as well as space for
  the image data
  ------------------------------------------------------*/
MRI *
MRIalloc(int width, int height, int depth, int type)
{
  return(MRIallocSequence(width, height, depth, type, 1)) ;
}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  Free and MRI data structure and all its attached memory
  ------------------------------------------------------*/
int
MRIfree(MRI **pmri)
{
  MRI *mri ;
  int slice, i ;
#if !USE_ELECTRIC_FENCE
  int  row ;
#endif

  mris_alloced-- ;
  mri = *pmri ;
  if (!mri)
    ErrorReturn(ERROR_BADPARM, (ERROR_BADPARM, "MRIfree: null pointer\n")) ;

  if (mri->xi)
    free(mri->xi-MAX_INDEX) ;
  if (mri->yi)
    free(mri->yi-MAX_INDEX) ;
  if (mri->zi)
    free(mri->zi-MAX_INDEX) ;

  if (mri->slices)
    {
      for (slice = 0 ; slice < mri->depth*mri->nframes ; slice++)
        {
          if (mri->slices[slice])
            {
#if USE_ELECTRIC_FENCE
              free(mri->slices[slice][0]) ;
#else
              for (row = 0 ; row < mri->height ; row++)
                free(mri->slices[slice][row]) ;
#endif
              free(mri->slices[slice]) ;
            }
        }
      free(mri->slices) ;
    }

  if (mri->free_transform)
    delete_general_transform(&mri->transform) ;

  if(mri->register_mat != NULL)
    MatrixFree(&(mri->register_mat));

  if (mri->i_to_r__)
    MatrixFree(&mri->i_to_r__);
  if (mri->r_to_i__)
    MatrixFree(&mri->r_to_i__);

  for (i = 0 ; i < mri->ncmds ; i++)
    free(mri->cmdlines[i]) ;

  free(mri) ;
  *pmri = NULL ;

  return(NO_ERROR) ;
}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  Free and MRI data structure and all its attached memory
  ------------------------------------------------------*/
int
MRIfreeFrames(MRI *mri, int start_frame)
{
  int slice, row, end_frame ;

  end_frame = mri->nframes-1 ;
  if (!mri)
    ErrorReturn(ERROR_BADPARM, (ERROR_BADPARM, "MRIfree: null pointer\n")) ;

  if (mri->slices)
    {
      for (slice = start_frame*mri->depth ;
           slice < mri->depth*end_frame ; slice++)
        {
          if (mri->slices[slice])
            {
              for (row = 0 ; row < mri->height ; row++)
                free(mri->slices[slice][row]) ;
              free(mri->slices[slice]) ;
            }
        }
    }

  mri->nframes -= (end_frame-start_frame+1) ;
  return(NO_ERROR) ;
}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  Dump the MRI header to a file
  ------------------------------------------------------*/
int
MRIdump(MRI *mri, FILE *fp)
{
  fprintf(fp, "%6.6s = %s\n", "fname", mri->fname);
  fprintf(fp, "%6.6s = %d\n", "height", mri->height);
  fprintf(fp, "%6.6s = %d\n", "width", mri->width);
  fprintf(fp, "%6.6s = %d\n", "depth", mri->depth);
  fprintf(fp, "%6.6s = %d\n", "nframes", mri->nframes);
  fprintf(fp, "%6.6s = %d\n", "imnr0", mri->imnr0);
  fprintf(fp, "%6.6s = %d\n", "imnr1", mri->imnr1);
  fprintf(fp, "%6.6s = %d\n", "xnum", mri->width);
  fprintf(fp, "%6.6s = %d\n", "ynum", mri->height);
  fprintf(fp, "%6.6s = %f\n", "fov", mri->fov);
  fprintf(fp, "%6.6s = %f\n", "thick", mri->thick);
  fprintf(fp, "%6.6s = %f\n", "xstart", mri->xstart); /* strtx */
  fprintf(fp, "%6.6s = %f\n", "xend", mri->xend); /* endx */
  fprintf(fp, "%6.6s = %f\n", "ystart", mri->ystart); /* strty */
  fprintf(fp, "%6.6s = %f\n", "yend", mri->yend); /* endy */
  fprintf(fp, "%6.6s = %f\n", "zstart", mri->zstart); /* strtz */
  fprintf(fp, "%6.6s = %f\n", "zend", mri->zend); /* endz */
  fprintf(fp, "%6.6s = %d\n", "type", mri->type);
  fprintf(fp, "%6.6s = %f\n", "xsize", mri->xsize);
  fprintf(fp, "%6.6s = %f\n", "ysize", mri->ysize);
  fprintf(fp, "%6.6s = %f\n", "zsize", mri->zsize);
  fprintf(fp, "%6.6s = %f %f %f\n", "x ras", mri->x_r, mri->x_a, mri->x_s);
  fprintf(fp, "%6.6s = %f %f %f\n", "y ras", mri->y_r, mri->y_a, mri->y_s);
  fprintf(fp, "%6.6s = %f %f %f\n", "z ras", mri->z_r, mri->z_a, mri->z_s);
  fprintf(fp, "%6.6s = %f %f %f\n", "c ras", mri->c_r, mri->c_a, mri->c_s);
  fprintf(fp, "%s = %f\n", "det(xyz_ras)", MRIvolumeDeterminant(mri));
  fprintf(fp, "%s = %d\n", "ras_good_flag", mri->ras_good_flag);
  fprintf(fp, "%s = %d\n", "brightness", mri->brightness);
  fprintf(fp, "%s = %s\n", "subject_name", mri->subject_name);
  fprintf(fp, "%s = %s\n", "path_to_t1", mri->path_to_t1);
  fprintf(fp, "%s = %s\n", "fname_format", mri->fname_format);
  if(mri->register_mat != NULL)
    {
      fprintf(fp, "%s = \n", "register_mat");
      MatrixPrint(fp, mri->register_mat);
    }
  return(NO_ERROR) ;
}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  Dump the non-zero elements of an MRI buffer to a file
  ------------------------------------------------------*/
int
MRIdumpBuffer(MRI *mri, FILE *fp)
{
  int  x, y, z ;

  for (z = 0 ; z < mri->depth ; z++)
    {
      for (y = 0 ; y < mri->height ; y++)
        {
          for (x = 0 ; x < mri->width ; x++)
            {
              switch (mri->type)
                {
                case MRI_UCHAR:
                  if (!FZERO(mri->slices[z][y][x]))
                    fprintf
                      (fp,
                       "[%d][%d][%d]: %d\n",
                       x,y,z,(int)mri->slices[z][y][x]);
                  break ;
                case MRI_FLOAT:
                  /*          if (!FZERO(MRIFvox(mri,x,y,z)))*/
                  fprintf
                    (fp,
                     "[%d][%d][%d]: %2.3f\n",
                     x,y,z, MRIFvox(mri,x,y,z));
                  break ;
                }
            }
        }
    }
  return(NO_ERROR) ;
}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  Find the peak intensity in an MRI image
  ------------------------------------------------------*/
int
MRIpeak(MRI *mri, int *px, int *py, int *pz)
{
  int      max_row, max_col, max_slice, row, col, slice, width, height, depth ;
  BUFTYPE  val, max_val, *im ;
  long     lval, lmax_val, *lim ;
  int      ival, imax_val, *iim ;
  float    fval, fmax_val, *fim ;

  max_val = 0 ;
  lmax_val = 0L ;
  imax_val = 0 ;
  fmax_val = 0.0f ;
  max_row = max_col = max_slice = -1 ;   /* to prevent compiler warning */
  width = mri->width ;
  height = mri->height ;
  depth = mri->depth ;
  for (slice = 0 ; slice < depth ; slice++)
    {
      for (row = 0 ; row < height ; row++)
        {
          switch (mri->type)
            {
            case MRI_UCHAR:
              im = mri->slices[slice][row] ;
              for (col = 0 ; col < width ; col++)
                {
                  val = *im++ ;
                  if (val > max_val)
                    {
                      max_val = val ;
                      max_row = row ;
                      max_col = col ;
                      max_slice = slice ;
                    }
                }
              break ;
            case MRI_LONG:
              lim = (long *)mri->slices[slice][row] ;
              for (col = 0 ; col < width ; col++)
                {
                  lval = *lim++ ;
                  if (lval > lmax_val)
                    {
                      lmax_val = lval ;
                      max_row = row ;
                      max_col = col ;
                      max_slice = slice ;
                    }
                }
              break ;
            case MRI_FLOAT:
              fim = (float *)mri->slices[slice][row] ;
              for (col = 0 ; col < width ; col++)
                {
                  fval = *fim++ ;
                  if (fval > fmax_val)
                    {
                      fmax_val = fval ;
                      max_row = row ;
                      max_col = col ;
                      max_slice = slice ;
                    }
                }
              break ;
            case MRI_INT:
              iim = (int *)mri->slices[slice][row] ;
              for (col = 0 ; col < width ; col++)
                {
                  ival = *iim++ ;
                  if (ival > imax_val)
                    {
                      imax_val = ival ;
                      max_row = row ;
                      max_col = col ;
                      max_slice = slice ;
                    }
                }
              break ;
            }
        }
    }

  *px = max_col ;
  *py = max_row ;
  *pz = max_slice ;
  return(NO_ERROR) ;
}
/*--------------------------------------------------------------
  Description: Copy the header information from one MRI into another.
  Does not copy the dimension lengths, only the geometry, pulse seq,
  etc.
  ------------------------------------------------------*/
int MRIcopyHeader(MRI *mri_src, MRI *mri_dst)
{
  int i ;

  mri_dst->dof = mri_src->dof ;
  mri_dst->mean = mri_src->mean ;
  mri_dst->xsize = mri_src->xsize ;
  mri_dst->ysize = mri_src->ysize ;
  mri_dst->zsize = mri_src->zsize ;
  if (mri_src->linear_transform)
    {
      copy_general_transform(&mri_src->transform, &mri_dst->transform) ;
      mri_dst->linear_transform = mri_src->linear_transform ;
      mri_dst->inverse_linear_transform = mri_src->inverse_linear_transform ;
      mri_dst->linear_transform =
        get_linear_transform_ptr(&mri_dst->transform) ;
      mri_dst->inverse_linear_transform =
        get_inverse_linear_transform_ptr(&mri_dst->transform) ;
      mri_dst->free_transform = 1 ;
    }
  strcpy(mri_dst->transform_fname, mri_src->transform_fname) ;
  if (mri_dst->depth == mri_src->depth)
    {
      mri_dst->imnr0 = mri_src->imnr0 ;
      mri_dst->imnr1 = mri_src->imnr1 ;
    }
  mri_dst->ptype = mri_src->ptype ;
  mri_dst->fov = mri_src->fov ;
  mri_dst->thick = mri_src->thick ;
  mri_dst->ps = mri_src->ps ;
  mri_dst->location = mri_src->location ;
  mri_dst->xstart = mri_src->xstart ;
  mri_dst->xend = mri_src->xend ;
  mri_dst->ystart = mri_src->ystart ;
  mri_dst->yend = mri_src->yend ;
  mri_dst->zstart = mri_src->zstart ;
  mri_dst->zend = mri_src->zend ;
  mri_dst->flip_angle = mri_src->flip_angle ;
  mri_dst->tr = mri_src->tr ;
  mri_dst->te = mri_src->te ;
  mri_dst->ti = mri_src->ti ;
  strcpy(mri_dst->fname, mri_src->fname) ;
  mri_dst->x_r = mri_src->x_r;
  mri_dst->x_a = mri_src->x_a;
  mri_dst->x_s = mri_src->x_s;
  mri_dst->y_r = mri_src->y_r;
  mri_dst->y_a = mri_src->y_a;
  mri_dst->y_s = mri_src->y_s;
  mri_dst->z_r = mri_src->z_r;
  mri_dst->z_a = mri_src->z_a;
  mri_dst->z_s = mri_src->z_s;
  mri_dst->c_r = mri_src->c_r;
  mri_dst->c_a = mri_src->c_a;
  mri_dst->c_s = mri_src->c_s;
  mri_dst->ras_good_flag = mri_src->ras_good_flag;

  mri_dst->brightness = mri_src->brightness;
  if(mri_src->register_mat != NULL)
    MatrixCopy(mri_src->register_mat, mri_dst->register_mat);
  else
    mri_dst->register_mat = NULL;
  strcpy(mri_dst->subject_name, mri_src->subject_name);
  strcpy(mri_dst->path_to_t1, mri_src->path_to_t1);
  strcpy(mri_dst->fname_format, mri_src->fname_format);

  strcpy(mri_dst->gdf_image_stem, mri_src->gdf_image_stem);

  mri_dst->i_to_r__ = MatrixCopy(mri_src->i_to_r__, mri_dst->i_to_r__);
  mri_dst->r_to_i__ = MatrixCopy(mri_src->r_to_i__, mri_dst->r_to_i__);

  for (i = 0 ; i < mri_src->ncmds ; i++)
    {
      mri_dst->cmdlines[i] =
        (char *)calloc(strlen(mri_src->cmdlines[i])+1, sizeof(char)) ;
      strcpy(mri_dst->cmdlines[i], mri_src->cmdlines[i]) ;
    }
  mri_dst->ncmds = mri_src->ncmds ;
  return(NO_ERROR) ;
}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  Translate the MRI image mri_src by the vector
  dx, dy, dz  and store the result in mri_dst.
  ------------------------------------------------------*/
MRI *
MRItranslate(MRI *mri_src, MRI *mri_dst, double dx, double dy, double dz)
{
  int      y1, y2, y3, width, height, depth ;
  BUFTYPE *pdst ;
  Real     x1, x2, x3, val ;

  width = mri_src->width ;
  height = mri_src->height ;
  depth = mri_src->depth ;
  if (!mri_dst)
    mri_dst = MRIclone(mri_src, NULL) ;
  else
    MRIclear(mri_dst) ;

  for (y3 = 0 ; y3 < depth ; y3++)
    {
      x3 = (Real)y3 - dz ;
      if (x3 < 0 || x3 >= depth)
        continue ;

      for (y2 = 0 ; y2 < height ; y2++)
        {
          x2 = (Real)y2 - dy ;
          if (x2 < 0 || x2 >= height)
            continue ;

          pdst = &MRIvox(mri_dst, 0, y2, y3) ;
          for (y1 = 0 ; y1 < width ; y1++, pdst++)
            {
              x1 = (Real)y1 - dx ;
              if (x1 >= 0 && x1 < width)
                {
                  MRIsampleVolume(mri_src, x1, x2, x3, &val) ;
                  *pdst = (BUFTYPE)nint(val) ;
                }
            }
        }
    }

  mri_dst->ras_good_flag = 0;

  return(mri_dst) ;
}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  Rotate mri_src around the Y axis and return the
  result in mri_dst
  ------------------------------------------------------*/
MRI *
MRIrotateX(MRI *mri_src, MRI *mri_dst, float x_angle)
{
  int    width, height, depth ;
  MATRIX *m, *mO ;

  width = mri_src->width ;
  height = mri_src->height ;
  depth = mri_src->depth ;
  if (!mri_dst)
    mri_dst = MRIclone(mri_src, NULL) ;
  else
    MRIclear(mri_dst) ;

  m = MatrixAllocRotation(3, x_angle, X_ROTATION) ;

  mO = MatrixAlloc(3, 1, MATRIX_REAL) ;
  mO->rptr[1][1] = mri_src->width / 2 ;
  mO->rptr[2][1] = mri_src->height / 2 ;
  mO->rptr[3][1] = mri_src->depth / 2 ;

  /* build rotation matrix */
  mri_dst = MRIrotate(mri_src, NULL, m, mO) ;
  MatrixFree(&m) ;
  MatrixFree(&mO) ;

  mri_dst->ras_good_flag = 0;

  return(mri_dst) ;
}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  Rotate mri_src around the Y axis and return the
  result in mri_dst
  ------------------------------------------------------*/
MRI *
MRIrotateY(MRI *mri_src, MRI *mri_dst, float y_angle)
{
  int    width, height, depth ;
  MATRIX *m, *mO ;

  width = mri_src->width ;
  height = mri_src->height ;
  depth = mri_src->depth ;
  if (!mri_dst)
    mri_dst = MRIclone(mri_src, NULL) ;
  else
    MRIclear(mri_dst) ;

  /* origin of coordinate system */
  mO = MatrixAlloc(3, 1, MATRIX_REAL) ;
  mO->rptr[1][1] = mri_src->width / 2 ;
  mO->rptr[2][1] = mri_src->height / 2 ;
  mO->rptr[3][1] = mri_src->depth / 2 ;

  m = MatrixAllocRotation(3, y_angle, Y_ROTATION) ;

  /* build rotation matrix */
  mri_dst = MRIrotate(mri_src, NULL, m, mO) ;
  MatrixFree(&m) ;
  MatrixFree(&mO) ;

  mri_dst->ras_good_flag = 0;

  return(mri_dst) ;
}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  Rotate mri_src around the Z axis and return the
  result in mri_dst
  ------------------------------------------------------*/
MRI *
MRIrotateZ(MRI *mri_src, MRI *mri_dst, float z_angle)
{
  int    width, height, depth ;
  MATRIX *m, *mO ;

  width = mri_src->width ;
  height = mri_src->height ;
  depth = mri_src->depth ;
  if (!mri_dst)
    mri_dst = MRIclone(mri_src, NULL) ;
  else
    MRIclear(mri_dst) ;

  m = MatrixAllocRotation(3, z_angle, Z_ROTATION) ;
  mO = MatrixAlloc(3, 1, MATRIX_REAL) ;
  mO->rptr[1][1] = mri_src->width / 2 ;
  mO->rptr[2][1] = mri_src->height / 2 ;
  mO->rptr[3][1] = mri_src->depth / 2 ;

  mri_dst = MRIrotate(mri_src, NULL, m, mO) ;
  MatrixFree(&m) ;
  MatrixFree(&mO) ;

  mri_dst->ras_good_flag = 0;

  return(mri_dst) ;
}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  Rotate mri_src around the Y axis and return the
  result in mri_dst
  ------------------------------------------------------*/
MRI *
MRIrotateX_I(MRI *mri_src, MRI *mri_dst, float x_angle)
{
  int    width, height, depth ;
  MATRIX *m ;

  width = mri_src->width ;
  height = mri_src->height ;
  depth = mri_src->depth ;
  if (!mri_dst)
    mri_dst = MRIclone(mri_src, NULL) ;
  else
    MRIclear(mri_dst) ;

  m = MatrixAllocRotation(3, x_angle, X_ROTATION) ;

  /* build rotation matrix */
  mri_dst = MRIrotate_I(mri_src, NULL, m, NULL) ;
  MatrixFree(&m) ;

  mri_dst->ras_good_flag = 0;

  return(mri_dst) ;
}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  Rotate mri_src around the Y axis and return the
  result in mri_dst
  ------------------------------------------------------*/
MRI *
MRIrotateY_I(MRI *mri_src, MRI *mri_dst, float y_angle)
{
  int    width, height, depth ;
  MATRIX *m ;

  width = mri_src->width ;
  height = mri_src->height ;
  depth = mri_src->depth ;
  if (!mri_dst)
    mri_dst = MRIclone(mri_src, NULL) ;
  else
    MRIclear(mri_dst) ;

  m = MatrixAllocRotation(3, y_angle, Y_ROTATION) ;

  /* build rotation matrix */
  mri_dst = MRIrotate_I(mri_src, NULL, m, NULL) ;
  MatrixFree(&m) ;

  mri_dst->ras_good_flag = 0;

  return(mri_dst) ;
}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  Rotate mri_src around the Z axis and return the
  result in mri_dst
  ------------------------------------------------------*/
MRI *
MRIrotateZ_I(MRI *mri_src, MRI *mri_dst, float z_angle)
{
  int    width, height, depth ;
  MATRIX *m ;

  width = mri_src->width ;
  height = mri_src->height ;
  depth = mri_src->depth ;
  if (!mri_dst)
    mri_dst = MRIclone(mri_src, NULL) ;
  else
    MRIclear(mri_dst) ;

  m = MatrixAllocRotation(3, z_angle, Z_ROTATION) ;
  mri_dst = MRIrotate_I(mri_src, NULL, m, NULL) ;
  MatrixFree(&m) ;

  mri_dst->ras_good_flag = 0;

  return(mri_dst) ;
}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  Scale the MRI image mri_src by sx,sy,sz in the
  x, y, and z directions respectively.
  ------------------------------------------------------*/
MRI *
MRIscale(MRI *mri_src, MRI *mri_dst, float sx, float sy, float sz)
{
  int    width, height, depth ;
  MATRIX *m ;

  width = mri_src->width ;
  height = mri_src->height ;
  depth = mri_src->depth ;
  if (!mri_dst)
    {
      mri_dst = MRIalloc(width, height, depth, mri_src->type) ;
      MRIcopyHeader(mri_src, mri_dst) ;
    }

  m = MatrixAlloc(4, 4, MATRIX_REAL) ;

  /* build rotation matrix */
  m->rptr[1][1] = sx ;
  m->rptr[2][2] = sy ;
  m->rptr[3][3] = sz ;
  m->rptr[4][4] = 1.0 ;
  mri_dst = MRIlinearTransform(mri_src, NULL, m) ;
  MatrixFree(&m) ;

  mri_dst->ras_good_flag = 0;

  return(mri_dst) ;
}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  Rotate about the point mO
  ------------------------------------------------------*/
MRI *
MRIrotate(MRI *mri_src, MRI *mri_dst, MATRIX *mR, MATRIX *mO)
{
  int    x1, x2, x3, y1, y2, y3, width, height, depth, y1o, y2o, y3o, freeit ;
  MATRIX *mX, *mY ;   /* original and transformed coordinate systems */
  MATRIX *mRinv ;   /* inverse of R */

  mRinv = MatrixInverse(mR, NULL) ;
  if (!mRinv)
    ErrorReturn(NULL,(ERROR_BADPARM,"MRIrotate: rotation matrix is singular"));

  width = mri_src->width ;
  height = mri_src->height ;
  depth = mri_src->depth ;
  if (!mri_dst)
    mri_dst = MRIclone(mri_src, NULL) ;
  else
    MRIclear(mri_dst) ;

  if (!mO)
    {
      mO = MatrixAlloc(3, 1, MATRIX_REAL) ;
      mO->rptr[1][1] = mri_src->width / 2 ;
      mO->rptr[2][1] = mri_src->height / 2 ;
      mO->rptr[3][1] = mri_src->depth / 2 ;
      freeit = 1 ;
    }
  else
    freeit = 0 ;

  mX = MatrixAlloc(3, 1, MATRIX_REAL) ;  /* input coordinates */
  mY = MatrixAlloc(3, 1, MATRIX_REAL) ;  /* transformed coordinates */


  y1o = mO->rptr[1][1] ;
  y2o = mO->rptr[2][1] ;
  y3o = mO->rptr[3][1] ;
  for (y3 = 0 ; y3 < depth ; y3++)
    {
      mY->rptr[3][1] = y3 - y3o ;
      for (y2 = 0 ; y2 < height ; y2++)
        {
          mY->rptr[2][1] = y2 - y2o ;
          for (y1 = 0 ; y1 < width ; y1++)
            {
              mY->rptr[1][1] = y1 - y1o ;
              MatrixMultiply(mRinv, mY, mX) ;
              MatrixAdd(mX, mO, mX) ;

              /* should do bilinear interpolation here */
              x1 = nint(mX->rptr[1][1]) ;
              x2 = nint(mX->rptr[2][1]) ;
              x3 = nint(mX->rptr[3][1]) ;
              if (x1 >= 0 && x1 < width &&
                  x2 >= 0 && x2 < height &&
                  x3 >= 0 && x3 < depth)
                mri_dst->slices[y3][y2][y1] = mri_src->slices[x3][x2][x1] ;
            }
        }
    }

  MatrixFree(&mX) ;
  MatrixFree(&mRinv) ;
  MatrixFree(&mY) ;
  if (freeit)
    MatrixFree(&mO) ;

  mri_dst->ras_good_flag = 0;

  return(mri_dst) ;
}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  Rotate about the point mO using trilinear interpolation
  ------------------------------------------------------*/
MRI *
MRIrotate_I(MRI *mri_src, MRI *mri_dst, MATRIX *mR, MATRIX *mO)
{
  int    y1, y2, y3, width, height, depth, y1o, y2o, y3o, freeit ;
  MATRIX *mX, *mY ;   /* original and transformed coordinate systems */
  MATRIX *mRinv ;   /* inverse of R */
  float  x1, x2, x3 ;
  Real   val ;

  mRinv = MatrixInverse(mR, NULL) ;
  if (!mRinv)
    ErrorReturn(NULL,(ERROR_BADPARM,
                      "MRIrotate_I: rotation matrix is singular"));

  width = mri_src->width ;
  height = mri_src->height ;
  depth = mri_src->depth ;
  if (!mri_dst)
    mri_dst = MRIclone(mri_src, NULL) ;
  else
    MRIclear(mri_dst) ;

  if (!mO)
    {
      mO = MatrixAlloc(3, 1, MATRIX_REAL) ;
      mO->rptr[1][1] = mri_src->width / 2 ;
      mO->rptr[2][1] = mri_src->height / 2 ;
      mO->rptr[3][1] = mri_src->depth / 2 ;
      freeit = 1 ;
    }
  else
    freeit = 0 ;

  mX = MatrixAlloc(3, 1, MATRIX_REAL) ;  /* input coordinates */
  mY = MatrixAlloc(3, 1, MATRIX_REAL) ;  /* transformed coordinates */


  y1o = mO->rptr[1][1] ;
  y2o = mO->rptr[2][1] ;
  y3o = mO->rptr[3][1] ;
  if (Gdiag == 99)
    MatrixPrint(stdout, mRinv) ;
  if (Gdiag == 99)
    MatrixPrint(stdout, mO) ;
  for (y3 = 0 ; y3 < depth ; y3++)
    {
      mY->rptr[3][1] = y3 - y3o ;
      for (y2 = 0 ; y2 < height ; y2++)
        {
          mY->rptr[2][1] = y2 - y2o ;
          for (y1 = 0 ; y1 < width ; y1++)
            {
              mY->rptr[1][1] = y1 - y1o ;
              MatrixMultiply(mRinv, mY, mX) ;
              MatrixAdd(mX, mO, mX) ;

              /* do trilinear interpolation here */
              x1 = mX->rptr[1][1] ;
              x2 = mX->rptr[2][1] ;
              x3 = mX->rptr[3][1] ;

              MRIsampleVolume(mri_src, x1, x2, x3, &val) ;
              mri_dst->slices[y3][y2][y1] = (BUFTYPE)nint(val) ;
            }
        }
    }

  MatrixFree(&mX) ;
  MatrixFree(&mRinv) ;
  MatrixFree(&mY) ;
  if (freeit)
    MatrixFree(&mO) ;

  mri_dst->ras_good_flag = 0;

  return(mri_dst) ;
}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  Perform an affine coordinate transformation x' = Ax + B on
  the MRI image mri_src into mri_dst
  ------------------------------------------------------*/
MRI *
MRIaffine(MRI *mri_src, MRI *mri_dst, MATRIX *mA, MATRIX *mB)
{
  int    x1, x2, x3, y1, y2, y3, width, height, depth ;
  MATRIX *mX, *mY ;   /* original and transformed coordinate systems */

  width = mri_src->width ;
  height = mri_src->height ;
  depth = mri_src->depth ;
  if (!mri_dst)
    {
      mri_dst = MRIalloc(width, height, depth, mri_src->type) ;
      MRIcopyHeader(mri_src, mri_dst) ;
    }

  mX = MatrixAlloc(3, 1, MATRIX_REAL) ;  /* input coordinates */
  mY = MatrixAlloc(3, 1, MATRIX_REAL) ;  /* transformed coordinates */

  for (x3 = 0 ; x3 < depth ; x3++)
    {
      mX->rptr[3][1] = x3 ;
      for (x2 = 0 ; x2 < height ; x2++)
        {
          mX->rptr[2][1] = x2 ;
          for (x1 = 0 ; x1 < width ; x1++)
            {
              mX->rptr[1][1] = x1 ;
              if (mA)
                MatrixMultiply(mA, mX, mY) ;
              else
                MatrixCopy(mX, mY) ;
              if (mB)
                MatrixAdd(mY, mB, mY) ;
              y1 = nint(mY->rptr[1][1]) ;
              y2 = nint(mY->rptr[2][1]) ;
              y3 = nint(mY->rptr[3][1]) ;
              if (y1 >= 0 && y1 < width &&
                  y2 >= 0 && y2 < height &&
                  y3 >= 0 && y3 < depth)
                mri_dst->slices[y3][y2][y1] = mri_src->slices[x3][x2][x1] ;
            }
        }
    }


  MatrixFree(&mX) ;
  MatrixFree(&mY) ;

  mri_dst->ras_good_flag = 0;

  return(mri_dst) ;
}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  Convert a slice of an MRI data structure into a HIPS image.
  ------------------------------------------------------*/
IMAGE *
MRItoImageView(MRI *mri, IMAGE *I, int slice, int view, int frame)
{
  int      width, height, depth, x, y, yp, w, h, d,
    xm, ym, zm, format ;
  float    fmin, fmax ;
  Real     val ;
  int src_slice_direction;
  int xsign, ysign;

  xsign=ysign=1; // let compiler be satisfied

  src_slice_direction = getSliceDirection(mri);
  // only strict slice direction is supported
  if (src_slice_direction == MRI_UNDEFINED)
    ErrorReturn(NULL,
                (ERROR_UNSUPPORTED,
                 "MRItoImageView(%d, %d): unsupported view/slice direction %d",
                 slice, view, src_slice_direction)) ;

  // generic routines to get the right axes
  //
  // should be able to do this in generic terms.
  // don't have time to do that
  //
  // Define the view to be the following:
  // coronal    = (-R, -S)
  // sagittal   = ( A, -S)
  // horizontal = (-R,  A)
  //
  // translate these direction into (x,y)
  d = w = h = xm = ym = zm = 0 ;  /* silly compiler warnings */
  width = mri->width ;
  height = mri->height ;
  depth = mri->depth ;

  switch(src_slice_direction)
    {
    case MRI_CORONAL: // x direction can be -R or R,
      // y direction can be -S or S, z direction can be -A or A
      //     +/-1  0  0
      //      0    0  +/-1
      //      0  +/-1 0
      switch(view)
        {
        case MRI_CORONAL:
          w = width; h = height; d = depth;
          xsign = (mri->x_r > 0) ? -1 : 1; ysign = (mri->y_s > 0) ? -1 : 1;
          break;
        case MRI_SAGITTAL:
          w = depth; h = height; d = width;
          xsign = (mri->z_a > 0) ?  1 : -1; ysign = (mri->y_s > 0) ? -1 : 1;
          break;
        case MRI_HORIZONTAL:
          w = width; h = depth;  d = height;
          xsign = (mri->x_r > 0) ? -1 : 1; ysign = (mri->z_a > 0) ? 1 : -1;
          break;
        }
      break;
    case MRI_SAGITTAL: // x direction can be -A or A,
      // y direction can be -S or S, z direction can be -R or R
      //    0    0   +/-1
      //  +/-1   0    0
      //    0  +/-1   0
      switch(view)
        {
        case MRI_CORONAL:
          w = depth; h = height; d = width;
          xsign =
            (mri->z_r > 0) ? -1 : 1; ysign = (mri->y_s > 0) ? -1 : 1;
          break;
        case MRI_SAGITTAL:
          w = width; h = height; d = depth;
          xsign = (mri->x_a > 0) ? 1 : -1; ysign = (mri->y_s > 0) ? -1 : 1;
          break;
        case MRI_HORIZONTAL:
          w = depth; h = width;  d = height;
          xsign = (mri->z_r > 0) ? -1 : 1; ysign = (mri->x_a > 0) ? 1 : -1;
          break;
        }
      break;
    case MRI_HORIZONTAL: // x direction can be -R or R,
      // y direction can be -A or A, z direction can be -S or S
      //   +/-1  0   0
      //    0   +/-1 0
      //    0    0  +/-1
      switch(view)
        {
        case MRI_CORONAL:
          w = width; h = depth; d = height;
          xsign = (mri->x_r > 0) ? -1 : 1; ysign = (mri->z_s > 0) ? -1 : 1;
          break;
        case MRI_SAGITTAL:
          w = height; h = depth; d = width;
          xsign = (mri->y_a > 0) ? 1 : -1; ysign = (mri->z_s > 0) ? -1 : 1;
          break;
        case MRI_HORIZONTAL:
          w = width; h = height;  d = depth;
          xsign = (mri->x_r > 0) ? -1 : 1; ysign = (mri->y_a > 0) ?  1 : -1;
          break;
        }
      break;
    default:
      ErrorReturn
        (NULL,
         (ERROR_UNSUPPORTED,
          "MRItoImageView(%d, %d): unsupported view/slice direction %d",
          slice, view, src_slice_direction)) ;
    }
  if (slice < 0 || slice >= d)
    ErrorReturn(NULL, (ERROR_BADPARM, "MRItoImageView: bad slice %d\n",slice));

#if 0
  format = (mri->type == MRI_UCHAR) ? PFBYTE :
    mri->type == MRI_INT   ? PFINT :
    mri->type == MRI_FLOAT ? PFFLOAT :
    mri->type == MRI_SHORT ? PFFLOAT : PFBYTE ;
#else
  format = PFBYTE ;
#endif

  if (I && ((I->rows != h) || (I->cols != w) || (I->pixel_format != format)))
    {
      ImageFree(&I);
      I = NULL ;  /* must allocate a new one */
    }
  if (!I)
    I = ImageAlloc(h, w, format, 1) ;

  fmin = 10000000 ; fmax = -fmin ;

  // Image values assigned from MRI
  // we need to gert min and max in IMAGE
  for (y = 0 ; y < h ; y++)
    {
      for (x = 0 ; x < w ; x++)
        {
          switch(src_slice_direction)
            {
            case MRI_CORONAL:
              switch (view)   /* calculate coordinates in MR structure */
                {
                case MRI_CORONAL:
                  xm = (xsign> 0) ? x : (w - 1 -x);
                  ym = (ysign> 0) ? y : (h - 1 -y);
                  zm = slice ;
                  break ;
                case MRI_SAGITTAL:
                  xm = slice ;
                  ym = (ysign>0) ? y : (h - 1 - y);
                  zm = (xsign>0) ? x : (w - 1 - x);
                  break ;
                case MRI_HORIZONTAL:
                  xm = (xsign>0) ? x : (w - 1 - x);
                  ym = slice ;
                  zm = (ysign>0) ? y : (h - 1 - y);
                  break ;
                }
              break;
            case MRI_SAGITTAL:
              switch (view)   /* calculate coordinates in MR structure */
                {
                case MRI_CORONAL:
                  xm = slice;
                  ym = (ysign>0) ? y : (h - 1 - y);
                  zm = (xsign>0) ? x : (w - 1 - x);
                  break ;
                case MRI_SAGITTAL:
                  xm = (xsign>0) ? x : (w - 1 - x);
                  ym = (ysign>0) ? y : (h - 1 - y);
                  zm = slice ;
                  break ;
                case MRI_HORIZONTAL:
                  xm = (ysign>0) ? y : (h - 1 - y);
                  ym = slice;
                  zm = (xsign>0) ? x : (w - 1 - x);
                  break ;
                }
              break;
            case MRI_HORIZONTAL:
              switch (view)   /* calculate coordinates in MR structure */
                {
                case MRI_CORONAL:
                  xm = (xsign>0) ? x : (w - 1 - x);
                  ym = slice ;
                  zm = (ysign>0) ? y : (h - 1 - y);
                  break ;
                case MRI_SAGITTAL:
                  xm = slice ;
                  ym = (xsign>0) ? x : (w - 1 - x);
                  zm = (ysign>0) ? y : (h - 1 - y);
                  break ;
                case MRI_HORIZONTAL:
                  xm = (xsign>0) ? x : (w - 1 - x);
                  ym = (ysign>0) ? y : (h - 1 - y);
                  zm = slice ;
                  break ;
                }
              break;
            }
          MRIsampleVolumeFrame(mri, xm, ym, zm, frame, &val) ;
          // check min max
          if (val > fmax)
            fmax = val ;
          if (val < fmin)
            fmin = val ;
        }
    }
  if (FZERO(fmax-fmin))
    ErrorReturn(I, (ERROR_BADPARM, "MRItoImageView: constant image")) ;

  // after all these calculation, we are going to do it again?
  for (y = 0 ; y < h ; y++)
    {

      for (x = 0 ; x < w ; x++)
        {
          switch(src_slice_direction)
            {
            case MRI_CORONAL:
              switch (view)   /* calculate coordinates in MR structure */
                {
                case MRI_CORONAL:
                  xm = (xsign> 0) ? x : (w - 1 -x);
                  ym = (ysign> 0) ? y : (h - 1 -y);
                  zm = slice ;
                  break ;
                case MRI_SAGITTAL:
                  xm = slice ;
                  ym = (ysign>0) ? y : (h - 1 - y);
                  zm = (xsign>0) ? x : (w - 1 - x);
                  break ;
                case MRI_HORIZONTAL:
                  xm = (xsign>0) ? x : (w - 1 - x);
                  ym = slice ;
                  zm = (ysign>0) ? y : (h - 1 - y);
                  break ;
                }
              break;
            case MRI_SAGITTAL:
              switch (view)   /* calculate coordinates in MR structure */
                {
                case MRI_CORONAL:
                  xm = slice;
                  ym = (ysign>0) ? y : (h - 1 - y);
                  zm = (xsign>0) ? x : (w - 1 - x);
                  break ;
                case MRI_SAGITTAL:
                  xm = (xsign>0) ? x : (w - 1 - x);
                  ym = (ysign>0) ? y : (h - 1 - y);
                  zm = slice ;
                  break ;
                case MRI_HORIZONTAL:
                  xm = (ysign>0) ? y : (h - 1 - y);
                  ym = slice;
                  zm = (xsign>0) ? x : (w - 1 - x);
                  break ;
                }
              break;
            case MRI_HORIZONTAL:
              switch (view)   /* calculate coordinates in MR structure */
                {
                case MRI_CORONAL:
                  xm = (xsign>0) ? x : (w - 1 - x);
                  ym = slice ;
                  zm = (ysign>0) ? y : (h - 1 - y);
                  break ;
                case MRI_SAGITTAL:
                  xm = slice ;
                  ym = (xsign>0) ? x : (w - 1 - x);
                  zm = (ysign>0) ? y : (h - 1 - y);
                  break ;
                case MRI_HORIZONTAL:
                  xm = (xsign>0) ? x : (w - 1 - x);
                  ym = (ysign>0) ? y : (h - 1 - y);
                  zm = slice ;
                  break ;
                }
              break;
            }
          MRIsampleVolumeFrame(mri, xm, ym, zm, frame, &val) ;
          yp = h - (y+1) ;   /* hips coordinate system is inverted */
          *IMAGEpix(I, x, yp) = (byte)(255.0 * (val - fmin) / (fmax - fmin)) ;
        }
    }

  return(I) ;
}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  Convert a slice of an MRI data structure into a HIPS image.
  ------------------------------------------------------*/
IMAGE *
MRItoImage(MRI *mri, IMAGE *I, int slice)
{
  int           width, height, y, yp ;

  width = mri->width ;
  height = mri->height ;
  if (slice < 0 || slice >= mri->depth)
    ErrorReturn(NULL, (ERROR_BADPARM, "MRItoImage: bad slice %d\n", slice)) ;

  if (!I)
    {
      I = ImageAlloc(height, width,
                     mri->type == MRI_UCHAR ? PFBYTE :
                     mri->type == MRI_INT   ? PFINT :
                     mri->type == MRI_FLOAT ? PFFLOAT : PFBYTE, 1) ;
    }

  for (y = 0 ; y < height ; y++)
    {
      yp = height - (y+1) ;

#if 0
      if (mri->type == MRI_UCHAR)
        image_to_buffer(I->image, mri, slice) ;
      else
#endif
        switch (mri->type)
          {
          case MRI_INT:
            memcpy
              (IMAGEIpix(I, 0, yp),mri->slices[slice][y],width*sizeof(int)) ;
            break ;
          case MRI_FLOAT:
            memcpy
              (IMAGEFpix(I, 0, yp),mri->slices[slice][y],width*sizeof(float));
            break ;
          case MRI_UCHAR:
            memcpy(IMAGEpix(I, 0, yp), mri->slices[slice][y],
                   width*sizeof(unsigned char)) ;
            break ;
          default:
          case MRI_LONG:
            ImageFree(&I) ;
            ErrorReturn
              (NULL,
               (ERROR_UNSUPPORTED,
                "MRItoImage: unsupported type %d",
                mri->type)) ;
            break ;
          }
    }

  return(I) ;
}

MRI *
ImageToMRI(IMAGE *I)
{
  MRI *mri;
  int  width, height, depth, type, nframes, y, yp, x ;
  int frames;

  type = MRI_UCHAR; // to make compiler happy
  width = I->ocols;
  height = I->orows;
  depth = 1;
  nframes = I->num_frame;

  switch(I->pixel_format)
    {
    case PFBYTE:
      type = MRI_UCHAR;
      break;
    case PFSHORT:
      type = MRI_SHORT;
      break;
    case PFINT:
      type = MRI_INT;
      break;
    case PFFLOAT:
      type = MRI_FLOAT;
      break;
    case PFDOUBLE:
    case PFCOMPLEX:
    default:
      ErrorExit
        (ERROR_BADPARM, "IMAGE type = %d not supported\n", I->pixel_format);
      break;
    }
  // allocate memory
  if (nframes <= 1)
    mri = MRIalloc(width, height, depth, type);
  else
    mri = MRIallocSequence(width, height, depth, type, nframes);

  // just fake the size
  mri->nframes = nframes;
  mri->imnr0 = 1;
  mri->imnr1 = 1;
  mri->thick = mri->ps = 1.0;
  mri->xsize = mri->ysize = mri->zsize = 1.0;
  mri->xend = mri->width  * mri->xsize / 2.0;
  mri->xstart = -mri->xend;
  mri->yend = mri->height * mri->ysize / 2.0;
  mri->ystart = -mri->yend;
  mri->zend = mri->depth  * mri->zsize / 2.0;
  mri->zstart = -mri->zend;
  mri->fov = ((mri->xend - mri->xstart) > (mri->yend - mri->ystart) ?
              (mri->xend - mri->xstart) : (mri->yend - mri->ystart));
  // set orientation to be coronal
  mri->x_r = -1; mri->y_r =  0; mri->z_r =  0; mri->c_r = 0;
  mri->x_a =  0; mri->y_a =  0; mri->z_a =  1; mri->c_a = 0;
  mri->x_s =  0; mri->y_s = -1; mri->z_s =  0; mri->c_s = 0;

  // hips coordinate system is inverted
  for (frames = 0; frames < nframes; ++frames)
    {

      for (y = 0 ; y < height ; y++)
        {
          yp = height - (y+1) ;

          for (x = 0; x < width; ++x)
            {
              switch (mri->type)
                {
                case MRI_UCHAR:
                  MRIseq_vox(mri, x, y, 0, frames) = *IMAGEpix(I, x, yp);
                  break ;
                case MRI_SHORT:
                  MRISseq_vox(mri, x, y, 0, frames) = *IMAGESpix(I, x, yp);
                  break;
                case MRI_INT:
                  MRIIseq_vox(mri, x, y, 0, frames) = *IMAGEIpix(I, x, yp);
                  break ;
                case MRI_FLOAT:
                  MRIFseq_vox(mri, x, y, 0, frames) = *IMAGEFpix(I, x, yp);
                  break ;
                default:
                  ErrorReturn
                    (NULL,
                     (ERROR_UNSUPPORTED,
                      "MRItoImage: unsupported type %d",
                      mri->type)) ;
                  break ;
                }
            }
        }
    } // frames
  return(mri) ;
}

/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  Build an MRI from all values s.t. min_val <= val <= max_val
  ------------------------------------------------------*/
MRI *
MRIextractValues(MRI *mri_src, MRI *mri_dst, float min_val, float max_val)
{
  BUFTYPE  *psrc, *pdst, val ;
  float    *pfsrc, *pfdst, fval ;
  int      frame, x, y, z, width, height, depth ;

  width = mri_src->width ;
  height = mri_src->height ;
  depth = mri_src->depth ;
  if (!mri_dst)
    mri_dst = MRIclone(mri_src, NULL) ;

  for (frame = 0 ; frame < mri_src->nframes ; frame++)
    {
      for (z = 0 ; z < depth ; z++)
        {
          for (y = 0 ; y < height ; y++)
            {
              switch (mri_src->type)
                {
                case MRI_UCHAR:
                  psrc = &MRIseq_vox(mri_src, 0, y, z, frame) ;
                  pdst = &MRIseq_vox(mri_dst, 0, y, z, frame) ;
                  for (x = 0 ; x < width ; x++)
                    {
                      val = *psrc++ ;
                      if ((val < min_val) || (val > max_val))
                        val = 0 ;
                      *pdst++ = val ;
                    }
                  break ;
                case MRI_FLOAT:
                  pfsrc = &MRIFseq_vox(mri_src, 0, y, z, frame) ;
                  pfdst = &MRIFseq_vox(mri_dst, 0, y, z, frame) ;
                  for (x = 0 ; x < width ; x++)
                    {
                      fval = *pfsrc++ ;
                      if ((fval < min_val) || (fval > max_val))
                        fval = 0.0f ;
                      *pfdst++ = fval ;
                    }
                  break ;
                default:
                  ErrorReturn
                    (NULL,
                     (ERROR_UNSUPPORTED,
                      "MRIextractValues: unsupported type %d",mri_src->type));
                }
            }
        }
    }

  return(mri_dst) ;
}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  ------------------------------------------------------*/
MRI *
MRIupsample2(MRI *mri_src, MRI *mri_dst)
{
  int     width, depth, height, x, y, z ;
  BUFTYPE *pdst ;
  short   *psdst ;
  float   *pfdst ;

  if (mri_dst && mri_src->type != mri_dst->type)
    ErrorReturn
      (NULL,
       (ERROR_UNSUPPORTED, "MRIupsample2: source and dst must be same type"));

  width = 2*mri_src->width ;
  height = 2*mri_src->height ;
  depth = 2*mri_src->depth ;

  if (!mri_dst)
    {
      mri_dst = MRIalloc(width, height, depth, mri_src->type) ;
      MRIcopyHeader(mri_src, mri_dst) ;
    }

  for (z = 0 ; z < depth ; z++)
    {
      for (y = 0 ; y < height ; y++)
        {
          switch (mri_src->type)
            {
            case MRI_UCHAR:
              pdst = &MRIvox(mri_dst, 0, y, z) ;
              for (x = 0 ; x < width ; x++)
                *pdst++ = MRIvox(mri_src, x/2, y/2, z/2) ;
              break ;
            case MRI_SHORT:
              psdst = &MRISvox(mri_dst, 0, y, z) ;
              for (x = 0 ; x < width ; x++)
                *psdst++ = MRISvox(mri_src, x/2, y/2, z/2) ;
              break ;
            case MRI_FLOAT:
              pfdst = &MRIFvox(mri_dst, 0, y, z) ;
              for (x = 0 ; x < width ; x++)
                *pfdst++ = MRIFvox(mri_src, x/2, y/2, z/2) ;
              break ;
            default:
              ErrorReturn
                (NULL,
                 (ERROR_UNSUPPORTED,
                  "MRIupsample2: unsupported src type %d", mri_src->type)) ;
            }

        }
    }

  mri_dst->imnr0 = mri_src->imnr0 ;
  mri_dst->imnr1 = mri_src->imnr0 + mri_dst->depth - 1 ;

  mri_dst->xsize = mri_src->xsize/2 ;
  mri_dst->ysize = mri_src->ysize/2 ;
  mri_dst->zsize = mri_src->zsize/2 ;

  mri_dst->ras_good_flag = 0;

  return(mri_dst) ;
}
MRI *
MRIdownsample2LabeledVolume(MRI *mri_src, MRI *mri_dst)
{
  int     width, depth, height, x, y, z, x1, y1, z1, counts[256], label,
    max_count, out_label ;
  BUFTYPE *psrc ;

  if (mri_src->type != MRI_UCHAR)
    ErrorReturn(NULL,
                (ERROR_UNSUPPORTED,
                 "MRIdownsample2LabeledVolume: source must be UCHAR"));

  width = mri_src->width/2 ;
  height = mri_src->height/2 ;
  depth = mri_src->depth/2 ;

  if (!mri_dst)
    {
      mri_dst = MRIalloc(width, height, depth, mri_src->type) ;
      MRIcopyHeader(mri_src, mri_dst) ;
    }

  MRIclear(mri_dst) ;
  for (z = 0 ; z < depth ; z++)
    {
      for (y = 0 ; y < height ; y++)
        {
          for (x = 0 ; x < width ; x++)
            {
              memset(counts, 0, sizeof(counts)) ;
              if (x == 96 && y == 66 && z == 56)
                DiagBreak() ;
              for (z1 = 2*z ; z1 <= 2*z+1 ; z1++)
                {
                  for (y1 = 2*y ; y1 <= 2*y+1 ; y1++)
                    {
                      psrc = &MRIvox(mri_src, 2*x, y1, z1) ;
                      for (x1 = 2*x ; x1 <= 2*x+1 ; x1++)
                        {
                          label = *psrc++ ;
                          counts[label]++ ;
                        }
                    }
                }
              for (out_label = label = 0, max_count = counts[0] ;
                   label <= 255 ;
                   label++)
                {
                  if (counts[label] > max_count)
                    {
                      out_label = label ;
                      max_count = counts[label] ;
                    }
                }
              MRIvox(mri_dst, x, y, z) = out_label ;
            }
        }
    }

  mri_dst->imnr0 = mri_src->imnr0 ;
  mri_dst->imnr1 = mri_src->imnr0 + mri_dst->depth - 1 ;
  mri_dst->xsize = mri_src->xsize*2 ;
  mri_dst->ysize = mri_src->ysize*2 ;
  mri_dst->zsize = mri_src->zsize*2 ;

  mri_dst->ras_good_flag = 0;

  return(mri_dst) ;
}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  ------------------------------------------------------*/
MRI *
MRIdownsample2(MRI *mri_src, MRI *mri_dst)
{
  int     width, depth, height, x, y, z, x1, y1, z1 ;
  BUFTYPE *psrc ;
  short   *pssrc ;
  float   val ;

  if (mri_dst && mri_src->type != mri_dst->type)
    ErrorReturn
      (NULL,
       (ERROR_UNSUPPORTED,
        "MRIdownsample2: source and dst must be same type"));

  width = mri_src->width/2 ;
  height = mri_src->height/2 ;
  depth = mri_src->depth/2 ;

  if (!mri_dst)
    {
      mri_dst = MRIalloc(width, height, depth, mri_src->type) ;
      MRIcopyHeader(mri_src, mri_dst) ;
    }

  MRIclear(mri_dst) ;
  for (z = 0 ; z < depth ; z++)
    {
      for (y = 0 ; y < height ; y++)
        {
          for (x = 0 ; x < width ; x++)
            {
              for (val = 0.0f, z1 = 2*z ; z1 <= 2*z+1 ; z1++)
                {
                  for (y1 = 2*y ; y1 <= 2*y+1 ; y1++)
                    {
                      switch (mri_src->type)
                        {
                        case MRI_UCHAR:
                          psrc = &MRIvox(mri_src, 2*x, y1, z1) ;
                          for (x1 = 2*x ; x1 <= 2*x+1 ; x1++)
                            val += *psrc++ ;
                          break ;
                        case MRI_SHORT:
                          pssrc = &MRISvox(mri_src, 2*x, y1, z1) ;
                          for (x1 = 2*x ; x1 <= 2*x+1 ; x1++)
                            val += *pssrc++ ;
                          break ;
                        default:
                          ErrorReturn
                            (NULL,
                             (ERROR_UNSUPPORTED,
                              "MRIdownsample2: unsupported input type %d",
                              mri_src->type));
                        }
                    }
                }
              switch (mri_src->type)
                {
                case MRI_UCHAR:
                  MRIvox(mri_dst, x, y, z) = (BUFTYPE)nint(val/8.0f) ;
                  break ;
                case MRI_SHORT:
                  MRISvox(mri_dst, x, y, z) = (short)nint(val/8.0f) ;
                  break ;
                }
            }
        }
    }

  mri_dst->imnr0 = mri_src->imnr0 ;
  mri_dst->imnr1 = mri_src->imnr0 + mri_dst->depth - 1 ;
  mri_dst->xsize = mri_src->xsize*2 ;
  mri_dst->ysize = mri_src->ysize*2 ;
  mri_dst->zsize = mri_src->zsize*2 ;

  mri_dst->ras_good_flag = 0;

  return(mri_dst) ;
}
/*-------------------------------------------------------------
  MRI MRIvalueFill(MRI *mri, float value) -- fills an mri volume
  with the given value. Type of mri can be anything.
  -------------------------------------------------------------*/
int MRIvalueFill(MRI *mri, float value)
{
  int c,r,s,f;

  for(c=0; c < mri->width; c++){
    for(r=0; r < mri->height; r++){
      for(s=0; s < mri->depth; s++){
        for(f=0; f < mri->nframes; f++){
          switch(mri->type){
          case MRI_UCHAR:
            MRIseq_vox(mri,c,r,s,f) = (unsigned char) (nint(value));
            break;
          case MRI_SHORT:
            MRISseq_vox(mri,c,r,s,f) = (short) (nint(value));
            break;
          case MRI_INT:
            MRIIseq_vox(mri,c,r,s,f) = (int) (nint(value));
            break;
          case MRI_FLOAT:
            MRIFseq_vox(mri,c,r,s,f) = value;
            break;
          }
        }
      }
    }
  }

  return(0);
}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  Iteratively set all voxels in mri_dst that neighbor
  a voxel that has already been filled (starting with the seed),
  and for which the corresponding voxel in mri_src is
  below threshold.
  ------------------------------------------------------*/
MRI *
MRIfill(MRI *mri_src, MRI *mri_dst, int seed_x, int seed_y,
        int seed_z, int threshold, int fill_val, int max_count)
{
  int     width, height, depth, x, y, z, nfilled, xmin, xmax, ymin, ymax,
    zmin, zmax, on, x0, x1, y0, y1, z0, z1 ;
  BUFTYPE *psrc, *pdst, val ;

  width = mri_src->width ;
  height = mri_src->height ;
  depth = mri_src->depth ;
  if (!mri_dst)
    mri_dst = MRIclone(mri_src, NULL) ;

  if (seed_x < 0)
    seed_x = 0 ;
  else if (seed_x >= width)
    seed_x = width-1 ;
  if (seed_y < 0)
    seed_y = 0 ;
  else if (seed_y >= height)
    seed_y = height-1 ;
  if (seed_z < 0)
    seed_z = 0 ;
  else if (seed_z >= depth)
    seed_z = depth-1 ;

  xmin = MAX(seed_x-1,0) ; xmax = MIN(seed_x+1, width-1) ;
  ymin = MAX(seed_y-1,0) ; ymax = MIN(seed_y+1, height-1) ;
  zmin = MAX(seed_z-1,0) ; zmax = MIN(seed_z+1, depth-1) ;

  /* replace all occurrences of fill_val with fill_val-1 */
  for (z = 0 ; z < depth ; z++)
    {
      for (y = 0 ; y < height ; y++)
        {
          pdst = &MRIvox(mri_dst, 0, y, z) ;
          for (x = 0 ; x < width ; x++, pdst++)
            {
              val = *pdst ;
              if (val == fill_val)
                *pdst = val-1 ;
            }
        }
    }

  MRIvox(mri_dst, seed_x, seed_y, seed_z) = fill_val ;
  do
    {
      z0 = zmin ; z1 = zmax ; y0 = ymin ; y1 = ymax ; x0 = xmin ; x1 = xmax ;
      nfilled = 0 ;
      for (z = zmin ; z <= zmax ; z++)
        {
          for (y = ymin ; y <= ymax ; y++)
            {
              psrc = &MRIvox(mri_src, xmin, y, z) ;
              pdst = &MRIvox(mri_dst, xmin, y, z) ;
              for (x = xmin ; x <= xmax ; x++, psrc++, pdst++)
                {
                  val = *psrc ;
                  if ((val > threshold) || (*pdst == fill_val))
                    continue ;

                  on = 0 ;
                  if (x > 0)
                    on = (MRIvox(mri_dst, x-1, y, z) == fill_val) ;
                  if (!on && (x < width-1))
                    on = (MRIvox(mri_dst, x+1, y, z) == fill_val) ;
                  if (!on && (y > 0))
                    on = (MRIvox(mri_dst, x, y-1, z) == fill_val) ;
                  if (!on && (y < height-1))
                    on = (MRIvox(mri_dst, x, y+1, z) == fill_val) ;
                  if (!on && (z > 0))
                    on = (MRIvox(mri_dst, x, y, z-1) == fill_val) ;
                  if (!on && (z < depth-1))
                    on = (MRIvox(mri_dst, x, y, z+1) == fill_val) ;
                  if (on)
                    {
                      if (x <= x0)
                        x0 = MAX(x-1,0) ;
                      if (x >= x1)
                        x1 = MIN(x+1,width-1) ;
                      if (y <= y0)
                        y0 = MAX(y-1,0) ;
                      if (y >= y1)
                        y1 = MIN(y+1,height-1) ;
                      if (z <= z0)
                        z0 = MAX(z-1,0) ;
                      if (z >= z1)
                        z1 = MIN(z+1,depth-1) ;
                      nfilled++ ;
                      *pdst = fill_val ;
                    }
                }
            }
        }
      zmin = z0 ; zmax = z1 ; ymin = y0 ; ymax = y1 ; xmin = x0 ; xmax = x1 ;
      /*    fprintf(stderr, "# filled = %d\n", nfilled) ;*/
      if ((max_count > 0) && (nfilled > max_count))
        break ;
    } while (nfilled > 0) ;

  return(mri_dst) ;
}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  ------------------------------------------------------*/
MRI *
MRIfillFG(MRI *mri_src, MRI *mri_dst, int seed_x, int seed_y,
          int seed_z, int threshold, int fill_val, int *npix)
{
  int     width, height, depth, x, y, z, nfilled, xmin, xmax, ymin, ymax,
    zmin, zmax, on, x0, x1, y0, y1, z0, z1, total_pix ;
  BUFTYPE *psrc, *pdst, val ;

  width = mri_src->width ;
  height = mri_src->height ;
  depth = mri_src->depth ;
  if (!mri_dst)
    mri_dst = MRIclone(mri_src, NULL) ;

  if (seed_x < 0)
    seed_x = 0 ;
  else if (seed_x >= width)
    seed_x = width-1 ;
  if (seed_y < 0)
    seed_y = 0 ;
  else if (seed_y >= height)
    seed_y = width-1 ;
  if (seed_z < 0)
    seed_z = 0 ;
  else if (seed_z >= depth)
    seed_z = width-1 ;

  xmin = MAX(seed_x-1,0) ; xmax = MIN(seed_x+1, width-1) ;
  ymin = MAX(seed_y-1,0) ; ymax = MIN(seed_y+1, height-1) ;
  zmin = MAX(seed_z-1,0) ; zmax = MIN(seed_z+1, depth-1) ;

  /* replace all occurrences of fill_val with fill_val-1 */
  for (z = 0 ; z < depth ; z++)
    {
      for (y = 0 ; y < height ; y++)
        {
          pdst = &MRIvox(mri_dst, 0, y, z) ;
          for (x = 0 ; x < width ; x++, pdst++)
            {
              val = *pdst ;
              if (val == fill_val)
                *pdst = val-1 ;
            }
        }
    }

  MRIvox(mri_dst, seed_x, seed_y, seed_z) = fill_val ;
  total_pix = 1 ;  /* include the seed point */
  do
    {
      z0 = zmin ; z1 = zmax ; y0 = ymin ; y1 = ymax ; x0 = xmin ; x1 = xmax ;
      nfilled = 0 ;
      for (z = zmin ; z <= zmax ; z++)
        {
          for (y = ymin ; y <= ymax ; y++)
            {
              psrc = &MRIvox(mri_src, xmin, y, z) ;
              pdst = &MRIvox(mri_dst, xmin, y, z) ;
              for (x = xmin ; x <= xmax ; x++, psrc++, pdst++)
                {
                  val = *psrc ;
                  if ((val < threshold) || (*pdst == fill_val))
                    continue ;

                  on = 0 ;
                  if (x > 0)
                    on = (MRIvox(mri_dst, x-1, y, z) == fill_val) ;
                  if (!on && (x < width-1))
                    on = (MRIvox(mri_dst, x+1, y, z) == fill_val) ;
                  if (!on && (y > 0))
                    on = (MRIvox(mri_dst, x, y-1, z) == fill_val) ;
                  if (!on && (y < height-1))
                    on = (MRIvox(mri_dst, x, y+1, z) == fill_val) ;
                  if (!on && (z > 0))
                    on = (MRIvox(mri_dst, x, y, z-1) == fill_val) ;
                  if (!on && (z < depth-1))
                    on = (MRIvox(mri_dst, x, y, z+1) == fill_val) ;
                  if (on)
                    {
                      if (x <= x0)
                        x0 = MAX(x-1,0) ;
                      if (x >= x1)
                        x1 = MIN(x+1,width-1) ;
                      if (y <= y0)
                        y0 = MAX(y-1,0) ;
                      if (y >= y1)
                        y1 = MIN(y+1,height-1) ;
                      if (z <= z0)
                        z0 = MAX(z-1,0) ;
                      if (z >= z1)
                        z1 = MIN(z+1,depth-1) ;
                      nfilled++ ;
                      *pdst = fill_val ;
                    }
                }
            }
        }
      zmin = z0 ; zmax = z1 ; ymin = y0 ; ymax = y1 ; xmin = x0 ; xmax = x1 ;
      total_pix += nfilled ;
      /*    fprintf(stderr, "# filled = %d\n", nfilled) ;*/
    } while (nfilled > 0) ;

  if (npix)
    *npix = total_pix ;
  return(mri_dst) ;
}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  ------------------------------------------------------*/
MRI *
MRIfillBG(MRI *mri_src, MRI *mri_dst, int seed_x, int seed_y,
          int seed_z, int threshold, int fill_val, int *npix)
{
  int     width, height, depth, x, y, z, nfilled, xmin, xmax, ymin, ymax,
    zmin, zmax, on, x0, x1, y0, y1, z0, z1, total_pix ;
  BUFTYPE *psrc, *pdst, val ;

  width = mri_src->width ;
  height = mri_src->height ;
  depth = mri_src->depth ;
  if (!mri_dst)
    mri_dst = MRIclone(mri_src, NULL) ;

  if (seed_x < 0)
    seed_x = 0 ;
  else if (seed_x >= width)
    seed_x = width-1 ;
  if (seed_y < 0)
    seed_y = 0 ;
  else if (seed_y >= height)
    seed_y = width-1 ;
  if (seed_z < 0)
    seed_z = 0 ;
  else if (seed_z >= depth)
    seed_z = width-1 ;

  xmin = MAX(seed_x-1,0) ; xmax = MIN(seed_x+1, width-1) ;
  ymin = MAX(seed_y-1,0) ; ymax = MIN(seed_y+1, height-1) ;
  zmin = MAX(seed_z-1,0) ; zmax = MIN(seed_z+1, depth-1) ;

  /* replace all occurrences of fill_val with fill_val-1 */
  for (z = 0 ; z < depth ; z++)
    {
      for (y = 0 ; y < height ; y++)
        {
          pdst = &MRIvox(mri_dst, 0, y, z) ;
          for (x = 0 ; x < width ; x++, pdst++)
            {
              val = *pdst ;
              if (val == fill_val)
                *pdst = val-1 ;
            }
        }
    }

  MRIvox(mri_dst, seed_x, seed_y, seed_z) = fill_val ;
  total_pix = 0 ;
  do
    {
      z0 = zmin ; z1 = zmax ; y0 = ymin ; y1 = ymax ; x0 = xmin ; x1 = xmax ;
      nfilled = 0 ;
      for (z = zmin ; z <= zmax ; z++)
        {
          for (y = ymin ; y <= ymax ; y++)
            {
              psrc = &MRIvox(mri_src, xmin, y, z) ;
              pdst = &MRIvox(mri_dst, xmin, y, z) ;
              for (x = xmin ; x <= xmax ; x++, psrc++, pdst++)
                {
                  if (z == 130 && (y == 145 || y == 146) && x == 142)
                    DiagBreak() ;
                  val = *psrc ;
                  if ((val > threshold) || (*pdst == fill_val))
                    continue ;

                  on = 0 ;
                  if (x > 0)
                    on = (MRIvox(mri_dst, x-1, y, z) == fill_val) ;
                  if (!on && (x < width-1))
                    on = (MRIvox(mri_dst, x+1, y, z) == fill_val) ;
                  if (!on && (y > 0))
                    on = (MRIvox(mri_dst, x, y-1, z) == fill_val) ;
                  if (!on && (y < height-1))
                    on = (MRIvox(mri_dst, x, y+1, z) == fill_val) ;
                  if (!on && (z > 0))
                    on = (MRIvox(mri_dst, x, y, z-1) == fill_val) ;
                  if (!on && (z < depth-1))
                    on = (MRIvox(mri_dst, x, y, z+1) == fill_val) ;
                  if (on)
                    {
                      if (z == 130 && (y == 145 || y == 146) && x == 142)
                        DiagBreak() ;
                      if (x <= x0)
                        x0 = MAX(x-1,0) ;
                      if (x >= x1)
                        x1 = MIN(x+1,width-1) ;
                      if (y <= y0)
                        y0 = MAX(y-1,0) ;
                      if (y >= y1)
                        y1 = MIN(y+1,height-1) ;
                      if (z <= z0)
                        z0 = MAX(z-1,0) ;
                      if (z >= z1)
                        z1 = MIN(z+1,depth-1) ;
                      nfilled++ ;
                      *pdst = fill_val ;
                    }
                }
            }
        }
      zmin = z0 ; zmax = z1 ; ymin = y0 ; ymax = y1 ; xmin = x0 ; xmax = x1 ;
      total_pix += nfilled ;
      /*    fprintf(stderr, "# filled = %d\n", nfilled) ;*/
    } while (nfilled > 0) ;

  if (npix)
    *npix = total_pix ;
  return(mri_dst) ;
}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  ------------------------------------------------------*/
int
MRIsetResolution(MRI *mri, float xres, float yres, float zres)
{
  mri->ps = (xres+yres+zres) / 3.0f ;
  mri->xsize = xres ;
  mri->ysize = yres ;
  mri->zsize = zres ;
  mri->xstart *= xres ; mri->xend *= xres ;
  mri->ystart *= yres ; mri->yend *= yres ;
  mri->zstart *= zres ; mri->zend *= zres ;
  mri->thick = MAX(MAX(xres,yres),zres) ;
#if 0
  mri->x_r = mri->x_r * xres ;
  mri->x_a = mri->x_a * xres ;
  mri->x_s = mri->x_s * xres ;
  mri->y_r = mri->y_r * yres ;
  mri->y_a = mri->y_a * yres ;
  mri->y_s = mri->y_s * yres ;
  mri->z_r = mri->z_r * zres ;
  mri->z_a = mri->z_a * zres ;
  mri->z_s = mri->z_s * zres ;
#endif
  return(NO_ERROR) ;
}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  ------------------------------------------------------*/
int
MRIsetTransform(MRI *mri,   General_transform *transform)
{
  mri->transform = *transform ;
  mri->linear_transform = get_linear_transform_ptr(transform) ;
  mri->inverse_linear_transform = get_inverse_linear_transform_ptr(transform) ;

  return(NO_ERROR) ;
}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  ------------------------------------------------------*/
MRI *
MRIextractTalairachPlane(MRI *mri_src, MRI *mri_dst, int orientation,
                         int x, int y, int z, int wsize)
{
  Real     e1_x, e1_y, e1_z, e2_x, e2_y, e2_z, xbase, ybase, zbase ;
  int      whalf, xk, yk, xi, yi, zi ;
  Real     ex, ey, ez, len, x0, y0, z0 ;

  whalf = (wsize-1)/2 ;

  if (!mri_dst)
    {
      mri_dst = MRIalloc(wsize, wsize, 1, MRI_UCHAR) ;
      MRIcopyHeader(mri_src, mri_dst) ;
      mri_dst->xstart = x-whalf*mri_dst->xsize ;
      mri_dst->ystart = y-whalf*mri_dst->ysize ;
      mri_dst->zstart = z-whalf*mri_dst->zsize ;
      mri_dst->xend = mri_dst->xstart + wsize*mri_dst->xsize ;
      mri_dst->yend = mri_dst->ystart + wsize*mri_dst->ysize ;
      mri_dst->zend = mri_dst->zstart + wsize*mri_dst->zsize ;
      mri_dst->imnr0 = z + mri_src->imnr0 ;
      mri_dst->imnr1 = mri_dst->imnr0 ;
    }

  MRIvoxelToTalairachVoxel(mri_src, x, y, z, &x0, &y0, &z0) ;
  switch (orientation)
    {
    default:
    case MRI_CORONAL:   /* basis vectors in x-y plane */
      /* the 'x' basis vector */
      ex = (Real)x0+1 ; ey = (Real)y0 ; ez = (Real)z0 ;
      MRItalairachVoxelToVoxel(mri_src, ex, ey, ez, &e1_x, &e1_y, &e1_z) ;
      e1_x -= (Real)x ; e1_y -= (Real)y ; e1_z -= (Real)z ;

      /* the 'y' basis vector */
      ex = (Real)x0 ; ey = (Real)y0+1 ; ez = (Real)z0 ;
      MRItalairachVoxelToVoxel(mri_src, ex, ey, ez, &e2_x, &e2_y, &e2_z) ;
      e2_x -= (Real)x ; e2_y -= (Real)y ; e2_z -= (Real)z ;
      break ;
    case MRI_HORIZONTAL:  /* basis vectors in x-z plane */
      /* the 'x' basis vector */
      ex = (Real)x0+1 ; ey = (Real)y0 ; ez = (Real)z0 ;
      MRItalairachVoxelToVoxel(mri_src, ex, ey, ez, &e1_x, &e1_y, &e1_z) ;
      e1_x -= (Real)x ; e1_y -= (Real)y ; e1_z -= (Real)z ;

      /* the 'y' basis vector */
      ex = (Real)x0 ; ey = (Real)y0 ; ez = (Real)z0+1 ;
      MRItalairachVoxelToVoxel(mri_src, ex, ey, ez, &e2_x, &e2_y, &e2_z) ;
      e2_x -= (Real)x ; e2_y -= (Real)y ; e2_z -= (Real)z ;
      break ;
    case MRI_SAGITTAL:    /* basis vectors in y-z plane */
      /* the 'x' basis vector */
      ex = (Real)x0 ; ey = (Real)y0 ; ez = (Real)z0+1.0 ;
      MRItalairachVoxelToVoxel(mri_src, ex, ey, ez, &e1_x, &e1_y, &e1_z) ;
      e1_x -= (Real)x ; e1_y -= (Real)y ; e1_z -= (Real)z ;

      /* the 'y' basis vector */
      ex = (Real)x0 ; ey = (Real)y0+1.0 ; ez = (Real)z0 ;
      MRItalairachVoxelToVoxel(mri_src, ex, ey, ez, &e2_x, &e2_y, &e2_z) ;
      e2_x -= (Real)x ; e2_y -= (Real)y ; e2_z -= (Real)z ;
      break ;
    }

  len = sqrt(e1_x*e1_x + e1_y*e1_y + e1_z*e1_z) ;
  /*  e1_x /= len ; e1_y /= len ; e1_z /= len ;*/
  len = sqrt(e2_x*e2_x + e2_y*e2_y + e2_z*e2_z) ;
  /*  e2_x /= len ; e2_y /= len ; e2_z /= len ;*/

  for (yk = -whalf ; yk <= whalf ; yk++)
    {
      xbase = (float)x + (float)yk * e2_x ;
      ybase = (float)y + (float)yk * e2_y ;
      zbase = (float)z + (float)yk * e2_z ;
      for (xk = -whalf ; xk <= whalf ; xk++)
        {
          /* in-plane vect. is linear combination of scaled basis vects */
          xi = mri_src->xi[nint(xbase + xk*e1_x)] ;
          yi = mri_src->yi[nint(ybase + xk*e1_y)] ;
          zi = mri_src->zi[nint(zbase + xk*e1_z)] ;
          MRIvox(mri_dst, xk+whalf,yk+whalf,0) = MRIvox(mri_src, xi, yi, zi) ;
        }
    }

  return(mri_dst) ;
}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  ------------------------------------------------------*/
MRI *
MRIextractArbitraryPlane(MRI *mri_src, MRI *mri_dst,
                         Real e1_x, Real e1_y, Real e1_z,
                         Real e2_x, Real e2_y, Real e2_z,
                         int x, int y, int z, int wsize)
{
  Real     xbase, ybase, zbase ;
  int      whalf, xk, yk, xi, yi, zi ;

  whalf = (wsize-1)/2 ;

  if (!mri_dst)
    {
      mri_dst = MRIalloc(wsize, wsize, 1, MRI_UCHAR) ;
      MRIcopyHeader(mri_src, mri_dst) ;
      mri_dst->xstart = x-whalf*mri_dst->xsize ;
      mri_dst->ystart = y-whalf*mri_dst->ysize ;
      mri_dst->zstart = z-whalf*mri_dst->zsize ;
      mri_dst->xend = mri_dst->xstart + wsize*mri_dst->xsize ;
      mri_dst->yend = mri_dst->ystart + wsize*mri_dst->ysize ;
      mri_dst->zend = mri_dst->zstart + wsize*mri_dst->zsize ;
      mri_dst->imnr0 = z + mri_src->imnr0 ;
      mri_dst->imnr1 = mri_dst->imnr0 ;
    }



  for (yk = -whalf ; yk <= whalf ; yk++)
    {
      xbase = (float)x + (float)yk * e2_x ;
      ybase = (float)y + (float)yk * e2_y ;
      zbase = (float)z + (float)yk * e2_z ;
      for (xk = -whalf ; xk <= whalf ; xk++)
        {
          /* in-plane vect. is linear combination of scaled basis vects */
          xi = mri_src->xi[nint(xbase + xk*e1_x)] ;
          yi = mri_src->yi[nint(ybase + xk*e1_y)] ;
          zi = mri_src->zi[nint(zbase + xk*e1_z)] ;
          MRIvox(mri_dst, xk+whalf,yk+whalf,0) = MRIvox(mri_src, xi, yi, zi) ;
        }
    }

  return(mri_dst) ;
}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  ------------------------------------------------------*/
int
MRIeraseTalairachPlaneNew(MRI *mri, MRI *mri_mask, int orientation, int x,
                          int y, int z, int wsize, int fill_val)
{
  Real     e1_x, e1_y, e1_z, e2_x, e2_y, e2_z, xbase, ybase, zbase ;
  int      whalf, xk, yk, xi, yi, zi, xki, yki, x0, y0 ;
  Real     ex, ey, ez, len, xt0, yt0, zt0 ;

  whalf = (wsize-1)/2 ;

  x0 = mri_mask->width/2 ; y0 = mri_mask->height/2 ;
  MRIvoxelToTalairachVoxel(mri, x, y, z, &xt0, &yt0, &zt0) ;
  switch (orientation)
    {
    default:
    case MRI_CORONAL:   /* basis vectors in x-y plane */
      /* the 'x' basis vector */
      ex = (Real)xt0+1 ; ey = (Real)yt0 ; ez = (Real)zt0 ;
      MRItalairachVoxelToVoxel(mri, ex, ey, ez, &e1_x, &e1_y, &e1_z) ;
      e1_x -= (Real)x ; e1_y -= (Real)y ; e1_z -= (Real)z ;

      /* the 'y' basis vector */
      ex = (Real)xt0 ; ey = (Real)yt0+1 ; ez = (Real)zt0 ;
      MRItalairachVoxelToVoxel(mri, ex, ey, ez, &e2_x, &e2_y, &e2_z) ;
      e2_x -= (Real)x ; e2_y -= (Real)y ; e2_z -= (Real)z ;
      break ;
    case MRI_HORIZONTAL:  /* basis vectors in x-z plane */
      /* the 'x' basis vector */
      ex = (Real)xt0+1 ; ey = (Real)yt0 ; ez = (Real)zt0 ;
      MRItalairachVoxelToVoxel(mri, ex, ey, ez, &e1_x, &e1_y, &e1_z) ;
      e1_x -= (Real)x ; e1_y -= (Real)y ; e1_z -= (Real)z ;

      /* the 'y' basis vector */
      ex = (Real)xt0 ; ey = (Real)yt0 ; ez = (Real)zt0+1 ;
      MRItalairachVoxelToVoxel(mri, ex, ey, ez, &e2_x, &e2_y, &e2_z) ;
      e2_x -= (Real)x ; e2_y -= (Real)y ; e2_z -= (Real)z ;
      break ;
    case MRI_SAGITTAL:    /* basis vectors in y-z plane */
      /* the 'x' basis vector */
      ex = (Real)xt0 ; ey = (Real)yt0 ; ez = (Real)zt0+1.0 ;
      MRItalairachVoxelToVoxel(mri, ex, ey, ez, &e1_x, &e1_y, &e1_z) ;
      e1_x -= (Real)x ; e1_y -= (Real)y ; e1_z -= (Real)z ;

      /* the 'y' basis vector */
      ex = (Real)xt0 ; ey = (Real)yt0+1.0 ; ez = (Real)zt0 ;
      MRItalairachVoxelToVoxel(mri, ex, ey, ez, &e2_x, &e2_y, &e2_z) ;
      e2_x -= (Real)x ; e2_y -= (Real)y ; e2_z -= (Real)z ;
      break ;
    }

  /*
     don't want to normalize basis - they are orthonormal in magnet space,
     not necessarily Talairach space.
  */
  len = sqrt(e1_x*e1_x + e1_y*e1_y + e1_z*e1_z) ;
  /*  e1_x /= len ; e1_y /= len ; e1_z /= len ;*/
  len = sqrt(e2_x*e2_x + e2_y*e2_y + e2_z*e2_z) ;
  /*  e2_x /= len ; e2_y /= len ; e2_z /= len ;*/

  for (yk = -whalf ; yk <= whalf ; yk++)
    {
      xbase = (float)x + (float)yk * e2_x ;
      ybase = (float)y + (float)yk * e2_y ;
      zbase = (float)z + (float)yk * e2_z ;
      for (xk = -whalf ; xk <= whalf ; xk++)
        {
          /* in-plane vect. is linear combination of scaled basis vects */
          xi = mri->xi[nint(xbase + xk*e1_x)] ;
          yi = mri->yi[nint(ybase + xk*e1_y)] ;
          zi = mri->zi[nint(zbase + xk*e1_z)] ;
          xki = mri_mask->xi[xk+x0] ; yki = mri_mask->yi[yk+y0] ;
          if (MRIvox(mri_mask, xki, yki,0))
            MRIvox(mri, xi, yi, zi) = fill_val ;
        }
    }

  return(NO_ERROR) ;
}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  ------------------------------------------------------*/
int
MRIeraseTalairachPlane(MRI *mri, MRI *mri_mask, int orientation, int x, int y,
                       int z, int wsize, int fill_val)
{
  Real     e1_x, e1_y, e1_z, e2_x, e2_y, e2_z, xbase, ybase, zbase ;
  int      whalf, xk, yk, xi, yi, zi ;
  Real     ex, ey, ez, len ;

  whalf = (wsize-1)/2 ;

  switch (orientation)
    {
    default:
    case MRI_CORONAL:   /* basis vectors in x-y plane */
      /* the 'x' basis vector */
      ex = (Real)x+1 ; ey = (Real)y ; ez = (Real)z ;
      MRIvoxelToTalairachVoxel(mri, ex, ey, ez, &e1_x, &e1_y, &e1_z) ;
      e1_x -= (Real)x ; e1_y -= (Real)y ; e1_z -= (Real)z ;

      /* the 'y' basis vector */
      ex = (Real)x ; ey = (Real)y+1 ; ez = (Real)z ;
      MRIvoxelToTalairachVoxel(mri, ex, ey, ez, &e2_x, &e2_y, &e2_z) ;
      e2_x -= (Real)x ; e2_y -= (Real)y ; e2_z -= (Real)z ;
      break ;
    case MRI_HORIZONTAL:  /* basis vectors in x-z plane */
      /* the 'x' basis vector */
      ex = (Real)x+1 ; ey = (Real)y ; ez = (Real)z ;
      MRIvoxelToTalairachVoxel(mri, ex, ey, ez, &e1_x, &e1_y, &e1_z) ;
      e1_x -= (Real)x ; e1_y -= (Real)y ; e1_z -= (Real)z ;

      /* the 'y' basis vector */
      ex = (Real)x ; ey = (Real)y ; ez = (Real)z+1 ;
      MRIvoxelToTalairachVoxel(mri, ex, ey, ez, &e2_x, &e2_y, &e2_z) ;
      e2_x -= (Real)x ; e2_y -= (Real)y ; e2_z -= (Real)z ;
      break ;
    case MRI_SAGITTAL:    /* basis vectors in y-z plane */
      /* the 'x' basis vector */
      ex = (Real)x ; ey = (Real)y ; ez = (Real)z+1.0 ;
      MRIvoxelToTalairachVoxel(mri, ex, ey, ez, &e1_x, &e1_y, &e1_z) ;
      e1_x -= (Real)x ; e1_y -= (Real)y ; e1_z -= (Real)z ;

      /* the 'y' basis vector */
      ex = (Real)x ; ey = (Real)y+1.0 ; ez = (Real)z ;
      MRIvoxelToTalairachVoxel(mri, ex, ey, ez, &e2_x, &e2_y, &e2_z) ;
      e2_x -= (Real)x ; e2_y -= (Real)y ; e2_z -= (Real)z ;
      break ;
    }

  /*
     don't want to normalize basis - they are orthonormal in magnet space,
     not necessarily Talairach space.
  */
  len = sqrt(e1_x*e1_x + e1_y*e1_y + e1_z*e1_z) ;
  /*  e1_x /= len ; e1_y /= len ; e1_z /= len ;*/
  len = sqrt(e2_x*e2_x + e2_y*e2_y + e2_z*e2_z) ;
  /*  e2_x /= len ; e2_y /= len ; e2_z /= len ;*/

  for (yk = -whalf ; yk <= whalf ; yk++)
    {
      xbase = (float)x + (float)yk * e2_x ;
      ybase = (float)y + (float)yk * e2_y ;
      zbase = (float)z + (float)yk * e2_z ;
      for (xk = -whalf ; xk <= whalf ; xk++)
        {
          /* in-plane vect. is linear combination of scaled basis vects */
          xi = mri->xi[nint(xbase + xk*e1_x)] ;
          yi = mri->yi[nint(ybase + xk*e1_y)] ;
          zi = mri->zi[nint(zbase + xk*e1_z)] ;
          if (MRIvox(mri_mask, xk+whalf,yk+whalf,0))
            MRIvox(mri, xi, yi, zi) = fill_val ;
        }
    }

  return(NO_ERROR) ;
}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  ------------------------------------------------------*/
MRI *
MRIextractPlane(MRI *mri_src, MRI *mri_dst, int orientation, int where)
{
  int      x, y, z, width, height ;

  switch (orientation)
    {
    default:
    case MRI_CORONAL:   /* basis vectors in x-y plane */
      width = mri_src->width ; height = mri_src->height ;
      break ;
    case MRI_HORIZONTAL:  /* basis vectors in x-z plane */
      width = mri_src->width ; height = mri_src->depth ;
      break ;
    case MRI_SAGITTAL:    /* basis vectors in y-z plane */
      width = mri_src->depth ; height = mri_src->height ;
      break ;
    }

  if (!mri_dst)
    {
      mri_dst = MRIalloc(width, height, 1, MRI_UCHAR) ;
      MRIcopyHeader(mri_src, mri_dst) ;
      mri_dst->zstart = where ; mri_dst->zend = where+1 ;
      mri_dst->imnr0 = 0 ; mri_dst->imnr1 = 1 ;
    }
  switch (orientation)
    {
    default:
    case MRI_CORONAL:   /* basis vectors in x-y plane */
      for (x = 0 ; x < mri_src->width ; x++)
        {
          for (y = 0 ; y < mri_src->height ; y++)
            MRIvox(mri_dst, x, y, 0) = MRIvox(mri_src, x, y, where) ;
        }
      break ;
    case MRI_HORIZONTAL:  /* basis vectors in x-z plane */
      for (x = 0 ; x < mri_src->width ; x++)
        {
          for (z = 0 ; z < mri_src->depth ; z++)
            MRIvox(mri_dst, x, z, 0) = MRIvox(mri_src, x, where, z) ;
        }
      break ;
    case MRI_SAGITTAL:    /* basis vectors in y-z plane */
      for (z = 0 ; z < mri_src->depth ; z++)
        {
          for (y = 0 ; y < mri_src->height ; y++)
            MRIvox(mri_dst, z, y, 0) = MRIvox(mri_src, where, y, z) ;
        }
      break ;
    }

  return(mri_dst) ;
}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  ------------------------------------------------------*/
MRI *
MRIfillPlane
(MRI *mri_mask, MRI *mri_dst, int orientation, int where, int fillval)
{
  int      x, y, z, width, height ;

  switch (orientation)
    {
    default:
    case MRI_CORONAL:   /* basis vectors in x-y plane */
      width = mri_mask->width ; height = mri_mask->height ;
      break ;
    case MRI_HORIZONTAL:  /* basis vectors in x-z plane */
      width = mri_mask->width ; height = mri_mask->depth ;
      break ;
    case MRI_SAGITTAL:    /* basis vectors in y-z plane */
      width = mri_mask->depth ; height = mri_mask->height ;
      break ;
    }

  switch (orientation)
    {
    default:
    case MRI_CORONAL:   /* basis vectors in x-y plane */
      for (x = 0 ; x < mri_dst->width ; x++)
        {
          for (y = 0 ; y < mri_dst->height ; y++)
            if (MRIvox(mri_mask, x, y, 0))
              MRIvox(mri_dst, x, y, where) = fillval ;
        }
      break ;
    case MRI_HORIZONTAL:  /* basis vectors in x-z plane */
      for (x = 0 ; x < mri_dst->width ; x++)
        {
          for (z = 0 ; z < mri_dst->depth ; z++)
            {
              if (MRIvox(mri_mask, x, z, 0))
                MRIvox(mri_dst, x, where, z) = fillval ;
            }
        }
      break ;
    case MRI_SAGITTAL:    /* basis vectors in y-z plane */
      for (z = 0 ; z < mri_dst->depth ; z++)
        {
          for (y = 0 ; y < mri_dst->height ; y++)
            if (MRIvox(mri_mask, z, y, 0))
              MRIvox(mri_dst, where, y, z) = fillval ;
        }
      break ;
    }

  return(mri_dst) ;
}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  ------------------------------------------------------*/
int
MRIerasePlane(MRI *mri, float x0, float y0, float z0,
              float dx, float dy, float dz, int fill_val)
{
  int      *pxi, *pyi, *pzi, xi, yi, zi, x, y, z ;
  float    e1_x, e1_y, e1_z, e2_x, e2_y, e2_z, maxlen, l1, l2 ;

  maxlen = MAX(mri->width, mri->height) ; maxlen = MAX(maxlen, mri->depth) ;

  /* don't care about sign (right-hand rule) */
  e1_x = dz*dz - dx*dy ;
  e1_y = dx*dx - dy*dz ;
  e1_z = dy*dy - dz*dx ;
  l1 = sqrt(e1_x*e1_x + e1_y*e1_y + e1_z*e1_z) ;
  e1_x /= l1 ; e1_y /= l1 ; e1_z /= l1 ;

  e2_x = e1_y*dz - e1_z*dy ;
  e2_y = e1_x*dz - e1_z*dx ;
  e2_z = e1_y*dx - e1_x*dy ;
  l2 = sqrt(e2_x*e2_x + e2_y*e2_y + e2_z*e2_z) ;
  e2_x /= l2 ; e2_y /= l2 ; e2_z /= l2 ;

  pxi = mri->xi ; pyi = mri->yi ; pzi = mri->zi ;
  maxlen *= 1.5 ;  /* make sure to get entire extent */
  for (l1 = -maxlen/2 ; l1 <= maxlen/2 ; l1 += 0.5f)
    {
      for (l2 = -maxlen/2 ; l2 <= maxlen/2 ; l2 += 0.5f)
        {
          x = nint(x0 + l1 * e1_x + l2 * e2_x) ; xi = pxi[x] ;
          y = nint(y0 + l1 * e1_y + l2 * e2_y) ; yi = pyi[y] ;
          z = nint(z0 + l1 * e1_z + l2 * e2_z) ; zi = pzi[z] ;
          MRIvox(mri, xi, yi, zi) = fill_val ;
        }
    }

  return(NO_ERROR) ;
}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  Interpolate the volume to cubic voxels.
  ------------------------------------------------------*/
MRI *
MRIinterpolate(MRI *mri_src, MRI *mri_dst)
{
  int     xs, ys, zs, xd, yd, zd, max_dim, xorig, yorig, zorig, dorig ;
  float   sx, sy, sz, psize ;
  int     width, height, depth, i ;
  float   xsmd, ysmd, zsmd, xspd, yspd, zspd, weights[8], fout ;
  int     xsp, xsm, ysp, ysm, zsp, zsm ;  /* surrounding coordinates */
  float vals[8], outval ;

  width = mri_src->width ; height = mri_src->height ; depth = mri_src->depth;
  if (width > height)
    {
      max_dim = width > depth ? width : depth ;
      psize = width > depth ? mri_src->xsize : mri_src->zsize ;
    }
  else
    {
      max_dim = height > depth ? height : depth ;
      psize = height > depth ? mri_src->ysize : mri_src->zsize ;
    }

  if (!mri_dst)
    {
      mri_dst = MRIalloc(max_dim, max_dim, max_dim, mri_src->type) ;
      MRIcopyHeader(mri_src, mri_dst) ;
    }

  mri_dst->xsize = mri_dst->ysize = mri_dst->zsize = psize ;
  sx = (float)width / (float)max_dim  ;
  sy = (float)height / (float)max_dim  ;
  sz = (float)depth / (float)max_dim  ;

  mri_dst->imnr0 = 1;
  mri_dst->imnr1 = mri_dst->depth;
  mri_dst->xstart = -mri_dst->xsize * mri_dst->width / 2;
  mri_dst->xend = -mri_dst->xstart;
  mri_dst->ystart = -mri_dst->ysize * mri_dst->height / 2;
  mri_dst->yend = -mri_dst->ystart;
  mri_dst->zstart = -mri_dst->zsize * mri_dst->depth / 2;
  mri_dst->zend = -mri_dst->zstart;

  xorig = (width-1)/2 ; yorig = (height-1)/2 ; zorig = (depth-1)/2 ;
  dorig = (max_dim-1)/2 ;

  /*
    for each output voxel, find the 8 nearest input voxels and interpolate
    the output voxel from them.
  */
  for (zd = 0 ; zd < max_dim ; zd++)
    {
      printf("interpolate: %d/%d\n",zd+1,max_dim);
      for (yd = 0 ; yd < max_dim ; yd++)
        {
          for (xd = 0 ; xd < max_dim ; xd++)
            {
              /* do trilinear interpolation here */
              xs = sx*(xd-dorig) + xorig ;
              ys = sy*(yd-dorig) + yorig ;
              zs = sz*(zd-dorig) + zorig ;

              /*
                 these boundary conditions will cause
                 reflection across the border
                 for the 1st negative pixel.
              */
              if (xs > -1 && xs < width &&
                  ys > -1 && ys < height &&
                  zs > -1 && zs < depth)
                {
                  xsm = (int)xs ;
                  xsp = MIN(width-1, xsm+1) ;
                  ysm = (int)ys ;
                  ysp = MIN(height-1, ysm+1) ;
                  zsm = (int)zs ;
                  zsp = MIN(depth-1, zsm+1) ;
                  xsmd = xs - (float)xsm ;
                  ysmd = ys - (float)ysm ;
                  zsmd = zs - (float)zsm ;
                  xspd = (1.0f - xsmd) ;
                  yspd = (1.0f - ysmd) ;
                  zspd = (1.0f - zsmd) ;

                  /*          vals[0] = mri_src->slices[zsm][ysm][xsm] ;
                              vals[1] = mri_src->slices[zsm][ysm][xsp] ;
                              vals[2] = mri_src->slices[zsm][ysp][xsm] ;
                              vals[3] = mri_src->slices[zsm][ysp][xsp] ;
                              vals[4] = mri_src->slices[zsp][ysm][xsm] ;
                              vals[5] = mri_src->slices[zsp][ysm][xsp] ;
                              vals[6] = mri_src->slices[zsp][ysp][xsm] ;
                              vals[7] = mri_src->slices[zsp][ysp][xsp] ;
                  */
                  /* different vox types... */
                  vals[0] = MRIFvox(mri_src, xsm, ysm, zsm);
                  vals[1] = MRIFvox(mri_src, xsp, ysm, zsm);
                  vals[2] = MRIFvox(mri_src, xsm, ysp, zsm);
                  vals[3] = MRIFvox(mri_src, xsp, ysp, zsm);
                  vals[4] = MRIFvox(mri_src, xsm, ysm, zsp);
                  vals[5] = MRIFvox(mri_src, xsp, ysm, zsp);
                  vals[6] = MRIFvox(mri_src, xsm, ysp, zsp);
                  vals[7] = MRIFvox(mri_src, xsp, ysp, zsp);

                  weights[0] = zsmd * ysmd * xsmd ;
                  weights[1] = zsmd * ysmd * xspd ;
                  weights[2] = zsmd * yspd * xsmd ;
                  weights[3] = zsmd * yspd * xspd ;
                  weights[4] = zspd * ysmd * xsmd ;
                  weights[5] = zspd * ysmd * xspd ;
                  weights[6] = zspd * yspd * xsmd ;
                  weights[7] = zspd * yspd * xspd ;
                  /*
                    for(i = 0;i < 8;i++)
                    printf("%d, %f, %f\n",i,vals[i], weights[i]);
                  */
                  for (fout = 0.0f, i = 0 ; i < 8 ; i++)
                    fout += (float)vals[i] * weights[i] ;
                  outval = (float)nint(fout) ;
                  MRIvox(mri_dst, xd, yd, zd) = (BUFTYPE)nint(fout) ;

                }
            }
        }
    }

  mri_dst->ras_good_flag = 0;

  return(mri_dst) ;
}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  ------------------------------------------------------*/
int
MRIsampleVolumeFrame(MRI *mri,Real x,Real y,Real z,int frame,Real *pval)
{
  int  OutOfBounds;
  int  xm, xp, ym, yp, zm, zp, width, height, depth ;
  Real val, xmd, ymd, zmd, xpd, ypd, zpd ;  /* d's are distances */

  if (FEQUAL((int)x,x) && FEQUAL((int)y,y) && FEQUAL((int)z, z))
    return(MRIsampleVolumeFrameType
           (mri, x, y, z, frame, SAMPLE_NEAREST, pval)) ;

  if (frame >= mri->nframes)
    {
      *pval = 1.0 ;
      return(NO_ERROR) ;
    }

  OutOfBounds = MRIindexNotInVolume(mri, x, y, z);
  if(OutOfBounds == 1){
    /* unambiguoulsy out of bounds */
    *pval = 0.0;
    return(NO_ERROR) ;
  }

  width = mri->width ; height = mri->height ; depth = mri->depth ;
  if (x >= width)
    x = width - 1.0 ;
  if (y >= height)
    y = height - 1.0 ;
  if (z >= depth)
    z = depth - 1.0 ;
  if (x < 0.0)
    x = 0.0 ;
  if (y < 0.0)
    y = 0.0 ;
  if (z < 0.0)
    z = 0.0 ;

  xm = MAX((int)x, 0) ;
  xp = MIN(width-1, xm+1) ;
  ym = MAX((int)y, 0) ;
  yp = MIN(height-1, ym+1) ;
  zm = MAX((int)z, 0) ;
  zp = MIN(depth-1, zm+1) ;

  xmd = x - (float)xm ;
  ymd = y - (float)ym ;
  zmd = z - (float)zm ;
  xpd = (1.0f - xmd) ;
  ypd = (1.0f - ymd) ;
  zpd = (1.0f - zmd) ;

  switch (mri->type)
    {
    case MRI_UCHAR:
      *pval = val =
        xpd * ypd * zpd * (Real)MRIseq_vox(mri, xm, ym, zm, frame) +
        xpd * ypd * zmd * (Real)MRIseq_vox(mri, xm, ym, zp, frame) +
        xpd * ymd * zpd * (Real)MRIseq_vox(mri, xm, yp, zm, frame) +
        xpd * ymd * zmd * (Real)MRIseq_vox(mri, xm, yp, zp, frame) +
        xmd * ypd * zpd * (Real)MRIseq_vox(mri, xp, ym, zm, frame) +
        xmd * ypd * zmd * (Real)MRIseq_vox(mri, xp, ym, zp, frame) +
        xmd * ymd * zpd * (Real)MRIseq_vox(mri, xp, yp, zm, frame) +
        xmd * ymd * zmd * (Real)MRIseq_vox(mri, xp, yp, zp, frame) ;
      break ;
    case MRI_FLOAT:
      *pval = val =
        xpd * ypd * zpd * (Real)MRIFseq_vox(mri, xm, ym, zm, frame) +
        xpd * ypd * zmd * (Real)MRIFseq_vox(mri, xm, ym, zp, frame) +
        xpd * ymd * zpd * (Real)MRIFseq_vox(mri, xm, yp, zm, frame) +
        xpd * ymd * zmd * (Real)MRIFseq_vox(mri, xm, yp, zp, frame) +
        xmd * ypd * zpd * (Real)MRIFseq_vox(mri, xp, ym, zm, frame) +
        xmd * ypd * zmd * (Real)MRIFseq_vox(mri, xp, ym, zp, frame) +
        xmd * ymd * zpd * (Real)MRIFseq_vox(mri, xp, yp, zm, frame) +
        xmd * ymd * zmd * (Real)MRIFseq_vox(mri, xp, yp, zp, frame) ;
      break ;
    case MRI_SHORT:
      *pval = val =
        xpd * ypd * zpd * (Real)MRISseq_vox(mri, xm, ym, zm, frame) +
        xpd * ypd * zmd * (Real)MRISseq_vox(mri, xm, ym, zp, frame) +
        xpd * ymd * zpd * (Real)MRISseq_vox(mri, xm, yp, zm, frame) +
        xpd * ymd * zmd * (Real)MRISseq_vox(mri, xm, yp, zp, frame) +
        xmd * ypd * zpd * (Real)MRISseq_vox(mri, xp, ym, zm, frame) +
        xmd * ypd * zmd * (Real)MRISseq_vox(mri, xp, ym, zp, frame) +
        xmd * ymd * zpd * (Real)MRISseq_vox(mri, xp, yp, zm, frame) +
        xmd * ymd * zmd * (Real)MRISseq_vox(mri, xp, yp, zp, frame) ;
      break ;
    case MRI_INT:
      *pval = val =
        xpd * ypd * zpd * (Real)MRIIseq_vox(mri, xm, ym, zm, frame) +
        xpd * ypd * zmd * (Real)MRIIseq_vox(mri, xm, ym, zp, frame) +
        xpd * ymd * zpd * (Real)MRIIseq_vox(mri, xm, yp, zm, frame) +
        xpd * ymd * zmd * (Real)MRIIseq_vox(mri, xm, yp, zp, frame) +
        xmd * ypd * zpd * (Real)MRIIseq_vox(mri, xp, ym, zm, frame) +
        xmd * ypd * zmd * (Real)MRIIseq_vox(mri, xp, ym, zp, frame) +
        xmd * ymd * zpd * (Real)MRIIseq_vox(mri, xp, yp, zm, frame) +
        xmd * ymd * zmd * (Real)MRIIseq_vox(mri, xp, yp, zp, frame) ;
      break ;
    case MRI_LONG:
      *pval = val =
        xpd * ypd * zpd * (Real)MRILseq_vox(mri, xm, ym, zm, frame) +
        xpd * ypd * zmd * (Real)MRILseq_vox(mri, xm, ym, zp, frame) +
        xpd * ymd * zpd * (Real)MRILseq_vox(mri, xm, yp, zm, frame) +
        xpd * ymd * zmd * (Real)MRILseq_vox(mri, xm, yp, zp, frame) +
        xmd * ypd * zpd * (Real)MRILseq_vox(mri, xp, ym, zm, frame) +
        xmd * ypd * zmd * (Real)MRILseq_vox(mri, xp, ym, zp, frame) +
        xmd * ymd * zpd * (Real)MRILseq_vox(mri, xp, yp, zm, frame) +
        xmd * ymd * zmd * (Real)MRILseq_vox(mri, xp, yp, zp, frame) ;
      break ;
    default:
      ErrorReturn(ERROR_UNSUPPORTED,
                  (ERROR_UNSUPPORTED,
                   "MRIsampleVolumeFrame: unsupported type %d", mri->type)) ;
      break ;
    }
  return(NO_ERROR) ;
}



/*---------------------------------------------------------------------------
  Purpose: to return the approximate fraction of a voxel centered
  at the given point
  is labeled with a given label by the labeled volume: mri

  Input: mri  is the labeled volume. Ea voxel contains the uchar label index
  x,y,z is the floating point location of the center of a voxel
  whose labeling is to be determined. The voxel is examined to see
  how much of it is labeled with the label, ucharLabel

  Output: pval is the fraction which the given voxel location is labeled
  by ucharLabel
  returns NO_ERROR or ERROR_UNSUPPORTED if an unsupported
  (non-uchar) mri labeled volume is passed in
  AAM: 7/26/00
  --------------------------------------------------------------------------*/

#ifndef uchar
#define uchar unsigned char
#endif
int
MRIsampleLabeledVolume
(MRI *mri, Real x, Real y, Real z, Real *pval, unsigned char ucharLabel)
{
  /* m's are the mri grid locations less than x (or y or z)
     (i.e. floor(x), p's are essentially rounding up  to the next
     grid location  greater than x  */
  int  xm, xp, ym, yp, zm, zp;
  int width, height, depth ;
  Real xmd, ymd, zmd, xpd, ypd, zpd ;  /* d's are distances */
  uchar ucharDmmm;
  uchar ucharDmmp;
  uchar ucharDmpm;
  uchar ucharDmpp;
  uchar ucharDpmm;
  uchar ucharDpmp;
  uchar ucharDppm;
  uchar ucharDppp;


  *pval=0.0;



  width = mri->width ;
  height = mri->height ;
  depth = mri->depth ;
  /*  if (x >= width)
      x = width - 1.0 ;
      if (y >= height)
      y = height - 1.0 ;
      if (z >= depth)
      z = depth - 1.0 ;
      if (x < 0.0)
      x = 0.0 ;
      if (y < 0.0)
      y = 0.0 ;
      if (z < 0.0)
      z = 0.0 ;
  */

  /* if the x,y,z point is out of range
     then return that none of the given voxel was labeled by ucharLabel */
  if (x >= width) return(NO_ERROR) ;
  if (y >= height) return(NO_ERROR) ;
  if (z >= depth) return(NO_ERROR) ;
  if (x < 0.0) return(NO_ERROR) ;
  if (y < 0.0) return(NO_ERROR) ;
  if (z < 0.0) return(NO_ERROR) ;

  xm = MAX((int)x, 0) ;
  xp = MIN(width-1, xm+1) ;
  ym = MAX((int)y, 0) ;
  yp = MIN(height-1, ym+1) ;
  zm = MAX((int)z, 0) ;
  zp = MIN(depth-1, zm+1) ;

  ucharDmmm = MRIvox(mri, xm, ym, zm) == ucharLabel ? 1 : 0;
  ucharDmmp = MRIvox(mri, xm, ym, zp) == ucharLabel ? 1 : 0;
  ucharDmpm = MRIvox(mri, xm, yp, zm) == ucharLabel ? 1 : 0;
  ucharDmpp = MRIvox(mri, xm, yp, zp) == ucharLabel ? 1 : 0;
  ucharDpmm = MRIvox(mri, xp, ym, zm) == ucharLabel ? 1 : 0;
  ucharDpmp = MRIvox(mri, xp, ym, zp) == ucharLabel ? 1 : 0;
  ucharDppm = MRIvox(mri, xp, yp, zm) == ucharLabel ? 1 : 0;
  ucharDppp = MRIvox(mri, xp, yp, zp) == ucharLabel ? 1 : 0;

  xmd = x - (float)xm ;
  ymd = y - (float)ym ;
  zmd = z - (float)zm ;
  xpd = (1.0f - xmd) ;
  ypd = (1.0f - ymd) ;
  zpd = (1.0f - zmd) ;



  switch (mri->type)
    {
    case MRI_UCHAR:
      *pval =
        xpd * ypd * zpd * (Real)ucharDmmm +
        xpd * ypd * zmd * (Real)ucharDmmp +
        xpd * ymd * zpd * (Real)ucharDmpm +
        xpd * ymd * zmd * (Real)ucharDmpp +
        xmd * ypd * zpd * (Real)ucharDpmm +
        xmd * ypd * zmd * (Real)ucharDpmp +
        xmd * ymd * zpd * (Real)ucharDppm +
        xmd * ymd * zmd * (Real)ucharDppp ;
      break ;
    default:
      ErrorReturn(ERROR_UNSUPPORTED,
                  (ERROR_UNSUPPORTED,
                   "MRIsampleVolume: unsupported type %d", mri->type)) ;
      break ;
    }
  return(NO_ERROR) ;
}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  ------------------------------------------------------*/
int
MRIsampleVolumeType(MRI *mri, Real x, Real y, Real z, Real *pval, int type)
{
  int   xv, yv, zv ;
  int OutOfBounds;

  switch (type)
    {
    default:
    case SAMPLE_NEAREST:
      break ;
    case SAMPLE_TRILINEAR:
      return(MRIsampleVolume(mri, x, y, z, pval)) ;
    case SAMPLE_CUBIC:
      return(MRIcubicSampleVolume(mri, x, y, z, pval)) ;
    case SAMPLE_SINC:
      return(MRIsincSampleVolume(mri, x, y, z, 5, pval)) ;
    }

  OutOfBounds = MRIindexNotInVolume(mri, x, y, z);
  if(OutOfBounds == 1){
    /* unambiguously out of bounds */
    *pval = 0.0;
    return(NO_ERROR) ;
  }

  xv = nint(x) ; yv = nint(y) ; zv = nint(z) ;
  if (xv < 0)
    xv = 0 ;
  if (xv >= mri->width)
    xv = mri->width-1 ;
  if (yv < 0)
    yv = 0 ;
  if (yv >= mri->height)
    yv = mri->height-1 ;
  if (zv < 0)
    zv = 0 ;
  if (zv >= mri->depth)
    zv = mri->depth-1 ;

  switch (mri->type)
    {
    case MRI_UCHAR:
      *pval = (float)MRIvox(mri, xv, yv, zv) ;
      break ;
    case MRI_SHORT:
      *pval = (float)MRISvox(mri, xv, yv, zv) ;
      break ;
    case MRI_INT:
      *pval = (float)MRIIvox(mri, xv, yv, zv) ;
      break ;
    case MRI_FLOAT:
      *pval = MRIFvox(mri, xv, yv, zv) ;
      break ;
    default:
      *pval = 0 ;
      ErrorReturn
        (ERROR_UNSUPPORTED,
         (ERROR_UNSUPPORTED,
          "MRIsampleVolumeType: unsupported volume type %d",
          mri->type)) ;
    }
  return(NO_ERROR) ;
}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  ------------------------------------------------------*/
int
MRIsampleVolumeFrameType
(MRI *mri, Real x, Real y, Real z, int frame, int type, Real *pval)
{
  int   xv, yv, zv ;
  int OutOfBounds;

  if (FEQUAL((int)x,x) && FEQUAL((int)y,y) && FEQUAL((int)z, z))
    type = SAMPLE_NEAREST ;

  switch (type)
    {
    case SAMPLE_NEAREST:
      break ;
    case SAMPLE_TRILINEAR:
      return(MRIsampleVolumeFrame(mri, x, y, z, frame, pval)) ;
    default:
      /*E* add SAMPLE_CUBIC here? */
    case SAMPLE_SINC:
      ErrorReturn
        (ERROR_UNSUPPORTED,
         (ERROR_UNSUPPORTED,
          "MRIsampleVolumeFrameType(%d): unsupported interpolation type",
          type));
      /*    return(MRIsincSampleVolume(mri, x, y, z, 5, pval)) ;*/
    }

  OutOfBounds = MRIindexNotInVolume(mri, x, y, z);
  if(OutOfBounds == 1){
    /* unambiguously out of bounds */
    *pval = 0.0;
    return(NO_ERROR) ;
  }

  xv = nint(x) ; yv = nint(y) ; zv = nint(z) ;
  if (xv < 0)
    xv = 0 ;
  if (xv >= mri->width)
    xv = mri->width-1 ;
  if (yv < 0)
    yv = 0 ;
  if (yv >= mri->height)
    yv = mri->height-1 ;
  if (zv < 0)
    zv = 0 ;
  if (zv >= mri->depth)
    zv = mri->depth-1 ;

  switch (mri->type)
    {
    case MRI_UCHAR:
      *pval = (float)MRIseq_vox(mri, xv, yv, zv, frame) ;
      break ;
    case MRI_SHORT:
      *pval = (float)MRISseq_vox(mri, xv, yv, zv, frame) ;
      break ;
    case MRI_INT:
      *pval = (float)MRIIseq_vox(mri, xv, yv, zv, frame) ;
      break ;
    case MRI_FLOAT:
      *pval = MRIFseq_vox(mri, xv, yv, zv, frame) ;
      break ;
    default:
      *pval = 0 ;
      ErrorReturn
        (ERROR_UNSUPPORTED,
         (ERROR_UNSUPPORTED,
          "MRIsampleVolumeFrameType: unsupported volume type %d",
          mri->type)) ;
    }
  return(NO_ERROR) ;
}


int
MRIinterpolateIntoVolume(MRI *mri, Real x, Real y, Real z, Real val)
{
  int  OutOfBounds;
  int  xm, xp, ym, yp, zm, zp, width, height, depth ;
  Real xmd, ymd, zmd, xpd, ypd, zpd ;  /* d's are distances */

  OutOfBounds = MRIindexNotInVolume(mri, x, y, z);
  if(OutOfBounds == 1){
    /* unambiguously out of bounds */
    return(NO_ERROR) ;
  }

  width = mri->width ; height = mri->height ; depth = mri->depth ;

  if (x >= width)    x = width - 1.0 ;
  if (y >= height)   y = height - 1.0 ;
  if (z >= depth)    z = depth - 1.0 ;
  if (x < 0.0)       x = 0.0 ;
  if (y < 0.0)       y = 0.0 ;
  if (z < 0.0)       z = 0.0 ;

  xm = MAX((int)x, 0) ;
  xp = MIN(width-1, xm+1) ;
  ym = MAX((int)y, 0) ;
  yp = MIN(height-1, ym+1) ;
  zm = MAX((int)z, 0) ;
  zp = MIN(depth-1, zm+1) ;

  xmd = x - (float)xm ;
  ymd = y - (float)ym ;
  zmd = z - (float)zm ;
  xpd = (1.0f - xmd) ;
  ypd = (1.0f - ymd) ;
  zpd = (1.0f - zmd) ;

  switch (mri->type)
    {
    case MRI_UCHAR:
      MRIvox(mri, xm, ym, zm) += nint(xpd * ypd * zpd * val) ;
      MRIvox(mri, xm, ym, zp) += nint(xpd * ypd * zmd * val) ;
      MRIvox(mri, xm, yp, zm) += nint(xpd * ymd * zpd * val) ;
      MRIvox(mri, xm, yp, zp) += nint(xpd * ymd * zmd * val) ;
      MRIvox(mri, xp, ym, zm) += nint(xmd * ypd * zpd * val) ;
      MRIvox(mri, xp, ym, zp) += nint(xmd * ypd * zmd * val) ;
      MRIvox(mri, xp, yp, zm) += nint(xmd * ymd * zpd * val) ;
      MRIvox(mri, xp, yp, zp) += nint(xmd * ymd * zmd * val) ;
      break ;
    case MRI_FLOAT:
      MRIFvox(mri, xm, ym, zm) += (xpd * ypd * zpd * val) ;
      MRIFvox(mri, xm, ym, zp) += (xpd * ypd * zmd * val) ;
      MRIFvox(mri, xm, yp, zm) += (xpd * ymd * zpd * val) ;
      MRIFvox(mri, xm, yp, zp) += (xpd * ymd * zmd * val) ;
      MRIFvox(mri, xp, ym, zm) += (xmd * ypd * zpd * val) ;
      MRIFvox(mri, xp, ym, zp) += (xmd * ypd * zmd * val) ;
      MRIFvox(mri, xp, yp, zm) += (xmd * ymd * zpd * val) ;
      MRIFvox(mri, xp, yp, zp) += (xmd * ymd * zmd * val) ;
      break ;
    case MRI_SHORT:
      MRISvox(mri, xm, ym, zm) += nint(xpd * ypd * zpd * val) ;
      MRISvox(mri, xm, ym, zp) += nint(xpd * ypd * zmd * val) ;
      MRISvox(mri, xm, yp, zm) += nint(xpd * ymd * zpd * val) ;
      MRISvox(mri, xm, yp, zp) += nint(xpd * ymd * zmd * val) ;
      MRISvox(mri, xp, ym, zm) += nint(xmd * ypd * zpd * val) ;
      MRISvox(mri, xp, ym, zp) += nint(xmd * ypd * zmd * val) ;
      MRISvox(mri, xp, yp, zm) += nint(xmd * ymd * zpd * val) ;
      MRISvox(mri, xp, yp, zp) += nint(xmd * ymd * zmd * val) ;
      break ;
    case MRI_INT:
      MRIIvox(mri, xm, ym, zm) += nint(xpd * ypd * zpd * val) ;
      MRIIvox(mri, xm, ym, zp) += nint(xpd * ypd * zmd * val) ;
      MRIIvox(mri, xm, yp, zm) += nint(xpd * ymd * zpd * val) ;
      MRIIvox(mri, xm, yp, zp) += nint(xpd * ymd * zmd * val) ;
      MRIIvox(mri, xp, ym, zm) += nint(xmd * ypd * zpd * val) ;
      MRIIvox(mri, xp, ym, zp) += nint(xmd * ypd * zmd * val) ;
      MRIIvox(mri, xp, yp, zm) += nint(xmd * ymd * zpd * val) ;
      MRIIvox(mri, xp, yp, zp) += nint(xmd * ymd * zmd * val) ;
      break ;
    case MRI_LONG:
      MRILvox(mri, xm, ym, zm) += nint(xpd * ypd * zpd * val) ;
      MRILvox(mri, xm, ym, zp) += nint(xpd * ypd * zmd * val) ;
      MRILvox(mri, xm, yp, zm) += nint(xpd * ymd * zpd * val) ;
      MRILvox(mri, xm, yp, zp) += nint(xpd * ymd * zmd * val) ;
      MRILvox(mri, xp, ym, zm) += nint(xmd * ypd * zpd * val) ;
      MRILvox(mri, xp, ym, zp) += nint(xmd * ypd * zmd * val) ;
      MRILvox(mri, xp, yp, zm) += nint(xmd * ymd * zpd * val) ;
      MRILvox(mri, xp, yp, zp) += nint(xmd * ymd * zmd * val) ;
      break ;
    default:
      ErrorReturn(ERROR_UNSUPPORTED,
                  (ERROR_UNSUPPORTED,
                   "MRIsampleVolume: unsupported type %d", mri->type)) ;
      break ;
    }
  return(NO_ERROR) ;
}

/*-------------------------------------------------------------------
  MRIsampleVolume() - performs trilinear interpolation on a
  single-frame volume. See MRIsampleSeqVolume() for sampling
  multi-frame.
  -------------------------------------------------------------------*/
int
MRIsampleVolume(MRI *mri, Real x, Real y, Real z, Real *pval)
{
  int  OutOfBounds;
  int  xm, xp, ym, yp, zm, zp, width, height, depth ;
  Real val, xmd, ymd, zmd, xpd, ypd, zpd ;  /* d's are distances */

  if (FEQUAL((int)x,x) && FEQUAL((int)y,y) && FEQUAL((int)z, z))
    return(MRIsampleVolumeType(mri, x, y, z, pval, SAMPLE_NEAREST)) ;

  OutOfBounds = MRIindexNotInVolume(mri, x, y, z);
  if(OutOfBounds == 1){
    /* unambiguously out of bounds */
    *pval = 0.0;
    return(NO_ERROR) ;
  }

  width = mri->width ; height = mri->height ; depth = mri->depth ;

  if (x >= width)    x = width - 1.0 ;
  if (y >= height)   y = height - 1.0 ;
  if (z >= depth)    z = depth - 1.0 ;
  if (x < 0.0)       x = 0.0 ;
  if (y < 0.0)       y = 0.0 ;
  if (z < 0.0)       z = 0.0 ;

  xm = MAX((int)x, 0) ;
  xp = MIN(width-1, xm+1) ;
  ym = MAX((int)y, 0) ;
  yp = MIN(height-1, ym+1) ;
  zm = MAX((int)z, 0) ;
  zp = MIN(depth-1, zm+1) ;

  xmd = x - (float)xm ;
  ymd = y - (float)ym ;
  zmd = z - (float)zm ;
  xpd = (1.0f - xmd) ;
  ypd = (1.0f - ymd) ;
  zpd = (1.0f - zmd) ;

  switch (mri->type)
    {
    case MRI_UCHAR:
      *pval = val =
        xpd * ypd * zpd * (Real)MRIvox(mri, xm, ym, zm) +
        xpd * ypd * zmd * (Real)MRIvox(mri, xm, ym, zp) +
        xpd * ymd * zpd * (Real)MRIvox(mri, xm, yp, zm) +
        xpd * ymd * zmd * (Real)MRIvox(mri, xm, yp, zp) +
        xmd * ypd * zpd * (Real)MRIvox(mri, xp, ym, zm) +
        xmd * ypd * zmd * (Real)MRIvox(mri, xp, ym, zp) +
        xmd * ymd * zpd * (Real)MRIvox(mri, xp, yp, zm) +
        xmd * ymd * zmd * (Real)MRIvox(mri, xp, yp, zp) ;
      break ;
    case MRI_FLOAT:
      *pval = val =
        xpd * ypd * zpd * (Real)MRIFvox(mri, xm, ym, zm) +
        xpd * ypd * zmd * (Real)MRIFvox(mri, xm, ym, zp) +
        xpd * ymd * zpd * (Real)MRIFvox(mri, xm, yp, zm) +
        xpd * ymd * zmd * (Real)MRIFvox(mri, xm, yp, zp) +
        xmd * ypd * zpd * (Real)MRIFvox(mri, xp, ym, zm) +
        xmd * ypd * zmd * (Real)MRIFvox(mri, xp, ym, zp) +
        xmd * ymd * zpd * (Real)MRIFvox(mri, xp, yp, zm) +
        xmd * ymd * zmd * (Real)MRIFvox(mri, xp, yp, zp) ;
      break ;
    case MRI_SHORT:
      *pval = val =
        xpd * ypd * zpd * (Real)MRISvox(mri, xm, ym, zm) +
        xpd * ypd * zmd * (Real)MRISvox(mri, xm, ym, zp) +
        xpd * ymd * zpd * (Real)MRISvox(mri, xm, yp, zm) +
        xpd * ymd * zmd * (Real)MRISvox(mri, xm, yp, zp) +
        xmd * ypd * zpd * (Real)MRISvox(mri, xp, ym, zm) +
        xmd * ypd * zmd * (Real)MRISvox(mri, xp, ym, zp) +
        xmd * ymd * zpd * (Real)MRISvox(mri, xp, yp, zm) +
        xmd * ymd * zmd * (Real)MRISvox(mri, xp, yp, zp) ;
      break ;
    case MRI_INT:
      *pval = val =
        xpd * ypd * zpd * (Real)MRIIvox(mri, xm, ym, zm) +
        xpd * ypd * zmd * (Real)MRIIvox(mri, xm, ym, zp) +
        xpd * ymd * zpd * (Real)MRIIvox(mri, xm, yp, zm) +
        xpd * ymd * zmd * (Real)MRIIvox(mri, xm, yp, zp) +
        xmd * ypd * zpd * (Real)MRIIvox(mri, xp, ym, zm) +
        xmd * ypd * zmd * (Real)MRIIvox(mri, xp, ym, zp) +
        xmd * ymd * zpd * (Real)MRIIvox(mri, xp, yp, zm) +
        xmd * ymd * zmd * (Real)MRIIvox(mri, xp, yp, zp) ;
      break ;
    case MRI_LONG:
      *pval = val =
        xpd * ypd * zpd * (Real)MRILvox(mri, xm, ym, zm) +
        xpd * ypd * zmd * (Real)MRILvox(mri, xm, ym, zp) +
        xpd * ymd * zpd * (Real)MRILvox(mri, xm, yp, zm) +
        xpd * ymd * zmd * (Real)MRILvox(mri, xm, yp, zp) +
        xmd * ypd * zpd * (Real)MRILvox(mri, xp, ym, zm) +
        xmd * ypd * zmd * (Real)MRILvox(mri, xp, ym, zp) +
        xmd * ymd * zpd * (Real)MRILvox(mri, xp, yp, zm) +
        xmd * ymd * zmd * (Real)MRILvox(mri, xp, yp, zp) ;
      break ;
    default:
      ErrorReturn(ERROR_UNSUPPORTED,
                  (ERROR_UNSUPPORTED,
                   "MRIsampleVolume: unsupported type %d", mri->type)) ;
      break ;
    }
  return(NO_ERROR) ;
}

/*------------------------------------------------------------------
  MRIsampleSeqVolume() - performs trilinear interpolation on a
  multi-frame volume. valvect is a vector of length nframes. No error
  checking for first and last frame. The caller is able to specify
  first frame and last frame so that all frames do not have to be
  sampled at the same time (this can be important in time-sensative
  applications).
  -------------------------------------------------------------------*/
int MRIsampleSeqVolume(MRI *mri, Real x, Real y, Real z, float *valvect,
                       int firstframe, int lastframe)
{
  int  OutOfBounds;
  int  f,xm, xp, ym, yp, zm, zp, width, height, depth ;
  Real xmd, ymd, zmd, xpd, ypd, zpd ;  /* d's are distances */

  OutOfBounds = MRIindexNotInVolume(mri, x, y, z);
  if(OutOfBounds == 1){
    /* unambiguously out of bounds */
    for(f=firstframe; f <= lastframe; f++) valvect[f] = 0.0;
    return(NO_ERROR) ;
  }

  width = mri->width ; height = mri->height ; depth = mri->depth ;

  if (x >= width)    x = width - 1.0 ;
  if (y >= height)   y = height - 1.0 ;
  if (z >= depth)    z = depth - 1.0 ;
  if (x < 0.0)       x = 0.0 ;
  if (y < 0.0)       y = 0.0 ;
  if (z < 0.0)       z = 0.0 ;

  xm = MAX((int)x, 0) ;
  xp = MIN(width-1, xm+1) ;
  ym = MAX((int)y, 0) ;
  yp = MIN(height-1, ym+1) ;
  zm = MAX((int)z, 0) ;
  zp = MIN(depth-1, zm+1) ;

  xmd = x - (float)xm ;
  ymd = y - (float)ym ;
  zmd = z - (float)zm ;
  xpd = (1.0f - xmd) ;
  ypd = (1.0f - ymd) ;
  zpd = (1.0f - zmd) ;

  for(f = firstframe; f <= lastframe; f++){
    switch (mri->type) {
    case MRI_UCHAR:
      valvect[f] =
        xpd * ypd * zpd * (Real)MRIseq_vox(mri, xm, ym, zm, f) +
        xpd * ypd * zmd * (Real)MRIseq_vox(mri, xm, ym, zp, f) +
        xpd * ymd * zpd * (Real)MRIseq_vox(mri, xm, yp, zm, f) +
        xpd * ymd * zmd * (Real)MRIseq_vox(mri, xm, yp, zp, f) +
        xmd * ypd * zpd * (Real)MRIseq_vox(mri, xp, ym, zm, f) +
        xmd * ypd * zmd * (Real)MRIseq_vox(mri, xp, ym, zp, f) +
        xmd * ymd * zpd * (Real)MRIseq_vox(mri, xp, yp, zm, f) +
        xmd * ymd * zmd * (Real)MRIseq_vox(mri, xp, yp, zp, f) ;
      break ;
    case MRI_FLOAT:
      valvect[f] =
        xpd * ypd * zpd * (Real)MRIFseq_vox(mri, xm, ym, zm, f) +
        xpd * ypd * zmd * (Real)MRIFseq_vox(mri, xm, ym, zp, f) +
        xpd * ymd * zpd * (Real)MRIFseq_vox(mri, xm, yp, zm, f) +
        xpd * ymd * zmd * (Real)MRIFseq_vox(mri, xm, yp, zp, f) +
        xmd * ypd * zpd * (Real)MRIFseq_vox(mri, xp, ym, zm, f) +
        xmd * ypd * zmd * (Real)MRIFseq_vox(mri, xp, ym, zp, f) +
        xmd * ymd * zpd * (Real)MRIFseq_vox(mri, xp, yp, zm, f) +
        xmd * ymd * zmd * (Real)MRIFseq_vox(mri, xp, yp, zp, f) ;
      break ;
    case MRI_SHORT:
      valvect[f] =
        xpd * ypd * zpd * (Real)MRISseq_vox(mri, xm, ym, zm, f) +
        xpd * ypd * zmd * (Real)MRISseq_vox(mri, xm, ym, zp, f) +
        xpd * ymd * zpd * (Real)MRISseq_vox(mri, xm, yp, zm, f) +
        xpd * ymd * zmd * (Real)MRISseq_vox(mri, xm, yp, zp, f) +
        xmd * ypd * zpd * (Real)MRISseq_vox(mri, xp, ym, zm, f) +
        xmd * ypd * zmd * (Real)MRISseq_vox(mri, xp, ym, zp, f) +
        xmd * ymd * zpd * (Real)MRISseq_vox(mri, xp, yp, zm, f) +
        xmd * ymd * zmd * (Real)MRISseq_vox(mri, xp, yp, zp, f) ;
      break ;
    case MRI_INT:
      valvect[f] =
        xpd * ypd * zpd * (Real)MRIIseq_vox(mri, xm, ym, zm, f) +
        xpd * ypd * zmd * (Real)MRIIseq_vox(mri, xm, ym, zp, f) +
        xpd * ymd * zpd * (Real)MRIIseq_vox(mri, xm, yp, zm, f) +
        xpd * ymd * zmd * (Real)MRIIseq_vox(mri, xm, yp, zp, f) +
        xmd * ypd * zpd * (Real)MRIIseq_vox(mri, xp, ym, zm, f) +
        xmd * ypd * zmd * (Real)MRIIseq_vox(mri, xp, ym, zp, f) +
        xmd * ymd * zpd * (Real)MRIIseq_vox(mri, xp, yp, zm, f) +
        xmd * ymd * zmd * (Real)MRIIseq_vox(mri, xp, yp, zp, f) ;
      break ;
    case MRI_LONG:
      valvect[f] =
        xpd * ypd * zpd * (Real)MRILseq_vox(mri, xm, ym, zm, f) +
        xpd * ypd * zmd * (Real)MRILseq_vox(mri, xm, ym, zp, f) +
        xpd * ymd * zpd * (Real)MRILseq_vox(mri, xm, yp, zm, f) +
        xpd * ymd * zmd * (Real)MRILseq_vox(mri, xm, yp, zp, f) +
        xmd * ypd * zpd * (Real)MRILseq_vox(mri, xp, ym, zm, f) +
        xmd * ypd * zmd * (Real)MRILseq_vox(mri, xp, ym, zp, f) +
        xmd * ymd * zpd * (Real)MRILseq_vox(mri, xp, yp, zm, f) +
        xmd * ymd * zmd * (Real)MRILseq_vox(mri, xp, yp, zp, f) ;
      break ;
    default:
      ErrorReturn(ERROR_UNSUPPORTED,
                  (ERROR_UNSUPPORTED,
                   "MRIsampleSeqVolume: unsupported type %d", mri->type)) ;
      break ;
    }
  }/* end loop over frames */

  return(NO_ERROR) ;
}

/*-----------------------------------------------------
  used by MRIcubicSampleVolume
  ------------------------------------------------------*/

double
localeval(Real x, int iter)
{
  double p;
  switch (iter)
    {
    case 0:
      p = ((2-x)*x-1)*x; break;
    case 1:
      p = (3*x-5)*x*x+2; break;
    case 2:
      p = ((4-3*x)*x+1)*x; break;
    case 3:
      p = (x-1)*x*x; break;
    default:
      ErrorReturn(ERROR_UNSUPPORTED,
                  (ERROR_UNSUPPORTED,
                   "localeval: called wrong by MRIcubicSampleVolume!")) ;
    }
  return(p);
}

/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description

  by analogy with
  /usr/pubsw/common/matlab/6.5/toolbox/matlab/polyfun/interp3.m

  uses localeval above

  ------------------------------------------------------*/
int
MRIcubicSampleVolume(MRI *mri, Real x, Real y, Real z, Real *pval)
{
  int  OutOfBounds;
  int  width, height, depth ;
  int ix_low,iy_low,iz_low,ix,iy,iz;
  double val,xx,yy,zz,fx,fy,fz,vv[4][4][4];

  if (FEQUAL((int)x,x) && FEQUAL((int)y,y) && FEQUAL((int)z, z))
    return(MRIsampleVolumeType(mri, x, y, z, pval, SAMPLE_NEAREST)) ;

  OutOfBounds = MRIindexNotInVolume(mri, x, y, z);
  if(OutOfBounds == 1){
    /* unambiguously out of bounds */
    *pval = val = 0.0;
    return(NO_ERROR) ;
  }
  width = mri->width ; height = mri->height ; depth = mri->depth ;

  /*E* I suppose these are for "ambiguously out of bounds" - within .5vox */

  /*E* I think this needs an edit - x is Real, whatever that is, not
    int, so any x>= width-1 should be set to width-1.
    if (x >= width)    x = width - 1.0 ;
    if (y >= height)   y = height - 1.0 ;
    if (z >= depth)    z = depth - 1.0 ;
  */

  if (x > width-1.0)    x = width - 1.0 ;
  if (y > height-1.0)   y = height - 1.0 ;
  if (z > depth-1.0)    z = depth - 1.0 ;
  if (x < 0.0)       x = 0.0 ;
  if (y < 0.0)       y = 0.0 ;
  if (z < 0.0)       z = 0.0 ;

  ix_low = floor((double)x);
  if ((ix_low = floor((double)x)) < width-1)
    xx = x - ix_low;
  else
    {
      ix_low--;
      xx = 1;
    }
  iy_low = floor((double)y);
  if ((iy_low = floor((double)y)) < height-1)
    yy = y - iy_low;
  else
    {
      iy_low--;
      yy = 1;
    }
  iz_low = floor((double)z);
  if ((iz_low = floor((double)z)) < depth-1)
    zz = z - iz_low;
  else
    {
      iz_low--;
      zz = 1;
    }

  /*E* build a little box of the local points plus boundary stuff -
    for this rev accept zeroes for the border expansion */

  for(iz= MAX(0,1-iz_low); iz<MIN(4,depth+1-iz_low); iz++)
    {
      for(iy= MAX(0,1-iy_low); iy<MIN(4,height+1-iy_low); iy++)
        {
          for(ix= MAX(0,1-ix_low); ix<MIN(4,width+1-ix_low); ix++)
            {
              switch (mri->type)
                {
                case MRI_UCHAR:
                  vv[ix][iy][iz] =
                    (double)MRIvox(mri,ix_low-1+ix,iy_low-1+iy,iz_low-1+iz);
                  break;
                case MRI_FLOAT:
                  vv[ix][iy][iz] =
                    (double)MRIFvox(mri,ix_low-1+ix,iy_low-1+iy,iz_low-1+iz);
                  break;
                case MRI_SHORT:
                  vv[ix][iy][iz] =
                    (double)MRISvox(mri,ix_low-1+ix,iy_low-1+iy,iz_low-1+iz);
                  break;
                case MRI_INT:
                  vv[ix][iy][iz] =
                    (double)MRIIvox(mri,ix_low-1+ix,iy_low-1+iy,iz_low-1+iz);
                  break;
                case MRI_LONG:
                  vv[ix][iy][iz] =
                    (double)MRILvox(mri,ix_low-1+ix,iy_low-1+iy,iz_low-1+iz);
                  break;
                default:
                  ErrorReturn(ERROR_UNSUPPORTED,
                              (ERROR_UNSUPPORTED,
                               "MRIcubicSampleVolume: unsupported type %d",
                               mri->type)) ;
                  break ;
                }
            }
        }
    }

  val = 0;

  for(iz=0; iz<=3; iz++)
    {
      fz = localeval(zz,iz);
      for(iy=0; iy<=3; iy++)
        {
          fy = localeval(yy,iy);
          for(ix=0; ix<=3; ix++)
            {
              fx = localeval(xx,ix);
              val += (Real)(vv[ix][iy][iz]*fx*fy*fz);
            }
        }
    }

  *pval = val/8.;

  return(NO_ERROR);
}

/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  ------------------------------------------------------*/
#define IMIN(a,b) (a < b ? a : b)
#define IMAX(a,b) (a > b ? a : b)
double ham_sinc(double x,double fullwidth)
{
  double ham;
  if( fabs(x) < 1.0e-5)
    ham = 1.0;
  else {
    ham = sin(PI*x)/(PI*x);
    ham *= 0.54 + 0.46 * cos(2.0*PI*x/fullwidth);
  }
  return ham;
}

/*-------------------------------------------------------------------------*/
int
MRIsincSampleVolume(MRI *mri, Real x, Real y, Real z, int hw, Real *pval)
{

  int  OutOfBounds;
  int  width, height, depth ;
  int nwidth;
  int ix_low,ix_high,iy_low,iy_high,iz_low,iz_high;
  int jx1,jy1,jz1,jx_rel,jy_rel,jz_rel;
  double coeff_x[128],coeff_y[128],coeff_z[128];
  double coeff_x_sum,coeff_y_sum,coeff_z_sum;
  double sum_x,sum_y,sum_z;
  double xsize,ysize,zsize;

  OutOfBounds = MRIindexNotInVolume(mri, x, y, z);
  if(OutOfBounds == 1){
    /* unambiguously out of bounds */
    *pval = 0.0;
    return(NO_ERROR) ;
  }

  xsize = mri->xsize; ysize=mri->ysize; zsize=mri->zsize;
  width = mri->width ; height = mri->height ; depth = mri->depth ;
  if (x >= width)    x = width - 1.0 ;
  if (y >= height)   y = height - 1.0 ;
  if (z >= depth)    z = depth - 1.0 ;
  if (x < 0.0)       x = 0.0 ;
  if (y < 0.0)       y = 0.0 ;
  if (z < 0.0)       z = 0.0 ;

  nwidth = hw;
  ix_low = floor((double)x);
  ix_high = ceil((double)x);
  iy_low = floor((double)y);
  iy_high = ceil((double)y);
  iz_low = floor((double)z);
  iz_high = ceil((double)z);

  coeff_x_sum = coeff_y_sum = coeff_z_sum = 0;
  if(iz_low>=0 && iz_high < depth)
    {
      for (jx1=IMAX(ix_high-nwidth,0), jx_rel=0;
           jx1<IMIN(ix_low+nwidth,width-1);
           jx1++,jx_rel++)
        {
          coeff_x[jx_rel] = ham_sinc((double)(x-jx1),2*nwidth);
          coeff_x_sum += coeff_x[jx_rel];
        }
      for (jy1=IMAX(iy_high-nwidth,0), jy_rel=0;
           jy1<IMIN(iy_low+nwidth,height-1);
           jy1++,jy_rel++)
        {
          coeff_y[jy_rel] = ham_sinc((double)(y-jy1),2*nwidth);
          coeff_y_sum += coeff_y[jy_rel];
        }
      for (jz1=IMAX(iz_high-nwidth,0), jz_rel=0;
           jz1<IMIN(iz_low+nwidth,depth-1);
           jz1++,jz_rel++)
        {
          coeff_z[jz_rel] = ham_sinc((double)(z-jz1),2*nwidth);
          coeff_z_sum += coeff_z[jz_rel];
        }

      for(sum_z=0., jz1=IMAX(iz_high-nwidth,0), jz_rel = 0;
          jz1 < IMIN(iz_low+nwidth,depth-1);
          jz1++, jz_rel++)
        {

          for(sum_y=0., jy1=IMAX(iy_high-nwidth,0), jy_rel = 0;
              jy1 < IMIN(iy_low+nwidth,height-1);
              jy1++, jy_rel++)
            {
              for(sum_x=0., jx1=IMAX(ix_high-nwidth,0), jx_rel = 0;
                  jx1 < IMIN(ix_low+nwidth,width-1);
                  jx1++, jx_rel++)
                {

                  switch(mri->type)
                    {
                    case MRI_UCHAR:
                      sum_x += (coeff_x[jx_rel]/coeff_x_sum)
                        * (double)MRIvox(mri,jx1,jy1,jz1);
                      break;
                    case MRI_SHORT:
                      sum_x += (coeff_x[jx_rel]/coeff_x_sum)
                        * (double)MRISvox(mri,jx1,jy1,jz1);
                      break;
                    case MRI_INT:
                      sum_x += (coeff_x[jx_rel]/coeff_x_sum)
                        * (double)MRIIvox(mri,jx1,jy1,jz1);
                      break;
                    case MRI_LONG:
                      sum_x += (coeff_x[jx_rel]/coeff_x_sum)
                        * (double)MRILvox(mri,jx1,jy1,jz1);
                      break;
                    case MRI_FLOAT:
                      sum_x += (coeff_x[jx_rel]/coeff_x_sum)
                        * (double)MRIFvox(mri,jx1,jy1,jz1);
                      break;
                    default:
                      ErrorReturn(ERROR_UNSUPPORTED,
                                  (ERROR_UNSUPPORTED,
                                   "MRIsincSampleVolume: unsupported type %d",
                                   mri->type)) ;
                      break;
                    }
                }
              sum_y += sum_x * (coeff_y[jy_rel]/coeff_y_sum);
            }
          sum_z += sum_y * (coeff_z[jz_rel]/coeff_z_sum);
        }
      if((mri->type == MRI_UCHAR || mri->type == MRI_SHORT) && sum_z<0.0)
        *pval = 0.0;
      else if(mri->type == MRI_UCHAR && sum_z >255.0)
        *pval = 255.0;
      else if(mri->type == MRI_SHORT && sum_z > 65535.0)
        *pval = 65535.0;
      else
        *pval = sum_z;
    }
  else
    *pval = 0.0;

  return(NO_ERROR);
}
/*-----------------------------------------------------------------
  MRIindexNotInVolume() - determines whether a col, row, slice point is
  in the mri volume. If it is unambiguously in the volume, then 0
  is returned. If it is within 0.5 of the edge of the volume, -1
  is returned. Otherwise 1 is returned. Flagging the case where
  the point is within 0.5 of the edge can be used for assigning
  a nearest neighbor when the point is outside but close to the
  volume. In this case the index of the nearest neighbor can safely
  be computed as the nearest integer to col, row, and slice.
  -----------------------------------------------------------------*/
int MRIindexNotInVolume(MRI *mri, Real col, Real row, Real slice)
{
  float nicol, nirow, nislice;

  /* unambiguously in the volume */
  if(col   >= 0 && col   <= mri->width-1  &&
     row   >= 0 && row   <= mri->height-1 &&
     slice >= 0 && slice <= mri->depth-1  )
    return(0);

  /* within 0.5 of the edge of the volume */
  nicol   = rint(col);
  nirow   = rint(row);
  nislice = rint(slice);
  if(nicol   >= 0 && nicol   < mri->width  &&
     nirow   >= 0 && nirow   < mri->height &&
     nislice >= 0 && nislice < mri->depth  )
    return(-1);

  /* unambiguously NOT in the volume */
  return(1);

}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  Interpolate the volume directional derivative using
  trilinear interpolation.
  ------------------------------------------------------*/
float
MRIsampleCardinalDerivative(MRI *mri, int x, int y, int z,
                            int xk, int yk, int zk)
{
  float d ;

  if (xk)
    d = MRIsampleXDerivative(mri, x, y, z, xk) ;
  else if (yk)
    d = MRIsampleYDerivative(mri, x, y, z, yk) ;
  else
    d = MRIsampleZDerivative(mri, x, y, z, zk) ;
  return(d) ;
}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  Interpolate the volume directional derivative using
  trilinear interpolation.
  ------------------------------------------------------*/
float
MRIsampleXDerivative(MRI *mri, int x, int y, int z, int dir)
{
  float dx ;
  int   yk, zk, xi, yi, zi, nvox ;

  dx = 0.0 ;

  xi = mri->xi[x+dir] ;
  for (nvox = 0, zk = -1 ; zk <= 1 ; zk++)
    {
      zi = mri->zi[z+zk] ;
      for (yk = -1 ; yk <= 1 ; yk++)
        {
          yi = mri->yi[y+yk] ;
          dx += dir*MRIvox(mri, xi, yi, zi) ;  /* x+dir */
          dx -= dir*MRIvox(mri, x, yi, zi) ;   /* - x */
          nvox += 2 ;
        }
    }
  dx /= ((float)nvox*mri->xsize) ;
  return(dx) ;
}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  Interpolate the volume directional derivative using
  trilinear interpolation.
  ------------------------------------------------------*/
float
MRIsampleYDerivative(MRI *mri, int x, int y, int z, int dir)
{
  float dy ;
  int   xk, zk, xi, yi, zi, nvox ;

  dy = 0.0 ;

  yi = mri->yi[y+dir] ;
  for (nvox = 0, zk = -1 ; zk <= 1 ; zk++)
    {
      zi = mri->zi[z+zk] ;
      for (xk = -1 ; xk <= 1 ; xk++)
        {
          xi = mri->xi[x+xk] ;
          dy += dir*MRIvox(mri, xi, yi, zi) ;  /* x+dir */
          dy -= dir*MRIvox(mri, x, yi, zi) ;   /* - x */
          nvox += 2 ;
        }
    }
  dy /= ((float)nvox*mri->ysize) ;
  return(dy) ;
}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  Interpolate the volume directional derivative using
  trilinear interpolation.
  ------------------------------------------------------*/
float
MRIsampleZDerivative(MRI *mri, int x, int y, int z, int dir)
{
  float dz ;
  int   xk, yk, xi, yi, zi, nvox ;

  dz = 0.0 ;

  zi = mri->zi[z+dir] ;
  for (nvox = 0, xk = -1 ; xk <= 1 ; xk++)
    {
      xi = mri->xi[x+xk] ;
      for (yk = -1 ; yk <= 1 ; yk++)
        {
          yi = mri->yi[y+yk] ;
          dz += dir*MRIvox(mri, xi, yi, zi) ;  /* x+dir */
          dz -= dir*MRIvox(mri, x, yi, zi) ;   /* - x */
          nvox += 2 ;
        }
    }
  dz /= ((float)nvox*mri->zsize) ;
  return(dz) ;
}
int
MRIsampleVolumeDirectionScale(MRI *mri, Real x, Real y, Real z,
                              Real dx, Real dy, Real dz, Real *pmag,
                              double sigma)
{
  int  width, height, depth ;
  Real xp1, xm1, yp1, ym1, zp1, zm1, len ;
  Real dist, val, k, ktotal, step_size, total_val ;
  int  n ;

  width = mri->width ; height = mri->height ; depth = mri->depth ;
  if (x >= width)
    x = width - 1.0 ;
  if (y >= height)
    y = height - 1.0 ;
  if (z >= depth)
    z = depth - 1.0 ;
  if (x < 0.0)
    x = 0.0 ;
  if (y < 0.0)
    y = 0.0 ;
  if (z < 0.0)
    z = 0.0 ;

  step_size = MAX(.5,sigma/2) ;
  for (total_val = ktotal = 0.0,n = 0, len = 0.0, dist = step_size ;
       dist <= MAX(2*sigma,step_size);
       dist += step_size, n++)
    {
      if (FZERO(sigma))
        k = 1.0 ;
      else
        k = exp(-dist*dist/(2*sigma*sigma)) ;
      ktotal += k ;
      len += dist ;
      xp1 = x + dist*dx ; yp1 = y + dist*dy ; zp1 = z + dist*dz ;
      MRIsampleVolume(mri, xp1, yp1, zp1, &val) ;
      total_val += k*val ;

      xm1 = x - dist*dx ; ym1 = y - dist*dy ; zm1 = z - dist*dz ;
      MRIsampleVolume(mri, xm1, ym1, zm1, &val) ;
      total_val += k*val ;
      if (FZERO(step_size))
        break ;
    }
  total_val /= (double)2.0*ktotal ;

  *pmag = total_val ;
  return(NO_ERROR) ;
}

int
MRIsampleVolumeDerivativeScale(MRI *mri, Real x, Real y, Real z, Real dx,
                               Real dy, Real dz, Real *pmag, double sigma)
{
  int  width, height, depth ;
  Real xp1, xm1, yp1, ym1, zp1, zm1, vp1, vm1, len ;
  Real dist, val, k, ktotal, step_size ;
  int  n ;

  width = mri->width ; height = mri->height ; depth = mri->depth ;
  if (x >= width)
    x = width - 1.0 ;
  if (y >= height)
    y = height - 1.0 ;
  if (z >= depth)
    z = depth - 1.0 ;
  if (x < 0.0)
    x = 0.0 ;
  if (y < 0.0)
    y = 0.0 ;
  if (z < 0.0)
    z = 0.0 ;

  step_size = MAX(.5,sigma/2) ;
  for (ktotal = 0.0,n = 0, len = vp1 = vm1 = 0.0, dist = step_size ;
       dist <= MAX(2*sigma,step_size);
       dist += step_size, n++)
    {
      if (FZERO(sigma))
        k = 1.0 ;
      else
        k = exp(-dist*dist/(2*sigma*sigma)) ;
      ktotal += k ;
      len += dist ;
      xp1 = x + dist*dx ; yp1 = y + dist*dy ; zp1 = z + dist*dz ;
      MRIsampleVolume(mri, xp1, yp1, zp1, &val) ;
      vp1 += k*val ;

      xm1 = x - dist*dx ; ym1 = y - dist*dy ; zm1 = z - dist*dz ;
      MRIsampleVolume(mri, xm1, ym1, zm1, &val) ;
      vm1 += k*val ;
      if (FZERO(step_size))
        break ;
    }
  vm1 /= (double)ktotal ; vp1 /= (double)ktotal ; len /= (double)ktotal ;

  *pmag = (vp1-vm1) / (2.0*len) ;
  return(NO_ERROR) ;
}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  Interpolate the volume directional derivative using
  trilinear interpolation.
  ------------------------------------------------------*/
int
MRIsampleVolumeDerivative(MRI *mri, Real x, Real y, Real z,
                          Real dx, Real dy, Real dz, Real *pmag)
{
  int  width, height, depth ;
  Real xp1, xm1, yp1, ym1, zp1, zm1, vp1, vm1, len ;

  width = mri->width ; height = mri->height ; depth = mri->depth ;
  if (x >= width)
    x = width - 1.0 ;
  if (y >= height)
    y = height - 1.0 ;
  if (z >= depth)
    z = depth - 1.0 ;
  if (x < 0.0)
    x = 0.0 ;
  if (y < 0.0)
    y = 0.0 ;
  if (z < 0.0)
    z = 0.0 ;
#if 1
  {
    Real dist, val ;
    int  n ;

    for (n = 0, len = vp1 = vm1 = 0.0, dist = .5 ; dist <= 2 ;
         dist += 0.5, n++)
      {
        len += dist ;
        xp1 = x + dist*dx ; yp1 = y + dist*dy ; zp1 = z + dist*dz ;
        MRIsampleVolume(mri, xp1, yp1, zp1, &val) ;
        vp1 += val ;

        xm1 = x - dist*dx ; ym1 = y - dist*dy ; zm1 = z - dist*dz ;
        MRIsampleVolume(mri, xm1, ym1, zm1, &val) ;
        vm1 += val ;
      }
    vm1 /= (double)n ; vp1 /= (double)n ; len /= (double)n ;
  }
#else
  xp1 = x + dx ; xm1 = x - dx ;
  yp1 = y + dy ; ym1 = y - dy ;
  zp1 = z + dz ; zm1 = z - dz ;
  len = sqrt(dx*dx+dy*dy+dz*dz) ;
  MRIsampleVolume(mri, xp1, yp1, zp1, &vp1) ;
  MRIsampleVolume(mri, xm1, ym1, zm1, &vm1) ;
#endif

  *pmag = (vp1-vm1) / (2.0*len) ;
  return(NO_ERROR) ;
}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  Interpolate the volume gradient to cubic voxels.
  ------------------------------------------------------*/
int
MRIsampleVolumeGradient(MRI *mri, Real x, Real y, Real z,
                        Real *pdx, Real *pdy, Real *pdz)
{
  int  width, height, depth ;
  Real xp1, xm1, yp1, ym1, zp1, zm1 ;

  width = mri->width ; height = mri->height ; depth = mri->depth ;
  if (x >= width)
    x = width - 1.0 ;
  if (y >= height)
    y = height - 1.0 ;
  if (z >= depth)
    z = depth - 1.0 ;
  if (x < 0.0)
    x = 0.0 ;
  if (y < 0.0)
    y = 0.0 ;
  if (z < 0.0)
    z = 0.0 ;
  MRIsampleVolume(mri, x+1.0, y, z, &xp1) ;
  MRIsampleVolume(mri, x-1.0, y, z, &xm1) ;

  MRIsampleVolume(mri, x, y+1.0, z, &yp1) ;
  MRIsampleVolume(mri, x, y-1.0, z, &ym1) ;

  MRIsampleVolume(mri, x, y, z+1.0, &zp1) ;
  MRIsampleVolume(mri, x, y, z-1.0, &zm1) ;

  *pdx = (xp1-xm1)/(2.0*mri->xsize) ;
  *pdy = (yp1-ym1)/(2.0*mri->ysize) ;
  *pdz = (zp1-zm1)/(2.0*mri->zsize) ;
  return(NO_ERROR) ;
}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  Interpolate the volume gradient to cubic voxels.
  ------------------------------------------------------*/
int
MRIsampleVolumeGradientFrame(MRI *mri, Real x, Real y, Real z,
                             Real *pdx, Real *pdy, Real *pdz, int frame)
{
  int  width, height, depth ;
  Real xp1, xm1, yp1, ym1, zp1, zm1 ;

  width = mri->width ; height = mri->height ; depth = mri->depth ;
  if (x >= width)
    x = width - 1.0 ;
  if (y >= height)
    y = height - 1.0 ;
  if (z >= depth)
    z = depth - 1.0 ;
  if (x < 0.0)
    x = 0.0 ;
  if (y < 0.0)
    y = 0.0 ;
  if (z < 0.0)
    z = 0.0 ;
  if (frame >= mri->nframes)
    frame = mri->nframes-1 ;
  if (frame < 0)
    frame = 0 ;

  MRIsampleVolumeFrame(mri, x+1.0, y, z, frame, &xp1) ;
  MRIsampleVolumeFrame(mri, x-1.0, y, z, frame, &xm1) ;

  MRIsampleVolumeFrame(mri, x, y+1.0, z, frame, &yp1) ;
  MRIsampleVolumeFrame(mri, x, y-1.0, z, frame, &ym1) ;

  MRIsampleVolumeFrame(mri, x, y, z+1.0, frame, &zp1) ;
  MRIsampleVolumeFrame(mri, x, y, z-1.0, frame, &zm1) ;

  *pdx = (xp1-xm1)/(2.0*mri->xsize) ;
  *pdy = (yp1-ym1)/(2.0*mri->ysize) ;
  *pdz = (zp1-zm1)/(2.0*mri->zsize) ;
  return(NO_ERROR) ;
}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  ------------------------------------------------------*/
int
MRIneighborsOn(MRI *mri, int x0, int y0, int z0, int min_val)
{
  int   nbrs = 0 ;

  if (MRIvox(mri,mri->xi[x0-1],y0,z0) >= min_val)
    nbrs++ ;
  if (MRIvox(mri,mri->xi[x0+1],y0,z0) >= min_val)
    nbrs++ ;
  if (MRIvox(mri,x0,mri->yi[y0+1],z0) >= min_val)
    nbrs++ ;
  if (MRIvox(mri,x0,mri->yi[y0-1],z0) >= min_val)
    nbrs++ ;
  if (MRIvox(mri,x0,y0,mri->zi[z0+1]) >= min_val)
    nbrs++ ;
  if (MRIvox(mri,x0,y0,mri->zi[z0-1]) >= min_val)
    nbrs++ ;
  return(nbrs) ;
}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  ------------------------------------------------------*/
int
MRIneighborsOn3x3(MRI *mri, int x, int y, int z, int min_val)
{
  int   xk, yk, zk, xi, yi, zi, nbrs ;

  for (nbrs = 0, zk = -1 ; zk <= 1 ; zk++)
    {
      zi = mri->zi[z+zk] ;
      for (yk = -1 ; yk <= 1 ; yk++)
        {
          yi = mri->yi[y+yk] ;
          for (xk = -1 ; xk <= 1 ; xk++)
            {
              xi = mri->xi[x+xk] ;
              if (!zk && !yk && !xk)
                continue ;
              if (MRIvox(mri, xi, yi, zi) > min_val)
                nbrs++ ;
            }
        }
    }
  return(nbrs) ;
}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  ------------------------------------------------------*/
int
MRIneighborsInWindow(MRI *mri, int x, int y, int z, int wsize, int val)
{
  int   xk, yk, zk, xi, yi, zi, nbrs, whalf ;

  whalf = (wsize-1)/2 ;

  for (nbrs = 0, zk = -whalf ; zk <= whalf ; zk++)
    {
      zi = mri->zi[z+zk] ;
      for (yk = -whalf ; yk <= whalf ; yk++)
        {
          yi = mri->yi[y+yk] ;
          for (xk = -whalf ; xk <= whalf ; xk++)
            {
              xi = mri->xi[x+xk] ;
              if (!zk && !yk && !xk)
                continue ;
              if (MRIvox(mri, xi, yi, zi) == val)
                nbrs++ ;
            }
        }
    }
  return(nbrs) ;
}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  ------------------------------------------------------*/
int
MRIneighbors(MRI *mri, int x0, int y0, int z0, int val)
{
  int   nbrs = 0 ;

  if (nint(MRIgetVoxVal(mri,mri->xi[x0-1],y0,z0,0)) == val)
    nbrs++ ;
  if (nint(MRIgetVoxVal(mri,mri->xi[x0+1],y0,z0,0)) == val)
    nbrs++ ;
  if (nint(MRIgetVoxVal(mri,x0,mri->yi[y0+1],z0,0)) == val)
    nbrs++ ;
  if (nint(MRIgetVoxVal(mri,x0,mri->yi[y0-1],z0,0)) == val)
    nbrs++ ;
  if (nint(MRIgetVoxVal(mri,x0,y0,mri->zi[z0+1],0)) == val)
    nbrs++ ;
  if (nint(MRIgetVoxVal(mri,x0,y0,mri->zi[z0-1],0)) == val)
    nbrs++ ;
  return(nbrs) ;
}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  ------------------------------------------------------*/
int
MRIneighbors3x3(MRI *mri, int x, int y, int z, int val)
{
  int   xk, yk, zk, xi, yi, zi, nbrs ;

  for (nbrs = 0, zk = -1 ; zk <= 1 ; zk++)
    {
      zi = mri->zi[z+zk] ;
      for (yk = -1 ; yk <= 1 ; yk++)
        {
          yi = mri->yi[y+yk] ;
          for (xk = -1 ; xk <= 1 ; xk++)
            {
              xi = mri->xi[x+xk] ;
              if (!zk && !yk && !xk)
                continue ;
              if (MRIvox(mri, xi, yi, zi) == val)
                nbrs++ ;
            }
        }
    }
  return(nbrs) ;
}
/*-----------------------------------------------------
  ------------------------------------------------------*/
int
MRIneighborsOff(MRI *mri, int x0, int y0, int z0, int min_val)
{
  int   nbrs = 0 ;

  if (MRIvox(mri,x0-1,y0,z0) < min_val)
    nbrs++ ;
  if (MRIvox(mri,x0+1,y0,z0) < min_val)
    nbrs++ ;
  if (MRIvox(mri,x0,y0+1,z0) < min_val)
    nbrs++ ;
  if (MRIvox(mri,x0,y0-1,z0) < min_val)
    nbrs++ ;
  if (MRIvox(mri,x0,y0,z0+1) < min_val)
    nbrs++ ;
  if (MRIvox(mri,x0,y0,z0-1) < min_val)
    nbrs++ ;
  return(nbrs) ;
}
/*-----------------------------------------------------
  ------------------------------------------------------*/
int
MRIneighborsOff3x3(MRI *mri, int x, int y, int z, int min_val)
{
  int   xk, yk, zk, xi, yi, zi, nbrs ;

  for (nbrs = 0, zk = -1 ; zk <= 1 ; zk++)
    {
      zi = mri->zi[z+zk] ;
      for (yk = -1 ; yk <= 1 ; yk++)
        {
          yi = mri->yi[y+yk] ;
          for (xk = -1 ; xk <= 1 ; xk++)
            {
              xi = mri->xi[x+xk] ;
              if (!zk && !yk && !xk)
                continue ;
              if (MRIvox(mri, xi, yi, zi) < min_val)
                nbrs++ ;
            }
        }
    }
  return(nbrs) ;
}
/*-----------------------------------------------------
  Perform an linear coordinate transformation x' = Ax on
  the MRI image mri_src into mri_dst
  ------------------------------------------------------*/
MRI *
MRIinverseLinearTransform(MRI *mri_src, MRI *mri_dst, MATRIX *mA)
{
  MATRIX  *m_inv ;

  m_inv = MatrixInverse(mA, NULL) ;
  if   (!m_inv)
    ErrorReturn
      (NULL,
       (ERROR_BADPARM,
        "MRIinverseLinearTransform: xform is singular!")) ;
  fprintf
    (stderr,
     "applying the vox-to-vox linear transform (calculated inverse)\n");
  MatrixPrint(stderr, m_inv);
  mri_dst = MRIlinearTransform(mri_src, mri_dst, m_inv) ;
  MatrixFree(&m_inv) ;
  return(mri_dst) ;
}
/*-----------------------------------------------------
  Convert a transform from RAS to voxel coordinates, then apply
  it to an MRI.
  ------------------------------------------------------*/
MRI *
MRIapplyRASlinearTransform(MRI *mri_src, MRI *mri_dst, MATRIX *m_ras_xform)
{
  MATRIX   *m_voxel_xform ;

  m_voxel_xform = MRIrasXformToVoxelXform(mri_src, mri_dst, m_ras_xform, NULL);
  fprintf(stderr, "applying the vox to vox linear transform\n");
  MatrixPrint(stderr, m_voxel_xform);
  mri_dst = MRIlinearTransform(mri_src, mri_dst, m_voxel_xform) ;
  MatrixFree(&m_voxel_xform) ;
  return(mri_dst) ;
}
/*-----------------------------------------------------
  Convert a transform from RAS to voxel coordinates, then apply
  it to an MRI.
  ------------------------------------------------------*/
MRI *
MRIapplyRASlinearTransformInterp
(MRI *mri_src, MRI *mri_dst, MATRIX *m_ras_xform, int interp)
{
  MATRIX   *m_voxel_xform ;

  m_voxel_xform = MRIrasXformToVoxelXform(mri_src, mri_dst, m_ras_xform, NULL);
  fprintf(stderr, "applying the vox to vox linear transform\n");
  MatrixPrint(stderr, m_voxel_xform);
  mri_dst = MRIlinearTransformInterp(mri_src, mri_dst, m_voxel_xform, interp) ;
  MatrixFree(&m_voxel_xform) ;
  return(mri_dst) ;
}
/*-----------------------------------------------------
  Convert a transform from RAS to voxel coordinates, then apply
  it to an MRI.
  ------------------------------------------------------*/
MRI *
MRIapplyRASinverseLinearTransform(MRI *mri_src, MRI *mri_dst,
                                  MATRIX *m_ras_xform)
{
  MATRIX   *m_voxel_xform ;

  m_voxel_xform = MRIrasXformToVoxelXform(mri_src, mri_dst, m_ras_xform, NULL);
  mri_dst = MRIinverseLinearTransform(mri_src, mri_dst, m_voxel_xform) ;
  MatrixFree(&m_voxel_xform) ;
  return(mri_dst) ;
}

/*-----------------------------------------------------
  Perform an linear coordinate transformation x' = Ax on
  the MRI image mri_src into mri_dst using sinc interp.
  ------------------------------------------------------*/
MRI *
MRIsincTransform(MRI *mri_src, MRI *mri_dst, MATRIX *mA, int hw)
{
  int    y1, y2, y3, width, height, depth ;
  VECTOR *v_X, *v_Y ;   /* original and transformed coordinate systems */
  MATRIX *mAinv ;     /* inverse of mA */
  Real   val, x1, x2, x3 ;

  mAinv = MatrixInverse(mA, NULL) ;      /* will sample from dst back to src */
  if (!mAinv)
    ErrorReturn(NULL, (ERROR_BADPARM,
                       "MRIsincTransform: xform is singular")) ;

  width = mri_src->width ; height = mri_src->height ; depth = mri_src->depth ;
  if (!mri_dst)
    mri_dst = MRIclone(mri_src, NULL) ;
  else
    MRIclear(mri_dst) ;

  v_X = VectorAlloc(4, MATRIX_REAL) ;  /* input (src) coordinates */
  v_Y = VectorAlloc(4, MATRIX_REAL) ;  /* transformed (dst) coordinates */

  v_Y->rptr[4][1] = 1.0f ;
  for (y3 = 0 ; y3 < depth ; y3++)
    {
      V3_Z(v_Y) = y3 ;
      for (y2 = 0 ; y2 < height ; y2++)
        {
          V3_Y(v_Y) = y2 ;
          for (y1 = 0 ; y1 < width ; y1++)
            {
              V3_X(v_Y) = y1 ;
              MatrixMultiply(mAinv, v_Y, v_X) ;

              x1 = V3_X(v_X) ; x2 = V3_Y(v_X) ; x3 = V3_Z(v_X) ;

              if (nint(y1) == 13 && nint(y2) == 10 && nint(y3) == 7)
                DiagBreak() ;
              if (nint(x1) == 13 && nint(x2) == 10 && nint(x3) == 7)
                {
#if 0
                  fprintf
                    (stderr,
                     "(%2.1f, %2.1f, %2.1f) --> (%2.1f, %2.1f, %2.1f)\n",
                     (float)x1, (float)x2, (float)x3,
                     (float)y1, (float)y2, (float)y3) ;
#endif
                  DiagBreak() ;
                }

              if (x1 > -1 && x1 < width &&
                  x2 > -1 && x2 < height &&
                  x3 > -1 && x3 < depth)
                {
                  MRIsincSampleVolume(mri_src, x1, x2, x3, hw, &val);
                  MRIvox(mri_dst,y1,y2,y3) = (BUFTYPE)nint(val) ;
                }
            }
        }
    }

  MatrixFree(&v_X) ;
  MatrixFree(&mAinv) ;
  MatrixFree(&v_Y) ;

  mri_dst->ras_good_flag = 0;

  return(mri_dst) ;
}
/*-----------------------------------------------------------------
  MRIlinearTransform() - for historical reasons, this uses trilinear
  interpolation. This the operations under this function name can
  now (2/20/02) be found under MRIlinearTransformInterp().
  -----------------------------------------------------------------*/
MRI *
MRIlinearTransform(MRI *mri_src, MRI *mri_dst, MATRIX *mA)
{
  mri_dst = MRIlinearTransformInterp(mri_src, mri_dst, mA, SAMPLE_TRILINEAR);
  return(mri_dst);
}
/*-------------------------------------------------------------------
  MRIlinearTransformInterp() Perform linear coordinate transformation
  x' = Ax on the MRI image mri_src into mri_dst using the specified
  interpolation method. A is a voxel-to-voxel transform.
  ------------------------------------------------------------------*/
MRI *
MRIlinearTransformInterp(MRI *mri_src, MRI *mri_dst, MATRIX *mA,
                         int InterpMethod)
{
  int    y1, y2, y3, width, height, depth ;
  VECTOR *v_X, *v_Y ;   /* original and transformed coordinate systems */
  MATRIX *mAinv ;     /* inverse of mA */
  Real   val, x1, x2, x3 ;

  if(InterpMethod != SAMPLE_NEAREST &&
     InterpMethod != SAMPLE_TRILINEAR &&
     InterpMethod != SAMPLE_CUBIC &&
     InterpMethod != SAMPLE_SINC ){
    printf("ERROR: MRIlinearTransformInterp: unrecoginzed interpolation "
           "method %d\n",InterpMethod);
  }

  mAinv = MatrixInverse(mA, NULL) ;      /* will sample from dst back to src */
  if (!mAinv)
    ErrorReturn(NULL, (ERROR_BADPARM,
                       "MRIlinearTransform: xform is singular")) ;

  if (!mri_dst)
    mri_dst = MRIclone(mri_src, NULL) ;
  else
    MRIclear(mri_dst) ;

  width = mri_dst->width ; height = mri_dst->height ; depth = mri_dst->depth ;
  v_X = VectorAlloc(4, MATRIX_REAL) ;  /* input (src) coordinates */
  v_Y = VectorAlloc(4, MATRIX_REAL) ;  /* transformed (dst) coordinates */

  if (Gdiag & DIAG_SHOW && DIAG_VERBOSE_ON)
    printf("MRIlinearTransformInterp: Applying transform\n");

  v_Y->rptr[4][1] = 1.0f ;
  for (y3 = 0 ; y3 < depth ; y3++)
    {
      V3_Z(v_Y) = y3 ;
      for (y2 = 0 ; y2 < height ; y2++)
        {
          V3_Y(v_Y) = y2 ;
          for (y1 = 0 ; y1 < width ; y1++)
            {
              V3_X(v_Y) = y1 ;
              MatrixMultiply(mAinv, v_Y, v_X) ;

              x1 = V3_X(v_X) ; x2 = V3_Y(v_X) ; x3 = V3_Z(v_X) ;

              if (nint(y1) == 13 && nint(y2) == 10 && nint(y3) == 7)
                DiagBreak() ;
              if (nint(x1) == Gx && nint(x2) == Gy && nint(x3) == Gz)
                {
#if 0
                  fprintf
                    (stderr,
                     "(%2.1f, %2.1f, %2.1f) --> (%2.1f, %2.1f, %2.1f)\n",
                     (float)x1, (float)x2, (float)x3,
                     (float)y1, (float)y2, (float)y3) ;
#endif
                  DiagBreak() ;
                }

              //MRIsampleVolume(mri_src, x1, x2, x3, &val);
              MRIsampleVolumeType(mri_src, x1, x2, x3, &val, InterpMethod);
              switch (mri_dst->type)
                {
                case MRI_UCHAR:
                  MRIvox(mri_dst,y1,y2,y3) = (BUFTYPE)nint(val) ;
                  break ;
                case MRI_SHORT:
                  MRISvox(mri_dst,y1,y2,y3) = (short)nint(val) ;
                  break ;
                case MRI_FLOAT:
                  MRIFvox(mri_dst,y1,y2,y3) = (float)(val) ;
                  break ;
                case MRI_INT:
                  MRIIvox(mri_dst,y1,y2,y3) = nint(val) ;
                  break ;
                default:
                  ErrorReturn(NULL,
                              (ERROR_UNSUPPORTED,
                               "MRIlinearTransform: unsupported dst type %d",
                               mri_dst->type)) ;
                  break ;
                }
            }
        }
    }

  MatrixFree(&v_X) ;
  MatrixFree(&mAinv) ;
  MatrixFree(&v_Y) ;

  mri_dst->ras_good_flag = 1;

  if (Gdiag & DIAG_SHOW && DIAG_VERBOSE_ON)
    {
      printf("MRIlinearTransform: done\n");
      printf("mri_dst:\n");
      printf(" vox res: %g %g %g\n",
             mri_dst->xsize,mri_dst->ysize,mri_dst->zsize);
      printf(" vox dim: %d %d %d\n",
             mri_dst->width,mri_dst->height,mri_dst->depth);
    }

  return(mri_dst) ;
}
MRI *
MRIconcatenateFrames(MRI *mri_frame1, MRI *mri_frame2, MRI *mri_dst)
{
  int       width, height, depth, x, y, z ;
  BUFTYPE   *pf1, *pf2, *pdst1, *pdst2 ;

  if (mri_frame1->type != MRI_UCHAR || mri_frame1->type != MRI_UCHAR)
    ErrorReturn(NULL,
                (ERROR_UNSUPPORTED,"MRIconcatenateFrames: src must be UCHAR"));

  width = mri_frame1->width ;
  height = mri_frame1->height ;
  depth = mri_frame1->depth ;
  if (mri_dst == NULL)
    {
      mri_dst = MRIallocSequence(width, height, depth, mri_frame1->type, 2) ;
      MRIcopyHeader(mri_frame1, mri_dst) ;
    }
  if (!mri_dst)
    ErrorExit(ERROR_NOMEMORY, "MRIconcatenateFrames: could not alloc dst") ;


  for (z = 0 ; z < depth ; z++)
    {
      for (y = 0 ; y < height ; y++)
        {
          pdst1 = &MRIvox(mri_dst, 0, y, z) ;
          pdst2 = &MRIseq_vox(mri_dst, 0, y, z, 1) ;
          pf1 = &MRIvox(mri_frame1, 0, y, z) ;
          pf2 = &MRIvox(mri_frame2, 0, y, z) ;
          for (x = 0 ; x < width ; x++)
            {
              *pdst1++ = *pf1++ ;
              *pdst2++ = *pf2++ ;
            }
        }
    }
  return(mri_dst) ;
}
/*-----------------------------------------------------
  ------------------------------------------------------*/
MRI *
MRIcopyFrame(MRI *mri_src, MRI *mri_dst, int src_frame, int dst_frame)
{
  int       width, height, depth, y, z, bytes ;
  BUFTYPE   *psrc, *pdst ;

  width = mri_src->width ;
  height = mri_src->height ;
  depth = mri_src->depth ;

  if (!mri_dst)
    mri_dst =
      MRIallocSequence(width, height, depth, mri_src->type, dst_frame+1) ;
  if (!mri_dst)
    ErrorExit(ERROR_NOMEMORY, "MRIcopyFrame: could not alloc dst") ;

  if (mri_src->type != mri_dst->type)
    ErrorReturn(NULL,(ERROR_UNSUPPORTED,
                      "MRIcopyFrame: src and dst must be same type"));
  if (dst_frame >= mri_dst->nframes)
    ErrorReturn
      (NULL,
       (ERROR_BADPARM,
        "MRIcopyFrame: dst frame #%d out of range (nframes=%d)\n",
        dst_frame, mri_dst->nframes)) ;
  MRIcopyHeader(mri_src, mri_dst) ;

  switch (mri_src->type)
    {
    case MRI_UCHAR:
      bytes = sizeof(unsigned char) ;
      break ;
    case MRI_SHORT:
      bytes = sizeof(short) ;
      break ;
    case MRI_INT:
      bytes = sizeof(int) ;
      break ;
    case MRI_FLOAT:
      bytes = sizeof(float) ;
      break ;
    default:
      ErrorReturn(NULL, (ERROR_BADPARM,
                         "MRIcopyFrame: unsupported src format %d",
                         mri_src->type));
      break ;
    }
  bytes *= width ;
  for (z = 0 ; z < depth ; z++)
    {
      for (y = 0 ; y < height ; y++)
        {
          switch (mri_src->type)
            {
            default:  /* already handled above */
            case MRI_UCHAR:
              psrc = &MRIseq_vox(mri_src, 0, y, z, src_frame) ;
              pdst = &MRIseq_vox(mri_dst, 0, y, z, dst_frame) ;
              break ;
            case MRI_SHORT:
              psrc = (BUFTYPE *)&MRISseq_vox(mri_src, 0, y, z, src_frame) ;
              pdst = (BUFTYPE *)&MRISseq_vox(mri_dst, 0, y, z, dst_frame) ;
              break ;
            case MRI_INT:
              psrc = (BUFTYPE *)&MRIIseq_vox(mri_src, 0, y, z, src_frame) ;
              pdst = (BUFTYPE *)&MRIIseq_vox(mri_dst, 0, y, z, dst_frame) ;
              break ;
            case MRI_FLOAT:
              psrc = (BUFTYPE *)&MRIFseq_vox(mri_src, 0, y, z, src_frame) ;
              pdst = (BUFTYPE *)&MRIFseq_vox(mri_dst, 0, y, z, dst_frame) ;
              break ;
            }
          memmove(pdst, psrc, bytes) ;
        }
    }
  return(mri_dst) ;
}
/*-----------------------------------------------------
  Parameters:

  Returns value:

  Description
  ------------------------------------------------------*/
double
MRImeanFrame(MRI *mri, int frame)
{
  int       width, height, depth, x, y, z ;
  double    mean ;
  Real      val ;

  width = mri->width ;
  height = mri->height ;
  depth = mri->depth ;
  if (mri->nframes <= frame)
    ErrorReturn(0.0,(ERROR_BADPARM,
                     "MRImeanFrame: frame %d out of bounds (%d)",
                     frame, mri->nframes));

  for (mean = 0.0, z = 0 ; z < depth ; z++)
    {
      for (y = 0 ; y < height ; y++)
        {
          for (x = 0 ; x < width ; x++)
            {
              MRIsampleVolumeType(mri, x, y, z, &val, SAMPLE_NEAREST) ;
              mean += (double)val ;
            }
        }
    }
  mean /= (double)(width*height*depth) ;
  return(mean) ;
}

#ifndef UCHAR_MIN
#define UCHAR_MIN  0.0
#endif
#ifndef UCHAR_MAX
#define UCHAR_MAX  255.0
#endif
#ifndef SHORT_MIN
#define SHORT_MIN  -32768.0
#endif
#ifndef SHORT_MAX
#define SHORT_MAX  32767.0
#endif
#ifndef INT_MIN
#define INT_MIN    -2147483648.0
#endif
#ifndef INT_MAX
#define INT_MAX    2147483647.0
#endif
#ifndef LONG_MIN
#define LONG_MIN   -2147483648.0
#endif
#ifndef LONG_MAX
#define LONG_MAX   2147483647.0
#endif

#define N_HIST_BINS 1000

/*--------------------------------------------------------------
  MRISeqchangeType() - changes the data type for a 3D or 4D volume.
  This simply changes the volume dimensions so that it appears to be a
  3D volume, then calls MRIchangeType(), and then resets the
  dimensions to their original values. The values of the volume can be
  rescaled between f_low and f_high.
  ------------------------------------------------------------*/
MRI *MRISeqchangeType(MRI *vol, int dest_type, float f_low,
                      float f_high, int no_scale_option_flag)
{
  int nslices, nframes;
  MRI *mri;

  /* Change vol dimensions to make it look like a single frame */
  nslices = vol->depth;
  nframes = vol->nframes;
  vol->depth = nslices*nframes;
  vol->nframes = 1;

  /* Change the type */
  mri = MRIchangeType(vol,dest_type,f_low,f_high,no_scale_option_flag);

  /* Change vol dimensions back to original */
  vol->depth = nslices;
  vol->nframes = nframes;

  /* Check for error */
  if(mri == NULL) {
    fprintf(stderr,"ERROR: MRISeqchangeType: MRIchangeType\n");
    return(NULL);
  }

  /* Change mri dimensions back to original */
  mri->depth = nslices;
  mri->nframes = nframes;

  return(mri);
}
/*-----------------------------------------------------------
  MRIchangeType() - changes the data type of a 3D MRI volume,
  with optional rescaling. Use MRISeqchangeType() for 3D or
  4D volumes.
  ---------------------------------------------------------*/
MRI *MRIchangeType(MRI *src, int dest_type, float f_low,
                   float f_high, int no_scale_option_flag)
{

  MRI *dest = NULL;
  int i, j, k;
  float val;
  int no_scale_flag = FALSE;
  float scale, dest_min, dest_max; /* new = scale * (val - min) */
  float src_min, src_max;
  int hist_bins[N_HIST_BINS];
  float bin_size;
  int bin;
  int nth, n_passed;

  /* ----- shut the compiler up ----- */
  val = 0.0;
  dest_min = dest_max = 0.0;

  if(src->type == dest_type)
    {
      dest = MRIcopy(src, NULL);
      return(dest);
    }

  if(src->type == MRI_UCHAR &&
     (dest_type == MRI_SHORT ||
      dest_type == MRI_INT ||
      dest_type == MRI_LONG ||
      dest_type == MRI_FLOAT))
    no_scale_flag = TRUE;
  else if(src->type == MRI_SHORT &&
          (dest_type == MRI_INT ||
           dest_type == MRI_LONG ||
           dest_type == MRI_FLOAT))
    no_scale_flag = TRUE;
  else if(src->type == MRI_LONG &&
          (dest_type == MRI_INT ||
           dest_type == MRI_FLOAT))
    no_scale_flag = TRUE;
  else if(src->type == MRI_INT &&
          (dest_type == MRI_LONG ||
           dest_type == MRI_FLOAT))
    no_scale_flag = TRUE;
  else
    {
      MRIlimits(src, &src_min, &src_max);

      if(no_scale_option_flag)
        {
          if(dest_type == MRI_UCHAR &&
             src_min >= UCHAR_MIN &&
             src_max <= UCHAR_MAX)
            no_scale_flag = TRUE;
          if(dest_type == MRI_SHORT &&
             src_min >= SHORT_MIN &&
             src_max <= SHORT_MAX)
            no_scale_flag = TRUE;
          if(dest_type == MRI_INT &&
             src_min >= INT_MIN &&
             src_max <= INT_MAX)
            no_scale_flag = TRUE;
          if(dest_type == MRI_LONG &&
             src_min >= LONG_MIN &&
             src_max <= LONG_MAX)
            no_scale_flag = TRUE;
        }
    }

  if(no_scale_flag)
    {

      dest = MRIalloc(src->width, src->height, src->depth, dest_type);
      MRIcopyHeader(src, dest);
      dest->type = dest_type;

      for(k = 0;k < src->depth;k++)
        for(j = 0;j < src->height;j++)
          for(i = 0;i < src->width;i++)
            {

              if(src->type == MRI_UCHAR)
                val = (float)MRIvox(src, i, j, k);
              if(src->type == MRI_SHORT)
                val = (float)MRISvox(src, i, j, k);
              if(src->type == MRI_INT)
                val = (float)MRIIvox(src, i, j, k);
              if(src->type == MRI_LONG)
                val = (float)MRILvox(src, i, j, k);
              if(src->type == MRI_FLOAT)
                val = (float)MRIFvox(src, i, j, k);

              if(dest_type == MRI_UCHAR)
                MRIvox(dest, i, j, k) = (unsigned char)val;
              if(dest_type == MRI_SHORT)
                MRISvox(dest, i, j, k) = (short)val;
              if(dest_type == MRI_INT)
                MRIIvox(dest, i, j, k) = (int)val;
              if(dest_type == MRI_LONG)
                MRILvox(dest, i, j, k) = (long)val;
              if(dest_type == MRI_FLOAT)
                MRIFvox(dest, i, j, k) = (float)val;
            }
    }
  else
    {
      long nonzero = 0 ;

      /* ----- build a histogram ----- */
      printf("MRIchangeType: Building histogram \n");
      bin_size = (src_max - src_min) / (float)N_HIST_BINS;

      for(i = 0;i < N_HIST_BINS;i++)
        hist_bins[i] = 0;

      for(i = 0;i < src->width;i++)
        for(j = 0;j < src->height;j++)
          for(k = 0;k < src->depth;k++)
            {

              if(src->type == MRI_UCHAR)
                val = (float)MRIvox(src, i, j, k);
              if(src->type == MRI_SHORT)
                val = (float)MRISvox(src, i, j, k);
              if(src->type == MRI_INT)
                val = (float)MRIIvox(src, i, j, k);
              if(src->type == MRI_LONG)
                val = (float)MRILvox(src, i, j, k);
              if(src->type == MRI_FLOAT)
                val = (float)MRIFvox(src, i, j, k);

              if (!DZERO(val))
                nonzero++ ;
              bin = (int)((val - src_min) / bin_size);

              if(bin < 0)
                bin = 0;
              if(bin >= N_HIST_BINS)
                bin = N_HIST_BINS-1;

              hist_bins[bin]++;

            }

      nth = (int)(f_low * src->width * src->height * src->depth);
      for(n_passed = 0,bin = 0;n_passed < nth && bin < N_HIST_BINS;bin++)
        n_passed += hist_bins[bin];
      src_min = (float)bin * bin_size + src_min;

#if 1  // handle mostly empty volumes
      nth = (int)((1.0-f_high) * nonzero);
#else
      nth = (int)((1.0-f_high) * src->width * src->height * src->depth);
#endif
      for(n_passed = 0,bin = N_HIST_BINS-1;n_passed < nth && bin > 0;bin--)
        n_passed += hist_bins[bin];
      src_max = (float)bin * bin_size + src_min;

      if(src_min >= src_max)
        {
          ErrorReturn
            (NULL,
             (ERROR_BADPARM,
              "MRIchangeType(): after hist: src_min = %g, "
              "src_max = %g (f_low = %g, f_high = %g)",
              src_min, src_max, f_low, f_high));
        }

      /* ----- continue ----- */

      if(dest_type == MRI_UCHAR)
        {
          dest_min = UCHAR_MIN;
          dest_max = UCHAR_MAX;
        }
      if(dest_type == MRI_SHORT)
        {
          dest_min = SHORT_MIN;
          dest_max = SHORT_MAX;
        }
      if(dest_type == MRI_INT)
        {
          dest_min = INT_MIN;
          dest_max = INT_MAX;
        }
      if(dest_type == MRI_LONG)
        {
          dest_min = LONG_MIN;
          dest_max = LONG_MAX;
        }

      scale = (dest_max - dest_min) / (src_max - src_min);

      dest = MRIalloc(src->width, src->height, src->depth, dest_type);
      MRIcopyHeader(src, dest);
      dest->type = dest_type;

      for(i = 0;i < src->width;i++)
        for(j = 0;j < src->height;j++)
          for(k = 0;k < src->depth;k++)
            {

              if(src->type == MRI_SHORT)
                val = MRISvox(src, i, j, k);
              if(src->type == MRI_INT)
                val = MRIIvox(src, i, j, k);
              if(src->type == MRI_LONG)
                val = MRILvox(src, i, j, k);
              if(src->type == MRI_FLOAT)
                val = MRIFvox(src, i, j, k);

              val = dest_min + scale * (val - src_min);

              if(dest->type == MRI_UCHAR)
                {
                  if(val < UCHAR_MIN)
                    val = UCHAR_MIN;
                  if(val > UCHAR_MAX)
                    val = UCHAR_MAX;
                  MRIvox(dest, i, j, k) = (unsigned char)val;
                }
              if(dest->type == MRI_SHORT)
                {
                  if(val < SHORT_MIN)
                    val = SHORT_MIN;
                  if(val > SHORT_MAX)
                    val = SHORT_MAX;
                  MRISvox(dest, i, j, k) = (short)val;
                }
              if(dest->type == MRI_INT)
                {
                  if(val < INT_MIN)
                    val = INT_MIN;
                  if(val > INT_MAX)
                    val = INT_MAX;
                  MRIIvox(dest, i, j, k) = (int)val;
                }
              if(dest->type == MRI_LONG)
                {
                  if(val < LONG_MIN)
                    val = LONG_MIN;
                  if(val > LONG_MAX)
                    val = LONG_MAX;
                  MRILvox(dest, i, j, k) = (long)val;
                }

            }

    }

  return(dest);

} /* end MRIchangeType() */

/*-----------------------------------------------------*/
MATRIX *MRIgetResampleMatrix(MRI *src, MRI *template_vol)
{

  MATRIX *src_mat, *dest_mat; /* from i to ras */
  float src_det, dest_det;
  MATRIX *src_inv, *m;

  /* ----- fake the ras values if ras_good_flag is not set ----- */
  int src_slice_direction = getSliceDirection(src);
  if(!src->ras_good_flag
     && (src_slice_direction != MRI_CORONAL)
     && (src_slice_direction != MRI_SAGITTAL)
     && (src_slice_direction != MRI_HORIZONTAL))
    {
      ErrorReturn
        (NULL,
         (ERROR_BADPARM,
          "MRIresample(): source volume orientation is unknown"));
    }

  /*

  : solve each row of src_mat * [midx;midy;midz;1] = centerr, centera, centers
  and for dest

  S = M * s_v

  where M = [(x_r y_r z_r)(xsize  0     0  )  s14]
      s_v = (center_x)   S = (c_r)   etc.
  [(x_a y_a z_a)(  0  ysize   0  )  s24]        (center_y)       (c_a)
  [(x_s y_s z_s)(  0    0   zsize)  s34]        (center_z)       (c_s)
  [       0        0       0         1 ]        (    1   )       ( 1 )

  Write M = [  m    s],  s_v = (c_v), S = (c_R), then  c_R = m * c_v + s or
  [ 0 0 0 1]         ( 1 )      ( 1 )

  The translation s is given by   s = c_R - m*c_v

  Note the convention c_(r,a,s) being defined in
  terms of c_v = (width/2., height/2, depth/2.),
  not ((width-1)/2, (height-1)/2, (depth-1)/2).

  */

  src_mat = extract_i_to_r(src); // error when allocation fails
  if(src_mat == NULL)
    return NULL; // did ErrorPrintf in extract_i_to_r()

  dest_mat = extract_i_to_r(template_vol); // error when allocation fails
  if(dest_mat == NULL)
    {
      MatrixFree(&src_mat);
      return NULL; // did ErrorPrintf in extract_i_to_r()
    }

  // error check
  src_det = MatrixDeterminant(src_mat);
  dest_det = MatrixDeterminant(dest_mat);

  if(src_det == 0.0)
    {
      errno = 0;
      ErrorPrintf
        (ERROR_BADPARM,
         "MRIresample(): source matrix has zero determinant; matrix is:");
      MatrixPrint(stderr, src_mat);
      MatrixFree(&src_mat);
      MatrixFree(&dest_mat);
      return(NULL);
    }

  if(dest_det == 0.0)
    {
      errno = 0;
      ErrorPrintf
        (ERROR_BADPARM,
         "MRIresample(): destination matrix has zero determinant; matrix is:");
      MatrixPrint(stderr, dest_mat);
      MatrixFree(&src_mat);
      MatrixFree(&dest_mat);
      return(NULL);
    }

  src_inv = MatrixInverse(src_mat, NULL);

  if(src_inv == NULL)
    {
      errno = 0;
      ErrorPrintf
        (ERROR_BADPARM,
         "MRIresample(): error inverting matrix; determinant is "
         "%g, matrix is:", src_det);
      MatrixPrint(stderr, src_mat);
      MatrixFree(&src_mat);
      MatrixFree(&dest_mat);
      return(NULL);
    }

  m = MatrixMultiply(src_inv, dest_mat, NULL);
  if(m == NULL)
    return(NULL);

  MatrixFree(&src_inv);
  MatrixFree(&src_mat);
  MatrixFree(&dest_mat);

  if (Gdiag & DIAG_SHOW && DIAG_VERBOSE_ON)
    {
      printf("MRIresample() matrix is:\n");
      MatrixPrint(stdout, m);
    }
  return(m) ;

} /* end MRIreslice() */


MRI *
MRIresample(MRI *src, MRI *template_vol, int resample_type)
{
  return(MRIresampleFill(src, template_vol, resample_type, 0)) ;
} /* end MRIresample() */


MRI *MRIresampleFill
(MRI *src, MRI *template_vol, int resample_type, float fill_val)
{

  MRI *dest = NULL;
  MATRIX *m;
  int di, dj, dk;
  int si, sj, sk;
  float si_f, sj_f, sk_f;
  float si_ff, sj_ff, sk_ff;
  MATRIX *sp, *dp;
  float val, val000, val001, val010, val011, val100, val101, val110, val111;
  float w000, w001, w010, w011, w100, w101, w110, w111;
  float si_f2, sj_f2, sk_f2;
  float ii2, ij2, ik2, isi_f2, isj_f2, isk_f2;
  float w[8];
  int wi[8];
  int mwi;
  Real pval;
  int i_good_flag, i1_good_flag;
  int j_good_flag, j1_good_flag;
  int k_good_flag, k1_good_flag;

  /* ----- keep the compiler quiet ----- */
  val = 0.0;
  val000 = val001 = val010 = val011 = val100 = val101 = val110 = val111 = 0.0;

#if 0
  if(src->type != template_vol->type)
    {
      ErrorReturn
        (NULL,
         (ERROR_UNSUPPORTED,
          "MRIresample(): source and destination types must be identical"));
    }
#endif

  /* ----- fake the ras values if ras_good_flag is not set ----- */
  if(!src->ras_good_flag)
    printf
      ("MRIresample(): WARNING: ras_good_flag "
       "is not set, changing orientation\n"
       "to default.\n");

  // get dst voxel -> src voxel transform
  m = MRIgetResampleMatrix(src, template_vol);
  if(m == NULL)
    return(NULL);

  if (Gdiag & DIAG_SHOW && DIAG_VERBOSE_ON)
    {
      printf("MRIresample() matrix is:\n");
      MatrixPrint(stdout, m);
    }

  dest = MRIalloc(template_vol->width,
                  template_vol->height,
                  template_vol->depth,
                  src->type);
  if(dest == NULL)
    return(NULL);
  MRIreplaceValues(dest, dest, 0.0f, fill_val) ;

  MRIcopyHeader(template_vol, dest);
  MRIcopyPulseParameters(src, dest) ;

  sp = MatrixAlloc(4, 1, MATRIX_REAL);
  dp = MatrixAlloc(4, 1, MATRIX_REAL);

  *MATRIX_RELT(dp, 4, 1) = 1.0;

  for(di = 0;di < template_vol->width;di++)
    {
      for(dj = 0;dj < template_vol->height;dj++)
        {
          for(dk = 0;dk < template_vol->depth;dk++)
            {

              *MATRIX_RELT(dp, 1, 1) = (float)di;
              *MATRIX_RELT(dp, 2, 1) = (float)dj;
              *MATRIX_RELT(dp, 3, 1) = (float)dk;

              MatrixMultiply(m, dp, sp);

              si_ff = *MATRIX_RELT(sp, 1, 1);
              sj_ff = *MATRIX_RELT(sp, 2, 1);
              sk_ff = *MATRIX_RELT(sp, 3, 1);

              si = (int)floor(si_ff);
              sj = (int)floor(sj_ff);
              sk = (int)floor(sk_ff);

              if (si == 147 && sj == 91 && sk == 86)
                DiagBreak() ;
              if (di == 129 && dj == 164 && dk == 147)
                DiagBreak() ;
              si_f = si_ff - si;
              sj_f = sj_ff - sj;
              sk_f = sk_ff - sk;

#if 0
              if(di % 20 == 0 && dj == di && dk == dj)
                {
                  printf("MRIresample() sample point: %3d %3d %3d: "
                         "%f (%3d + %f) %f (%3d + %f) %f (%3d + %f)\n",
                         di, dj, dk,
                         si_ff, si, si_f,
                         sj_ff, sj, sj_f,
                         sk_ff, sk, sk_f);
                }
#endif

              if(resample_type == SAMPLE_SINC)
                {
                  MRIsincSampleVolume(src, si_ff, sj_ff, sk_ff, 5, &pval);
                  val = (float)pval;
                }
              else if(resample_type == SAMPLE_CUBIC)
                {
                  MRIcubicSampleVolume(src, si_ff, sj_ff, sk_ff, &pval);
                  val = (float)pval;
                }
              else
                {
                  i_good_flag = (si >= 0 && si < src->width);
                  i1_good_flag = (si+1 >= 0 && si+1 < src->width);
                  j_good_flag = (sj >= 0 && sj < src->height);
                  j1_good_flag = (sj+1 >= 0 && sj+1 < src->height);
                  k_good_flag = (sk >= 0 && sk < src->depth);
                  k1_good_flag = (sk+1 >= 0 && sk+1 < src->depth);

                  if(src->type == MRI_UCHAR)
                    {
                      val000 = ( !i_good_flag ||
                                 !j_good_flag ||
                                 !k_good_flag ? 0.0 :
                                 (float)MRIvox(src, si    , sj    , sk    ));
                      val001 = ( !i_good_flag ||
                                 !j_good_flag ||
                                 !k1_good_flag ? 0.0 :
                                 (float)MRIvox(src, si    , sj    , sk + 1));
                      val010 = ( !i_good_flag ||
                                 !j1_good_flag ||
                                 !k_good_flag ? 0.0 :
                                 (float)MRIvox(src, si    , sj + 1, sk    ));
                      val011 = ( !i_good_flag ||
                                 !j1_good_flag ||
                                 !k1_good_flag ? 0.0 :
                                 (float)MRIvox(src, si    , sj + 1, sk + 1));
                      val100 = (!i1_good_flag ||
                                !j_good_flag ||
                                !k_good_flag ? 0.0 :
                                (float)MRIvox(src, si + 1, sj    , sk    ));
                      val101 = (!i1_good_flag ||
                                !j_good_flag ||
                                !k1_good_flag ? 0.0 :
                                (float)MRIvox(src, si + 1, sj    , sk + 1));
                      val110 = (!i1_good_flag ||
                                !j1_good_flag ||
                                !k_good_flag ? 0.0 :
                                (float)MRIvox(src, si + 1, sj + 1, sk    ));
                      val111 = (!i1_good_flag ||
                                !j1_good_flag ||
                                !k1_good_flag ? 0.0 :
                                (float)MRIvox(src, si + 1, sj + 1, sk + 1));
                    }

                  if (si == 154 && sj == 134 && sk == 136)
                    DiagBreak() ;

                  if(src->type == MRI_SHORT)
                    {
                      val000 = ( !i_good_flag ||
                                 !j_good_flag ||
                                 !k_good_flag ? 0.0 :
                                 (float)MRISvox(src, si    , sj    , sk    ));
                      val001 = ( !i_good_flag ||
                                 !j_good_flag ||
                                 !k1_good_flag ? 0.0 :
                                 (float)MRISvox(src, si    , sj    , sk + 1));
                      val010 = ( !i_good_flag ||
                                 !j1_good_flag ||
                                 !k_good_flag ? 0.0 :
                                 (float)MRISvox(src, si    , sj + 1, sk    ));
                      val011 = ( !i_good_flag ||
                                 !j1_good_flag ||
                                 !k1_good_flag ? 0.0 :
                                 (float)MRISvox(src, si    , sj + 1, sk + 1));
                      val100 = (!i1_good_flag ||
                                !j_good_flag ||
                                !k_good_flag ? 0.0 :
                                (float)MRISvox(src, si + 1, sj    , sk    ));
                      val101 = (!i1_good_flag ||
                                !j_good_flag ||
                                !k1_good_flag ? 0.0 :
                                (float)MRISvox(src, si + 1, sj    , sk + 1));
                      val110 = (!i1_good_flag ||
                                !j1_good_flag ||
                                !k_good_flag ? 0.0 :
                                (float)MRISvox(src, si + 1, sj + 1, sk    ));
                      val111 = (!i1_good_flag ||
                                !j1_good_flag ||
                                !k1_good_flag ? 0.0 :
                                (float)MRISvox(src, si + 1, sj + 1, sk + 1));
                    }

                  if(src->type == MRI_INT)
                    {
                      val000 = ( !i_good_flag ||
                                 !j_good_flag ||
                                 !k_good_flag ? 0.0 :
                                 (float)MRIIvox(src, si    , sj    , sk    ));
                      val001 = ( !i_good_flag ||
                                 !j_good_flag ||
                                 !k1_good_flag ? 0.0 :
                                 (float)MRIIvox(src, si    , sj    , sk + 1));
                      val010 = ( !i_good_flag ||
                                 !j1_good_flag ||
                                 !k_good_flag ? 0.0 :
                                 (float)MRIIvox(src, si    , sj + 1, sk    ));
                      val011 = ( !i_good_flag ||
                                 !j1_good_flag ||
                                 !k1_good_flag ? 0.0 :
                                 (float)MRIIvox(src, si    , sj + 1, sk + 1));
                      val100 = (!i1_good_flag ||
                                !j_good_flag ||
                                !k_good_flag ? 0.0 :
                                (float)MRIIvox(src, si + 1, sj    , sk    ));
                      val101 = (!i1_good_flag ||
                                !j_good_flag ||
                                !k1_good_flag ? 0.0 :
                                (float)MRIIvox(src, si + 1, sj    , sk + 1));
                      val110 = (!i1_good_flag ||
                                !j1_good_flag ||
                                !k_good_flag ? 0.0 :
                                (float)MRIIvox(src, si + 1, sj + 1, sk    ));
                      val111 = (!i1_good_flag ||
                                !j1_good_flag ||
                                !k1_good_flag ? 0.0 :
                                (float)MRIIvox(src, si + 1, sj + 1, sk + 1));
                    }

                  if(src->type == MRI_LONG)
                    {
                      val000 = ( !i_good_flag ||
                                 !j_good_flag ||
                                 !k_good_flag ? 0.0 :
                                 (float)MRILvox(src, si    , sj    , sk    ));
                      val001 = ( !i_good_flag ||
                                 !j_good_flag ||
                                 !k1_good_flag ? 0.0 :
                                 (float)MRILvox(src, si    , sj    , sk + 1));
                      val010 = ( !i_good_flag ||
                                 !j1_good_flag ||
                                 !k_good_flag ? 0.0 :
                                 (float)MRILvox(src, si    , sj + 1, sk    ));
                      val011 = ( !i_good_flag ||
                                 !j1_good_flag ||
                                 !k1_good_flag ? 0.0 :
                                 (float)MRILvox(src, si    , sj + 1, sk + 1));
                      val100 = (!i1_good_flag ||
                                !j_good_flag ||
                                !k_good_flag ? 0.0 :
                                (float)MRILvox(src, si + 1, sj    , sk    ));
                      val101 = (!i1_good_flag ||
                                !j_good_flag ||
                                !k1_good_flag ? 0.0 :
                                (float)MRILvox(src, si + 1, sj    , sk + 1));
                      val110 = (!i1_good_flag ||
                                !j1_good_flag ||
                                !k_good_flag ? 0.0 :
                                (float)MRILvox(src, si + 1, sj + 1, sk    ));
                      val111 = (!i1_good_flag ||
                                !j1_good_flag ||
                                !k1_good_flag ? 0.0 :
                                (float)MRILvox(src, si + 1, sj + 1, sk + 1));
                    }

                  if(src->type == MRI_FLOAT)
                    {
                      val000 = ( !i_good_flag ||
                                 !j_good_flag ||
                                 !k_good_flag ? 0.0 :
                                 (float)MRIFvox(src, si    , sj    , sk    ));
                      val001 = ( !i_good_flag ||
                                 !j_good_flag ||
                                 !k1_good_flag ? 0.0 :
                                 (float)MRIFvox(src, si    , sj    , sk + 1));
                      val010 = ( !i_good_flag ||
                                 !j1_good_flag ||
                                 !k_good_flag ? 0.0 :
                                 (float)MRIFvox(src, si    , sj + 1, sk    ));
                      val011 = ( !i_good_flag ||
                                 !j1_good_flag ||
                                 !k1_good_flag ? 0.0 :
                                 (float)MRIFvox(src, si    , sj + 1, sk + 1));
                      val100 = (!i1_good_flag ||
                                !j_good_flag ||
                                !k_good_flag ? 0.0 :
                                (float)MRIFvox(src, si + 1, sj    , sk    ));
                      val101 = (!i1_good_flag ||
                                !j_good_flag ||
                                !k1_good_flag ? 0.0 :
                                (float)MRIFvox(src, si + 1, sj    , sk + 1));
                      val110 = (!i1_good_flag ||
                                !j1_good_flag ||
                                !k_good_flag ? 0.0 :
                                (float)MRIFvox(src, si + 1, sj + 1, sk    ));
                      val111 = (!i1_good_flag ||
                                !j1_good_flag ||
                                !k1_good_flag ? 0.0 :
                                (float)MRIFvox(src, si + 1, sj + 1, sk + 1));
                    }

                  if(resample_type == SAMPLE_TRILINEAR)
                    {
                      val = (1.0-si_f) * (1.0-sj_f) * (1.0-sk_f) * val000 +
                        (1.0-si_f) * (1.0-sj_f) * (    sk_f) * val001 +
                        (1.0-si_f) * (    sj_f) * (1.0-sk_f) * val010 +
                        (1.0-si_f) * (    sj_f) * (    sk_f) * val011 +
                        (    si_f) * (1.0-sj_f) * (1.0-sk_f) * val100 +
                        (    si_f) * (1.0-sj_f) * (    sk_f) * val101 +
                        (    si_f) * (    sj_f) * (1.0-sk_f) * val110 +
                        (    si_f) * (    sj_f) * (    sk_f) * val111;
                    }

                  if(resample_type == SAMPLE_NEAREST)
                    {
                      if(si_f < 0.5)
                        {
                          if(sj_f < 0.5)
                            {
                              if(sk_f < 0.5)
                                val = val000;
                              else
                                val = val001;
                            }
                          else
                            {
                              if(sk_f < 0.5)
                                val = val010;
                              else
                                val = val011;
                            }
                        }
                      else
                        {
                          if(sj_f < 0.5)
                            {
                              if(sk_f < 0.5)
                                val = val100;
                              else
                                val = val101;
                            }
                          else
                            {
                              if(sk_f < 0.5)
                                val = val110;
                              else
                                val = val111;
                            }
                        }
                    }

                  if(resample_type == SAMPLE_WEIGHTED)
                    {
                      /* unfinished */
                      si_f2 = si_f * si_f;
                      sj_f2 = sj_f * sj_f;
                      sk_f2 = sk_f * sk_f;

                      ii2 = 1. / (1 - 2*si_f + si_f2);
                      ij2 = 1. / (1 - 2*sj_f + sj_f2);
                      ik2 = 1. / (1 - 2*sk_f + sk_f2);

                      isi_f2 = 1 / si_f2;
                      isj_f2 = 1 / sj_f2;
                      isk_f2 = 1 / sk_f2;

                      w000 =   ii2    + ij2    + ik2;
                      w001 =   ii2    + ij2    + isk_f2;
                      w010 =   ii2    + isj_f2 + ik2;
                      w011 =   ii2    + isj_f2 + isk_f2;
                      w100 =   isi_f2 + ij2    + ik2;
                      w101 =   isi_f2 + ij2    + isk_f2;
                      w110 =   isi_f2 + isj_f2 + ik2;
                      w111 =   isi_f2 + isj_f2 + isk_f2;

                      w[0] = w[1] = w[2] = w[3] =
                        w[4] = w[5] = w[6] = w[7] = 0.0;

                      wi[0] = 0;
                      wi[1] = 1;
                      wi[2] = 2;
                      wi[3] = 3;
                      wi[4] = 4;
                      wi[5] = 5;
                      wi[6] = 6;
                      wi[7] = 7;

                      if(val001 == val000)
                        wi[1] = 0;

                      if(val010 == val001)
                        wi[2] = 1;
                      if(val010 == val000)
                        wi[2] = 0;

                      if(val011 == val010)
                        wi[3] = 2;
                      if(val011 == val001)
                        wi[3] = 1;
                      if(val011 == val000)
                        wi[3] = 0;

                      if(val100 == val011)
                        wi[4] = 3;
                      if(val100 == val010)
                        wi[4] = 2;
                      if(val100 == val001)
                        wi[4] = 1;
                      if(val100 == val000)
                        wi[4] = 0;

                      if(val101 == val100)
                        wi[5] = 4;
                      if(val101 == val011)
                        wi[5] = 3;
                      if(val101 == val010)
                        wi[5] = 2;
                      if(val101 == val001)
                        wi[5] = 1;
                      if(val101 == val000)
                        wi[5] = 0;

                      if(val110 == val101)
                        wi[6] = 5;
                      if(val110 == val100)
                        wi[6] = 4;
                      if(val110 == val011)
                        wi[6] = 3;
                      if(val110 == val010)
                        wi[6] = 2;
                      if(val110 == val001)
                        wi[6] = 1;
                      if(val110 == val000)
                        wi[6] = 0;

                      if(val111 == val110)
                        wi[7] = 6;
                      if(val111 == val101)
                        wi[7] = 5;
                      if(val111 == val100)
                        wi[7] = 4;
                      if(val111 == val011)
                        wi[7] = 3;
                      if(val111 == val010)
                        wi[7] = 2;
                      if(val111 == val001)
                        wi[7] = 1;
                      if(val111 == val000)
                        wi[7] = 0;

                      w[wi[0]] += w000;
                      w[wi[1]] += w001;
                      w[wi[2]] += w010;
                      w[wi[3]] += w011;
                      w[wi[4]] += w100;
                      w[wi[5]] += w101;
                      w[wi[6]] += w110;
                      w[wi[7]] += w111;

                      mwi = 0;

                      if(w[1] > w[mwi])
                        mwi = 1;
                      if(w[2] > w[mwi])
                        mwi = 2;
                      if(w[3] > w[mwi])
                        mwi = 3;
                      if(w[4] > w[mwi])
                        mwi = 4;
                      if(w[5] > w[mwi])
                        mwi = 5;
                      if(w[6] > w[mwi])
                        mwi = 6;
                      if(w[7] > w[mwi])
                        mwi = 7;

                      if(mwi == 0)
                        val = val000;
                      if(mwi == 1)
                        val = val001;
                      if(mwi == 2)
                        val = val010;
                      if(mwi == 3)
                        val = val011;
                      if(mwi == 4)
                        val = val100;
                      if(mwi == 5)
                        val = val101;
                      if(mwi == 6)
                        val = val110;
                      if(mwi == 7)
                        val = val111;

                    }

                }

              if(dest->type == MRI_UCHAR)
                MRIvox(dest, di, dj, dk) = (unsigned char)val;
              if(dest->type == MRI_SHORT)
                MRISvox(dest, di, dj, dk) = (short)val;
              if(dest->type == MRI_INT)
                MRIIvox(dest, di, dj, dk) = (int)val;
              if(dest->type == MRI_LONG)
                MRILvox(dest, di, dj, dk) = (long)val;
              if(dest->type == MRI_FLOAT)
                MRIFvox(dest, di, dj, dk) = (float)val;

            }
        }
    }

  {
    MATRIX  *m_old_voxel_to_ras, *m_voxel_to_ras,
      *m_old_ras_to_voxel, *v_ras, *v_vox, *m_new_ras_to_voxel, *v_vox2 ;

    m_old_voxel_to_ras = MRIgetVoxelToRasXform(src) ;

    m_voxel_to_ras = MatrixMultiply(m_old_voxel_to_ras, m, NULL) ;

    dest->x_r = *MATRIX_RELT(m_voxel_to_ras,1,1)/dest->xsize;
    dest->x_a = *MATRIX_RELT(m_voxel_to_ras,2,1)/dest->xsize;
    dest->x_s = *MATRIX_RELT(m_voxel_to_ras,3,1)/dest->xsize;

    dest->y_r = *MATRIX_RELT(m_voxel_to_ras,1,2)/dest->ysize;
    dest->y_a = *MATRIX_RELT(m_voxel_to_ras,2,2)/dest->ysize;
    dest->y_s = *MATRIX_RELT(m_voxel_to_ras,3,2)/dest->ysize;

    dest->z_r = *MATRIX_RELT(m_voxel_to_ras,1,3)/dest->zsize;
    dest->z_a = *MATRIX_RELT(m_voxel_to_ras,2,3)/dest->zsize;
    dest->z_s = *MATRIX_RELT(m_voxel_to_ras,3,3)/dest->zsize;

    /* compute the RAS coordinates of the center of the dest. image
       and put them in c_r, c_a, and c_s.

       C = M * c_v
    */
    v_vox = VectorAlloc(4, MATRIX_REAL) ;

    /* voxel coords of center of dest image */
    VECTOR_ELT(v_vox,4) = 1.0 ;
    VECTOR_ELT(v_vox,1) = (dest->width)/2.0 ;
    VECTOR_ELT(v_vox,2) = (dest->height)/2.0 ;
    VECTOR_ELT(v_vox,3) = (dest->depth)/2.0 ;

    v_vox2 =
      MatrixMultiply(m, v_vox, NULL) ; /* voxel coords in source image */
    v_ras =
      MatrixMultiply(m_old_voxel_to_ras, v_vox2, NULL) ; /* ras cntr of dest */

    dest->c_r = VECTOR_ELT(v_ras, 1);
    dest->c_a = VECTOR_ELT(v_ras, 2);
    dest->c_s = VECTOR_ELT(v_ras, 3);
    dest->ras_good_flag = 1 ;

    if (Gdiag & DIAG_SHOW && DIAG_VERBOSE_ON)
      {
        m_new_ras_to_voxel = MRIgetRasToVoxelXform(dest) ;
        m_old_ras_to_voxel = MRIgetRasToVoxelXform(src) ;
        V3_X(v_ras) = V3_Y(v_ras) = V3_Z(v_ras) = 0.0 ;
        MatrixMultiply(m_old_ras_to_voxel, v_ras, v_vox) ;
        printf("old RAS (0,0,0) -> (%2.0f, %2.0f, %2.0f)\n",
               VECTOR_ELT(v_vox,1), VECTOR_ELT(v_vox,2), VECTOR_ELT(v_vox,3)) ;
        MatrixMultiply(m_new_ras_to_voxel, v_ras, v_vox) ;
        printf("new RAS (0,0,0) -> (%2.0f, %2.0f, %2.0f)\n",
               VECTOR_ELT(v_vox,1), VECTOR_ELT(v_vox,2), VECTOR_ELT(v_vox,3)) ;
        MatrixFree(&m_new_ras_to_voxel) ; MatrixFree(&m_old_ras_to_voxel) ;
      }

    MatrixFree(&v_vox) ; MatrixFree(&v_vox2) ; MatrixFree(&v_ras) ;
    MatrixFree(&m_voxel_to_ras) ; MatrixFree(&m_old_voxel_to_ras) ;
  }


  MatrixFree(&dp);
  MatrixFree(&sp);
  MatrixFree(&m);

  return(dest);

} /* end MRIresample() */

int MRIlimits(MRI *mri, float *min, float *max)
{

  float val;
  int i, j, k;

  if(mri == NULL)
    return(NO_ERROR);

  if(mri->slices == NULL)
    return(NO_ERROR);

  if(mri->type == MRI_UCHAR)
    {
      *min = *max = (float)MRIvox(mri, 0, 0, 0);
      for(i = 0;i < mri->width;i++)
        for(j = 0;j < mri->height;j++)
          for(k = 0;k < mri->depth;k++)
            {
              val = (float)MRIvox(mri, i, j, k);
              if(val < *min)
                *min = val;
              if(val > *max)
                *max = val;
            }

    }

  if(mri->type == MRI_SHORT)
    {

      *min = *max = (float)MRISvox(mri, 0, 0, 0);

      for(i = 0;i < mri->width;i++)
        for(j = 0;j < mri->height;j++)
          for(k = 0;k < mri->depth;k++)
            {
              val = (float)MRISvox(mri, i, j, k);
              if(val < *min)
                *min = val;
              if(val > *max)
                *max = val;
            }

    }

  if(mri->type == MRI_INT)
    {

      *min = *max = (float)MRILvox(mri, 0, 0, 0);

      for(i = 0;i < mri->width;i++)
        for(j = 0;j < mri->height;j++)
          for(k = 0;k < mri->depth;k++)
            {
              val = (float)MRILvox(mri, i, j, k);
              if(val < *min)
                *min = val;
              if(val > *max)
                *max = val;
            }

    }

  if(mri->type == MRI_LONG)
    {

      *min = *max = (float)MRILvox(mri, 0, 0, 0);

      for(i = 0;i < mri->width;i++)
        for(j = 0;j < mri->height;j++)
          for(k = 0;k < mri->depth;k++)
            {
              val = (float)MRILvox(mri, i, j, k);
              if(val < *min)
                *min = val;
              if(val > *max)
                *max = val;
            }

    }

  if(mri->type == MRI_FLOAT)
    {

      *min = *max = (float)MRIFvox(mri, 0, 0, 0);

      for(i = 0;i < mri->width;i++)
        for(j = 0;j < mri->height;j++)
          for(k = 0;k < mri->depth;k++)
            {
              val = (float)MRIFvox(mri, i, j, k);
              if(val < *min)
                *min = val;
              if(val > *max)
                *max = val;
            }

    }

  return(NO_ERROR);

} /* end MRIlimits() */

int MRIprintStats(MRI *mri, FILE *stream)
{

  float min, max, mean, std;
  int n;
  double com[3];

  MRIstats(mri, &min, &max, &n, &mean, &std);

  fprintf(stream, "%d values\n", n);
  fprintf(stream, "min = %g\n", min);
  fprintf(stream, "max = %g\n", max);
  fprintf(stream, "mean = %g\n", mean);
  fprintf(stream, "std = %g\n", std);

  MRIcenterOfMass(mri, com, 0);

  fprintf(stream, "com = %g %g %g\n", com[0], com[1], com[2]);

  return(NO_ERROR);

} /* end MRIprintStats() */

int MRIstats
(MRI *mri, float *min, float *max, int *n_voxels, float *mean, float *std)
{

  float val;
  float sum, sq_sum;
  int i, j, k, t;
  float n;

  if(mri == NULL)
    return(NO_ERROR);

  if(mri->slices == NULL)
    return(NO_ERROR);

  sum = sq_sum = 0.0;
  *n_voxels = mri->width * mri->height * mri->depth * mri->nframes;
  n = (float)(*n_voxels);

  if(mri->type == MRI_UCHAR)
    {

      *max = *min = MRIseq_vox(mri, 0, 0, 0, 0);

      for(i = 0;i < mri->width;i++)
        for(j = 0;j < mri->height;j++)
          for(k = 0;k < mri->depth;k++)
            for(t = 0;t < mri->nframes;t++)
              {

                val = (float)MRIseq_vox(mri, i, j, k, t);

                if(val < *min)
                  *min = val;
                if(val > *max)
                  *max = val;

                sum += val;
                sq_sum += val * val;

              }

    }

  if(mri->type == MRI_SHORT)
    {

      *min = *max = (float)MRISseq_vox(mri, 0, 0, 0, 0);

      for(i = 0;i < mri->width;i++)
        for(j = 0;j < mri->height;j++)
          for(k = 0;k < mri->depth;k++)
            for(t = 0;t < mri->nframes;t++)
              {

                val = (float)MRISseq_vox(mri, i, j, k, t);

                if(val < *min)
                  *min = val;
                if(val > *max)
                  *max = val;

                sum += val;
                sq_sum += val * val;

              }

    }

  if(mri->type == MRI_INT)
    {

      *min = *max = (float)MRILseq_vox(mri, 0, 0, 0, 0);

      for(i = 0;i < mri->width;i++)
        for(j = 0;j < mri->height;j++)
          for(k = 0;k < mri->depth;k++)
            for(t = 0;t < mri->nframes;t++)
              {

                val = (float)MRILseq_vox(mri, i, j, k, t);

                if(val < *min)
                  *min = val;
                if(val > *max)
                  *max = val;

                sum += val;
                sq_sum += val * val;

              }

    }

  if(mri->type == MRI_LONG)
    {

      *min = *max = (float)MRILseq_vox(mri, 0, 0, 0, 0);

      for(i = 0;i < mri->width;i++)
        for(j = 0;j < mri->height;j++)
          for(k = 0;k < mri->depth;k++)
            for(t = 0;t < mri->nframes;t++)
              {

                val = (float)MRILseq_vox(mri, i, j, k, t);

                if(val < *min)
                  *min = val;
                if(val > *max)
                  *max = val;

                sum += val;
                sq_sum += val * val;

              }

    }

  if(mri->type == MRI_FLOAT)
    {

      *min = *max = (float)MRIFseq_vox(mri, 0, 0, 0, 0);

      for(i = 0;i < mri->width;i++)
        for(j = 0;j < mri->height;j++)
          for(k = 0;k < mri->depth;k++)
            for(t = 0;t < mri->nframes;t++)
              {

                val = (float)MRIFseq_vox(mri, i, j, k, t);

                if(val < *min)
                  *min = val;
                if(val > *max)
                  *max = val;

                sum += val;
                sq_sum += val * val;

              }

    }

  *mean = sum / n;
  *std = (sq_sum - n * (*mean) * (*mean)) / (n - 1.0);

  *std = sqrt(*std);

  return(NO_ERROR);

} /* end MRIstats() */

float MRIvolumeDeterminant(MRI *mri)
{

  MATRIX *m;
  float det;

  m = MatrixAlloc(4, 4, MATRIX_REAL);
  if(m == NULL)
    return(0.0);

  stuff_four_by_four(m, mri->x_r, mri->y_r, mri->z_r, 0.0,
                     mri->x_a, mri->y_a, mri->z_a, 0.0,
                     mri->x_s, mri->y_s, mri->z_s, 0.0,
                     0.0,      0.0,      0.0, 1.0);

  det = MatrixDeterminant(m);

  MatrixFree(&m);

  return(det);

} /* end MRIvolumeDeterminant() */

int stuff_four_by_four(MATRIX *m, float m11, float m12, float m13, float m14,
                       float m21, float m22, float m23, float m24,
                       float m31, float m32, float m33, float m34,
                       float m41, float m42, float m43, float m44)
{

  if(m == NULL)
    {
      ErrorReturn
        (ERROR_BADPARM,
         (ERROR_BADPARM,
          "stuff_four_by_four(): matrix is NULL"));
    }

  if(m->rows != 4 || m->cols != 4)
    {
      ErrorReturn
        (ERROR_BADPARM,
         (ERROR_BADPARM,
          "stuff_four_by_four(): matrix is not four-by-four"));
    }

  *MATRIX_RELT(m, 1, 1) = m11;
  *MATRIX_RELT(m, 1, 2) = m12;
  *MATRIX_RELT(m, 1, 3) = m13;
  *MATRIX_RELT(m, 1, 4) = m14;
  *MATRIX_RELT(m, 2, 1) = m21;
  *MATRIX_RELT(m, 2, 2) = m22;
  *MATRIX_RELT(m, 2, 3) = m23;
  *MATRIX_RELT(m, 2, 4) = m24;
  *MATRIX_RELT(m, 3, 1) = m31;
  *MATRIX_RELT(m, 3, 2) = m32;
  *MATRIX_RELT(m, 3, 3) = m33;
  *MATRIX_RELT(m, 3, 4) = m34;
  *MATRIX_RELT(m, 4, 1) = m41;
  *MATRIX_RELT(m, 4, 2) = m42;
  *MATRIX_RELT(m, 4, 3) = m43;
  *MATRIX_RELT(m, 4, 4) = m44;

  return(NO_ERROR);

} /* end stuff_four_by_four() */

int apply_i_to_r(MRI *mri, MATRIX *m)
{

  float x_r, x_a, x_s;
  float y_r, y_a, y_s;
  float z_r, z_a, z_s;
  float mag;
  MATRIX *origin, *c;

  x_r = *MATRIX_RELT(m, 1, 1);
  x_a = *MATRIX_RELT(m, 2, 1);
  x_s = *MATRIX_RELT(m, 3, 1);
  mag = sqrt(x_r*x_r + x_a*x_a + x_s*x_s);
  mri->x_r = x_r / mag;  mri->x_a = x_a / mag;  mri->x_s = x_s / mag;
  mri->xsize = mag;

  y_r = *MATRIX_RELT(m, 1, 2);
  y_a = *MATRIX_RELT(m, 2, 2);
  y_s = *MATRIX_RELT(m, 3, 2);
  mag = sqrt(y_r*y_r + y_a*y_a + y_s*y_s);
  mri->y_r = y_r / mag;  mri->y_a = y_a / mag;  mri->y_s = y_s / mag;
  mri->ysize = mag;

  z_r = *MATRIX_RELT(m, 1, 3);
  z_a = *MATRIX_RELT(m, 2, 3);
  z_s = *MATRIX_RELT(m, 3, 3);
  mag = sqrt(z_r*z_r + z_a*z_a + z_s*z_s);
  mri->z_r = z_r / mag;  mri->z_a = z_a / mag;  mri->z_s = z_s / mag;
  mri->zsize = mag;

  origin = MatrixAlloc(4, 1, MATRIX_REAL);
  if(origin == NULL)
    {
      ErrorReturn
        (ERROR_BADPARM,
         (ERROR_BADPARM,
          "apply_i_to_r(): error allocating matrix"));
    }
  *MATRIX_RELT(origin, 1, 1) = (mri->width  - 1.0) / 2.0;
  *MATRIX_RELT(origin, 2, 1) = (mri->height - 1.0) / 2.0;
  *MATRIX_RELT(origin, 3, 1) = (mri->depth  - 1.0) / 2.0;
  *MATRIX_RELT(origin, 4, 1) = 1.0;

  c = MatrixMultiply(m, origin, NULL);
  if(c == NULL)
    {
      MatrixFree(&origin);
      ErrorReturn
        (ERROR_BADPARM,
         (ERROR_BADPARM,
          "apply_i_to_r(): error multiplying matrices"));
    }

  mri->c_r = *MATRIX_RELT(c, 1, 1);
  mri->c_a = *MATRIX_RELT(c, 2, 1);
  mri->c_s = *MATRIX_RELT(c, 3, 1);

  MatrixFree(&origin);
  MatrixFree(&c);

  mri->ras_good_flag = 1;

  return(NO_ERROR);

} /* end apply_i_to_r() */

MATRIX *
MRIrasXformToVoxelXform(MRI *mri_src, MRI *mri_dst, MATRIX *m_ras_xform,
                        MATRIX *m_voxel_xform)
{
  MATRIX *m_ras_to_voxel, *m_voxel_to_ras, *m_tmp ;

  if (!mri_dst)
    mri_dst = mri_src ;   /* assume they will be in the same space */

  m_voxel_to_ras = MRIgetVoxelToRasXform(mri_src) ;
  m_ras_to_voxel = MRIgetRasToVoxelXform(mri_dst) ;

  m_tmp = MatrixMultiply(m_ras_xform, m_voxel_to_ras, NULL) ;
  m_voxel_xform = MatrixMultiply(m_ras_to_voxel, m_tmp, m_voxel_xform) ;

  MatrixFree(&m_voxel_to_ras); MatrixFree(&m_ras_to_voxel); MatrixFree(&m_tmp);

  return(m_voxel_xform) ;
}

MATRIX *
MRIvoxelXformToRasXform(MRI *mri_src, MRI *mri_dst, MATRIX *m_voxel_xform,
                        MATRIX *m_ras_xform)
{
  MATRIX *m_ras_to_voxel, *m_voxel_to_ras, *m_tmp ;

  if (!mri_dst)
    mri_dst = mri_src ;  /* assume they will be in the same space */

  m_ras_to_voxel = MRIgetRasToVoxelXform(mri_src) ;
  m_voxel_to_ras = MRIgetVoxelToRasXform(mri_dst) ;

  m_tmp = MatrixMultiply(m_voxel_xform, m_ras_to_voxel, NULL) ;
  m_ras_xform = MatrixMultiply(m_voxel_to_ras, m_tmp, m_ras_xform) ;

  MatrixFree(&m_voxel_to_ras); MatrixFree(&m_ras_to_voxel); MatrixFree(&m_tmp);

  return(m_ras_xform) ;
}

MATRIX *extract_r_to_i(MRI *mri)
{
  MATRIX *m_ras_to_voxel, *m_voxel_to_ras ;

  m_voxel_to_ras = extract_i_to_r(mri) ;
  m_ras_to_voxel = MatrixInverse(m_voxel_to_ras, NULL) ;
  MatrixFree(&m_voxel_to_ras) ;
  return(m_ras_to_voxel) ;
}

int
MRIsetVoxelToRasXform(MRI *mri, MATRIX *m_vox2ras)
{
  float ci, cj, ck;

  mri->x_r = *MATRIX_RELT(m_vox2ras, 1, 1) / mri->xsize ;
  mri->y_r = *MATRIX_RELT(m_vox2ras, 1, 2) / mri->ysize ;
  mri->z_r = *MATRIX_RELT(m_vox2ras, 1, 3) / mri->zsize ;

  mri->x_a = *MATRIX_RELT(m_vox2ras, 2, 1) / mri->xsize ;
  mri->y_a = *MATRIX_RELT(m_vox2ras, 2, 2) / mri->ysize ;
  mri->z_a = *MATRIX_RELT(m_vox2ras, 2, 3) / mri->zsize ;

  mri->x_s = *MATRIX_RELT(m_vox2ras, 3, 1) / mri->xsize ;
  mri->y_s = *MATRIX_RELT(m_vox2ras, 3, 2) / mri->ysize ;
  mri->z_s = *MATRIX_RELT(m_vox2ras, 3, 3) / mri->zsize ;

  ci = (mri->width) / 2.0;
  cj = (mri->height) / 2.0;
  ck = (mri->depth) / 2.0;
  mri->c_r = *MATRIX_RELT(m_vox2ras, 1, 4) +
    (*MATRIX_RELT(m_vox2ras, 1, 1) * ci +
     *MATRIX_RELT(m_vox2ras, 1, 2) * cj +
     *MATRIX_RELT(m_vox2ras, 1, 3) * ck);
  mri->c_a = *MATRIX_RELT(m_vox2ras, 2, 4) +
    (*MATRIX_RELT(m_vox2ras, 2, 1) * ci +
     *MATRIX_RELT(m_vox2ras, 2, 2) * cj +
     *MATRIX_RELT(m_vox2ras, 2, 3) * ck);
  mri->c_s = *MATRIX_RELT(m_vox2ras, 3, 4) +
    (*MATRIX_RELT(m_vox2ras, 3, 1) * ci +
     *MATRIX_RELT(m_vox2ras, 3, 2) * cj +
     *MATRIX_RELT(m_vox2ras, 3, 3) * ck);
  MRIreInitCache(mri);
  return(NO_ERROR) ;
}

// allocate memory and the user must free it.
MATRIX *extract_i_to_r(MRI *mri)
{
  MATRIX *m;
  float m11, m12, m13, m14;
  float m21, m22, m23, m24;
  float m31, m32, m33, m34;
  float ci, cj, ck;

  m = MatrixAlloc(4, 4, MATRIX_REAL);
  if(m == NULL)
    {
      ErrorReturn
        (NULL,
         (ERROR_BADPARM,
          "extract_i_to_r(): error allocating matrix"));
    }

  m11 = mri->xsize * mri->x_r;
  m12 = mri->ysize * mri->y_r;
  m13 = mri->zsize * mri->z_r;
  m21 = mri->xsize * mri->x_a;
  m22 = mri->ysize * mri->y_a;
  m23 = mri->zsize * mri->z_a;
  m31 = mri->xsize * mri->x_s;
  m32 = mri->ysize * mri->y_s;
  m33 = mri->zsize * mri->z_s;

  ci = (mri->width) / 2.0;
  cj = (mri->height) / 2.0;
  ck = (mri->depth) / 2.0;

  m14 = mri->c_r - (m11 * ci + m12 * cj + m13 * ck);
  m24 = mri->c_a - (m21 * ci + m22 * cj + m23 * ck);
  m34 = mri->c_s - (m31 * ci + m32 * cj + m33 * ck);

  stuff_four_by_four(m, m11, m12, m13, m14,
                     m21, m22, m23, m24,
                     m31, m32, m33, m34,
                     0.0, 0.0, 0.0, 1.0);


  return(m);

} /* end extract_i_to_r() */

/* eof */
MRI *
MRIscaleMeanIntensities(MRI *mri_src, MRI *mri_ref, MRI *mri_dst)
{
  int    width, height, depth, x, y, z, val ;
  double ref_mean, src_mean, nref_vox, nsrc_vox, scale ;

  mri_dst = MRIcopy(mri_src, mri_dst) ;

  width = mri_dst->width ; height = mri_dst->height ; depth = mri_dst->depth;

  nref_vox = nsrc_vox = src_mean = ref_mean = 0.0 ;
  for (z = 0 ; z < depth ; z++)
    {
      for (y = 0 ; y < height ; y++)
        {
          for (x = 0 ; x < width ; x++)
            {
              if (MRIvox(mri_ref,x,y,z) > 10)
                {
                  nref_vox++ ;
                  ref_mean += (double)MRIvox(mri_ref, x, y, z) ;
                }
              if (MRIvox(mri_src,x,y,z) > 10)
                {
                  src_mean += (double)MRIvox(mri_src, x, y, z) ;
                  nsrc_vox++ ;
                }
            }
        }
    }

  ref_mean /= nref_vox ; src_mean /= nsrc_vox ;
  fprintf(stderr, "mean brightnesses: ref = %2.1f, in = %2.1f\n",
          ref_mean, src_mean) ;
  scale = ref_mean / src_mean ;
  for (z = 0 ; z < depth ; z++)
    {
      for (y = 0 ; y < height ; y++)
        {
          for (x = 0 ; x < width ; x++)
            {
              val = MRIvox(mri_src, x, y, z) ;
              val = nint(val*scale) ;
              if (val > 255)
                val = 255 ;
              MRIvox(mri_src, x, y, z) = val ;
            }
        }
    }

  return(mri_dst) ;
}

MRI *MRIsmoothParcellation(MRI *mri, int smooth_parcellation_count)
{

  MRI *mri2;
  int i, j, k;
  short vals[26];
  int counts[32768];
  int c;

  if(mri->type != MRI_SHORT)
    {
      ErrorReturn
        (NULL,
         (ERROR_UNSUPPORTED,
          "MRIsmoothParcellation(): only supported for shorts data"));
    }

  mri2 = MRIcopy(mri, NULL);
  if(mri2 == NULL)
    {
      ErrorReturn
        (NULL,
         (ERROR_NOMEMORY,
          "MRIsmoothParcellation(): error copying structre"));
    }

  for(i = 1;i < mri->width-1;i++)
    {
      for(j = 1;j < mri->height-1;j++)
        {
          for(k = 1;k < mri->depth-1;k++)
            {

              memset(counts, 0x00, 32768 * sizeof(int));

              vals[ 0] = MRISvox(mri, i+1, j+1, k+1);
              vals[ 1] = MRISvox(mri, i+1, j+1, k  );
              vals[ 2] = MRISvox(mri, i+1, j+1, k-1);
              vals[ 3] = MRISvox(mri, i+1, j  , k+1);
              vals[ 4] = MRISvox(mri, i+1, j  , k  );
              vals[ 5] = MRISvox(mri, i+1, j  , k-1);
              vals[ 6] = MRISvox(mri, i+1, j-1, k+1);
              vals[ 7] = MRISvox(mri, i+1, j-1, k  );
              vals[ 8] = MRISvox(mri, i+1, j-1, k-1);

              vals[ 9] = MRISvox(mri, i  , j+1, k+1);
              vals[10] = MRISvox(mri, i  , j+1, k  );
              vals[11] = MRISvox(mri, i  , j+1, k-1);
              vals[12] = MRISvox(mri, i  , j  , k+1);
              /* --- ignore the voxel itself --- */
              vals[13] = MRISvox(mri, i  , j  , k-1);
              vals[14] = MRISvox(mri, i  , j-1, k+1);
              vals[15] = MRISvox(mri, i  , j-1, k  );
              vals[16] = MRISvox(mri, i  , j-1, k-1);

              vals[17] = MRISvox(mri, i-1, j+1, k+1);
              vals[18] = MRISvox(mri, i-1, j+1, k  );
              vals[19] = MRISvox(mri, i-1, j+1, k-1);
              vals[20] = MRISvox(mri, i-1, j  , k+1);
              vals[21] = MRISvox(mri, i-1, j  , k  );
              vals[22] = MRISvox(mri, i-1, j  , k-1);
              vals[23] = MRISvox(mri, i-1, j-1, k+1);
              vals[24] = MRISvox(mri, i-1, j-1, k  );
              vals[25] = MRISvox(mri, i-1, j-1, k-1);

              for(c = 0;c < 26;c++)
                counts[vals[c]]++;

              for(c = 0;c < 26;c++)
                if(counts[vals[c]] >= smooth_parcellation_count)
                  MRISvox(mri2, i, j, k) = vals[c];

            }
        }
    }

  return(mri2);

} /* end MRIsmoothParcellation() */

int
MRIeraseBorderPlanes(MRI *mri)
{
  int  x, y, z ;

  for (x = 0 ; x < mri->width ; x++)
    for (y = 0 ; y < mri->height ; y++)
      {
        MRIvox(mri, x, y, 0) = MRIvox(mri, x, y, mri->depth-1) = 0 ;
      }

  for (y = 0 ; y < mri->height ; y++)
    for (z = 0 ; z < mri->depth ; z++)
      {
        MRIvox(mri, 0, y, z) = MRIvox(mri, mri->width-1, y, z) = 0 ;
      }

  for (x = 0 ; x < mri->width ; x++)
    for (z = 0 ; z < mri->depth ; z++)
      {
        MRIvox(mri, x, 0, z) = MRIvox(mri, x, mri->height-1, z) = 0 ;
      }

  return(NO_ERROR) ;
}
int
MRIcopyPulseParameters(MRI *mri_src, MRI *mri_dst)
{
  mri_dst->flip_angle = mri_src->flip_angle ;
  mri_dst->tr = mri_src->tr ;
  mri_dst->te = mri_src->te ;
  mri_dst->ti = mri_src->ti ;
  return(NO_ERROR) ;
}
float
MRIfindNearestNonzero(MRI *mri, int wsize, Real xr, Real yr, Real zr)
{
  int   xk, yk, zk, xi, yi, zi, whalf, x, y, z ;
  float dist, min_dist, min_val, dx, dy, dz ;

  x = mri->xi[nint(xr)] ; y = mri->yi[nint(yr)] ; z = mri->zi[nint(zr)] ;
  if (MRIvox(mri, x, y, z) > 0)
    return((float)MRIvox(mri, x, y, z)) ;

  min_dist = 100000 ; min_val = 0 ;
  whalf = (wsize-1)/2 ;
  for (zk = -whalf ; zk <= whalf ; zk++)
    {
      zi = mri->zi[z+zk] ;
      dz = zi-zr ;
      for (yk = -whalf ; yk <= whalf ; yk++)
        {
          yi = mri->yi[y+yk] ;
          dy = yi-yr ;
          for (xk = -whalf ; xk <= whalf ; xk++)
            {
              xi = mri->xi[x+xk] ;
              dx = xi-xr ;
              if (MRIgetVoxVal(mri, xi, yi, zi,0) > 0)
                {
                  dist = sqrt(dx*dx + dy*dy + dz*dz) ;
                  if (dist < min_dist)
                    {
                      min_dist = dist ;
                      min_val = MRIgetVoxVal(mri, xi, yi, zi,0) ;
                    }
                }
            }
        }
    }
  return(min_val) ;
}
int
MRIcountNonzeroInNbhd(MRI *mri, int wsize, int x, int y, int z)
{
  int   xk, yk, zk, xi, yi, zi, whalf, total ;

  whalf = (wsize-1)/2 ;
  for (total = 0, zk = -whalf ; zk <= whalf ; zk++)
    {
      zi = mri->zi[z+zk] ;
      for (yk = -whalf ; yk <= whalf ; yk++)
        {
          yi = mri->yi[y+yk] ;
          for (xk = -whalf ; xk <= whalf ; xk++)
            {
              xi = mri->xi[x+xk] ;
              if (MRIgetVoxVal(mri, xi, yi, zi,0) > 0)
                total++ ;
            }
        }
    }
  return(total) ;
}
int
MRIareNonzeroInNbhd(MRI *mri, int wsize, int x, int y, int z)
{
  int   xk, yk, zk, xi, yi, zi, whalf ;

  whalf = (wsize-1)/2 ;
  for (zk = -whalf ; zk <= whalf ; zk++)
    {
      zi = mri->zi[z+zk] ;
      for (yk = -whalf ; yk <= whalf ; yk++)
        {
          yi = mri->yi[y+yk] ;
          for (xk = -whalf ; xk <= whalf ; xk++)
            {
              xi = mri->xi[x+xk] ;
              if (MRIgetVoxVal(mri, xi, yi, zi,0) > 0)
                return(1) ;
            }
        }
    }
  return(0) ;
}
float
MRIfindNearestNonzeroLocation(MRI *mri, int wsize, Real xr, Real yr, Real zr,
                              int *pxv, int *pyv, int *pzv)
{
  int   xk, yk, zk, xi, yi, zi, whalf, x, y, z ;
  float dist, min_dist, min_val, dx, dy, dz ;

  x = nint(xr) ; y = nint(yr) ; z = nint(zr) ;
  if (MRIvox(mri, x, y, z) > 0)
    return((float)MRIvox(mri, x, y, z)) ;

  min_dist = 100000 ; min_val = 0 ;
  whalf = (wsize-1)/2 ;
  for (zk = -whalf ; zk <= whalf ; zk++)
    {
      zi = mri->zi[z+zk] ;
      dz = zi-zr ;
      for (yk = -whalf ; yk <= whalf ; yk++)
        {
          yi = mri->yi[y+yk] ;
          dy = yi-yr ;
          for (xk = -whalf ; xk <= whalf ; xk++)
            {
              xi = mri->xi[x+xk] ;
              dx = xi-xr ;
              if (MRIvox(mri, xi, yi, zi) > 0)
                {
                  dist = sqrt(dx*dx + dy*dy + dz*dz) ;
                  if (dist < min_dist)
                    {
                      if (pxv)
                        { *pxv = xi ; *pyv = yi ; *pzv = zi ;}
                      min_dist = dist ;
                      min_val = MRIvox(mri, xi, yi, zi) ;
                    }
                }
            }
        }
    }
  return(min_val) ;
}

MRI *
MRIfromTalairach(MRI *mri_src, MRI *mri_dst)
{
  int  x, y, z, xv, yv, zv ;
  Real xt, yt, zt, xn, yn, zn, val ;

  if (!mri_dst)
    mri_dst = MRIclone(mri_src, NULL) ;

  for (x = 0 ; x < mri_dst->width ; x++)
    {
      xn = (Real)x ;
      for (y = 0 ; y < mri_dst->height ; y++)
        {
          yn = (Real)y ;
          for (z = 0 ; z < mri_dst->depth ; z++)
            {
              zn = (Real)z ;
              MRIvoxelToTalairachVoxel(mri_src, xn, yn, zn, &xt, &yt, &zt) ;
              xv = nint(xt) ; yv = nint(yt) ; zv = nint(zt) ;
              if ((xv >= 0 && xv < mri_src->width) &&
                  (yv >= 0 && yv < mri_src->height) &&
                  (zv >= 0 && zv < mri_src->depth))
                {
                  MRIsampleVolume(mri_src, xt, yt, zt, &val) ;
                  MRIvox(mri_dst, x, y, z) = val ;
                }
              else
                MRIvox(mri_dst, x, y, z) = 0 ;
            }
        }
    }

  return(mri_dst) ;
}
MRI *
MRItoTalairach(MRI *mri_src, MRI *mri_dst)
{
  int  x, y, z, xv, yv, zv ;
  Real xt, yt, zt, xn, yn, zn, val ;

  if (!mri_dst)
    mri_dst = MRIclone(mri_src, NULL) ;

  for (x = 0 ; x < mri_dst->width ; x++)
    {
      xt = (Real)x ;
      for (y = 0 ; y < mri_dst->height ; y++)
        {
          yt = (Real)y ;
          for (z = 0 ; z < mri_dst->depth ; z++)
            {
              zt = (Real)z ;
              MRItalairachVoxelToVoxel(mri_src, xt, yt, zt, &xn, &yn, &zn) ;
              xv = nint(xn) ; yv = nint(yn) ; zv = nint(zt) ;
              if ((xv >= 0 && xv < mri_src->width) &&
                  (yv >= 0 && yv < mri_src->height) &&
                  (zv >= 0 && zv < mri_src->depth))
                {
                  MRIsampleVolume(mri_src, xn, yn, zn, &val) ;
                  MRIvox(mri_dst, x, y, z) = val ;
                }
              else
                MRIvox(mri_dst, x, y, z) = 0 ;
            }
        }
    }

  return(mri_dst) ;
}
/*-------------------------------------------------------------------
  MRIlog10() - computes the log10 of the values at each voxel and
  frame. If a value is zero, the result is set to 10000000000.0. If
  the negflag is set, then -log10 is computed. The result is stored
  in outmri. If outmri is NULL, the output MRI is alloced and its
  pointer returned.
  ------------------------------------------------------------------*/
MRI *MRIlog10(MRI *inmri, MRI *outmri, int negflag)
{
  int c, r, s, f;
  float val;

  if(outmri==NULL){
    outmri = MRIallocSequence(inmri->width, inmri->height, inmri->depth,
                              MRI_FLOAT, inmri->nframes);
    MRIcopyHeader(inmri,outmri);

    if(outmri==NULL){
      printf("ERROR: fMRIlog10: could not alloc\n");
      return(NULL);
    }
  }
  else{
    if(inmri->width   != outmri->width  ||
       inmri->height  != outmri->height ||
       inmri->depth   != outmri->depth  ||
       inmri->nframes != outmri->nframes){
      printf("ERROR: MRIlog10: output dimension mismatch\n");
      return(NULL);
    }
    if(outmri->type != MRI_FLOAT){
      printf("ERROR: MRIlog10: structure passed is not MRI_FLOAT\n");
      return(NULL);
    }
  }

  for(c=0; c < inmri->width; c++){
    for(r=0; r < inmri->height; r++){
      for(s=0; s < inmri->depth; s++){
        for(f=0; f < inmri->nframes; f++){
          val = MRIgetVoxVal(inmri, c, r, s, f);
          if(val == 0)     MRIFseq_vox(outmri,c,r,s,f) = 10000000000.0;
          else{
            if(negflag){
              if(val < 0) MRIFseq_vox(outmri,c,r,s,f) =  log10(fabs(val));
              else        MRIFseq_vox(outmri,c,r,s,f) = -log10(val);
            }
            else{
              if(val < 0) MRIFseq_vox(outmri,c,r,s,f) = -log10(fabs(val));
              else        MRIFseq_vox(outmri,c,r,s,f) =  log10(val);
            }
          }
        }
      }
    }
  }

  return(outmri);
}
/*---------------------------------------------------------------------
  MRIrandn() - fills an MRI structure with values sampled from a
  normal distribution with mean avg and standard devation stddev.
  --------------------------------------------------------*/
MRI *MRIrandn(int ncols, int nrows, int nslices, int nframes,
              float avg, float stddev, MRI *mri)
{
  int c, r, s, f;

  if(mri==NULL){
    mri = MRIallocSequence(ncols, nrows, nslices, MRI_FLOAT, nframes);
    if(mri==NULL){
      printf("ERROR: MRIrandn: could not alloc\n");
      return(NULL);
    }
  }
  else{
    if(mri->width != ncols   || mri->height != nrows ||
       mri->depth != nslices || mri->nframes != nframes){
      printf("ERROR: MRIrandn: dimension mismatch\n");
      return(NULL);
    }
    if(mri->type != MRI_FLOAT){
      printf("ERROR: MRIrandn: structure passed is not MRI_FLOAT\n");
      return(NULL);
    }
  }

  for(f=0; f<nframes; f++){
    for(s=0; s<nslices; s++){
      for(r=0; r<nrows; r++){
        for(c=0; c<ncols; c++){
          MRIFseq_vox(mri,c,r,s,f) =
            stddev*PDFgaussian() + avg;
        }
      }
    }
  }

  return(mri);
}
/*---------------------------------------------------------------------
  MRIrande() - fills an MRI structure with values sampled from an
  Erlang distribution with mean avg and order order. The variance
  will be (avg^2)/order. Theoretical distribution is
  r = order
  pdf(x) = r*((r*(x-avg+1))^(r-1)) * exp(-r*(x-avg+1)) / (r-1)!
  when order=1, this generates an exponential distribution.
  --------------------------------------------------------*/
MRI *MRIrande(int ncols, int nrows, int nslices, int nframes,
              float avg, int order, MRI *mri)
{
  int c, r, s, f;

  if(mri==NULL){
    mri = MRIallocSequence(ncols, nrows, nslices, MRI_FLOAT, nframes);
    if(mri==NULL){
      printf("ERROR: MRIrande: could not alloc\n");
      return(NULL);
    }
  }
  else{
    if(mri->width != ncols   || mri->height != nrows ||
       mri->depth != nslices || mri->nframes != nframes){
      printf("ERROR: MRIrande: dimension mismatch\n");
      return(NULL);
    }
    if(mri->type != MRI_FLOAT){
      printf("ERROR: MRIrande: structure passed is not MRI_FLOAT\n");
      return(NULL);
    }
  }

  avg = avg - 1; // PDFrande() already has average of 1
  for(f=0; f<nframes; f++){
    for(s=0; s<nslices; s++){
      for(r=0; r<nrows; r++){
        for(c=0; c<ncols; c++){
          MRIFseq_vox(mri,c,r,s,f) =
            PDFerlang(order) + avg;
        }
      }
    }
  }

  return(mri);
}
/*---------------------------------------------------------------------
  MRIdrand48() - fills an MRI structure with values sampled from a
  the drand48 uniform random number generator. The user must specify
  the min and max. Note: the stddev = (max-min)*0.289. If mri is NULL,
  it will alloc a MRI_FLOAT volume, otherwise, it will use the type
  as specified in mri.
  --------------------------------------------------------*/
MRI *MRIdrand48(int ncols, int nrows, int nslices, int nframes,
                float min, float max, MRI *mri)
{
  int c, r, s, f, n;
  float range, v;
  BUFTYPE *pmri=NULL;
  short   *psmri=NULL;
  int     *pimri=NULL;
  long    *plmri=NULL;
  float   *pfmri=NULL;

  if(mri==NULL){
    mri = MRIallocSequence(ncols, nrows, nslices, MRI_FLOAT, nframes);
    if(mri==NULL){
      printf("ERROR: MRIdrand48: could not alloc\n");
      return(NULL);
    }
  }
  else{
    if(mri->width != ncols   || mri->height != nrows ||
       mri->depth != nslices || mri->nframes != nframes){
      printf("ERROR: MRIdrand48: dimension mismatch\n");
      return(NULL);
    }
  }

  range = max-min;
  n = 0;
  for(f=0; f<nframes; f++){
    for(s=0; s<nslices; s++){
      for(r=0; r<nrows; r++){
        switch(mri->type){
        case MRI_UCHAR: pmri  =           mri->slices[n][r]; break;
        case MRI_SHORT: psmri = (short *) mri->slices[n][r]; break;
        case MRI_INT:   pimri = (int *)   mri->slices[n][r]; break;
        case MRI_LONG:  plmri = (long *)  mri->slices[n][r]; break;
        case MRI_FLOAT: pfmri = (float *) mri->slices[n][r]; break;
        }
        for(c=0; c<ncols; c++) {
          v = range*drand48() + min;
          switch(mri->type){
          case MRI_UCHAR: *pmri++  = (BUFTYPE) v; break;
          case MRI_SHORT: *psmri++ = (short) v; break;
          case MRI_INT:   *pimri++ = (int)   v; break;
          case MRI_LONG:  *plmri++ = (long)  v; break;
          case MRI_FLOAT: *pfmri++ = (float) v; break;
          }
        }
      }
      n++;
    }
  }

  return(mri);
}
/*---------------------------------------------------------------------
  MRIsampleCDF() - fills an MRI structure with values sampled from a
  the given CDF. See PDFsampleCDF(). CDF[n] is the probability that
  the random number is <= xCDF[n].
  --------------------------------------------------------------------*/
MRI *MRIsampleCDF(int ncols, int nrows, int nslices, int nframes,
                  double *xCDF, double *CDF, int nCDF, MRI *mri)
{
  int c, r, s, f;

  if(mri==NULL){
    mri = MRIallocSequence(ncols, nrows, nslices, MRI_FLOAT, nframes);
    if(mri==NULL){
      printf("ERROR: MRIsampleCDF: could not alloc\n");
      return(NULL);
    }
  }
  else{
    if(mri->width != ncols   || mri->height != nrows ||
       mri->depth != nslices || mri->nframes != nframes){
      printf("ERROR: MRIsampleCDF: dimension mismatch\n");
      return(NULL);
    }
    if(mri->type != MRI_FLOAT){
      printf("ERROR: MRIsampleCDF: structure passed is not MRI_FLOAT\n");
      return(NULL);
    }
  }

  for(f=0; f<nframes; f++){
    for(s=0; s<nslices; s++){
      for(r=0; r<nrows; r++){
        for(c=0; c<ncols; c++){
          MRIFseq_vox(mri,c,r,s,f) = PDFsampleCDF(xCDF,CDF,nCDF);
        }
      }
    }
  }

  return(mri);
}
/*---------------------------------------------------------------------
  MRIconst() - fills an MRI structure with the given value. If mri is
  NULL, it will alloc a MRI_FLOAT volume, otherwise, it will use the type
  as specified in mri.
  --------------------------------------------------------*/
MRI *MRIconst(int ncols, int nrows, int nslices, int nframes,
              float val, MRI *mri)
{
  int c, r, s, f, n;
  BUFTYPE *pmri=NULL;
  short   *psmri=NULL;
  int     *pimri=NULL;
  long    *plmri=NULL;
  float   *pfmri=NULL;

  if(mri==NULL){
    mri = MRIallocSequence(ncols, nrows, nslices, MRI_FLOAT, nframes);
    if(mri==NULL){
      printf("ERROR: MRIdconst: could not alloc\n");
      return(NULL);
    }
  }
  else{
    if(mri->width != ncols   || mri->height != nrows ||
       mri->depth != nslices || mri->nframes != nframes){
      printf("ERROR: MRIconst: dimension mismatch\n");
      return(NULL);
    }
  }

  n = 0;
  for(f=0; f<nframes; f++){
    for(s=0; s<nslices; s++){
      for(r=0; r<nrows; r++){
        switch(mri->type){
        case MRI_UCHAR: pmri  =           mri->slices[n][r]; break;
        case MRI_SHORT: psmri = (short *) mri->slices[n][r]; break;
        case MRI_INT:   pimri = (int *)   mri->slices[n][r]; break;
        case MRI_LONG:  plmri = (long *)  mri->slices[n][r]; break;
        case MRI_FLOAT: pfmri = (float *) mri->slices[n][r]; break;
        }
        for(c=0; c<ncols; c++) {
          switch(mri->type){
          case MRI_UCHAR: *pmri++  = (BUFTYPE) val; break;
          case MRI_SHORT: *psmri++ = (short) val; break;
          case MRI_INT:   *pimri++ = (int)   val; break;
          case MRI_LONG:  *plmri++ = (long)  val; break;
          case MRI_FLOAT: *pfmri++ = (float) val; break;
          }
        }
      }
      n++;
    }
  }

  return(mri);
}
/*--------------------------------------------------------------*/

int
MRInormalizeSequence(MRI *mri, float target)
{
  int    x, y, z, frame ;
  double norm ;
  Real   val ;

  for (x = 0 ; x < mri->width ; x++)
    {
      for (y = 0 ; y < mri->height ; y++)
        {
          for (z = 0 ; z < mri->depth ; z++)
            {
              for (frame = 0, norm = 0 ; frame < mri->nframes ; frame++)
                {
                  MRIsampleVolumeFrame(mri, x, y, z, frame, &val) ;
                  norm += (val*val) ;
                }
              norm = sqrt(norm) / target ;
              if (FZERO(norm))
                norm = 1 ;
              for (frame = 0 ; frame < mri->nframes ; frame++)
                {
                  switch (mri->type)
                    {
                    default:
                      ErrorReturn
                        (ERROR_UNSUPPORTED,
                         (ERROR_UNSUPPORTED,
                          "MRInormalizeSequence: unsupported input type %d",
                          mri->type)) ;
                      break ;
                    case MRI_SHORT:
                      MRISseq_vox(mri, x, y, z, frame) =
                        MRISseq_vox(mri, x, y, z, frame) / norm ;
                      break ;
                    case MRI_FLOAT:
                      MRIFseq_vox(mri, x, y, z, frame) /= norm ;
                      break ;
                    case MRI_UCHAR:
                      MRIseq_vox(mri, x, y, z, frame) /= norm ;
                      break ;
                    }
                }
            }
        }
    }

  return(NO_ERROR) ;
}

double
MRImeanInLabel(MRI *mri_src, MRI *mri_labeled, int label)
{
  int  x, y, z, nvox, l ;
  double mean = 0.0 ;
  float  val ;

  nvox = 0 ;
  for (x = 0 ; x < mri_src->width ; x++)
    {
      for (y = 0 ; y < mri_src->height ; y++)
        {
          for (z = 0 ; z < mri_src->depth ; z++)
            {
              l = nint(MRIgetVoxVal(mri_labeled, x, y, z, 0)) ;
              if (l == label)
                {
                  val = MRIgetVoxVal(mri_src, x, y, z, 0) ;
                  mean += val ;
                  nvox++ ;
                }
            }
        }
    }
  if (!nvox)
    nvox = 1 ;
  return(mean/nvox) ;
}

MRI *
MRImakePositive(MRI *mri_src, MRI *mri_dst)
{
  float   fmin, fmax, val ;
  int     x, y, z,f  ;

  MRIvalRange(mri_src, &fmin, &fmax) ;
  mri_dst = MRIcopy(mri_src, mri_dst) ;
  if (fmin >= 0)
    return(mri_dst) ;

  for (f = 0 ; f < mri_dst->nframes ; f++)
    {
      for (x = 0 ; x < mri_dst->width ; x++)
        {
          for (y = 0 ; y < mri_dst->height ; y++)
            {
              for (z = 0 ; z < mri_dst->depth ; z++)
                {
                  val = MRIgetVoxVal(mri_src, x, y, z, f) ;
                  val -= fmin ;
                  MRIsetVoxVal(mri_dst, x, y, z, f, val) ;
                }
            }
        }
    }

  return(mri_dst) ;
}
MRI *
MRIeraseNegative(MRI *mri_src, MRI *mri_dst)
{
  int  x, y, z ;
  float  val ;

  mri_dst = MRIcopy(mri_src, mri_dst) ;

  for (x = 0 ; x < mri_src->width ; x++)
    {
      for (y = 0 ; y < mri_src->height ; y++)
        {
          for (z = 0 ; z < mri_src->depth ; z++)
            {
              val = MRIgetVoxVal(mri_src, x, y, z, 0) ;
              if (val < 0)
                MRIsetVoxVal(mri_dst, x, y, z, 0, 0) ;
            }
        }
    }
  return(mri_dst) ;
}
int
MRIsampleVolumeSlice
(MRI *mri, Real x, Real y, Real z, Real *pval, int slice_direction)
{
  int  OutOfBounds;
  int  xm, xp, ym, yp, zm, zp, width, height, depth ;
  Real val, xmd, ymd, xpd, ypd ;  /* d's are distances */
  Real val11, val12, val21, val22 ;

  if (FEQUAL((int)x,x) && FEQUAL((int)y,y) && FEQUAL((int)z, z))
    return(MRIsampleVolumeType(mri, x, y, z, pval, SAMPLE_NEAREST)) ;

  OutOfBounds = MRIindexNotInVolume(mri, x, y, z);
  if(OutOfBounds == 1){
    /* unambiguously out of bounds */
    *pval = 0.0;
    return(NO_ERROR) ;
  }

  width = mri->width ; height = mri->height ; depth = mri->depth ;

  if (x >= width)    x = width - 1.0 ;
  if (y >= height)   y = height - 1.0 ;
  if (z >= depth)    z = depth - 1.0 ;
  if (x < 0.0)       x = 0.0 ;
  if (y < 0.0)       y = 0.0 ;
  if (z < 0.0)       z = 0.0 ;

  xm = MAX((int)x, 0) ;
  xp = MIN(width-1, xm+1) ;
  ym = MAX((int)y, 0) ;
  yp = MIN(height-1, ym+1) ;
  zm = MAX((int)z, 0) ;
  zp = MIN(depth-1, zm+1) ;

  xmd = x - (float)xm ;
  ymd = y - (float)ym ;
  xpd = (1.0f - xmd) ;
  ypd = (1.0f - ymd) ;

  switch (slice_direction)
    {
    case MRI_CORONAL:
      zp = nint(z) ;
      val11 = MRIgetVoxVal(mri, xm, ym, zp, 0) ;
      val12 = MRIgetVoxVal(mri, xm, yp, zp, 0) ;
      val21 = MRIgetVoxVal(mri, xp, ym, zp, 0) ;
      val22 = MRIgetVoxVal(mri, xp, yp, zp, 0) ;
      *pval = val = xpd * ypd * val11 + xpd * ymd * val12 + xmd * ypd * val21 +
        xmd * ymd * val22 ;
      break ;
    default:
      ErrorReturn
        (ERROR_UNSUPPORTED,
         (ERROR_UNSUPPORTED,
          "MRIsampleVolumeSlice: unsupported direction %d", slice_direction)) ;
      break ;
    }
  return(NO_ERROR) ;
}

float
MRIvoxelsInLabelWithPartialVolumeEffects(MRI *mri, MRI *mri_vals, int label)
{
  float   volume, vox_vol ;
  int     x, y, z, nbr_label_counts[MAX_CMA_LABELS];
  int     label_counts[MAX_CMA_LABELS], this_label, border;
  int     nbr_label, max_count, vox_label ;
  MRI     *mri_border ;
  float   label_means[MAX_CMA_LABELS], pv, mean_label, mean_nbr, val ;

  vox_vol = mri->xsize*mri->ysize*mri->zsize ;

  /* first find border voxels */
  mri_border = MRImarkLabelBorderVoxels(mri, NULL, label, 1, 1) ;
  if (DIAG_VERBOSE_ON && (Gdiag & DIAG_WRITE))
    MRIwrite(mri_border, "b.mgz") ;
  volume = 0 ;
  for (x = 0 ; x < mri->width ; x++)
    {
      for (y = 0 ; y < mri->height ; y++)
        {
          for (z = 0 ; z < mri->depth ; z++)
            {
              if (x == Gx && y == Gy && z == Gz)
                DiagBreak() ;
              vox_label = MRIgetVoxVal(mri, x, y, z, 0) ;
              border = MRIgetVoxVal(mri_border, x, y, z, 0) ;
              if ((vox_label != label) && (border == 0))
                continue ;

              if (border == 0)
                volume += vox_vol ;
              else /* compute partial volume */
                {
                  MRIcomputeLabelNbhd
                    (mri, mri_vals, x, y, z,
                     nbr_label_counts, label_means, 1, MAX_CMA_LABELS) ;
                  MRIcomputeLabelNbhd
                    (mri, mri_vals, x, y, z,
                     label_counts, label_means, 7, MAX_CMA_LABELS) ;
                  val =
                    MRIgetVoxVal(mri_vals, x, y, z, 0) ;  /* compute partial
                                                             volume based on
                                                             intensity */
                  mean_label = label_means[vox_label] ;
                  nbr_label = -1 ; max_count = 0 ;
                  /* look for a label that is a nbr and is
                     on the other side of val from the label mean */
                  for (this_label = 0 ;
                       this_label < MAX_CMA_LABELS ;
                       this_label++)
                    {
                      if (this_label == vox_label)
                        continue ;
                      if (nbr_label_counts[this_label] == 0)   /* not a nbr */
                        continue ;

                      if ((label_counts[this_label] > max_count) &&
                          ((label_means[this_label] - val) *
                           (mean_label - val) < 0))
                        {
                          max_count = label_means[this_label] ;
                          nbr_label = this_label ;
                        }
                    }
                  if (vox_label != label &&
                      nbr_label != label)  /* this struct not in voxel */
                    continue ;

                  if (max_count == 0)  /* couldn't find an appropriate label */
                    volume += vox_vol ;
                  else    /* compute partial volume pct */
                    {
                      mean_nbr = label_means[nbr_label] ;
                      pv = (val - mean_nbr) / (mean_label - mean_nbr) ;
                      if (vox_label == label)
                        volume += vox_vol * pv ;
                      else
                        volume += vox_vol * (1-pv) ;
                      if (pv < 0 || pv > 1)
                        DiagBreak() ;
                    }
                }
            }
        }
    }

  MRIfree(&mri_border) ;
  return(volume) ;
}

MRI *
MRImakeDensityMap(MRI *mri, MRI *mri_vals, int label, MRI *mri_dst)
{
  float   vox_vol, volume ;
  int     x, y, z, nbr_label_counts[MAX_CMA_LABELS];
  int     label_counts[MAX_CMA_LABELS], this_label, border;
  int     nbr_label, max_count, vox_label ;
  MRI     *mri_border ;
  float   label_means[MAX_CMA_LABELS], pv, mean_label, mean_nbr, val ;

  if (mri_dst == NULL)
    mri_dst = MRIalloc(mri->width, mri->height, mri->depth, MRI_FLOAT) ;

  /* first find border voxels */
  mri_border = MRImarkLabelBorderVoxels(mri, NULL, label, 1, 1) ;
  if (DIAG_VERBOSE_ON && (Gdiag & DIAG_WRITE))
    MRIwrite(mri_border, "b.mgz") ;
  vox_vol = mri->xsize*mri->ysize*mri->zsize ;
  for (x = 0 ; x < mri->width ; x++)
    {
      for (y = 0 ; y < mri->height ; y++)
        {
          for (z = 0 ; z < mri->depth ; z++)
            {
              if (x == Gx && y == Gy && z == Gz)
                DiagBreak() ;
              vox_label = MRIgetVoxVal(mri, x, y, z, 0) ;
              border = MRIgetVoxVal(mri_border, x, y, z, 0) ;
              if ((vox_label != label) && (border == 0))
                continue ;

              volume = vox_vol ;
              if (border == 0)
                volume = vox_vol ;
              else  /* compute partial volume */
                {
                  MRIcomputeLabelNbhd
                    (mri, mri_vals, x, y, z,
                     nbr_label_counts, label_means, 1, MAX_CMA_LABELS) ;
                  MRIcomputeLabelNbhd
                    (mri, mri_vals, x, y, z,
                     label_counts, label_means, 7, MAX_CMA_LABELS) ;
                  val = 
                    MRIgetVoxVal(mri_vals, x, y, z, 0) ;  /* compute partial
                                                             volume based on
                                                             intensity */
                  mean_label = label_means[vox_label] ;
                  nbr_label = -1 ; max_count = 0 ;
                  /* look for a label that is a nbr and is
                     on the other side of val from the label mean */
                  for (this_label = 0 ;
                       this_label < MAX_CMA_LABELS ;
                       this_label++)
                    {
                      if (this_label == vox_label)
                        continue ;
                      if (nbr_label_counts[this_label] == 0)   /* not a nbr */
                        continue ;

                      if ((label_counts[this_label] > max_count) &&
                          ((label_means[this_label] - val) *
                           (mean_label - val) < 0))
                        {
                          max_count = label_means[this_label] ;
                          nbr_label = this_label ;
                        }
                    }
                  if (vox_label != label &&
                      nbr_label != label)  /* this struct not in voxel */
                    continue ;

                  if (max_count > 0)  /* compute partial volume pct */
                    {
                      mean_nbr = label_means[nbr_label] ;
                      pv = (val - mean_nbr) / (mean_label - mean_nbr) ;
                      if (vox_label == label)
                        volume = pv*vox_vol ;
                      else
                        volume = vox_vol * (1-pv) ;
                      if (pv < 0 || pv > 1)
                        DiagBreak() ;
                    }
                }
              MRIsetVoxVal(mri_dst, x, y, z, 0, volume) ;
            }
        }
    }

  MRIfree(&mri_border) ;
  return(mri_dst) ;
}

MRI *
MRImarkLabelBorderVoxels
(MRI *mri_src, MRI *mri_dst, int label, int mark, int six_connected)
{
  int  x, y, z, xk, yk, zk, xi, yi, zi, this_label, that_label, border ;

  if (mri_dst == NULL)
    mri_dst = MRIclone(mri_src, NULL) ;

  for (x = 0 ; x < mri_src->width ; x++)
    {
      for (y = 0 ; y < mri_src->height ; y++)
        {
          for (z = 0 ; z < mri_src->depth ; z++)
            {
              this_label = MRIgetVoxVal(mri_src, x, y, z, 0) ;
              border = 0 ;
              for (xk = -1 ; xk <= 1 && !border ; xk++)
                {
                  xi = mri_src->xi[x+xk] ;
                  for (yk = -1 ; yk <= 1 && !border ; yk++)
                    {
                      yi = mri_src->yi[y+yk] ;
                      for (zk = -1 ; zk <= 1 ; zk++)
                        {
                          if (six_connected && (abs(xk)+abs(yk)+abs(zk) != 1))
                            continue ;
                          zi = mri_src->zi[z+zk] ;
                          that_label = MRIgetVoxVal(mri_src, xi, yi, zi, 0) ;
                          if (((this_label == label) &&
                               (that_label != label)) ||
                              ((this_label != label) &&
                               (that_label == label)))
                            {
                              border = 1 ;
                              break ;
                            }
                        }
                    }
                }
              if (border)
                MRIsetVoxVal(mri_dst, x, y, z, 0, mark) ;
            }
        }
    }

  return(mri_dst) ;
}

int
MRIcomputeLabelNbhd
(MRI *mri_labels, MRI *mri_vals,
 int x, int y, int z, int *label_counts, float *label_means,
 int whalf, int max_labels)
{
  int    xi, yi, zi, xk, yk, zk, label ;
  float  val ;

  memset(label_counts, 0, sizeof(label_counts[0])*max_labels) ;
  memset(label_means, 0, sizeof(label_means[0])*max_labels) ;
  for (xk = -whalf ; xk <= whalf ; xk++)
    {
      xi = mri_vals->xi[x+xk] ;
      for (yk = -whalf ; yk <= whalf ; yk++)
        {
          yi = mri_vals->yi[y+yk] ;
          for (zk = -whalf ; zk <= whalf ; zk++)
            {
              zi = mri_vals->zi[z+zk] ;
              label = MRIgetVoxVal(mri_labels, xi, yi, zi, 0) ;
              val = MRIgetVoxVal(mri_vals, xi, yi, zi, 0) ;
              label_counts[label]++ ;
              label_means[label] += val ;
            }
        }
    }

  for (label = 0 ; label < max_labels ; label++)
    if (label_counts[label] > 0)
      label_means[label] /= label_counts[label] ;
  return(NO_ERROR) ;
}

/**
 * void MRIcalCRASforSampledVolume
 *
 * @param src MRI* volume
 * @param dst MRI* sampled volume
 * @param pr  output ptr to c_r
 * @param pa  output ptr to c_a
 * @param ps  output ptr to c_s
 */
void MRIcalcCRASforSampledVolume
(MRI *src, MRI *dst, Real *pr, Real *pa, Real *ps)
{
  // get the voxel position of the "center" voxel of the dst in the src volume
  // i.e. sample is 2, then get the voxel position 64 in the src volume
  //      thus  it is 128.5 (in the src) for 64 (in the dst)
  Real sx, sy, sz;
  int dx, dy, dz;
  int samplex, sampley, samplez;

  // error check first
  if ((src->width)%(dst->width) != 0)
    ErrorExit
      (ERROR_BADPARM, "src width must be integer multiple of dst width");
  if ((src->height)%(dst->height) != 0)
    ErrorExit
      (ERROR_BADPARM, "src height must be integer multiple of dst height");
  if ((src->depth)%(dst->depth) != 0)
    ErrorExit
      (ERROR_BADPARM, "src depth must be integer multiple of dst depth");

  samplex = src->width/dst->width;
  sampley = src->height/dst->height;
  samplez = src->depth/dst->depth;

  // "center" voxel position in dst
  dx = dst->width/2; // if the length is odd,
                     // then it does the right thing (truncation)
  dy = dst->height/2;// i.e.  0 1 2 then 3/2 = 1,  0 1 2 3 then 4/2=2
  dz = dst->depth/2;
  // corresponding position in src
  sx = dx*samplex + (samplex-1.)/2.; // ((dx*samplex - 0.5) +
                                     // ((dx+1)*samplex -0.5))/2.
  sy = dy*sampley + (sampley-1.)/2.;
  sz = dz*samplez + (samplez-1.)/2.;
  //
  // Example
  // | 0 1 2 | 3 4 5 | 6 7 8 | -(sample by 3).
  // |   0   |   1   |   2   |
  //                                       1 (3/2) in dst corresponds
  //                                       to 1*3+(3-1)/2 = 4! in src
  //
  // | 0 1 2 3 | 4 5 6 7 |     -(sample by 4)->0 1
  // |    0    |    1    |                        1 (2/2) in dst corresponds
  //                                              to 1*4+(4-1)/2 = 5.5 in src

  // get ras of the "center" voxel position in dst
  if (!src->i_to_r__)
    {
      src->i_to_r__ = extract_i_to_r(src);
      src->r_to_i__ = extract_r_to_i(src);
    }
  TransformWithMatrix(src->i_to_r__, sx, sy, sz, pr, pa, ps);

  if (Gdiag & DIAG_SHOW && DIAG_VERBOSE_ON)
    fprintf(stderr, "c_ras for sample volume is (%f, %f, %f) "
            "compared with the src (%f, %f, %f)\n",
            *pr, *pa, *ps, src->c_r, src->c_a, src->c_s);
}

/**
 * MRIcalcCRASfroExtractedVolume
 *
 * @param src  MRI* src volume
 * @param x0   src start position of the extraction region
 * @param y0
 * @param z0
 * @param x1   target start position of the extracted region
 * @param y1
 * @param z1
 * @param pr   output Real*  c_r
 * @param pa                 c_a
 * @param ps                 c_s
 */
void MRIcalcCRASforExtractedVolume
(MRI *src, MRI *dst, int x0, int y0, int z0, int x1, int y1, int z1,
 Real *pr, Real *pa, Real *ps)
{
  Real cx, cy, cz;
  // The "center" voxel position of the
  // extracted volume in the original voxel position
  //        x1 of dst corresponds to x0 of src
  // Thus, the "center" of dst corresponds to that of the src is
  cx = x0+(Real)dst->width/2 - x1;  // integer divide cutoff extra
  cy = y0+(Real)dst->height/2 - y1;
  cz = z0+(Real)dst->depth/2 - z1;

  if (!src->i_to_r__)
    {
      src->i_to_r__ = extract_i_to_r(src);
      src->r_to_i__ = extract_r_to_i(src);
    }
  TransformWithMatrix(src->i_to_r__, cx, cy, cz, pr, pa, ps);
  // got where the RAS position of the new volume position
  // we have to translate so that we can get the same value
  // under the new volume

  if (Gdiag & DIAG_SHOW && DIAG_VERBOSE_ON)
    fprintf(stderr, "c_ras for sample volume is (%f, %f, %f) "
            "compared with the src (%f, %f, %f)\n",
            *pr, *pa, *ps, src->c_r, src->c_a, src->c_s);
}

// transform is the hires to lowres vox-to-vox transform
void MRIcalcCRASforHiresVolume
(MRI *hires, MRI *lowres, MATRIX *vox_xform, Real *pr, Real *pa, Real *ps)
{
  // get where the center of hires volume goes to in the lowres volume
  Real cx, cy, cz;
  Real dx, dy, dz;
  cx = hires->width/2; cy = hires->height/2; cz = hires->depth/2;
  TransformWithMatrix(vox_xform, cx, cy, cz, &dx, &dy, &dz);
  // get the c_ras values for this position
  TransformWithMatrix(lowres->i_to_r__, dx, dy, dz, pr, pa, ps);
  // if we use this c_ras value for the transformed hires volume, then
  // the volume will be containing the original points
}

// transform is src to dst vox to vox transform
// return rotated src whose center is at the right place
// Just rotating the original volume make the non-zero voxels go
// outside of the rotated volume.  This routine will keep the
// center of the rotated volume at the right location.
MRI *
MRIsrcTransformedCentered
(MRI *src, MRI *dst, MATRIX *stod_voxtovox, int interp_method)
{
  Real cr, ca, cs;
  MRI *rotated;
  MATRIX *stosrotVox;
  MATRIX *tmp;

  // get the c_ras value for the rotated volume
  MRIcalcCRASforHiresVolume(src, dst, stod_voxtovox, &cr, &ca, &cs);
  rotated = MRIclone(src, NULL);
  // reset the rotated volume
  rotated->c_r = cr;
  rotated->c_a = ca;
  rotated->c_s = cs;
  MRIreInitCache(rotated); // recalculate the transform
  // the map is the following
  //
  //     src -->    RAS
  //       |         |
  //       |given    |
  //       V         V
  //     dst -->    RAS
  //       |         |
  //       |       (identity)
  //       V         |
  //     src'  -->  RAS
  // new rotated src volume whose center is at the right location
  //
  tmp = MatrixMultiply(rotated->r_to_i__, dst->i_to_r__, NULL);
  stosrotVox = MatrixMultiply(tmp, stod_voxtovox, NULL);
  MRIlinearTransformInterp(src, rotated, stosrotVox, interp_method);
  return rotated;
}

////////////////////////////////////////////////////////////////////////////
// No sample method ////////////////////////////////////////////////////////
// using the src and orig_dst to modify
// the direction cosines and c_(ras) value
// so that it will be rotated in the RAS space but no pixel is sampled
MRI *MRITransformedCenteredMatrix(MRI *src, MRI *orig_dst, MATRIX *m_L)
{
  LTA *lta ;
  MRI *mri_dst ;

  lta = LTAalloc(1, NULL) ;
  MatrixCopy(m_L, lta->xforms[0].m_L) ;
  lta->type = LINEAR_VOX_TO_VOX ;
  mri_dst = MRITransformedCentered(src, orig_dst, lta) ;
  LTAfree(&lta) ;
  return(mri_dst) ;
}
MRI *MRITransformedCentered(MRI *src, MRI *orig_dst, LTA *lta)
{
  MRI *dst = 0;
  Real cx, cy, cz;
  Real cr, ca, cs;
  MATRIX *dstToRas = 0;
  MATRIX *SI = 0;
  MATRIX *D = 0;
  LT *tran = &lta->xforms[0];

  dst = MRIcopy(src, NULL);

  if (lta->num_xforms > 1)
    ErrorExit(ERROR_BADPARM, "The LTA contains more than one transforms.");

  // first verify the consistency of the transform stored geometry vs. argument
  if (tran->dst.valid == 1)
    {
      // compare the one with orig_dst to the stored one
      VG dstvg, storedvg;
      getVolGeom(orig_dst, &dstvg);
      storedvg.valid = tran->dst.valid;
      storedvg.width = tran->dst.width;
      storedvg.height = tran->dst.height;
      storedvg.depth = tran->dst.depth;
      storedvg.xsize = tran->dst.xsize;
      storedvg.ysize = tran->dst.ysize;
      storedvg.zsize = tran->dst.zsize;
      storedvg.x_r = tran->dst.x_r;
      storedvg.x_a = tran->dst.x_a;
      storedvg.x_s = tran->dst.x_s;
      storedvg.y_r = tran->dst.y_r;
      storedvg.y_a = tran->dst.y_a;
      storedvg.y_s = tran->dst.y_s;
      storedvg.z_r = tran->dst.z_r;
      storedvg.z_a = tran->dst.z_a;
      storedvg.z_s = tran->dst.z_s;
      storedvg.c_r = tran->dst.c_r;
      storedvg.c_a = tran->dst.c_a;
      storedvg.c_s = tran->dst.c_s;
      if (!vg_isEqual(&dstvg, &storedvg))
        {
          fprintf
            (stderr,
             "WARNING: stored destination volume for the "
             "transform differes from the argument.");
        }
    }
  if (tran->src.valid == 1)
    {
      // compare the one with orig_dst to the stored one
      VG srcvg, storedvg;
      getVolGeom(src, &srcvg);
      storedvg.valid = tran->src.valid;
      storedvg.width = tran->src.width;
      storedvg.height = tran->src.height;
      storedvg.depth = tran->src.depth;
      storedvg.xsize = tran->src.xsize;
      storedvg.ysize = tran->src.ysize;
      storedvg.zsize = tran->src.zsize;
      storedvg.x_r = tran->src.x_r;
      storedvg.x_a = tran->src.x_a;
      storedvg.x_s = tran->src.x_s;
      storedvg.y_r = tran->src.y_r;
      storedvg.y_a = tran->src.y_a;
      storedvg.y_s = tran->src.y_s;
      storedvg.z_r = tran->src.z_r;
      storedvg.z_a = tran->src.z_a;
      storedvg.z_s = tran->src.z_s;
      storedvg.c_r = tran->src.c_r;
      storedvg.c_a = tran->src.c_a;
      storedvg.c_s = tran->src.c_s;
      if (!vg_isEqual(&srcvg, &storedvg))
        {
          fprintf(stderr, "WARNING: stored destination volume for the "
                  "transform differes from the argument.");
        }
    }

  if (lta->type == LINEAR_RAS_TO_RAS)
    {
      MATRIX *tmp;
      //
      //      src   -->   RAS
      //       |           |
      //       | M         | Y
      //       V           V
      //    orig_dst -->  RAS
      //
      // transform it to the vox-to-vox
      // now calculate M
      tmp = MatrixMultiply(tran->m_L, src->i_to_r__, NULL);
      MatrixFree(&tran->m_L);
      tran->m_L = MatrixMultiply(orig_dst->r_to_i__, tmp, NULL);
      MatrixFree(&tmp);
      lta->type = LINEAR_VOX_TO_VOX;
    }

  if (lta->type != LINEAR_VOX_TO_VOX)
    ErrorExit
      (ERROR_BADPARM,
       "The LTA does not contain LINEASR_RAS_TO_RAS nor LINEAR_VOX_TO_VOX.");
  //
  //      src   -->   RAS
  //       |           |
  //       | M         | Y
  //       V           V
  //    orig_dst -->  RAS
  //       |           |
  //       |       (identity)
  //       V           |
  //      dst   -->   RAS
  // new rotated src volume whose center is at the right location
  //            ?
  // we try src->dst is identity (no sampling)
  //
  //    Y = i_to_r(orig_dst) * M * r_to_i(src)
  //
  // Thus we have simpler picture
  //
  //      src   -----> RAS
  //       |            |
  //    (identity)     (Y)
  //       |            |
  //       V            V
  //      dst   -----> RAS
  //            (???)
  //
  // Thus
  //      (???) = Y * i_to_r(src)
  //            = i_to_r(orig_dst) * M * r_to_i(src) * i_to_r(src)
  //
  //      (???) = i_to_r(orig_dst) * M           (A)
  //
  // Thus we derive direction cosines and c_(ras) from ???
  //
  //      (???)  =  ( X   T ) ( S  0 )    where S is the voxel size
  //                ( 0   1 ) ( 0  1 )
  // or
  //      ( X  T ) = (???) * (S^(-1) 0 )         (B)
  //      ( 0  1 )           ( 0     1 )
  //
  //
  dstToRas = MatrixMultiply(orig_dst->i_to_r__, tran->m_L, NULL);

  SI = MatrixAlloc(4, 4, MATRIX_REAL);
  *MATRIX_RELT(SI, 1, 1) = 1./dst->xsize ;
  *MATRIX_RELT(SI, 2, 2) = 1./dst->ysize ;
  *MATRIX_RELT(SI, 3, 3) = 1./dst->zsize ;
  *MATRIX_RELT(SI, 4, 4) = 1. ;

  D = MatrixMultiply(dstToRas, SI, NULL);

  dst->x_r = *MATRIX_RELT(D, 1, 1);
  dst->x_a = *MATRIX_RELT(D, 2, 1);
  dst->x_s = *MATRIX_RELT(D, 3, 1);
  dst->y_r = *MATRIX_RELT(D, 1, 2);
  dst->y_a = *MATRIX_RELT(D, 2, 2);
  dst->y_s = *MATRIX_RELT(D, 3, 2);
  dst->z_r = *MATRIX_RELT(D, 1, 3);
  dst->z_a = *MATRIX_RELT(D, 2, 3);
  dst->z_s = *MATRIX_RELT(D, 3, 3);

  MatrixFree(&D);
  MatrixFree(&SI);

  // c_ras is calculated by
  //
  //      ( X  T )(S 0)(dstwidth/2 )  = (c_r)
  // or  i_to_r(orig_dst) * M * (dstwidth/2 ) = C'
  //      ( 0  1 )(0 1)(dstheight/2)    (c_a)
  //                               (dstheight/2)
  //                   (dstdepth/2 )    (c_s)
  //                                                  (dstdepth/2 )
  //                   (    1      )    ( 1 )               (    1      )
  //
  // This is the same as the original center point
  // is mapped to orig_dst and then obtaining
  // its ras position! (because src and dst has the
  // same volume size).  The only difference
  // is its orientation with respect to orig_dst RAS.  Good
  //
  // get where the center of hires volume goes to in the lowres volume
  cx = dst->width/2; cy = dst->height/2; cz = dst->depth/2;
  // get the c_ras values for this position
  TransformWithMatrix(dstToRas, cx, cy, cz, &cr, &ca, &cs);
  // if we use this c_ras value for the transformed hires volume, then
  // the volume will be containing the original points
  dst->c_r = cr;
  dst->c_a = ca;
  dst->c_s = cs;

  // when you change the direction cosine, you have to do this
  MRIreInitCache(dst);

  MatrixFree(&dstToRas);

  return dst;
}
int
MRIcropBoundingBox(MRI *mri,    MRI_REGION  *box)
{
  box->x = MAX(0, box->x) ; box->y = MAX(0, box->y) ;box->z = MAX(0, box->z) ;
  box->dx = MIN(mri->width-box->x-1, box->dx) ;
  box->dy = MIN(mri->height-box->y-1, box->dy) ;
  box->dz = MIN(mri->depth-box->z-1, box->dz) ;
  return(NO_ERROR) ;
}

MATRIX *
MRIgetVoxelToVoxelXform(MRI *mri_src, MRI *mri_dst)
{
  MATRIX *m_ras2vox_dst, *m_vox2ras_src, *m_vox2vox ;

  m_vox2ras_src = MRIgetVoxelToRasXform(mri_src) ;
  m_ras2vox_dst = MRIgetRasToVoxelXform(mri_dst) ;
  m_vox2vox = MatrixMultiply(m_ras2vox_dst, m_vox2ras_src, NULL) ;
  MatrixFree(&m_vox2ras_src) ; MatrixFree(&m_ras2vox_dst) ;
  return(m_vox2vox) ;
}

/*--------------------------------------------------------------
  MRIfovCol(mri) - computes the edge-to-edge FOV in the column
  direction. fov is in mm.
  -------------------------------------------------------------*/
float MRIfovCol(MRI *mri)
{
  MATRIX *M,*v,*a,*b,*d;
  float fov;

  M = MRIgetVoxelToRasXform(mri) ;
  v = MatrixAlloc(4,1,MATRIX_REAL);
  v->rptr[1][4] = 1;
  v->rptr[1][1] = mri->width-1+0.5; // edge of last column
  a = MatrixMultiply(M,v,NULL);     // xyz of last column
  v->rptr[1][1] = -0.5;             // edge of first column
  b = MatrixMultiply(M,v,NULL);     // xyz of first column
  d = MatrixSubtract(a,b,NULL); // xyz difference
  fov = VectorLen(d);           // fov is in mm
  MatrixFree(&M);
  MatrixFree(&v);
  MatrixFree(&a);
  MatrixFree(&b);
  MatrixFree(&d);

  //printf("MRIfovCol() %g\n",fov);
  return(fov);
}
/* ---------------------------------------------------------------------
   MRIorientationStringToDircos() - sets the direction cosines of to
   be that dictated by the Orientation String. This is helpful for
   setting the direction cosines when the information is not present
   in a header (eg, with FSL analyze format).  The Orientation String
   is a three character string indicating the primary direction of
   each axis in the 3d matrix. The characters can be L,R,A,P,I,S.  The
   string must be valid (see MRIcheckOrientationString). If the string
   is not valid, the errors are printed and a 1 is returned. Eg, of
   valid strings are RAS, LPS, LAI. Invalid are ABC (B and C are not
   valid), RAP (the AP axis is represented twice, IS axis not at all).
   There are 48 possible valid strings. This should only be used to
   get the direction cosines "about right".
   --------------------------------------------------------------------*/

int MRIorientationStringToDircos(MRI *mri, char *ostr)
{
  int c,r=0;
  double Mdc[3][3], v=0;
  char *errstr;

  errstr = MRIcheckOrientationString(ostr);
  if(errstr != NULL){
    printf("ERROR: in orientation string %s\n",ostr);
    printf("%s",errstr);
    free(errstr);
    return(1);
  }

  // Initialize the matrix of direction cosines (Mdc)
  for(c=0; c<3; c++) for(r=0; r<3; r++) Mdc[r][c] = 0;

  // Each column of Mdc corresponds to a different axis which corresonds to
  // a different letter in the orientation string. The value of the letter
  // determine which row of the Mdc will be affected.
  for(c=0; c<3; c++){
    switch(toupper(ostr[c])){
    case 'L': r=0; v=-1; break;
    case 'R': r=0; v=+1; break;
    case 'P': r=1; v=-1; break;
    case 'A': r=1; v=+1; break;
    case 'I': r=2; v=-1; break;
    case 'S': r=2; v=+1; break;
    }
    Mdc[r][c] = v;
  }
  mri->x_r = Mdc[0][0];
  mri->x_a = Mdc[1][0];
  mri->x_s = Mdc[2][0];

  mri->y_r = Mdc[0][1];
  mri->y_a = Mdc[1][1];
  mri->y_s = Mdc[2][1];

  mri->z_r = Mdc[0][2];
  mri->z_a = Mdc[1][2];
  mri->z_s = Mdc[2][2];

  return(0);
}
/* ---------------------------------------------------------------
   MRIcheckOrientationString() - this checks the orientation string
   to make sure that it is valid. "Valid" means that all axes are
   represented exactly once and no invalid characters are present
   in the string. Case-insensitive. Returns NULL if everything is
   ok, otherwise is returns a string that lists all the errors
   it encountered.
   ---------------------------------------------------------------*/
char *MRIcheckOrientationString(char *ostr)
{
  int c, nsag=0, ncor=0, nax=0, err;
  char errstr[1000], *errstrret=NULL;

  errstr[0] = '\0';
  err = 0;

  for(c=0; c<3; c++){
    switch(toupper(ostr[c])){
    case 'L': nsag++; break;
    case 'R': nsag++; break;
    case 'P': ncor++; break;
    case 'A': ncor++; break;
    case 'I': nax++;  break;
    case 'S': nax++;  break;
    default:
      sprintf(errstr,"%s  Character %c in position %d is invalid.\n",
              errstr,ostr[c],c+1);
      err = 1;
      break;
    }
  }

  if(nsag > 1){
    sprintf(errstr,"%s  LR axis represented multiple times.\n",errstr);
    err = 1;
  }
  if(ncor > 1){
    sprintf(errstr,"%s  PA axis represented multiple times.\n",errstr);
    err = 1;
  }
  if(nax > 1){
    sprintf(errstr,"%s  IS axis represented multiple times.\n",errstr);
    err = 1;
  }
  if(nsag == 0){
    sprintf(errstr,"%s  LR axis not represented.\n",errstr);
    err = 1;
  }
  if(ncor == 0){
    sprintf(errstr,"%s  PA axis not represented.\n",errstr);
    err = 1;
  }
  if(nax == 0){
    sprintf(errstr,"%s  IS axis not represented.\n",errstr);
    err = 1;
  }

  if(err){
    errstrret = (char *) calloc(sizeof(char),strlen(errstr)+1);
    memcpy(errstrret,errstr,strlen(errstr));
  }

  return(errstrret);
}
/*------------------------------------------------------------------
  MRIdircosToOrientationString() - examines the direction cosines and
  creates an Orientation String. The Orientation String is a three
  character string indicating the primary direction of each axis
  in the 3d matrix. The characters can be L,R,A,P,I,S. Case is not
  important, but upper case is used here. If ras_good_flag == 0,
  then ostr = ??? and 1 is returned.
  ------------------------------------------------------------------*/
int MRIdircosToOrientationString(MRI *mri, char *ostr)
{
  int c;
  float Mdc[3][3], sag, cor, ax;

  if(! mri->ras_good_flag){
    ostr[0] = '?';
    ostr[1] = '?';
    ostr[2] = '?';
    return(1);
  }

  Mdc[0][0] = mri->x_r;
  Mdc[1][0] = mri->x_a;
  Mdc[2][0] = mri->x_s;

  Mdc[0][1] = mri->y_r;
  Mdc[1][1] = mri->y_a;
  Mdc[2][1] = mri->y_s;

  Mdc[0][2] = mri->z_r;
  Mdc[1][2] = mri->z_a;
  Mdc[2][2] = mri->z_s;

  for(c=0; c<3; c++) ostr[c] = '\0';

  for(c=0; c<3; c++){
    sag = Mdc[0][c]; // LR axis
    cor = Mdc[1][c]; // PA axis
    ax  = Mdc[2][c]; // IS axis
    //printf("c = %d, sag = %g, cor = %g, ax = %g\n",c,sag,cor,ax);
    if(fabs(sag) > fabs(cor) && fabs(sag) > fabs(ax)){
      if(sag > 0) ostr[c] = 'R';
      else        ostr[c] = 'L';
      continue;
    }
    if(fabs(cor) > fabs(ax)){
      if(cor > 0) ostr[c] = 'A';
      else        ostr[c] = 'P';
      continue;
    }
    if(ax > 0) ostr[c] = 'S';
    else       ostr[c] = 'I';
  }
  return(0);

}
/*-------------------------------------------------------------------
  MRIsliceDirectionName() - returns the name of the primary slice
  orientation base on the direction cosine. If mri->ras_good_flag=0,
  then "unknown" is returned.
  -------------------------------------------------------------------*/
char *MRIsliceDirectionName(MRI *mri)
{
  char ostr[4];
  char *slicedir = NULL;
  char *rtstr;
  int len;

  ostr[3] = '\0';

  MRIdircosToOrientationString(mri, ostr);
  if(toupper(ostr[2]) == 'L'|| toupper(ostr[2]) == 'R')
    slicedir = "sagittal";
  if(toupper(ostr[2]) == 'P'|| toupper(ostr[2]) == 'A')
    slicedir = "coronal";
  if(toupper(ostr[2]) == 'I'|| toupper(ostr[2]) == 'S')
    slicedir = "axial";
  if(! mri->ras_good_flag) slicedir = "unknown";

  len = strlen(slicedir);
  rtstr = (char *) calloc(sizeof(char),len+1);
  memcpy(rtstr,slicedir,len);

  return(rtstr);
}
MRI *
MRIdistanceTransform
(MRI *mri_src, MRI *mri_dist, int label, float max_dist, int mode)
{
  int x, y, z, width, height, depth, xi, yi, zi;
  int xk, yk, zk, changed, found, i ;
  MRI *mri_processed ;
  float dist, min_dist ;
  VOXEL_LIST  *vl_current, *vl_new ;

  width = mri_src->width ; height = mri_src->height ; depth = mri_src->depth ;
  if (max_dist < 0)
    max_dist = 2*MAX(MAX(width,height),depth);
  if (mri_dist == NULL)
    {
      mri_dist = MRIalloc(width, height, depth, MRI_FLOAT) ;
      MRIcopyHeader(mri_src, mri_dist) ;
    }
  else
    MRIclear(mri_dist) ;

  if (mode != DTRANS_MODE_OUTSIDE)
    vl_current = VLSTcreate(mri_src, label, label, NULL, 0, 1) ;
  else
    vl_current = VLSTcreate(mri_src, label, label, NULL, 0, 0) ;
  vl_new = VLSTdilate(vl_current, VL_DILATE_REPLACE, NULL) ;

  mri_processed = VLSTcreateMri(vl_current, 128) ;
  if (Gdiag & DIAG_WRITE && DIAG_VERBOSE_ON)
    MRIwrite(mri_processed, "p.mgz") ;

  do
    {
      for (i = 0 ; i < vl_new->nvox ; i++)
        {
          x = vl_new->xi[i] ;
          y = vl_new->yi[i] ;
          z = vl_new->zi[i] ;
          found = 0 ; min_dist = 1e8 ;
          for (xk = -1 ; xk <= 1 ; xk++)  // find min dist of nbrs
            {
              xi = mri_src->xi[x+xk] ;
              for (yk = -1 ; yk <= 1 ; yk++)
                {
                  yi = mri_src->yi[y+yk] ;
                  for (zk = -1 ; zk <= 1 ; zk++)
                    {
                      zi = mri_src->zi[z+zk] ;
                      if (MRIvox(mri_processed, xi, yi, zi) == 0)
                        continue ;
                      dist = MRIgetVoxVal(mri_dist, xi, yi, zi, 0) ;
                      dist += sqrt(xk*xk+yk*yk+zk*zk) ;
                      if (found == 0 || dist < min_dist)
                        {
                          found = 1 ;
                          min_dist = dist ;
                        }
                    }
                }
            }

          if (found > 0)
            {
              changed++ ;
              MRIsetVoxVal(mri_dist, x, y, z, 0, min_dist) ;
            }
          else
            DiagBreak() ;
        }

      VLSTfree(&vl_current) ;
      VLSTaddToMri(vl_new, mri_processed, 128) ;
      vl_current = vl_new ;
      vl_new = VLSTdilate(vl_current, VL_DILATE_REPLACE, mri_processed) ;
#if 0
      if (vl_new == NULL)
        printf("complete\n") ;
      else
        printf("examining %d voxels...\n", vl_new->nvox) ;
#endif
    } while (vl_new != NULL) ;

  if (mode != DTRANS_MODE_OUTSIDE)  // set interior distances to negative vals
    {
      int x, y, z ;

      for (x = 0 ; x < mri_src->width ; x++)
        {
          for (y = 0 ; y < mri_src->height ; y++)
            {
              for (z = 0 ; z < mri_src->depth ; z++)
                {
                  if (x == Gx && y == Gy && z == Gz)
                    DiagBreak() ;
                  if (FEQUAL(MRIgetVoxVal(mri_src, x, y, z, 0), label))
                    {
                      dist = MRIgetVoxVal(mri_dist, x, y, z, 0) ;
                      if (!FZERO(dist))
                        MRIsetVoxVal(mri_dist, x, y, z, 0, -dist) ;
                    }
                }
            }
        }
    }

  if (Gdiag & DIAG_WRITE && DIAG_VERBOSE_ON)
    MRIwrite(mri_dist, "dist.mgz") ;
  VLSTfree(&vl_current)  ;
  MRIfree(&mri_processed) ;

  if (mode == DTRANS_MODE_UNSIGNED)
    MRIabs(mri_dist, mri_dist) ;
  return(mri_dist) ;
}

/*-------------------------------------------------------------------
  MRIreverseSliceOrder() - reverses the order of the slices WITHOUT
  changing the gemoetry information. This is specially desgined to
  undo the reversal that Siemens sometimes makes to volumes. If
  using FreeSurfer unpacking (ie, DICOMRead.c), it should only need
  to be done to mosaics. Note: cannot be done in-place!
  -------------------------------------------------------------------*/
MRI *MRIreverseSliceOrder(MRI *invol, MRI *outvol)
{
  int c,r,s1,s2,f;
  double val;

  if(invol == outvol){
    printf("ERROR: MRIreverseSliceOrder: cannot be done in-place\n");
    return(NULL);
  }

  outvol = MRIclone(invol,outvol);

  s2 = invol->depth;
  for(s1=0; s1 < invol->depth; s1++){
    s2--;
    for(c=0; c < invol->width; c++){
      for(r=0; r < invol->height; r++){
        for(f=0; f < invol->nframes; f++){
          val = MRIgetVoxVal(invol,c,r,s1,f);
          MRIsetVoxVal(outvol,c,r,s2,f,val);
        }
      }
    }
  }

  return(outvol);
}
/*-------------------------------------------------------------------
  MRIcopyVolGeomToMRI - copies the volume geometry passed in into the MRI
  structure.
  -------------------------------------------------------------------*/
int
MRIcopyVolGeomToMRI(MRI *mri, VOL_GEOM *vg)
{
  mri->xsize = vg->xsize ;
  mri->ysize = vg->ysize ;
  mri->zsize = vg->zsize ;
  mri->x_r = vg->x_r;
  mri->y_r = vg->y_r;
  mri->z_r = vg->z_r;
  mri->c_r = vg->c_r;
  mri->x_a = vg->x_a;
  mri->y_a = vg->y_a;
  mri->z_a = vg->z_a;
  mri->c_a = vg->c_a;
  mri->x_s = vg->x_s;
  mri->y_s = vg->y_s;
  mri->z_s = vg->z_s;
  mri->c_s = vg->c_s;
  return(NO_ERROR) ;
}

#define NDIRS 3
static int dirs[NDIRS][3] =
  {
    { 0, 0, 1},
    { 0, 1, 0},
    { 1, 0, 0}
  } ;

MRI *
MRInonMaxSuppress(MRI *mri_src, MRI *mri_sup, float thresh, int thresh_dir)
{
  int  x, y, z, i, max_i ;
  Real val, dx, dy, dz, Ix, Iy, Iz;
  Real dot, max_dot, p1_x, p1_y, p1_z, p2_x, p2_y, p2_z;
  Real p3_x, p3_y, p3_z, nx, ny, nz, u, xf, yf, zf, oval, numer, denom ;

  mri_sup = MRIcopy(mri_src, mri_sup) ;

  for (x = 0 ; x < mri_src->width ; x++)
    {
      for (y = 0 ; y < mri_src->height ; y++)
        {
          for (z = 0 ; z < mri_src->depth ; z++)
            {
              if (x == Gx && y == Gy && z == Gz)
                DiagBreak() ;
              val = MRIgetVoxVal(mri_src, x, y, z,0) ;
              val = val*thresh_dir ;
              if (val <= thresh)
                {
                  MRIsetVoxVal(mri_sup, x, y, z, 0, 0.0) ;
                  continue ;
                }
              MRIsampleVolumeGradient(mri_src, x, y, z,  &Ix, &Iy, &Iz) ;
              dx = dirs[0][0] ; dy = dirs[0][1] ; dz = dirs[0][2] ;
              max_i = 0 ;
              max_dot = fabs(dx*Ix+dy*Iy+dz*Iz) ;
              for (i = 1 ; i < NDIRS ; i++)
                {
                  // compute intersection of gradient
                  // direction with coordinate planes
                  dx = dirs[i][0] ; dy = dirs[i][1] ; dz = dirs[i][2] ;
                  dot = fabs(dx*Ix+dy*Iy+dz*Iz) ;
                  if (dot > max_dot)
                    {
                      max_i = i ;
                      max_dot = dot ;
                    }
                }
              // normal to coordinate plane -
              // compute intersections of gradient dir with plane
              nx = dirs[max_i][0] ;
              ny = dirs[max_i][1] ;
              nz = dirs[max_i][2] ;
              p1_x = x ;    p1_y = y ;    p1_z = z ;    // point on line
              p2_x = x+Ix ; p2_y = y+Iy ; p2_z = z+Iz ; // 2nd point on
              //                                           gradient line
              p3_x = x+nx ; p3_y = y+ny ; p3_z = z+nz ; // point on plane
              numer =
                nx * (p3_x - p1_x) +
                ny * (p3_y - p1_y) +
                nz * (p3_z - p1_z) ;
              denom =
                nx * (p2_x - p1_x) +
                ny * (p2_y - p1_y) +
                nz * (p2_z - p1_z) ;
              if (DZERO(denom))
                {
                  DiagBreak() ;
                  continue ;
                }
              u = numer / denom ;
              xf = p1_x + u * Ix ;  yf = p1_y + u * Iy ;  zf = p1_z + u * Iz ;
              MRIsampleVolume(mri_src, xf, yf, zf, &oval) ;
              oval = oval*thresh_dir ;
              if (oval > val)   // not at a maximum
                {
                  MRIsetVoxVal(mri_sup, x, y, z, 0, 0) ;
                  continue ;
                }
              xf = p1_x - u * Ix ;  yf = p1_y - u * Iy ;  zf = p1_z - u * Iz ;
              MRIsampleVolume(mri_src, xf, yf, zf, &oval) ;
              oval = oval*thresh_dir ;
              if (oval > val)   // not at a maximum
                {
                  MRIsetVoxVal(mri_sup, x, y, z, 0, 0) ;
                  continue ;
                }
            }
        }
    }

  return(mri_sup) ;
}
MRI *
MRIextractRegionAndPad
(MRI *mri_src, MRI *mri_dst, MRI_REGION *region, int pad)
{
  MRI *mri_tmp ;
  MRI_REGION box ;

  if (region == NULL)
    {
      region = & box ;
      box.x = box.y = box.z = 0 ;
      box.dx = mri_src->width ;
      box.dy = mri_src->height ;
      box.dz = mri_src->depth ;
    }
  mri_dst =
    MRIalloc
    (region->dx+2*pad, region->dy+2*pad, region->dz+2*pad, mri_src->type) ;
  MRIcopyHeader(mri_src, mri_dst) ;
  mri_tmp = MRIextractInto(mri_src, NULL, region->x, region->y, region->z,
                           region->dx, region->dy, region->dz, 0, 0, 0) ;
  MRIextractInto
    (mri_tmp, mri_dst, 0, 0, 0,
     region->dx, region->dy, region->dz, pad, pad, pad);
  return(mri_dst) ;
}

MRI *
MRIsetValuesOutsideRegion
(MRI *mri_src, MRI_REGION *region, MRI *mri_dst, float val)
{
  int x, y, z ;

  mri_dst = MRIcopy(mri_src, mri_dst) ;

  for (x = 0 ; x < mri_dst->width ; x++)
    {
      if (x >= region->x && x < region->x+region->dx)
        continue ;
      for (y = 0 ; y < mri_dst->height ; y++)
        {
          if (y >= region->y && y < region->y+region->dy)
            continue ;
          for (z = 0 ; z < mri_dst->depth ; z++)
            {
              if (z >= region->z && z < region->z+region->dz)
                continue ;
              MRIsetVoxVal(mri_dst, x, y, z, 0, val) ;
            }
        }
    }

  return(mri_dst) ;
}

int
MRIlabelsInNbhd(MRI *mri, int x, int y, int z, int whalf, int label)
{
  int xi, yi, zi, xk, yk, zk, count ;

  for (count = 0, zk = -whalf ; zk <= whalf ; zk++)
    {
      zi = mri->zi[z+zk] ;
      for (yk = -whalf ; yk <= whalf ; yk++)
        {
          yi = mri->yi[y+yk] ;
          for (xk = -whalf ; xk <= whalf ; xk++)
            {
              xi = mri->xi[x+xk] ;
              if (MRIvox(mri, xi, yi, zi) == label)
                count++;
            }
        }
    }
  return(count) ;
}