/*****************************************************************************
   Major portions of this software are copyrighted by the Medical College
   of Wisconsin, 1994-2000, and are released under the Gnu General Public
   License, Version 2.  See the file README.Copyright for details.
******************************************************************************/

/*---------------------------------------------------------------------------*/
/*
  This file contains routines used by plug_intracranial and 3dIntracranial.

  File:    Intracranial.c
  Author:  B. Douglas Ward
  Date:    04 June 1999

  Mod:     Correction to initialization in center of mass calculation.
  Date:    11 February 2000

  Mod:     Added option to suppress spatial smoothing of segmentation mask.
  Date:    03 December 2001
*/


/*---------------------------------------------------------------------------*/
/*
  Verify that inputs are acceptable.
*/

int verify_inputs()
{
  char message[MAX_STRING_LENGTH];    /* error message */


  /*----- Check for required datasets -----*/
  if (anat == NULL)
    { 
      sprintf (message, "No anatomical image");
      SI_error (message); 
      return (0);
    }
  if (! ISANAT(anat))
    { 
      sprintf (message, "Input dataset must be anatomical image");
      SI_error (message); 
      return (0);
    }
  if (DSET_BRICK_TYPE(anat,0) != MRI_short)
    {
      sprintf (message, "Anatomical image must have data type: short integer");
      SI_error (message);
      return (0);
    }

#ifdef PLUG_INTRACRANIAL
  if (dset == NULL)
    { 
      sprintf (message, "No output dataset");
      SI_error (message);
      return (0);
    }
  if (DSET_BRICK_TYPE(dset,0) != MRI_short)
    {
      sprintf (message, "Output dataset must have data type: short integer");
      SI_error (message);
      return (0);
    }
#else
  if (prefix_filename == NULL)
    {
      sprintf (message, "Must specify output prefix filename");
      SI_error (message);
      return (0);
    }

  /*----- Check whether output file already exists -----*/
  check_one_output_file (prefix_filename);
#endif


  /*----- Check compatibility of datasets -----*/
#ifdef PLUG_INTRACRANIAL
  if (! EQUIV_DATAXES (anat->daxes, dset->daxes))
    {
      sprintf (message, 
	       "Anatomical image and output dataset are incompatible");
      SI_error (message);
      return (0);
    }
#endif
    

  /*----- Check allowed range of intensity values -----*/
  if (min_val_float >= max_val_float)
    {
      sprintf (message, "min_val >= max_val ???");
      SI_error (message);
      return (0);
    }

  return (1);

}


/*---------------------------------------------------------------------------*/
/*
   Flood filling a byte array:
     nx = 1st dimension
     ny = 2nd dimension
     ix = start point
     jy = end point
     ar = array, with 0's everywhere except 1's as barriers to flooding

   All filled points (starting with ix,jy) will get the value 2.

   This routine was adapted from:  plug_drawdset.
*/

void draw_2dfiller( int nx , int ny , int ix , int jy , byte * ar )
{
   int ii,jj , ip,jp , num ;

#define AR(i,j) ar[(i)+(j)*nx]


   /*----- Test for early termination -----*/
   if (AR(ix,jy) != 0)  return;


   /* fill out in cross from 1st point */

   ip = ix ; jp = jy ; AR(ip,jp) = 2 ;

   for( ii=ip+1; ii < nx && AR(ii,jp) == 0; ii++ ) AR(ii,jp) = 2;
   for( ii=ip-1; ii >= 0 && AR(ii,jp) == 0; ii-- ) AR(ii,jp) = 2;
   for( jj=jp+1; jj < ny && AR(ip,jj) == 0; jj++ ) AR(ip,jj) = 2;
   for( jj=jp-1; jj >= 0 && AR(ip,jj) == 0; jj-- ) AR(ip,jj) = 2;

   /* brute force repetition of the cross technique */

   do {
      num = 0 ;
      for( jp=0 ; jp < ny ; jp++ ){
         for( ip=0 ; ip < nx ; ip++ ){
            if( AR(ip,jp) == 2 ){
               for( ii=ip+1; ii < nx && AR(ii,jp) == 0; ii++ ){ AR(ii,jp) = 2; num++; }
               for( ii=ip-1; ii >= 0 && AR(ii,jp) == 0; ii-- ){ AR(ii,jp) = 2; num++; }
               for( jj=jp+1; jj < ny && AR(ip,jj) == 0; jj++ ){ AR(ip,jj) = 2; num++; }
               for( jj=jp-1; jj >= 0 && AR(ip,jj) == 0; jj-- ){ AR(ip,jj) = 2; num++; }
            }
         }
      }
   } while( num > 0 ) ;

   return ;
}


/*---------------------------------------------------------------------------*/
/*
  Find the center of mass of the brain image.
*/

void center_of_mass (int * cx, int * cy, int * cz)

{
  int nx, ny, nz, nxy, nxyz;               /* dataset dimensions in voxels */
  int ix, jy, kz, ixyz;                    /* voxel indices */
  short * anat_data  = NULL;               /* data from anatomical image */
  float f, sum, sumx, sumy, sumz;          /* sums for calculating means */


  /*----- Progress report -----*/
  if (! quiet)  printf ("\nCalculating center of mass \n");


  /*----- Initialize local variables -----*/
  anat_data  = (short *) DSET_BRICK_ARRAY(anat,0) ;
  nx = DSET_NX(anat);   ny = DSET_NY(anat);   nz = DSET_NZ(anat);
  nxy = nx * ny;   nxyz = nxy * nz;


  /*----- Sum over all voxels -----*/
  sum = 0.0;   sumx = 0.0;   sumy = 0.0;   sumz = 0.0;
  for (kz = 0;  kz < nz;  kz++)
    for (jy = 0;  jy < ny;  jy++)
      for (ix = 0;  ix < nx;  ix++)
	{
	  ixyz = ix + jy*nx + kz*nxy;
	  f = anat_data[ixyz];
	  sum += f;   sumx += ix*f;   sumy += jy*f;   sumz += kz*f;
	}


  /*----- Calculate center of mass -----*/
  *cx = sumx / sum;
  *cy = sumy / sum;
  *cz = sumz / sum;

  if (! quiet)  printf ("Center of mass:   ix = %d   jy = %d   kz = %d  \n", 
			*cx, *cy, *cz);
 
}


/*---------------------------------------------------------------------------*/
/*
  Segment the slices perpendicular to the x-axis.
*/

void segment_x_slices
(
  short * cv,                  /* volume with 1's at non-brain locations */
  int cx,                      /* center slice location */
  RwcBoolean axial_slice          /* true if x-slices are axial slices */
)

{
  const int MAXNPTS = 1000;    /* max. number of random initial test points */
  const int MAXFOUND = 10;     /* max. number of initial search points */

  int nx, ny, nz, nxy, nxz, nyz, nxyz;     /* dataset dimensions in voxels */
  int ix, jy, kz, ixy, ixz, iyz, ixyz;     /* voxel indices */

  int m;                       /* slice index */
  short * anat_data  = NULL;   /* data from anatomical image */
  float z;                     /* voxel gray-scale intensity */
  int nmpts, found;            /* random search counters */
  byte * slice = NULL;         /* current slice brain mask */
  byte * pslice = NULL;        /* previous slice non-brain mask */

  int count;                   /* count of brain voxels in current slice */
  int maxcount = 0;            /* maximum number of brain voxels in a slice */
  float threshold = 0.05;      /* stop if count falls below 5% of maximum */


  /*----- Progress report -----*/
  if (! quiet)  printf ("\nSegmenting slices perpendicular to x-axis \n");


  /*----- Initialize local variables -----*/
  anat_data  = (short *) DSET_BRICK_ARRAY(anat,0) ;
  nx = DSET_NX(anat);   ny = DSET_NY(anat);   nz = DSET_NZ(anat);
  nxy = nx*ny;   nxz = nx*nz;   nyz = ny*nz;   nxyz = nxy*nz;


  /*----- Allocate memory -----*/
  slice = (byte *)  malloc (nyz * sizeof(byte));
  pslice = (byte *) malloc (nyz * sizeof(byte));


  /*----- Loop over x-slices -----*/
  m = 0; 
  while (m <= nx)
    {
      /*----- Start from center slice -----*/
      if (m <= cx)  ix = cx - m;
      else          ix = m-1;
      

      /*----- Initialize mask  -----*/
      if (ix == cx)  
	{
	  if (axial_slice)
	    for (iyz = 0;  iyz < nyz;  iyz++)   pslice[iyz] = 0;
	  else
	    {
	      for (kz = 0;  kz < nz;  kz++)
		for (jy = 0;  jy < ny;  jy++)
		  {
		    iyz = jy + kz*ny;
		    ixyz = ix + jy*nx + kz*nxy;
		    if (cv[ixyz])   pslice[iyz] = 2;
		    else            pslice[iyz] = 0;
		  }
	    }
	}


      /*----- Examine each voxel for possible inclusion in brain -----*/
      for (kz = 0;  kz < nz;  kz++)
	for (jy = 0;  jy < ny;  jy++)
	  {
	    iyz = jy + kz*ny;
	    ixyz = ix + jy*nx + kz*nxy;
	    z = anat_data[ixyz];

	    if (pslice[iyz] == 2)        slice[iyz] = 1;
	    else if (z < min_val_float)  slice[iyz] = 1;
	    else if (z > max_val_float)  slice[iyz] = 1;
	    else                         slice[iyz] = 0;
	  }


      /*----- Fill brain voxels from center out -----*/
      draw_2dfiller (ny, nz, ny/2, nz/2, slice);


      /*----- Use additional random starting points for robustness -----*/
      nmpts = 0;   found = 0;
      while ((nmpts < MAXNPTS) && (found < MAXFOUND))
	{
	  nmpts++;
	  if ((ix == cx) && axial_slice)
	    {
	      jy = (3*ny/8) + (int) (rand_uniform(0.0,1.0) * ny/4);
	      kz = (3*nz/8) + (int) (rand_uniform(0.0,1.0) * nz/4);
	    }
	  else
	    {
	      jy = (int) (rand_uniform(0.0,1.0) * ny);
	      kz = (int) (rand_uniform(0.0,1.0) * nz);
	    }
	  if (slice[jy+kz*ny] != 1)  found++;
	  draw_2dfiller (ny, nz, jy, kz, slice);
	}


      /*----- Filled brain voxels become barriers -----*/
      for (iyz = 0;  iyz < nyz;  iyz++)
	if (slice[iyz] == 2)  pslice[iyz] = 1;
	else	              pslice[iyz] = 0;


      /*----- Now fill non-brain voxels from outside in -----*/
      draw_2dfiller (ny, nz, 0,    0,    pslice);
      draw_2dfiller (ny, nz, ny-1, 0,    pslice);
      draw_2dfiller (ny, nz, 0,    nz-1, pslice);
      draw_2dfiller (ny, nz, ny-1, nz-1, pslice);


      /*----- Save results for this slice -----*/
      count = 0;
      for (kz = 0;  kz < nz;  kz++)
	for (jy = 0;  jy < ny;  jy++)
	  {
	    iyz = jy + kz*ny;
	    ixyz = ix + jy*nx + kz*nxy;
	    if (pslice[iyz] != 2)
	      {
		cv[ixyz] = 0;
		count++;
	      }
	  }


      /*----- Slightly increase size of brain mask for next slice -----*/
      for (kz = 1;  kz < nz-1;  kz++)
	for (jy = 1;  jy < ny-1;  jy++)
	  {
	    iyz = jy + kz*ny;
	    ixyz = ix + jy*nx + kz*nxy;
	    if (!cv[ixyz])  
	      {
		pslice[iyz]    = 0;
		pslice[iyz+1]  = 0;
		pslice[iyz-1]  = 0;
		pslice[iyz-ny] = 0;
		pslice[iyz+ny] = 0;
	      }
	  }


      /*----- Examine count of brain voxels for this slice -----*/
      if (count > maxcount)  maxcount = count;
      if (count < threshold * maxcount)
	{
	  if (m <= cx)  m = cx;
	  else          m = nx;
	}


      /*----- Increment slice index -----*/
      m++;

    }  /* Loop over x-slices */


  /*----- Release memory -----*/
  free (pslice);   pslice = NULL;
  free (slice);    slice = NULL;


  return;

}


/*---------------------------------------------------------------------------*/
/*
  Segment the slices perpendicular to the y-axis.
*/

void segment_y_slices
(
  short * cv,                  /* volume with 1's at non-brain locations */
  int cy,                      /* center slice location */
  RwcBoolean axial_slice          /* true if y-slices are axial slices */
)

{
  const int MAXNPTS = 1000;    /* max. number of random initial test points */
  const int MAXFOUND = 10;     /* max. number of initial search points */

  int nx, ny, nz, nxy, nxz, nyz, nxyz;     /* dataset dimensions in voxels */
  int ix, jy, kz, ixy, ixz, iyz, ixyz;     /* voxel indices */

  int m;                       /* slice index */
  short * anat_data  = NULL;   /* data from anatomical image */
  float z;                     /* voxel gray-scale intensity */
  int nmpts, found;            /* random search counters */
  byte * slice = NULL;         /* current slice brain mask */
  byte * pslice = NULL;        /* previous slice non-brain mask */

  int count;                   /* count of brain voxels in current slice */
  int maxcount = 0;            /* maximum number of brain voxels in a slice */
  float threshold = 0.05;      /* stop if count falls below 5% of maximum */


  /*----- Progress report -----*/
  if (! quiet)  printf ("\nSegmenting slices perpendicular to y-axis \n");


  /*----- Initialize local variables -----*/
  anat_data  = (short *) DSET_BRICK_ARRAY(anat,0) ;
  nx = DSET_NX(anat);   ny = DSET_NY(anat);   nz = DSET_NZ(anat);
  nxy = nx*ny;   nxz = nx*nz;   nyz = ny*nz;   nxyz = nxy*nz;


  /*----- Allocate memory -----*/
  slice = (byte *)  malloc (nxz * sizeof(byte));
  pslice = (byte *) malloc (nxz * sizeof(byte));


  /*----- Loop over y-slices -----*/
  m = 0;
  while (m <= ny)
    {
      /*----- Start from center slice -----*/
      if (m <= cy)  jy = cy - m;
      else          jy = m-1;
      

      /*----- Initialize mask  -----*/
      if (jy == cy) 
	{ 
	  if (axial_slice)
	    for (ixz = 0;  ixz < nxz;  ixz++)   pslice[ixz] = 0;
	  else 
	    { 
	      for (kz = 0;  kz < nz;  kz++)
		for (ix = 0;  ix < nx;  ix++)
		  {
		    ixz = ix + kz*nx;
		    ixyz = ix + jy*nx + kz*nxy;
		    if (cv[ixyz])   pslice[ixz] = 2;
		    else            pslice[ixz] = 0;
		  }
	    }
	}


      /*----- Examine each voxel for possible inclusion in brain -----*/
      for (kz = 0;  kz < nz;  kz++)
	for (ix = 0;  ix < nx;  ix++)
	  {
	    ixz = ix + kz*nx;
	    ixyz = ix + jy*nx + kz*nxy;
	    z = anat_data[ixyz];

	    if (pslice[ixz] == 2)        slice[ixz] = 1;
	    else if (z < min_val_float)  slice[ixz] = 1;
	    else if (z > max_val_float)  slice[ixz] = 1;
	    else                         slice[ixz] = 0;
	  }


      /*----- Fill brain voxels from center out -----*/
      draw_2dfiller (nx, nz, nx/2, nz/2, slice);


      /*----- Use additional random starting points for robustness -----*/
      nmpts = 0;   found = 0;
      while ((nmpts < MAXNPTS) && (found < MAXFOUND))
	{
	  nmpts++;
	  if ((jy == cy) && axial_slice)
	    {
	      ix = (3*nx/8) + (int) (rand_uniform(0.0,1.0) * nx/4);
	      kz = (3*nz/8) + (int) (rand_uniform(0.0,1.0) * nz/4);
	    }
	  else
	    {
	      ix = (int) (rand_uniform(0.0,1.0) * nx);
	      kz = (int) (rand_uniform(0.0,1.0) * nz);
	    }
	  if (slice[ix+kz*nx] != 1)  found++;
	  draw_2dfiller (nx, nz, ix, kz, slice);
	}


      /*----- Filled brain voxels become barriers -----*/
      for (ixz = 0;  ixz < nxz;  ixz++)
	if (slice[ixz] == 2)  pslice[ixz] = 1;
	else	              pslice[ixz] = 0;


      /*----- Now fill non-brain voxels from outside in -----*/
      draw_2dfiller (nx, nz, 0,    0,    pslice);
      draw_2dfiller (nx, nz, nx-1, 0,    pslice);
      draw_2dfiller (nx, nz, 0,    nz-1, pslice);
      draw_2dfiller (nx, nz, nx-1, nz-1, pslice);


      /*----- Save results for this slice -----*/
      count = 0;
      for (kz = 0;  kz < nz;  kz++)
	for (ix = 0;  ix < nx;  ix++)
	  {
	    ixz = ix + kz*nx;
	    ixyz = ix + jy*nx + kz*nxy;
	    if (pslice[ixz] != 2)  
	      {
		cv[ixyz] = 0;
		count++;
	      }
	  }


      /*----- Slightly increase size of brain mask for next slice -----*/
      for (kz = 1;  kz < nz-1;  kz++)
	for (ix = 1;  ix < nx-1;  ix++)
	  {
	    ixz = ix + kz*nx;
	    ixyz = ix + jy*nx + kz*nxy;
	    if (!cv[ixyz])  
	      {
		pslice[ixz]    = 0;
		pslice[ixz+1]  = 0;
		pslice[ixz-1]  = 0;
		pslice[ixz-nx] = 0;
		pslice[ixz+nx] = 0;
	      }
	  }


      /*----- Examine count of brain voxels for this slice -----*/
      if (count > maxcount)  maxcount = count;
      if (count < threshold * maxcount)
	{
	  if (m <= cy)  m = cy;
	  else          m = ny;
	}
      

      /*----- Increment slice index -----*/
      m++;

    }  /* Loop over y-slices */


  /*----- Release memory -----*/
  free (pslice);   pslice = NULL;
  free (slice);    slice = NULL;


  return;

}


/*---------------------------------------------------------------------------*/
/*
  Segment the slices perpendicular to the z-axis.
*/

void segment_z_slices
(
  short * cv,                  /* volume with 1's at non-brain locations */
  int cz,                      /* center slice location */
  RwcBoolean axial_slice          /* true if z-slices are axial slices */
)

{
  const int MAXNPTS = 1000;    /* max. number of random initial test points */
  const int MAXFOUND = 10;     /* max. number of initial search points */

  int nx, ny, nz, nxy, nxz, nyz, nxyz;     /* dataset dimensions in voxels */
  int ix, jy, kz, ixy, ixz, iyz, ixyz;     /* voxel indices */

  int m;                       /* slice index */
  short * anat_data  = NULL;   /* data from anatomical image */
  float z;                     /* voxel gray-scale intensity */
  int nmpts, found;            /* random search counters */
  byte * slice = NULL;         /* current slice brain mask */
  byte * pslice = NULL;        /* previous slice non-brain mask */

  int count;                   /* count of brain voxels in current slice */
  int maxcount = 0;            /* maximum number of brain voxels in a slice */
  float threshold = 0.05;      /* stop if count falls below 5% of maximum */


  /*----- Progress report -----*/
  if (! quiet)  printf ("\nSegmenting slices perpendicular to z-axis \n");


  /*----- Initialize local variables -----*/
  anat_data  = (short *) DSET_BRICK_ARRAY(anat,0) ;
  nx = DSET_NX(anat);   ny = DSET_NY(anat);   nz = DSET_NZ(anat);
  nxy = nx*ny;   nxz = nx*nz;   nyz = ny*nz;   nxyz = nxy*nz;


  /*----- Allocate memory -----*/
  slice = (byte *)  malloc (nxy * sizeof(byte));
  pslice = (byte *) malloc (nxy * sizeof(byte));


  /*----- Loop over z-slices -----*/
  m = 0;
  while (m <= nz)
    {
      /*----- Start from center slice -----*/
      if (m <= cz)  kz = cz - m;
      else          kz = m-1;

      
      /*----- Initialize mask -----*/
      if (kz == cz)  
	{
	  if (axial_slice)
	    for (ixy = 0;  ixy < nxy;  ixy++)   pslice[ixy] = 0;
	  else
	    {
	      for (ix = 0;  ix < nx;  ix++)
		for (jy = 0;  jy < ny;  jy++)
		  {
		    ixy = ix + jy*nx;
		    ixyz = ix + jy*nx + kz*nxy;
		    if (cv[ixyz])   pslice[ixy] = 2;
		    else            pslice[ixy] = 0;
		  }
	    }
	}


      /*----- Examine each voxel for possible inclusion in brain -----*/
      for (jy = 0;  jy < ny;  jy++)
	for (ix = 0;  ix < nx;  ix++)
	  {
	    ixy = ix + jy*nx;
	    ixyz = ix + jy*nx + kz*nxy;
	    z = anat_data[ixyz];

	    if (pslice[ixy] == 2)        slice[ixy] = 1;
	    else if (z < min_val_float)  slice[ixy] = 1;
	    else if (z > max_val_float)  slice[ixy] = 1;
	    else                         slice[ixy] = 0;
	  }


      /*----- Fill brain voxels from center out -----*/
      draw_2dfiller (nx, ny, nx/2, ny/2, slice);


      /*----- Use additional random starting points for robustness -----*/
      nmpts = 0;   found = 0;
      while ((nmpts < MAXNPTS) && (found < MAXFOUND))
	{
	  nmpts++;
	  if ((kz == cz) && axial_slice)
	    {
	      ix = (3*nx/8) + (int) (rand_uniform(0.0,1.0) * nx/4);
	      jy = (3*ny/8) + (int) (rand_uniform(0.0,1.0) * ny/4);
	    }
	  else
	    {
	      ix = (int) (rand_uniform(0.0,1.0) * nx);
	      jy = (int) (rand_uniform(0.0,1.0) * ny);
	    }
	  if (slice[ix+jy*nx] != 1)  found++;
	  draw_2dfiller (nx, ny, ix, jy, slice);
	}


      /*----- Filled brain voxels become barriers -----*/
      for (ixy = 0;  ixy < nxy;  ixy++)
	if (slice[ixy] == 2)  pslice[ixy] = 1;
	else	              pslice[ixy] = 0;


      /*----- Now fill non-brain voxels from outside in -----*/
      draw_2dfiller (nx, ny, 0,    0,    pslice);
      draw_2dfiller (nx, ny, nx-1, 0,    pslice);
      draw_2dfiller (nx, ny, 0,    ny-1, pslice);
      draw_2dfiller (nx, ny, nx-1, ny-1, pslice);


      /*----- Save results for this slice -----*/
      count = 0;
      for (jy = 0;  jy < ny;  jy++)
	for (ix = 0;  ix < nx;  ix++)
	  {
	    ixy = ix + jy*nx;
	    ixyz = ix + jy*nx + kz*nxy;
	    if (pslice[ixy] != 2)   
	      {
		cv[ixyz] = 0;
		count++;
	      }
	  }


      /*----- Slightly increase size of brain mask for next slice -----*/
      for (jy = 1;  jy < ny-1;  jy++)
	for (ix = 1;  ix < nx-1;  ix++)
	  {
	    ixy = ix + jy*nx;
	    ixyz = ix + jy*nx + kz*nxy;
	    if (!cv[ixyz])  
	      {
		pslice[ixy]    = 0;
		pslice[ixy+1]  = 0;
		pslice[ixy-1]  = 0;
		pslice[ixy-nx] = 0;
		pslice[ixy+nx] = 0;
	      }
	  }


      /*----- Examine count of brain voxels for this slice -----*/
      if (count > maxcount)  maxcount = count;
      if (count < threshold * maxcount)
	{
	  if (m <= cz)  m = cz;
	  else          m = nz;
	}


      /*----- Increment slice index -----*/
      m++;

    }  /* Loop over z-slices */


  /*----- Release memory -----*/
  free (pslice);   pslice = NULL;
  free (slice);    slice = NULL;


  return;

}


/*---------------------------------------------------------------------------*/
/*
  Create envelope for segmented image.
*/

void segment_envelope
(
  short * cv,                  /* volume with 1's at non-brain locations */
  int cx, int cy, int cz       /* location of center of mass */
)

{
#define MAXLAT 90
#define MAXLNG 180

  int nx, ny, nz, nxy, nxz, nyz, nxyz;     /* dataset dimensions in voxels */
  int ix, jy, kz, ixy, ixz, iyz, ixyz;     /* voxel indices */

  /* 24 Mar 2004: on Mac OS X, the compiler generates incorrect code
                  for these large arrays when optimization is turned on;
                  instead, use malloc() to get them if AIZE is not defined */

#undef AIZE
#ifndef DARWIN
# define AIZE
#endif

#ifdef  AIZE
  float radius[MAXLAT][MAXLNG];      /* max. radius vector to brain voxel */
  float smradius[MAXLAT][MAXLNG];    /* array of smoothed radius vectors */
#else
  float **radius , **smradius ;
#endif

  int ilat, jlat;              /* radius array latitude index */
  int ilng, jlng;              /* radius array longitude index */
  float deltalat, deltalng;    /* increments in latitude and longitude */
  float lat, clat, slat;       /* voxel latitude */
  float lng, clng, slng;       /* voxel longitude */
  int ir, i, j;                /* indices */
  float rxy;                   /* radius to voxel in xy-plane */
  float r;                     /* radius to voxel from center of brain */
  int nr;                      /* maximum test radius */
  float maxr;                  /* maximum radius to voxel inside the brain */


  /*----- Progress report -----*/
  if (! quiet)  printf ("\nCreating envelope for segmented image \n");

#ifndef AIZE
  radius   = (float **) malloc(sizeof(float *)*MAXLAT) ;
  smradius = (float **) malloc(sizeof(float *)*MAXLAT) ;
  for (ilat = 0;  ilat < MAXLAT;  ilat++){
      radius[ilat] = (float *)malloc(sizeof(float)*MAXLNG) ;
    smradius[ilat] = (float *)malloc(sizeof(float)*MAXLNG) ;
  }
#endif


  /*----- Initialize local variables -----*/
  nx = DSET_NX(anat);   ny = DSET_NY(anat);   nz = DSET_NZ(anat);
  nxy = nx*ny;   nxz = nx*nz;   nyz = ny*nz;   nxyz = nxy*nz;
  deltalat = PI / MAXLAT;
  deltalng = 2.0 * PI / MAXLNG;
  nr = nx;  
  if (ny > nr)  nr = ny;
  if (nz > nr)  nr = nz;

    
  
  /*----- Calculate the radius vector -----*/
  for (ilat = 0;  ilat < MAXLAT;  ilat++)
    {
      lat = ilat * deltalat - PI/2;
      slat = sin(lat);
      clat = cos(lat);
      
      for (ilng = 0;  ilng < MAXLNG;  ilng++)
	{
	  lng = ilng * deltalng;
	  clng = cos(lng);
	  slng = sin(lng);
	  
	  radius[ilat][ilng] = 0.0;
	  for (ir = 0;  ir < nr;  ir++)
	    {
	      ix = cx + ir*clat*clng;
	      jy = cy + ir*clat*slng;
	      kz = cz + ir * slat;

	      if ((ix >= 0) && (ix < nx) && (jy >= 0) && (jy < ny)
		  && (kz >= 0) && (kz < nz))
		{
		  ixyz = ix + jy*nx + kz*nxy;
		  if (! cv[ixyz])  radius[ilat][ilng] = (float) ir; 
		}
	    }
	}
    }
  
  /*----- Smooth the radius vectors -----*/
  for (ilat = 0;  ilat < MAXLAT;  ilat++)
    {
      for (ilng = 0;  ilng < MAXLNG;  ilng++)
	{
	  smradius[ilat][ilng] = 0.0;
	  
	  for (i = -1;  i <= 1;  i++)
	    {
	      jlat = (ilat + i + MAXLAT) % MAXLAT;
	      
	      if (ilat == 0)  jlat = 0;
	      if (ilat == MAXLAT-1)  jlat = MAXLAT-1;
	      
	      for (j = -2;  j <= 2;  j++)
		{
		  jlng = (ilng + j + MAXLNG) % MAXLNG;
		  smradius[ilat][ilng] += radius[jlat][jlng] / 15.0;
		}
	    }
	}
    }

  /*----- Find maximum radius to a brain voxel -----*/
  maxr = 0.0;
  for (ilat = 0;  ilat < MAXLAT;  ilat++)
    for (ilng = 0;  ilng < MAXLNG;  ilng++)    
      if (maxr < smradius[ilat][ilng])  maxr = smradius[ilat][ilng];
	  
  /*----- Smooth the brain mask -----*/
  for (ix = 0;  ix < nx;  ix++){
    for (jy = 0;  jy < ny;  jy++)
      {
	rxy = hypot ((float) (ix-cx), (float) (jy-cy));
	lng = atan2 (jy-cy,  ix-cx);
	ilng = (int) ((lng/deltalng) + MAXLNG) % MAXLNG;

	for (kz = 0;  kz < nz;  kz++)
	  {
	    ixyz = ix + jy*nx + kz*nxy;

	    r = hypot (rxy, (float) (kz-cz));	  

	    if (r < maxr)
	      {
		lat = atan2 (kz-cz, rxy);
		ilat = (int) (((lat+PI/2)/deltalat) + MAXLAT) % MAXLAT;
		
		if (r < smradius[ilat][ilng])  cv[ixyz] = 0;
	      }
	    
	  }
      }
   }

#ifndef AIZE
  for (ilat = 0;  ilat < MAXLAT;  ilat++){
    free(  radius[ilat]) ; free(smradius[ilat]) ;
  }
  free(smradius); free(radius);
#endif
}


/*---------------------------------------------------------------------------*/
/*
  Segment the intracranial voxels
*/

void segment_volume 
(
  short * cv                    /* volume with 1's at non-brain locations */
)

{
  THD_dataxes * daxes;                  /* dataset axes */
  char xxorient, yyorient, zzorient;    /* dataset axes orientations */ 
  int nxyz;                             /* dataset dimension in voxels */
  int ixyz;                             /* voxel indices */
  int cx, cy, cz;                       /* location of center of mass */


  /*----- Initialize local variables -----*/
  daxes = anat->daxes;
  xxorient = ORIENT_typestr[daxes->xxorient][0];
  yyorient = ORIENT_typestr[daxes->yyorient][0];
  zzorient = ORIENT_typestr[daxes->zzorient][0];
  nxyz = DSET_NX(anat) * DSET_NY(anat) * DSET_NZ(anat);


  /*----- Calculate center of mass of brain image -----*/
  center_of_mass (&cx, &cy, &cz);


  /*----- Initialize mask for non-brain voxels -----*/
  for (ixyz = 0;  ixyz < nxyz;  ixyz++)
    cv[ixyz] = 1;


  /*----- Segment the axial slices -----*/
       if ((xxorient == 'S') || (xxorient == 'I'))  segment_x_slices (cv,cx,1);
  else if ((yyorient == 'S') || (yyorient == 'I'))  segment_y_slices (cv,cy,1);
  else if ((zzorient == 'S') || (zzorient == 'I'))  segment_z_slices (cv,cz,1);
  else SI_error ("Unable to determine dataset orientation");
 

  /*----- Segment the sagittal slices -----*/
       if ((xxorient == 'L') || (xxorient == 'R'))  segment_x_slices (cv,cx,0);
  else if ((yyorient == 'L') || (yyorient == 'R'))  segment_y_slices (cv,cy,0);
  else if ((zzorient == 'L') || (zzorient == 'R'))  segment_z_slices (cv,cz,0);
  else SI_error ("Unable to determine dataset orientation");
  
 
  /*----- Segment the coronal slices -----*/       
       if ((xxorient == 'A') || (xxorient == 'P'))  segment_x_slices (cv,cx,0);
  else if ((yyorient == 'A') || (yyorient == 'P'))  segment_y_slices (cv,cy,0);
  else if ((zzorient == 'A') || (zzorient == 'P'))  segment_z_slices (cv,cz,0);
  else SI_error ("Unable to determine dataset orientation");


  /*----- Create envelope for segmented image -----*/
  if (! nosmooth)
    segment_envelope (cv, cx, cy, cz);
 

  return;
}


/*---------------------------------------------------------------------------*/
/*
  Perform voxel connectivity tests.
*/

int connectivity_tests (short * cv)

{
  int nx, ny, nz, nxy, nxyz;        /* voxel counters */
  int ix, jy, kz, ixyz;             /* voxel indices */
  byte * dv = NULL;                 /* volume for intermediate data */


  /*----- Progress report -----*/
  if (! quiet)  printf ("\nPerforming voxel connectivity tests \n");


  /*----- Initialize local variables -----*/
  nx = DSET_NX(anat);   ny = DSET_NY(anat);   nz = DSET_NZ(anat);
  nxy = nx*ny;   nxyz = nxy*nz;


  /*----- Initialize voxel classification indicators -----*/
  dv = (byte *) malloc (nxyz * sizeof(byte));
  MTEST (dv);   if (dv == NULL)  return (0);
  for (ixyz = 0;  ixyz < nxyz;  ixyz++)  
    dv[ixyz] = 0;


  /*----- Determine which voxels will leave the target structure -----*/
  if (max_conn_int > -1)
    {
      for (ixyz = 0;  ixyz < nxyz;  ixyz++)
	{
	  if (! cv[ixyz])
	    {
	      IJK_TO_THREE (ixyz, ix, jy, kz, nx, nxy);
	      if (ix > 1)     dv[THREE_TO_IJK(ix-1,jy,kz,nx,nxy)]++;
	      if (ix < nx-1)  dv[THREE_TO_IJK(ix+1,jy,kz,nx,nxy)]++;
	      if (jy > 1)     dv[THREE_TO_IJK(ix,jy-1,kz,nx,nxy)]++;
	      if (jy < ny-1)  dv[THREE_TO_IJK(ix,jy+1,kz,nx,nxy)]++;
	      if (kz > 1)     dv[THREE_TO_IJK(ix,jy,kz-1,nx,nxy)]++;
	      if (kz < nz-1)  dv[THREE_TO_IJK(ix,jy,kz+1,nx,nxy)]++;
	    }
	}

      for (ixyz = 0;  ixyz < nxyz;  ixyz++)
	{
	  if (dv[ixyz] <= max_conn_int)  cv[ixyz] = 1;
	  dv[ixyz] = 0;
	}
    }


  /*----- Determine which voxels will enter the target structure -----*/
  if (min_conn_int < 7)
    {
      for (ixyz = 0;  ixyz < nxyz;  ixyz++)
	{
	  if (! cv[ixyz])
	    {
	      IJK_TO_THREE (ixyz, ix, jy, kz, nx, nxy);
	      if (ix > 1)     dv[THREE_TO_IJK(ix-1,jy,kz,nx,nxy)]++;
	      if (ix < nx-1)  dv[THREE_TO_IJK(ix+1,jy,kz,nx,nxy)]++;
	      if (jy > 1)     dv[THREE_TO_IJK(ix,jy-1,kz,nx,nxy)]++;
	      if (jy < ny-1)  dv[THREE_TO_IJK(ix,jy+1,kz,nx,nxy)]++;
	      if (kz > 1)     dv[THREE_TO_IJK(ix,jy,kz-1,nx,nxy)]++;	      
	      if (kz < nz-1)  dv[THREE_TO_IJK(ix,jy,kz+1,nx,nxy)]++;
	    }
	}
      
      for (ixyz = 0;  ixyz < nxyz;  ixyz++)
	if (dv[ixyz] >=  min_conn_int)  cv[ixyz] = 0;
    }


  /*----- Deallocate memory -----*/
  free (dv);   dv = NULL;

  
  return (1);
}


/*---------------------------------------------------------------------------*/
